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 Include="..\..\src\BeastConfig.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\abstract_ostream.hpp">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\amount.hpp">
|
||||
</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>
|
||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\global_suites.hpp">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\match.hpp">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\print.hpp">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\recorder.hpp">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\reporter.hpp">
|
||||
@@ -306,8 +294,6 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\include\beast\core\detail\stream_concepts.hpp">
|
||||
</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>
|
||||
<ClInclude Include="..\..\src\beast\include\beast\core\error.hpp">
|
||||
@@ -352,6 +338,8 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\include\beast\http\body_type.hpp">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\include\beast\http\concepts.hpp">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\include\beast\http\detail\basic_parser_v1.hpp">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\include\beast\http\detail\chunk_encode.hpp">
|
||||
@@ -394,8 +382,6 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\include\beast\http\string_body.hpp">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\include\beast\http\type_check.hpp">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\include\beast\http\write.hpp">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\beast\include\beast\websocket.hpp">
|
||||
@@ -428,6 +414,8 @@
|
||||
</None>
|
||||
<None Include="..\..\src\beast\include\beast\websocket\impl\handshake_op.ipp">
|
||||
</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>
|
||||
<None Include="..\..\src\beast\include\beast\websocket\impl\read_op.ipp">
|
||||
|
||||
@@ -537,24 +537,9 @@
|
||||
<ClInclude Include="..\..\src\BeastConfig.h">
|
||||
<Filter>.</Filter>
|
||||
</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">
|
||||
<Filter>extras\beast\unit_test</Filter>
|
||||
</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">
|
||||
<Filter>extras\beast\unit_test\detail</Filter>
|
||||
</ClInclude>
|
||||
@@ -564,9 +549,6 @@
|
||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\match.hpp">
|
||||
<Filter>extras\beast\unit_test</Filter>
|
||||
</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">
|
||||
<Filter>extras\beast\unit_test</Filter>
|
||||
</ClInclude>
|
||||
@@ -642,9 +624,6 @@
|
||||
<ClInclude Include="..\..\src\beast\include\beast\core\detail\stream_concepts.hpp">
|
||||
<Filter>include\beast\core\detail</Filter>
|
||||
</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">
|
||||
<Filter>include\beast\core\detail</Filter>
|
||||
</ClInclude>
|
||||
@@ -711,6 +690,9 @@
|
||||
<ClInclude Include="..\..\src\beast\include\beast\http\body_type.hpp">
|
||||
<Filter>include\beast\http</Filter>
|
||||
</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">
|
||||
<Filter>include\beast\http\detail</Filter>
|
||||
</ClInclude>
|
||||
@@ -774,9 +756,6 @@
|
||||
<ClInclude Include="..\..\src\beast\include\beast\http\string_body.hpp">
|
||||
<Filter>include\beast\http</Filter>
|
||||
</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">
|
||||
<Filter>include\beast\http</Filter>
|
||||
</ClInclude>
|
||||
@@ -825,6 +804,9 @@
|
||||
<None Include="..\..\src\beast\include\beast\websocket\impl\handshake_op.ipp">
|
||||
<Filter>include\beast\websocket\impl</Filter>
|
||||
</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">
|
||||
<Filter>include\beast\websocket\impl</Filter>
|
||||
</None>
|
||||
|
||||
@@ -34,8 +34,8 @@ packages: &gcc5_pkgs
|
||||
- autotools-dev
|
||||
- libc6-dbg
|
||||
|
||||
packages: &clang36_pkgs
|
||||
- clang-3.6
|
||||
packages: &clang38_pkgs
|
||||
- clang-3.8
|
||||
- g++-5
|
||||
- python-software-properties
|
||||
- libssl-dev
|
||||
@@ -53,56 +53,57 @@ packages: &clang36_pkgs
|
||||
matrix:
|
||||
include:
|
||||
# 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
|
||||
env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=64
|
||||
env: GCC_VER=5 VARIANT=coverage ADDRESS_MODEL=64
|
||||
addons: &ao_gcc5
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: *gcc5_pkgs
|
||||
|
||||
# - compiler: gcc
|
||||
# env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=32
|
||||
# addons: *ao_gcc5
|
||||
# # Clang/Debug
|
||||
# - compiler: clang
|
||||
# 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
|
||||
- compiler: gcc
|
||||
env: GCC_VER=5 VARIANT=release ADDRESS_MODEL=64
|
||||
addons: *ao_gcc5
|
||||
# # Clang/Release
|
||||
# - compiler: clang
|
||||
# env: GCC_VER=5 VARIANT=release CLANG_VER=3.8 ADDRESS_MODEL=64
|
||||
# addons: *ao_clang38
|
||||
|
||||
# # - compiler: gcc
|
||||
# # env: GCC_VER=5 VARIANT=release ADDRESS_MODEL=32
|
||||
# # addons: *ao_gcc5
|
||||
|
||||
# Clang/Debug
|
||||
# Clang/AddressSanitizer
|
||||
- compiler: clang
|
||||
env: GCC_VER=5 VARIANT=debug CLANG_VER=3.6 ADDRESS_MODEL=64
|
||||
addons: &ao_clang36
|
||||
env: GCC_VER=5 VARIANT=asan CLANG_VER=3.8 ADDRESS_MODEL=64
|
||||
addons: &ao_clang38
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
|
||||
packages: *clang36_pkgs
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8']
|
||||
packages: *clang38_pkgs
|
||||
|
||||
# # - compiler: clang
|
||||
# # env: GCC_VER=5 VARIANT=debug CLANG_VER=3.6 ADDRESS_MODEL=32
|
||||
# # addons: *ao_clang36
|
||||
# Clang/MemorySanitizer
|
||||
# VFALCO Generates false positives unless libc++ is compiled with msan turned on
|
||||
#- 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
|
||||
env: GCC_VER=5 VARIANT=release CLANG_VER=3.6 ADDRESS_MODEL=64
|
||||
addons: *ao_clang36
|
||||
|
||||
# # - 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
|
||||
env: GCC_VER=5 VARIANT=usan CLANG_VER=3.8 ADDRESS_MODEL=64
|
||||
addons: *ao_clang38
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
||||
@@ -61,6 +61,22 @@ variant asan
|
||||
<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
|
||||
: requirements
|
||||
<include>.
|
||||
@@ -90,7 +106,7 @@ project beast
|
||||
<os>SOLARIS:<define>__EXTENSIONS__
|
||||
<os>SOLARIS:<library>socket
|
||||
<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>mswsock
|
||||
<os>NT,<toolset>gcc:<library>ws2_32
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
[](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://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
|
||||
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
|
||||
* Give callers control over the http request/response used during handshake
|
||||
* 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:
|
||||
* Define Parser concept in HTTP
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
[template mdash[] '''— ''']
|
||||
[template indexterm1[term1] '''<indexterm><primary>'''[term1]'''</primary></indexterm>''']
|
||||
[template indexterm2[term1 term2] '''<indexterm><primary>'''[term1]'''</primary><secondary>'''[term2]'''</secondary></indexterm>''']
|
||||
[template ticket[number]'''<ulink url="https://svn.boost.org/trac/boost/ticket/'''[number]'''">'''#[number]'''</ulink>''']
|
||||
[def __POSIX__ /POSIX/]
|
||||
[def __Windows__ /Windows/]
|
||||
[def __accept__ [@http://www.opengroup.org/onlinepubs/000095399/functions/accept.html `accept()`]]
|
||||
@@ -191,8 +190,15 @@ supporting its development.
|
||||
[include websocket.qbk]
|
||||
|
||||
[section:types Type Requirements]
|
||||
[include core_types.qbk]
|
||||
[include http_types.qbk]
|
||||
[include types/Body.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]
|
||||
|
||||
[include design.qbk]
|
||||
|
||||
@@ -41,14 +41,19 @@
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||
<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_ReadableBody">is_ReadableBody</link></member>
|
||||
<member><link linkend="beast.ref.http__is_WritableBody">is_WritableBody</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
<entry valign="top">
|
||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||
<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_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__read">read</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.Field">Field</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.Writer">Writer</link></member>
|
||||
</simplelist>
|
||||
@@ -70,16 +76,20 @@
|
||||
<bridgehead renderas="sect3">Classes</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<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__reason_string">reason_string</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Options</bridgehead>
|
||||
<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__decorate">decorate</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_message_max">read_message_max</link></member>
|
||||
<member><link linkend="beast.ref.websocket__write_buffer_size">write_buffer_size</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
<entry valign="top">
|
||||
@@ -91,6 +101,7 @@
|
||||
<bridgehead renderas="sect3">Constants</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<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>
|
||||
</simplelist>
|
||||
</entry>
|
||||
@@ -163,10 +174,10 @@
|
||||
<bridgehead renderas="sect3">Concepts</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.types.BufferSequence">BufferSequence</link></member>
|
||||
<member><link linkend="beast.types.stream.AsyncStream">AsyncStream</link></member>
|
||||
<member><link linkend="beast.types.stream.Stream">Stream</link></member>
|
||||
<member><link linkend="beast.types.streams.AsyncStream">AsyncStream</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.stream.SyncStream">SyncStream</link></member>
|
||||
<member><link linkend="beast.types.streams.SyncStream">SyncStream</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
@@ -1528,7 +1528,7 @@
|
||||
<xsl:text> </xsl:text>
|
||||
<xsl:choose>
|
||||
<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 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>
|
||||
@@ -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:when>
|
||||
<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 test="declname = 'Streambuf' or type = 'class Streambuf'">
|
||||
<xsl:text>class ``[link beast.types.Streambuf [*Streambuf]]``</xsl:text>
|
||||
</xsl:when>
|
||||
<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 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>
|
||||
|
||||
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)
|
||||
]
|
||||
|
||||
|
||||
|
||||
[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]
|
||||
|
||||
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:
|
||||
|
||||
* `X` denotes a class
|
||||
* `X` denotes a class meeting the requirements of [*`Streambuf`]
|
||||
* `a` denotes a value of type `X`
|
||||
* `n` denotes a value convertible to `std::size_t`
|
||||
* `U`, `T` denote unspecified types.
|
||||
@@ -62,12 +40,12 @@ In the table below:
|
||||
[
|
||||
[`X::const_buffers_type`]
|
||||
[`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`]
|
||||
[`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)`]
|
||||
@@ -98,7 +76,7 @@ In the table below:
|
||||
[
|
||||
[`a.max_size()`]
|
||||
[`std::size_t`]
|
||||
[Returns the maximum size of the `Streambuf`.]
|
||||
[Returns the maximum size of the stream buffer.]
|
||||
]
|
||||
[
|
||||
[`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)
|
||||
]
|
||||
|
||||
|
||||
|
||||
[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]
|
||||
|
||||
A `Writer` serializes the message body. The implementation creates an instance
|
||||
@@ -370,4 +177,3 @@ public:
|
||||
```
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -46,13 +46,15 @@ model, handler allocation, and handler invocation hooks. Calls to
|
||||
Beast.WebSocket asynchronous initiation functions allow callers the choice
|
||||
of using a completion handler, stackful or stackless coroutines, futures,
|
||||
or user defined customizations (for example, Boost.Fiber). The
|
||||
implementation uses handler invocation hooks (`asio_handler_invoke`),
|
||||
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
|
||||
identical to Boost.Asio. The implementation also uses handler allocation
|
||||
hooks (`asio_handler_allocate`) when allocating memory internally for
|
||||
composed operations.
|
||||
identical to Boost.Asio. The implementation also uses handler allocation hooks
|
||||
([@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asio_handler_allocate.html `asio_handler_allocate`])
|
||||
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
|
||||
maximum inlining and optimization.
|
||||
|
||||
@@ -67,43 +69,47 @@ both Boost.Asio and the WebSocket protocol specification described in
|
||||
[section:creating Creating the socket]
|
||||
|
||||
The interface to Beast's WebSocket implementation is a single template
|
||||
class [link beast.ref.websocket__stream `websocket::stream`] which wraps a
|
||||
"next layer" object. The next layer object must meet the requirements of
|
||||
`SyncReadStream` and `SyncWriteStream` if synchronous operations are performed,
|
||||
`AsyncReadStream` and `AsyncWriteStream` is asynchronous operations are
|
||||
performed, or both. Arguments supplied during construction are passed to
|
||||
next layer's constructor. Here we declare two websockets which have ownership
|
||||
of the next layer:
|
||||
class [link beast.ref.websocket__stream `beast::websocket::stream`] which
|
||||
wraps a "next layer" object. The next layer object must meet the requirements
|
||||
of [link beast.types.streams.SyncStream [*`SyncReadStream`]] if synchronous
|
||||
operations are performed, or
|
||||
[link beast.types.streams.AsyncStream [*`AsyncStream`]] if asynchronous
|
||||
operations are performed, or both. Arguments supplied during construction are
|
||||
passed to next layer's constructor. Here we declare two websockets which have
|
||||
ownership of the next layer:
|
||||
```
|
||||
io_service ios;
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
boost::asio::io_service ios;
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
||||
|
||||
ssl::context ctx(ssl::context::sslv23);
|
||||
websocket::stream<ssl::stream<ip::tcp::socket>> wss(ios, ctx);
|
||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
|
||||
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
|
||||
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
|
||||
this case, the caller is responsible for managing the lifetime of the
|
||||
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",
|
||||
permitting callers to interact directly with its interface.
|
||||
```
|
||||
ssl::context ctx(ssl::context::sslv23);
|
||||
websocket::stream<ssl::stream<ip::tcp::socket>> ws(ios, ctx);
|
||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
|
||||
beast::websocket::stream<
|
||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws(ios, ctx);
|
||||
...
|
||||
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:
|
||||
```
|
||||
std::string const host = "mywebapp.com";
|
||||
io_service ios;
|
||||
tcp::resolver r(ios);
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
connect(ws.next_layer(), r.resolve(tcp::resolver::query{host, "ws"}));
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r(ios);
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
||||
boost::asio::connect(ws.next_layer(),
|
||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "ws"}));
|
||||
```
|
||||
|
||||
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());
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
indicating that the request was accepted and that the connection has
|
||||
been upgraded. The HTTP Upgrade request must include the Host HTTP field,
|
||||
and the URI of the resource to request. `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.
|
||||
```
|
||||
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.handshake("ws.mywebapp.com:80", "/cgi-bin/bitcoin-prices");
|
||||
ws.set_option(beast::websocket::keep_alive(true));
|
||||
ws.handshake("ws.example.com:80", "/cgi-bin/bitcoin-prices");
|
||||
```
|
||||
|
||||
The `websocket::stream` automatically handles receiving and processing
|
||||
the HTTP response to the handshake request. The call to handshake is
|
||||
successful if a HTTP response is received with the 101 "Switching Protocols"
|
||||
status code. On failure, an error is returned or an exception is thrown.
|
||||
Depending on the keep alive setting, the socket may remain open for a
|
||||
subsequent handshake attempt
|
||||
The [link beast.ref.websocket__stream `beast::websocket::stream`] automatically
|
||||
handles receiving and processing the HTTP response to the handshake request.
|
||||
The call to handshake is successful if a HTTP response is received with the
|
||||
101 "Switching Protocols" status code. On failure, an error is returned or an
|
||||
exception is thrown. Depending on the keep alive setting, the socket may remain
|
||||
open for a subsequent handshake attempt
|
||||
|
||||
Performing a handshake for an incoming websocket upgrade request operates
|
||||
similarly. If the handshake fails, an error is returned or exception thrown:
|
||||
```
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
||||
...
|
||||
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
|
||||
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;
|
||||
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());
|
||||
...
|
||||
}
|
||||
@@ -192,12 +199,12 @@ void do_accept(tcp::socket& sock)
|
||||
Alternatively, the caller can pass an entire HTTP request if it was
|
||||
obtained elsewhere:
|
||||
```
|
||||
void do_accept(tcp::socket& sock)
|
||||
void do_accept(boost::asio::ip::tcp::socket& sock)
|
||||
{
|
||||
boost::asio::streambuf sb;
|
||||
http::request<http::empty_body> request;
|
||||
http::read(sock, request);
|
||||
if(http::is_upgrade(request))
|
||||
beast::http::request<http::empty_body> request;
|
||||
beast::http::read(sock, request);
|
||||
if(beast::http::is_upgrade(request))
|
||||
{
|
||||
websocket::stream<ip::tcp::socket&> ws(sock);
|
||||
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]
|
||||
|
||||
|
||||
@@ -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
|
||||
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;
|
||||
websocket::opcode::value op;
|
||||
beast::streambuf sb;
|
||||
beast::websocket::opcode::value op;
|
||||
ws.read(sb);
|
||||
|
||||
ws.set_option(websocket::message_type(op));
|
||||
websocket::write(ws, sb.data());
|
||||
ws.set_option(beast::websocket::message_type(op));
|
||||
ws.write(sb.data());
|
||||
sb.consume(sb.size());
|
||||
}
|
||||
```
|
||||
|
||||
[important Calls to `set_option` must be made from the same implicit
|
||||
or explicit strand as that used to perform other operations. ]
|
||||
[important Calls to [link beast.ref.websocket__stream.set_option `set_option`]
|
||||
must be made from the same implicit or explicit strand as that used to perform
|
||||
other operations. ]
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -249,18 +255,19 @@ message ahead of time:
|
||||
For these cases, the frame oriented interface may be used. This
|
||||
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;
|
||||
websocket::frame_info fi;
|
||||
beast::streambuf sb;
|
||||
beast::websocket::frame_info fi;
|
||||
for(;;)
|
||||
{
|
||||
ws.read_frame(fi, sb);
|
||||
if(fi.fin)
|
||||
break;
|
||||
}
|
||||
ws.set_option(websocket::message_type(fi.op));
|
||||
consuming_buffers<streambuf::const_buffers_type> cb(sb.data());
|
||||
ws.set_option(beast::websocket::message_type(fi.op));
|
||||
beast::consuming_buffers<
|
||||
beast::streambuf::const_buffers_type> cb(sb.data());
|
||||
for(;;)
|
||||
{
|
||||
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
|
||||
WebSocket control frames such as ping, pong, and close. Pings are replied
|
||||
to as soon as possible, pongs are noted. The receipt of a close frame
|
||||
initiates the WebSocket close procedure, eventually resulting in the
|
||||
error code `websocket::error::closed` being delivered to the caller in
|
||||
a subsequent read operation, assuming no other error takes place.
|
||||
to as soon as possible, pongs are delivered to the pong callback. The receipt
|
||||
of a close frame initiates the WebSocket close procedure, eventually resulting
|
||||
in the error code [link beast.ref.websocket__error `error::closed`] being
|
||||
delivered to the caller in a subsequent read operation, assuming no other error
|
||||
takes place.
|
||||
|
||||
To ensure timely delivery of control frames, large messages are broken up
|
||||
into smaller sized frames. The implementation chooses the size and number
|
||||
@@ -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:
|
||||
```
|
||||
...
|
||||
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
|
||||
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
|
||||
`websocket::stream::close`:
|
||||
[link beast.ref.websocket__stream.close `close`]:
|
||||
```
|
||||
ws.close();
|
||||
```
|
||||
|
||||
[note To receive the `websocket::error::closed` error, a read operation
|
||||
is required. ]
|
||||
[note To receive the [link beast.ref.websocket__error `error::closed`]
|
||||
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]
|
||||
|
||||
@@ -320,7 +358,8 @@ is required. ]
|
||||
|
||||
Because calls to read data may return a variable amount of bytes, the
|
||||
interface to calls that read data require an object that meets the requirements
|
||||
of `Streambuf`. This concept is modeled on `boost::asio::basic_streambuf`.
|
||||
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
|
||||
desired, these features should be provided by callers. The impact of this
|
||||
@@ -331,6 +370,7 @@ of the underlying TCP/IP connection.
|
||||
[endsect]
|
||||
|
||||
|
||||
|
||||
[section:async Asynchronous interface]
|
||||
|
||||
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 underlying stream is left to the callers, permitting any implementation
|
||||
strategy including one that does not require threads for environments where
|
||||
threads are unavailable. Beast.WSProto itself does not use or require threads.
|
||||
The creation and operation of the
|
||||
[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/io_service.html `boost::asio::io_service`]
|
||||
associated with the underlying stream is left to the callers, permitting any
|
||||
implementation strategy including one that does not require threads for
|
||||
environments where threads are unavailable. Beast.WebSocket itself does not
|
||||
use or require threads.
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -374,13 +416,13 @@ threads are unavailable. Beast.WSProto itself does not use or require threads.
|
||||
|
||||
[section:safety Thread Safety]
|
||||
|
||||
Like a regular asio socket, a `websocket::stream` is not thread safe. Callers
|
||||
are responsible for synchronizing operations on the socket using an implicit
|
||||
or explicit strand, as per the Asio documentation. The asynchronous interface
|
||||
supports one active read and one active write simultaneously. Undefined
|
||||
behavior results if two or more reads or two or more writes are attempted
|
||||
concurrently. Caller initiated WebSocket ping, pong, and close operations
|
||||
each count as an active write.
|
||||
Like a regular asio socket, a [link beast.ref.websocket__stream `stream`] is
|
||||
not thread safe. Callers are responsible for synchronizing operations on the
|
||||
socket using an implicit or explicit strand, as per the Asio documentation.
|
||||
The asynchronous interface supports one active read and one active write
|
||||
simultaneously. Undefined behavior results if two or more reads or two or
|
||||
more writes are attempted concurrently. Caller initiated WebSocket ping, pong,
|
||||
and close operations each count as an active write.
|
||||
|
||||
The implementation uses composed asynchronous operations internally; a high
|
||||
level read can cause both reads and writes to take place on the underlying
|
||||
|
||||
@@ -157,24 +157,34 @@ public:
|
||||
return next_layer_.async_write_some(buffers,
|
||||
std::forward<WriteHandler>(handler));
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
teardown(fail_stream<NextLayer>& stream,
|
||||
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 NextLayer, class TeardownHandler>
|
||||
void
|
||||
async_teardown(fail_stream<NextLayer>& stream,
|
||||
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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // test
|
||||
} // 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 {
|
||||
|
||||
template <class = void>
|
||||
/// Holds test suites registered during static initialization.
|
||||
inline
|
||||
suite_list&
|
||||
global_suites()
|
||||
{
|
||||
@@ -23,26 +24,20 @@ global_suites()
|
||||
return s;
|
||||
}
|
||||
|
||||
template <class Suite>
|
||||
template<class Suite>
|
||||
struct insert_suite
|
||||
{
|
||||
template <class = void>
|
||||
insert_suite (char const* name, char const* module,
|
||||
char const* library, bool 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> (
|
||||
insert_suite(char const* name, char const* module,
|
||||
char const* library, bool manual)
|
||||
{
|
||||
global_suites().insert<Suite>(
|
||||
name, module, library, manual);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/** Holds suites registered during static initialization. */
|
||||
/// Holds test suites registered during static initialization.
|
||||
inline
|
||||
suite_list const&
|
||||
global_suites()
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
//
|
||||
|
||||
#include <beast/unit_test/amount.hpp>
|
||||
#include <beast/unit_test/dstream.hpp>
|
||||
#include <beast/unit_test/global_suites.hpp>
|
||||
#include <beast/unit_test/match.hpp>
|
||||
#include <beast/unit_test/reporter.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <beast/unit_test/debug_ostream.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
@@ -25,11 +26,10 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace beast {
|
||||
namespace unit_test {
|
||||
|
||||
static
|
||||
std::string
|
||||
prefix(suite_info const& s)
|
||||
{
|
||||
@@ -38,32 +38,34 @@ prefix(suite_info const& s)
|
||||
return " ";
|
||||
}
|
||||
|
||||
template<class Log>
|
||||
static
|
||||
void
|
||||
print(Log& log, suite_list const& c)
|
||||
print(std::ostream& os, suite_list const& c)
|
||||
{
|
||||
std::size_t manual = 0;
|
||||
for(auto const& s : c)
|
||||
{
|
||||
log <<
|
||||
prefix (s) <<
|
||||
s.full_name();
|
||||
os << prefix (s) << s.full_name() << '\n';
|
||||
if(s.manual())
|
||||
++manual;
|
||||
}
|
||||
log <<
|
||||
os <<
|
||||
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
|
||||
print(Log& log)
|
||||
print(std::ostream& os)
|
||||
{
|
||||
log << "------------------------------------------";
|
||||
print(log, global_suites());
|
||||
log << "------------------------------------------";
|
||||
os << "------------------------------------------\n";
|
||||
print(os, global_suites());
|
||||
os << "------------------------------------------" <<
|
||||
std::endl;
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
@@ -97,11 +99,11 @@ int main(int ac, char const* av[])
|
||||
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||
po::notify(vm);
|
||||
|
||||
beast::debug_ostream log;
|
||||
dstream log;
|
||||
|
||||
if(vm.count("help"))
|
||||
{
|
||||
log << desc;
|
||||
log << desc << std::endl;
|
||||
}
|
||||
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/recorder.hpp>
|
||||
#include <beast/unit_test/abstract_ostream.hpp>
|
||||
#include <beast/unit_test/basic_std_ostream.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
@@ -30,7 +28,7 @@ namespace detail {
|
||||
/** A simple test runner that writes everything to a stream in real time.
|
||||
The totals are output when the object is destroyed.
|
||||
*/
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
class reporter : public runner
|
||||
{
|
||||
private:
|
||||
@@ -42,7 +40,11 @@ private:
|
||||
std::size_t total = 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
|
||||
@@ -51,14 +53,16 @@ private:
|
||||
std::size_t cases = 0;
|
||||
std::size_t total = 0;
|
||||
std::size_t failed = 0;
|
||||
typename clock_type::time_point start =
|
||||
clock_type::now();
|
||||
typename clock_type::time_point start = clock_type::now();
|
||||
|
||||
explicit
|
||||
suite_results (std::string const& name_ = "");
|
||||
suite_results(std::string const& name_ = "")
|
||||
: name(std::move(name_))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
add (case_results const& r);
|
||||
add(case_results const& r);
|
||||
};
|
||||
|
||||
struct results
|
||||
@@ -76,35 +80,30 @@ private:
|
||||
std::size_t total = 0;
|
||||
std::size_t failed = 0;
|
||||
std::vector<run_time> top;
|
||||
typename clock_type::time_point start =
|
||||
clock_type::now();
|
||||
typename clock_type::time_point start = clock_type::now();
|
||||
|
||||
void
|
||||
add (suite_results const& r);
|
||||
};
|
||||
|
||||
boost::optional <std_ostream> std_ostream_;
|
||||
std::reference_wrapper <beast::abstract_ostream> stream_;
|
||||
std::ostream& os_;
|
||||
results results_;
|
||||
suite_results suite_results_;
|
||||
case_results case_results_;
|
||||
|
||||
public:
|
||||
reporter (reporter const&) = delete;
|
||||
reporter& operator= (reporter const&) = delete;
|
||||
reporter(reporter const&) = delete;
|
||||
reporter& operator=(reporter const&) = delete;
|
||||
|
||||
~reporter();
|
||||
|
||||
explicit
|
||||
reporter (std::ostream& stream = std::cout);
|
||||
|
||||
explicit
|
||||
reporter (beast::abstract_ostream& stream);
|
||||
reporter(std::ostream& os = std::cout);
|
||||
|
||||
private:
|
||||
static
|
||||
std::string
|
||||
fmtdur (typename clock_type::duration const& d);
|
||||
fmtdur(typename clock_type::duration const& d);
|
||||
|
||||
virtual
|
||||
void
|
||||
@@ -137,42 +136,27 @@ private:
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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 _>
|
||||
template<class _>
|
||||
void
|
||||
reporter<_>::suite_results::add (case_results const& r)
|
||||
reporter<_>::
|
||||
suite_results::add(case_results const& r)
|
||||
{
|
||||
++cases;
|
||||
total += r.total;
|
||||
failed += r.failed;
|
||||
}
|
||||
|
||||
template <class _>
|
||||
template<class _>
|
||||
void
|
||||
reporter<_>::results::add (
|
||||
suite_results const& r)
|
||||
reporter<_>::
|
||||
results::add(suite_results const& r)
|
||||
{
|
||||
++suites;
|
||||
total += r.total;
|
||||
cases += r.cases;
|
||||
failed += r.failed;
|
||||
|
||||
auto const elapsed =
|
||||
clock_type::now() - r.start;
|
||||
if (elapsed >= std::chrono::seconds(1))
|
||||
auto const elapsed = clock_type::now() - r.start;
|
||||
if (elapsed >= std::chrono::seconds{1})
|
||||
{
|
||||
auto const iter = std::lower_bound(top.begin(),
|
||||
top.end(), elapsed,
|
||||
@@ -196,50 +180,40 @@ reporter<_>::results::add (
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class _>
|
||||
reporter<_>::reporter (
|
||||
std::ostream& stream)
|
||||
: std_ostream_ (std::ref (stream))
|
||||
, stream_ (*std_ostream_)
|
||||
template<class _>
|
||||
reporter<_>::
|
||||
reporter(std::ostream& os)
|
||||
: os_(os)
|
||||
{
|
||||
}
|
||||
|
||||
template <class _>
|
||||
template<class _>
|
||||
reporter<_>::~reporter()
|
||||
{
|
||||
if (results_.top.size() > 0)
|
||||
if(results_.top.size() > 0)
|
||||
{
|
||||
stream_.get() << "Longest suite times:";
|
||||
for (auto const& i : results_.top)
|
||||
stream_.get() << std::setw(8) <<
|
||||
fmtdur(i.second) << " " << i.first;
|
||||
os_ << "Longest suite times:\n";
|
||||
for(auto const& i : results_.top)
|
||||
os_ << std::setw(8) <<
|
||||
fmtdur(i.second) << " " << i.first << '\n';
|
||||
}
|
||||
auto const elapsed =
|
||||
clock_type::now() - results_.start;
|
||||
stream_.get() <<
|
||||
auto const elapsed = clock_type::now() - results_.start;
|
||||
os_ <<
|
||||
fmtdur(elapsed) << ", " <<
|
||||
amount (results_.suites, "suite") << ", " <<
|
||||
amount (results_.cases, "case") << ", " <<
|
||||
amount (results_.total, "test") << " total, " <<
|
||||
amount (results_.failed, "failure");
|
||||
amount{results_.suites, "suite"} << ", " <<
|
||||
amount{results_.cases, "case"} << ", " <<
|
||||
amount{results_.total, "test"} << " total, " <<
|
||||
amount{results_.failed, "failure"} <<
|
||||
std::endl;
|
||||
}
|
||||
|
||||
template <class _>
|
||||
reporter<_>::reporter (
|
||||
abstract_ostream& stream)
|
||||
: stream_ (stream)
|
||||
{
|
||||
}
|
||||
|
||||
template <class _>
|
||||
template<class _>
|
||||
std::string
|
||||
reporter<_>::fmtdur (
|
||||
typename clock_type::duration const& d)
|
||||
reporter<_>::fmtdur(typename clock_type::duration const& d)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto const ms =
|
||||
duration_cast<milliseconds>(d);
|
||||
if (ms < seconds(1))
|
||||
auto const ms = duration_cast<milliseconds>(d);
|
||||
if (ms < seconds{1})
|
||||
return std::to_string(ms.count()) + "ms";
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(1) <<
|
||||
@@ -247,67 +221,68 @@ reporter<_>::fmtdur (
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <class _>
|
||||
template<class _>
|
||||
void
|
||||
reporter<_>::on_suite_begin (
|
||||
suite_info const& info)
|
||||
reporter<_>::
|
||||
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
|
||||
reporter<_>::on_suite_end()
|
||||
{
|
||||
results_.add (suite_results_);
|
||||
results_.add(suite_results_);
|
||||
}
|
||||
|
||||
template <class _>
|
||||
template<class _>
|
||||
void
|
||||
reporter<_>::on_case_begin (
|
||||
std::string const& name)
|
||||
reporter<_>::
|
||||
on_case_begin(std::string const& name)
|
||||
{
|
||||
case_results_ = case_results (name);
|
||||
|
||||
stream_.get() <<
|
||||
os_ <<
|
||||
suite_results_.name <<
|
||||
(case_results_.name.empty() ?
|
||||
"" : (" " + case_results_.name));
|
||||
"" : (" " + case_results_.name)) << std::endl;
|
||||
}
|
||||
|
||||
template <class _>
|
||||
template<class _>
|
||||
void
|
||||
reporter<_>::on_case_end()
|
||||
reporter<_>::
|
||||
on_case_end()
|
||||
{
|
||||
suite_results_.add (case_results_);
|
||||
suite_results_.add(case_results_);
|
||||
}
|
||||
|
||||
template <class _>
|
||||
template<class _>
|
||||
void
|
||||
reporter<_>::on_pass()
|
||||
reporter<_>::
|
||||
on_pass()
|
||||
{
|
||||
++case_results_.total;
|
||||
}
|
||||
|
||||
template <class _>
|
||||
template<class _>
|
||||
void
|
||||
reporter<_>::on_fail (
|
||||
std::string const& reason)
|
||||
reporter<_>::
|
||||
on_fail(std::string const& reason)
|
||||
{
|
||||
++case_results_.failed;
|
||||
++case_results_.total;
|
||||
stream_.get() <<
|
||||
"#" << case_results_.total <<
|
||||
" failed" <<
|
||||
(reason.empty() ? "" : ": ") << reason;
|
||||
os_ <<
|
||||
"#" << case_results_.total << " failed" <<
|
||||
(reason.empty() ? "" : ": ") << reason << std::endl;
|
||||
}
|
||||
|
||||
template <class _>
|
||||
template<class _>
|
||||
void
|
||||
reporter<_>::on_log (
|
||||
std::string const& s)
|
||||
reporter<_>::
|
||||
on_log(std::string const& s)
|
||||
{
|
||||
stream_.get() << s;
|
||||
os_ << s;
|
||||
os_.flush();
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#define BEAST_UNIT_TEST_RUNNER_H_INCLUDED
|
||||
|
||||
#include <beast/unit_test/suite_info.hpp>
|
||||
#include <beast/unit_test/abstract_ostream.hpp>
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
@@ -23,28 +23,6 @@ namespace unit_test {
|
||||
*/
|
||||
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_;
|
||||
bool default_ = false;
|
||||
bool failed_ = false;
|
||||
@@ -52,21 +30,20 @@ private:
|
||||
std::recursive_mutex mutex_;
|
||||
|
||||
public:
|
||||
runner() = default;
|
||||
virtual ~runner() = default;
|
||||
runner (runner const&) = default;
|
||||
runner& operator= (runner const&) = default;
|
||||
|
||||
template <class = void>
|
||||
runner();
|
||||
runner(runner const&) = delete;
|
||||
runner& operator=(runner const&) = delete;
|
||||
|
||||
/** Set the argument string.
|
||||
|
||||
The argument string is available to suites and
|
||||
allows for customization of the test. Each suite
|
||||
defines its own syntax for the argumnet string.
|
||||
The same argument is passed to all suites.
|
||||
*/
|
||||
void
|
||||
arg (std::string const& s)
|
||||
arg(std::string const& s)
|
||||
{
|
||||
arg_ = s;
|
||||
}
|
||||
@@ -81,7 +58,7 @@ public:
|
||||
/** Run the specified suite.
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
bool
|
||||
run (suite_info const& s);
|
||||
|
||||
@@ -124,69 +101,63 @@ public:
|
||||
bool
|
||||
run_each_if (SequenceContainer const& c, Pred pred = Pred{});
|
||||
|
||||
private:
|
||||
protected:
|
||||
//
|
||||
// Overrides
|
||||
//
|
||||
|
||||
/** Called when a new suite starts. */
|
||||
/// Called when a new suite starts.
|
||||
virtual
|
||||
void
|
||||
on_suite_begin (suite_info const&)
|
||||
on_suite_begin(suite_info const&)
|
||||
{
|
||||
}
|
||||
|
||||
/** Called when a suite ends. */
|
||||
/// Called when a suite ends.
|
||||
virtual
|
||||
void
|
||||
on_suite_end()
|
||||
{
|
||||
}
|
||||
|
||||
/** Called when a new case starts. */
|
||||
/// Called when a new case starts.
|
||||
virtual
|
||||
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
|
||||
void
|
||||
on_case_end()
|
||||
{
|
||||
}
|
||||
|
||||
/** Called for each passing condition. */
|
||||
/// Called for each passing condition.
|
||||
virtual
|
||||
void
|
||||
on_pass ()
|
||||
on_pass()
|
||||
{
|
||||
}
|
||||
|
||||
/** Called for each failing condition. */
|
||||
/// Called for each failing condition.
|
||||
virtual
|
||||
void
|
||||
on_fail (std::string const&)
|
||||
on_fail(std::string const&)
|
||||
{
|
||||
}
|
||||
|
||||
/** Called when a test logs output. */
|
||||
/// Called when a test logs output.
|
||||
virtual
|
||||
void
|
||||
on_log (std::string const&)
|
||||
on_log(std::string const&)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend class suite;
|
||||
|
||||
abstract_ostream&
|
||||
stream()
|
||||
{
|
||||
return stream_;
|
||||
}
|
||||
|
||||
// Start a new testcase.
|
||||
template <class = 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>
|
||||
bool
|
||||
runner::run (suite_info const& s)
|
||||
|
||||
@@ -9,15 +9,23 @@
|
||||
#define BEAST_UNIT_TEST_SUITE_HPP
|
||||
|
||||
#include <beast/unit_test/runner.hpp>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace unit_test {
|
||||
|
||||
class thread;
|
||||
|
||||
enum abort_t
|
||||
{
|
||||
no_abort_on_fail,
|
||||
abort_on_fail
|
||||
};
|
||||
|
||||
/** A testsuite class.
|
||||
|
||||
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,
|
||||
derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a
|
||||
@@ -25,13 +33,6 @@ class thread;
|
||||
*/
|
||||
class suite
|
||||
{
|
||||
public:
|
||||
enum abort_t
|
||||
{
|
||||
no_abort_on_fail,
|
||||
abort_on_fail
|
||||
};
|
||||
|
||||
private:
|
||||
bool abort_ = false;
|
||||
bool aborted_ = false;
|
||||
@@ -44,95 +45,100 @@ private:
|
||||
char const*
|
||||
what() const noexcept override
|
||||
{
|
||||
return "suite aborted";
|
||||
return "test suite aborted";
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
// Memberspace
|
||||
class log_t
|
||||
template<class CharT, class Traits, class Allocator>
|
||||
class log_buf
|
||||
: public std::basic_stringbuf<CharT, Traits, Allocator>
|
||||
{
|
||||
private:
|
||||
friend class suite;
|
||||
suite* suite_ = nullptr;
|
||||
suite& suite_;
|
||||
|
||||
public:
|
||||
log_t () = default;
|
||||
explicit
|
||||
log_buf(suite& self)
|
||||
: suite_(self)
|
||||
{
|
||||
}
|
||||
|
||||
template <class T>
|
||||
abstract_ostream::scoped_stream_type
|
||||
operator<< (T const& t);
|
||||
~log_buf()
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
/** Returns the raw stream used for output. */
|
||||
abstract_ostream&
|
||||
stream();
|
||||
int
|
||||
sync() override
|
||||
{
|
||||
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;
|
||||
|
||||
// Memberspace
|
||||
class testcase_t
|
||||
{
|
||||
private:
|
||||
friend class suite;
|
||||
suite* suite_ = nullptr;
|
||||
suite& suite_;
|
||||
std::stringstream ss_;
|
||||
|
||||
public:
|
||||
testcase_t() = default;
|
||||
explicit
|
||||
testcase_t(suite& self)
|
||||
: suite_(self)
|
||||
{
|
||||
}
|
||||
|
||||
/** 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
|
||||
opened testcase. When the test first runs, a default unnamed
|
||||
case is opened. Tests with only one case may omit the call
|
||||
to testcase.
|
||||
@param abort If `true`, the suite will be stopped on first failure.
|
||||
|
||||
A testcase is a series of evaluated test conditions. A test
|
||||
suite may have multiple test cases. A test is associated with
|
||||
the last opened testcase. When the test first runs, a default
|
||||
unnamed case is opened. Tests with only one case may omit the
|
||||
call to testcase.
|
||||
|
||||
@param abort Determines if suite continues running after a failure.
|
||||
*/
|
||||
void
|
||||
operator() (std::string const& name,
|
||||
operator()(std::string const& name,
|
||||
abort_t abort = no_abort_on_fail);
|
||||
|
||||
/** Stream style composition of testcase names. */
|
||||
/** @{ */
|
||||
scoped_testcase
|
||||
operator() (abort_t abort);
|
||||
operator()(abort_t abort);
|
||||
|
||||
template <class T>
|
||||
scoped_testcase
|
||||
operator<< (T const& t);
|
||||
/** @} */
|
||||
operator<<(T const& t);
|
||||
};
|
||||
|
||||
public:
|
||||
/** Type for scoped stream logging.
|
||||
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();
|
||||
/** Logging output stream.
|
||||
|
||||
@code
|
||||
|
||||
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
|
||||
Text sent to the log output stream will be forwarded to
|
||||
the output stream associated with the runner.
|
||||
*/
|
||||
using scoped_stream = abstract_ostream::scoped_stream_type;
|
||||
|
||||
/** Memberspace for logging. */
|
||||
log_t log;
|
||||
log_os<char> log;
|
||||
|
||||
/** Memberspace for declaring test cases. */
|
||||
testcase_t testcase;
|
||||
@@ -145,6 +151,12 @@ public:
|
||||
return *p_this_suite();
|
||||
}
|
||||
|
||||
suite()
|
||||
: log(*this)
|
||||
, testcase(*this)
|
||||
{
|
||||
}
|
||||
|
||||
/** Invokes the test using the specified runner.
|
||||
Data members are set up here instead of the constructor as a
|
||||
convenience to writing the derived class to avoid repetition of
|
||||
@@ -272,83 +284,50 @@ 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
|
||||
class suite::scoped_testcase
|
||||
{
|
||||
private:
|
||||
suite* suite_;
|
||||
std::stringstream* ss_;
|
||||
suite& suite_;
|
||||
std::stringstream& ss_;
|
||||
|
||||
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* s, std::stringstream* ss, T const& t);
|
||||
scoped_testcase(suite& self, std::stringstream& ss)
|
||||
: 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&
|
||||
operator<< (T const& t);
|
||||
};
|
||||
|
||||
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;
|
||||
operator<<(T const& t)
|
||||
{
|
||||
ss_ << t;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -357,16 +336,16 @@ void
|
||||
suite::testcase_t::operator() (std::string const& name,
|
||||
abort_t abort)
|
||||
{
|
||||
suite_->abort_ = abort == abort_on_fail;
|
||||
suite_->runner_->testcase (name);
|
||||
suite_.abort_ = abort == abort_on_fail;
|
||||
suite_.runner_->testcase (name);
|
||||
}
|
||||
|
||||
inline
|
||||
suite::scoped_testcase
|
||||
suite::testcase_t::operator() (abort_t abort)
|
||||
{
|
||||
suite_->abort_ = abort == abort_on_fail;
|
||||
return { suite_, &ss_ };
|
||||
suite_.abort_ = abort == abort_on_fail;
|
||||
return { suite_, ss_ };
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@@ -374,7 +353,7 @@ inline
|
||||
suite::scoped_testcase
|
||||
suite::testcase_t::operator<< (T const& t)
|
||||
{
|
||||
return { suite_, &ss_, t };
|
||||
return { suite_, ss_, t };
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -511,8 +490,6 @@ void
|
||||
suite::run (runner& r)
|
||||
{
|
||||
runner_ = &r;
|
||||
log.suite_ = this;
|
||||
testcase.suite_ = this;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef BEAST_UNIT_TEST_SUITE_INFO_HPP
|
||||
#define BEAST_UNIT_TEST_SUITE_INFO_HPP
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -20,19 +21,28 @@ class runner;
|
||||
/** Associates a unit test type with metadata. */
|
||||
class suite_info
|
||||
{
|
||||
private:
|
||||
using run_type = std::function <void (runner&)>;
|
||||
using run_type = std::function<void(runner&)>;
|
||||
|
||||
std::string name_;
|
||||
std::string module_;
|
||||
std::string library_;
|
||||
bool m_manual;
|
||||
run_type m_run;
|
||||
bool manual_;
|
||||
run_type run_;
|
||||
|
||||
public:
|
||||
template <class = void>
|
||||
suite_info (std::string const& name, std::string const& module,
|
||||
std::string const& library, bool manual, run_type run);
|
||||
suite_info(
|
||||
std::string name,
|
||||
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&
|
||||
name() const
|
||||
@@ -52,61 +62,58 @@ public:
|
||||
return library_;
|
||||
}
|
||||
|
||||
/** Returns `true` if this suite only runs manually. */
|
||||
/// Returns `true` if this suite only runs manually.
|
||||
bool
|
||||
manual() const
|
||||
{
|
||||
return m_manual;
|
||||
return manual_;
|
||||
}
|
||||
|
||||
/** Return the canonical suite name as a string. */
|
||||
template <class = void>
|
||||
/// Return the canonical suite name as a string.
|
||||
std::string
|
||||
full_name() const;
|
||||
|
||||
/** Run a new instance of the associated test suite. */
|
||||
void
|
||||
run (runner& r) const
|
||||
full_name() 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>
|
||||
suite_info::suite_info (std::string const& name, std::string const& module,
|
||||
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>
|
||||
/// Convenience for producing suite_info for a given test type.
|
||||
template<class Suite>
|
||||
suite_info
|
||||
make_suite_info (std::string const& name, std::string const& module,
|
||||
std::string const& library, bool manual)
|
||||
make_suite_info(
|
||||
std::string name,
|
||||
std::string module,
|
||||
std::string library,
|
||||
bool manual)
|
||||
{
|
||||
return suite_info(name, module, library, manual,
|
||||
[](runner& r) { return Suite{}(r); });
|
||||
return suite_info(
|
||||
std::move(name),
|
||||
std::move(module),
|
||||
std::move(library),
|
||||
manual,
|
||||
[](runner& r)
|
||||
{
|
||||
Suite{}(r);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
|
||||
@@ -18,23 +18,27 @@
|
||||
namespace beast {
|
||||
namespace unit_test {
|
||||
|
||||
/** A container of test suites. */
|
||||
/// A container of test suites.
|
||||
class suite_list
|
||||
: public detail::const_container <std::set <suite_info>>
|
||||
{
|
||||
private:
|
||||
#ifndef NDEBUG
|
||||
std::unordered_set <std::string> names_;
|
||||
std::unordered_set <std::type_index> classes_;
|
||||
std::unordered_set<std::string> names_;
|
||||
std::unordered_set<std::type_index> classes_;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/** Insert a suite into the set.
|
||||
|
||||
The suite must not already exist.
|
||||
*/
|
||||
template <class Suite>
|
||||
void
|
||||
insert (char const* name, char const* module, char const* library,
|
||||
insert(
|
||||
char const* name,
|
||||
char const* module,
|
||||
char const* library,
|
||||
bool manual);
|
||||
};
|
||||
|
||||
@@ -42,7 +46,10 @@ public:
|
||||
|
||||
template <class Suite>
|
||||
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)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
@@ -59,9 +66,8 @@ suite_list::insert (char const* name, char const* module, char const* library,
|
||||
assert (result.second); // Duplicate type
|
||||
}
|
||||
#endif
|
||||
|
||||
cont().emplace (std::move (make_suite_info <Suite> (
|
||||
name, module, library, manual)));
|
||||
cont().emplace(make_suite_info<Suite>(
|
||||
name, module, library, manual));
|
||||
}
|
||||
|
||||
} // 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:
|
||||
#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
|
||||
{
|
||||
writer(writer const&) = delete;
|
||||
|
||||
@@ -463,8 +463,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
|
||||
case s_header_field:
|
||||
{
|
||||
for(; p != end; ch = *++p)
|
||||
for(; p != end; ++p)
|
||||
{
|
||||
ch = *p;
|
||||
auto c = to_field_char(ch);
|
||||
if(! c)
|
||||
break;
|
||||
@@ -660,8 +661,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
|
||||
case s_header_value_text:
|
||||
{
|
||||
for(; p != end; ch = *++p)
|
||||
for(; p != end; ++p)
|
||||
{
|
||||
ch = *p;
|
||||
if(ch == '\r')
|
||||
{
|
||||
if(cb(nullptr))
|
||||
|
||||
@@ -105,33 +105,6 @@ prepare_content_length(prepare_info& pi,
|
||||
|
||||
} // 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<
|
||||
bool isRequest, class Body, class Headers,
|
||||
class... Options>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#ifndef 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/type_check.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/handler_alloc.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
@@ -323,6 +323,12 @@ void
|
||||
parse(SyncReadStream& stream,
|
||||
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;
|
||||
parse(stream, streambuf, parser, ec);
|
||||
if(ec)
|
||||
@@ -403,6 +409,8 @@ read(SyncReadStream& stream, Streambuf& streambuf,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
static_assert(is_ReadableBody<Body>::value,
|
||||
"ReadableBody requirements not met");
|
||||
error_code ec;
|
||||
read(stream, streambuf, msg, ec);
|
||||
if(ec)
|
||||
@@ -420,6 +428,8 @@ read(SyncReadStream& stream, Streambuf& streambuf,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
static_assert(is_ReadableBody<Body>::value,
|
||||
"ReadableBody requirements not met");
|
||||
parser_v1<isRequest, Body, Headers> p;
|
||||
parse(stream, streambuf, p, ec);
|
||||
if(ec)
|
||||
@@ -441,6 +451,8 @@ async_read(AsyncReadStream& stream, Streambuf& streambuf,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
static_assert(is_ReadableBody<Body>::value,
|
||||
"ReadableBody requirements not met");
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code)> completion(handler);
|
||||
detail::read_op<AsyncReadStream, Streambuf,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef 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/detail/chunk_encode.hpp>
|
||||
#include <beast/http/detail/has_content_length.hpp>
|
||||
@@ -448,6 +449,8 @@ write(SyncWriteStream& stream,
|
||||
{
|
||||
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_WritableBody<Body>::value,
|
||||
"WritableBody requirements not met");
|
||||
error_code ec;
|
||||
write(stream, msg, ec);
|
||||
if(ec)
|
||||
@@ -463,6 +466,8 @@ write(SyncWriteStream& stream,
|
||||
{
|
||||
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_WritableBody<Body>::value,
|
||||
"WritableBody requirements not met");
|
||||
detail::write_preparation<isRequest, Body, Headers> wp(msg);
|
||||
wp.init(ec);
|
||||
if(ec)
|
||||
@@ -546,6 +551,8 @@ async_write(AsyncWriteStream& stream,
|
||||
static_assert(
|
||||
is_AsyncWriteStream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_WritableBody<Body>::value,
|
||||
"WritableBody requirements not met");
|
||||
beast::async_completion<WriteHandler,
|
||||
void(error_code)> completion(handler);
|
||||
detail::write_op<AsyncWriteStream, decltype(completion.handler),
|
||||
@@ -607,6 +614,8 @@ std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
message_v1<isRequest, Body, Headers> const& msg)
|
||||
{
|
||||
static_assert(is_WritableBody<Body>::value,
|
||||
"WritableBody requirements not met");
|
||||
detail::ostream_SyncStream oss(os);
|
||||
error_code ec;
|
||||
write(oss, msg, ec);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define BEAST_HTTP_PARSER_V1_HPP
|
||||
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/message_v1.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <functional>
|
||||
@@ -54,6 +55,9 @@ public:
|
||||
message_v1<isRequest, Body, Headers>;
|
||||
|
||||
private:
|
||||
static_assert(is_ReadableBody<Body>::value,
|
||||
"ReadableBody requirements not met");
|
||||
|
||||
std::string field_;
|
||||
std::string value_;
|
||||
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_STRING "1.0.0-b4"
|
||||
#define BEAST_VERSION_STRING "1.0.0-b5"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,6 +23,16 @@ namespace beast {
|
||||
namespace websocket {
|
||||
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
|
||||
struct frame_header
|
||||
{
|
||||
@@ -286,8 +296,7 @@ read_fh2(frame_header& fh, Streambuf& sb,
|
||||
//
|
||||
template<class Buffers>
|
||||
void
|
||||
read(ping_payload_type& data,
|
||||
Buffers const& bs, close_code::value& code)
|
||||
read(ping_data& data, Buffers const& bs)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
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 = std::uint32_t;
|
||||
//using prepared_key_type = std::uint64_t;
|
||||
using prepared_key_type =
|
||||
std::conditional<sizeof(void*) == 8,
|
||||
std::uint64_t, std::uint32_t>::type;
|
||||
|
||||
inline
|
||||
void
|
||||
@@ -93,19 +93,6 @@ prepare_key(std::uint64_t& prepared, std::uint32_t key)
|
||||
template<class T>
|
||||
inline
|
||||
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)
|
||||
{
|
||||
auto constexpr bits =
|
||||
@@ -120,7 +107,7 @@ ror(T t, unsigned n = 1)
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_safe(
|
||||
mask_inplace_general(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint32_t& key)
|
||||
{
|
||||
@@ -151,7 +138,7 @@ mask_inplace_safe(
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_safe(
|
||||
mask_inplace_general(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
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
|
||||
void
|
||||
mask_inplace(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint32_t& key)
|
||||
{
|
||||
mask_inplace_safe(b, key);
|
||||
//mask_inplace_32(b, key);
|
||||
//mask_inplace_x86(b, key);
|
||||
mask_inplace_general(b, key);
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -352,9 +188,7 @@ mask_inplace(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint64_t& key)
|
||||
{
|
||||
mask_inplace_safe(b, key);
|
||||
//mask_inplace_64(b, key);
|
||||
//mask_inplace_amd(b, key);
|
||||
mask_inplace_general(b, key);
|
||||
}
|
||||
|
||||
// Apply mask in place
|
||||
|
||||
@@ -30,15 +30,6 @@ namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
template<class String>
|
||||
inline
|
||||
void
|
||||
maybe_throw(error_code const& ec, String const&)
|
||||
{
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class UInt>
|
||||
static
|
||||
std::size_t
|
||||
@@ -59,6 +50,8 @@ clamp(UInt x, std::size_t limit)
|
||||
return static_cast<std::size_t>(x);
|
||||
}
|
||||
|
||||
using pong_cb = std::function<void(ping_data const&)>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct stream_base
|
||||
@@ -69,42 +62,46 @@ protected:
|
||||
detail::maskgen maskgen_; // source of mask keys
|
||||
decorator_type d_; // adorns http messages
|
||||
bool keep_alive_ = false; // close on failed upgrade
|
||||
role_type role_; // server or client
|
||||
bool error_ = false; // non-zero ec was delivered
|
||||
|
||||
std::size_t rd_msg_max_ =
|
||||
16 * 1024 * 1024; // max message size
|
||||
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::prepared_key_type rd_key_; // prepared masking key
|
||||
detail::utf8_checker rd_utf8_check_;// for current text msg
|
||||
std::uint64_t rd_size_; // size of the current message so far
|
||||
std::uint64_t rd_need_ = 0; // bytes left in msg frame payload
|
||||
opcode rd_opcode_; // opcode of current msg
|
||||
bool rd_cont_ = false; // expecting a continuation frame
|
||||
bool rd_close_ = false; // got close frame
|
||||
op* rd_block_ = nullptr; // op currently reading
|
||||
bool rd_cont_; // expecting a continuation frame
|
||||
|
||||
std::size_t
|
||||
wr_frag_size_ = 16 * 1024; // size of auto-fragments
|
||||
std::size_t wr_buf_size_ = 4096; // write buffer size
|
||||
opcode wr_opcode_ = opcode::text; // outgoing message type
|
||||
bool wr_close_ = false; // sent close frame
|
||||
bool wr_cont_ = false; // next write is continuation frame
|
||||
op* wr_block_ = nullptr; // op currenly writing
|
||||
bool wr_close_; // sent close frame
|
||||
bool wr_cont_; // next write is continuation frame
|
||||
op* wr_block_; // op currenly writing
|
||||
|
||||
ping_data* pong_data_; // where to put pong payload
|
||||
invokable rd_op_; // invoked after write completes
|
||||
invokable wr_op_; // invoked after read completes
|
||||
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()
|
||||
: d_(new decorator<default_decorator>{})
|
||||
{
|
||||
}
|
||||
|
||||
stream_base(stream_base&&) = default;
|
||||
stream_base(stream_base const&) = delete;
|
||||
stream_base& operator=(stream_base&&) = default;
|
||||
stream_base& operator=(stream_base const&) = delete;
|
||||
template<class = void>
|
||||
void
|
||||
open(role_type role);
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
@@ -118,7 +115,7 @@ protected:
|
||||
template<class Streambuf>
|
||||
void
|
||||
write_ping(Streambuf& sb, opcode op,
|
||||
ping_payload_type const& data);
|
||||
ping_data const& data);
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
@@ -33,7 +33,7 @@ class stream<NextLayer>::accept_op
|
||||
struct data
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
http::request_v1<http::empty_body> req;
|
||||
http::request_v1<http::string_body> req;
|
||||
Handler h;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
@@ -48,6 +48,7 @@ class stream<NextLayer>::accept_op
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
ws.reset();
|
||||
ws.stream_.buffer().commit(buffer_copy(
|
||||
ws.stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
@@ -133,8 +134,14 @@ operator()(error_code const& ec,
|
||||
// got message
|
||||
case 1:
|
||||
// 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>{
|
||||
std::move(d.h), d.ws, d.req, true};
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,9 @@ template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::close_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
using fb_type =
|
||||
detail::frame_streambuf;
|
||||
using fmb_type =
|
||||
typename fb_type::mutable_buffers_type;
|
||||
using alloc_type = handler_alloc<char, Handler>;
|
||||
|
||||
using fb_type = detail::frame_streambuf;
|
||||
|
||||
struct data : op
|
||||
{
|
||||
@@ -64,24 +61,19 @@ public:
|
||||
std::forward<DeducedHandler>(h), ws,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = false;
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec)
|
||||
{
|
||||
(*this)(ec, 0);
|
||||
(*this)(error_code{});
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
operator()(error_code ec, std::size_t);
|
||||
|
||||
void
|
||||
operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
@@ -117,12 +109,26 @@ public:
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::close_op<Handler>::operator()(
|
||||
error_code ec, std::size_t bytes_transferred, bool again)
|
||||
stream<NextLayer>::close_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>::close_op<Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
if(ec)
|
||||
goto upcall;
|
||||
for(;;)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
@@ -130,49 +136,52 @@ stream<NextLayer>::close_op<Handler>::operator()(
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 1;
|
||||
d.ws.rd_op_.template emplace<
|
||||
d.state = 2;
|
||||
d.ws.wr_op_.template emplace<
|
||||
close_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
if(d.ws.error_)
|
||||
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, 0));
|
||||
boost::asio::error::operation_aborted));
|
||||
return;
|
||||
}
|
||||
d.state = 2;
|
||||
break;
|
||||
// fall through
|
||||
|
||||
// resume
|
||||
case 1:
|
||||
if(d.ws.error_)
|
||||
{
|
||||
// call handler
|
||||
// send close frame
|
||||
d.state = 99;
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
break;
|
||||
}
|
||||
d.state = 2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// send close
|
||||
d.state = 99;
|
||||
assert(! d.ws.wr_close_);
|
||||
d.ws.wr_close_ = true;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
d.fb.data(), std::move(*this));
|
||||
return;
|
||||
|
||||
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)
|
||||
d.ws.error_ = true;
|
||||
upcall:
|
||||
if(d.ws.wr_block_ == &d)
|
||||
d.ws.wr_block_ = nullptr;
|
||||
d.ws.rd_op_.maybe_invoke();
|
||||
|
||||
@@ -48,6 +48,7 @@ class stream<NextLayer>::handshake_op
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
ws.reset();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -64,16 +65,11 @@ public:
|
||||
std::forward<DeducedHandler>(h), ws,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec)
|
||||
{
|
||||
(*this)(ec, 0);
|
||||
}
|
||||
|
||||
void operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
void
|
||||
operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
@@ -109,9 +105,8 @@ public:
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::handshake_op<
|
||||
Handler>::operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again)
|
||||
stream<NextLayer>::handshake_op<Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
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()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = false;
|
||||
(*this)(error_code{}, 0, false);
|
||||
(*this)(error_code{}, 0, true);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec)
|
||||
{
|
||||
(*this)(ec, 0);
|
||||
(*this)(ec, 0, true);
|
||||
}
|
||||
|
||||
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
|
||||
void* asio_handler_allocate(
|
||||
@@ -129,51 +130,71 @@ 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)
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
auto& d = *d_;
|
||||
if(ec)
|
||||
d.ws.failed_ = true;
|
||||
(*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
|
||||
{
|
||||
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;
|
||||
while(! ec && d.state != 99)
|
||||
do
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.ws.error_)
|
||||
case do_start:
|
||||
if(d.ws.failed_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.state = do_call_handler;
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
boost::asio::error::operation_aborted, 0));
|
||||
return;
|
||||
}
|
||||
if(d.ws.rd_need_ > 0)
|
||||
{
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
d.state = 2;
|
||||
d.state = d.ws.rd_need_ > 0 ?
|
||||
do_read_payload : do_read_fh;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// read payload
|
||||
d.state = 3;
|
||||
//------------------------------------------------------------------
|
||||
|
||||
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 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:
|
||||
case do_read_payload + 1:
|
||||
{
|
||||
d.ws.rd_need_ -= bytes_transferred;
|
||||
auto const pb = prepare_buffers(
|
||||
@@ -187,26 +208,38 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||
! d.ws.rd_utf8_check_.finish()))
|
||||
{
|
||||
// invalid utf8
|
||||
d.state = 16;
|
||||
code = close_code::bad_payload;
|
||||
d.state = do_fail;
|
||||
break;
|
||||
}
|
||||
}
|
||||
d.sb.commit(bytes_transferred);
|
||||
d.state = 4;
|
||||
if(d.ws.rd_need_ > 0)
|
||||
{
|
||||
d.state = do_read_payload;
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
case do_frame_done:
|
||||
// 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;
|
||||
goto upcall;
|
||||
|
||||
// got fixed header
|
||||
case 5:
|
||||
//------------------------------------------------------------------
|
||||
|
||||
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;
|
||||
@@ -215,10 +248,10 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// protocol error
|
||||
d.state = 16;
|
||||
d.state = do_fail;
|
||||
break;
|
||||
}
|
||||
d.state = 6;
|
||||
d.state = do_read_fh + 2;
|
||||
if (n == 0)
|
||||
{
|
||||
bytes_transferred = 0;
|
||||
@@ -230,8 +263,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||
return;
|
||||
}
|
||||
|
||||
// got variable header
|
||||
case 6:
|
||||
case do_read_fh + 2:
|
||||
d.fb.commit(bytes_transferred);
|
||||
code = close_code::none;
|
||||
detail::read_fh2(d.ws.rd_fh_,
|
||||
@@ -241,7 +273,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// protocol error
|
||||
d.state = 16;
|
||||
d.state = do_fail;
|
||||
break;
|
||||
}
|
||||
if(detail::is_control(d.ws.rd_fh_.op))
|
||||
@@ -249,87 +281,72 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||
if(d.ws.rd_fh_.len > 0)
|
||||
{
|
||||
// read control payload
|
||||
d.state = 7;
|
||||
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 = 8;
|
||||
d.state = do_control;
|
||||
break;
|
||||
}
|
||||
if(d.ws.rd_need_ > 0)
|
||||
{
|
||||
d.state = 1;
|
||||
d.state = do_read_payload;
|
||||
break;
|
||||
}
|
||||
if(! d.ws.rd_fh_.fin)
|
||||
{
|
||||
d.state = 2;
|
||||
break;
|
||||
}
|
||||
// empty frame with fin
|
||||
d.state = 4;
|
||||
// empty frame
|
||||
d.state = do_frame_done;
|
||||
break;
|
||||
|
||||
// got control payload
|
||||
case 7:
|
||||
//------------------------------------------------------------------
|
||||
|
||||
case do_control_payload:
|
||||
if(d.ws.rd_fh_.mask)
|
||||
detail::mask_inplace(
|
||||
*d.fmb, d.ws.rd_key_);
|
||||
d.fb.commit(bytes_transferred);
|
||||
d.state = 8;
|
||||
d.state = do_control; // VFALCO fall through?
|
||||
break;
|
||||
|
||||
// do control
|
||||
case 8:
|
||||
//------------------------------------------------------------------
|
||||
|
||||
case do_control:
|
||||
if(d.ws.rd_fh_.op == opcode::ping)
|
||||
{
|
||||
code = close_code::none;
|
||||
ping_payload_type data;
|
||||
detail::read(data, d.fb.data(), code);
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// protocol error
|
||||
d.state = 16;
|
||||
break;
|
||||
}
|
||||
ping_data data;
|
||||
detail::read(data, d.fb.data());
|
||||
d.fb.reset();
|
||||
if(d.ws.wr_close_)
|
||||
{
|
||||
d.state = 2;
|
||||
// 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_)
|
||||
{
|
||||
assert(d.ws.wr_block_ != &d);
|
||||
// suspend
|
||||
d.state = 13;
|
||||
d.state = do_pong_resume;
|
||||
assert(d.ws.wr_block_ != &d);
|
||||
d.ws.rd_op_.template emplace<
|
||||
read_frame_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.state = 14;
|
||||
d.state = do_pong;
|
||||
break;
|
||||
}
|
||||
else if(d.ws.rd_fh_.op == opcode::pong)
|
||||
{
|
||||
code = close_code::none;
|
||||
ping_payload_type data;
|
||||
detail::read(data, d.fb.data(), code);
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// protocol error
|
||||
d.state = 16;
|
||||
break;
|
||||
}
|
||||
ping_data payload;
|
||||
detail::read(payload, d.fb.data());
|
||||
if(d.ws.pong_cb_)
|
||||
d.ws.pong_cb_(payload);
|
||||
d.fb.reset();
|
||||
// VFALCO TODO maybe_invoke an async pong handler
|
||||
// For now just ignore the pong.
|
||||
d.state = 2;
|
||||
d.state = do_read_fh;
|
||||
break;
|
||||
}
|
||||
assert(d.ws.rd_fh_.op == opcode::close);
|
||||
@@ -337,7 +354,8 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||
detail::read(d.ws.cr_, d.fb.data(), code);
|
||||
if(code != close_code::none)
|
||||
{
|
||||
d.state = 16;
|
||||
// protocol error
|
||||
d.state = do_fail;
|
||||
break;
|
||||
}
|
||||
if(! d.ws.wr_close_)
|
||||
@@ -352,127 +370,90 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 9;
|
||||
d.state = do_close_resume;
|
||||
d.ws.rd_op_.template emplace<
|
||||
read_frame_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.state = 10;
|
||||
d.state = do_close;
|
||||
break;
|
||||
}
|
||||
// call handler;
|
||||
d.state = 99;
|
||||
ec = error::closed;
|
||||
break;
|
||||
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 10:
|
||||
d.state = 11;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
d.fb.data(), std::move(*this));
|
||||
return;;
|
||||
|
||||
// teardown
|
||||
case 11:
|
||||
d.state = 12;
|
||||
websocket_helpers::call_async_teardown(
|
||||
d.ws.next_layer(), std::move(*this));
|
||||
case do_pong_resume:
|
||||
d.state = do_pong_resume + 1;
|
||||
d.ws.get_io_service().post(bind_handler(
|
||||
std::move(*this), ec, bytes_transferred));
|
||||
return;
|
||||
|
||||
case 12:
|
||||
// call handler
|
||||
d.state = 99;
|
||||
ec = error::closed;
|
||||
break;
|
||||
|
||||
// resume
|
||||
case 13:
|
||||
if(d.ws.error_)
|
||||
case do_pong_resume + 1:
|
||||
if(d.ws.failed_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
break;
|
||||
goto upcall;
|
||||
}
|
||||
d.state = do_pong;
|
||||
break; // VFALCO fall through?
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
case do_pong:
|
||||
if(d.ws.wr_close_)
|
||||
{
|
||||
// ignore ping when closing
|
||||
d.fb.reset();
|
||||
d.state = 2;
|
||||
d.state = do_read_fh;
|
||||
break;
|
||||
}
|
||||
d.state = 14;
|
||||
break;
|
||||
|
||||
case 14:
|
||||
// write ping/pong
|
||||
d.state = 15;
|
||||
// send pong
|
||||
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;
|
||||
|
||||
// sent ping/pong
|
||||
case 15:
|
||||
case do_pong + 1:
|
||||
d.fb.reset();
|
||||
d.state = 2;
|
||||
d.state = do_read_fh;
|
||||
d.ws.wr_block_ = nullptr;
|
||||
break;
|
||||
|
||||
// fail the connection
|
||||
case 16:
|
||||
if(! d.ws.wr_close_)
|
||||
{
|
||||
d.fb.reset();
|
||||
d.ws.template write_close<
|
||||
static_streambuf>(d.fb, code);
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 17;
|
||||
d.ws.rd_op_.template emplace<
|
||||
read_frame_op>(std::move(*this));
|
||||
//------------------------------------------------------------------
|
||||
|
||||
case do_close_resume:
|
||||
d.state = do_close_resume + 1;
|
||||
d.ws.get_io_service().post(bind_handler(
|
||||
std::move(*this), ec, bytes_transferred));
|
||||
return;
|
||||
}
|
||||
d.state = 18;
|
||||
|
||||
case do_close_resume + 1:
|
||||
if(d.ws.failed_)
|
||||
{
|
||||
// call handler
|
||||
d.state = do_call_handler;
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
break;
|
||||
}
|
||||
|
||||
// resume
|
||||
case 17:
|
||||
if(d.ws.wr_close_)
|
||||
{
|
||||
d.state = 19;
|
||||
break;
|
||||
// call handler
|
||||
ec = error::closed;
|
||||
goto upcall;
|
||||
}
|
||||
d.state = 18;
|
||||
d.state = do_close;
|
||||
break;
|
||||
|
||||
case 18:
|
||||
// send close
|
||||
d.state = 19;
|
||||
//------------------------------------------------------------------
|
||||
|
||||
case do_close:
|
||||
d.state = do_close + 1;
|
||||
d.ws.wr_close_ = true;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
@@ -480,22 +461,84 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||
d.fb.data(), std::move(*this));
|
||||
return;
|
||||
|
||||
// teardown
|
||||
case 19:
|
||||
d.state = 20;
|
||||
case do_close + 1:
|
||||
d.state = do_close + 2;
|
||||
websocket_helpers::call_async_teardown(
|
||||
d.ws.next_layer(), std::move(*this));
|
||||
return;
|
||||
|
||||
case 20:
|
||||
case do_close + 2:
|
||||
// call handler
|
||||
d.state = 99;
|
||||
ec = error::failed;
|
||||
ec = error::closed;
|
||||
goto upcall;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
case do_fail:
|
||||
if(d.ws.wr_close_)
|
||||
{
|
||||
d.state = do_fail + 4;
|
||||
break;
|
||||
}
|
||||
d.fb.reset();
|
||||
d.ws.template write_close<
|
||||
static_streambuf>(d.fb, code);
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = do_fail + 2;
|
||||
d.ws.rd_op_.template emplace<
|
||||
read_frame_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
if(ec)
|
||||
d.ws.error_ = true;
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
while(! ec);
|
||||
}
|
||||
upcall:
|
||||
if(d.ws.wr_block_ == &d)
|
||||
d.ws.wr_block_ = nullptr;
|
||||
d.ws.wr_op_.maybe_invoke();
|
||||
|
||||
@@ -105,24 +105,34 @@ operator()(error_code const& ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
while(! ec)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
// read payload
|
||||
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.fi, d.sb, std::move(*this));
|
||||
#else
|
||||
d.ws.async_read_frame(d.fi, d.sb, *this);
|
||||
#endif
|
||||
return;
|
||||
|
||||
// got payload
|
||||
case 1:
|
||||
d.op = d.fi.op;
|
||||
d.state = d.fi.fin ? 99 : 0;
|
||||
if(d.fi.fin)
|
||||
goto upcall;
|
||||
d.state = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
upcall:
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ class stream<NextLayer>::response_op
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(cont_)
|
||||
{
|
||||
// can't call stream::reset() here
|
||||
// otherwise accept_op will malfunction
|
||||
//
|
||||
if(resp.status != 101)
|
||||
final_ec = error::handshake_failed;
|
||||
}
|
||||
@@ -123,7 +126,7 @@ operator()(error_code ec, bool again)
|
||||
d.state = 99;
|
||||
ec = d.final_ec;
|
||||
if(! ec)
|
||||
d.ws.role_ = role_type::server;
|
||||
d.ws.open(detail::role_type::server);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <beast/websocket/impl/accept_op.ipp>
|
||||
#include <beast/websocket/impl/close_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_frame_op.ipp>
|
||||
#include <beast/websocket/impl/response_op.ipp>
|
||||
@@ -40,6 +41,13 @@ namespace websocket {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class _>
|
||||
void
|
||||
stream_base::open(role_type role)
|
||||
{
|
||||
role_ = role;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
void
|
||||
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;
|
||||
}
|
||||
if(rd_size_ > rd_msg_max_)
|
||||
if(rd_msg_max_ && rd_size_ > rd_msg_max_)
|
||||
{
|
||||
code = close_code::too_big;
|
||||
return;
|
||||
@@ -100,7 +108,7 @@ stream_base::write_close(
|
||||
fh.rsv3 = false;
|
||||
fh.len = cr.code == close_code::none ?
|
||||
0 : 2 + cr.reason.size();
|
||||
fh.mask = role_ == role_type::client;
|
||||
fh.mask = role_ == detail::role_type::client;
|
||||
if(fh.mask)
|
||||
fh.key = maskgen_();
|
||||
detail::write(sb, fh);
|
||||
@@ -136,7 +144,7 @@ stream_base::write_close(
|
||||
template<class Streambuf>
|
||||
void
|
||||
stream_base::write_ping(Streambuf& sb,
|
||||
opcode op, ping_payload_type const& data)
|
||||
opcode op, ping_data const& data)
|
||||
{
|
||||
frame_header fh;
|
||||
fh.op = op;
|
||||
@@ -184,7 +192,8 @@ accept()
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
accept(boost::asio::null_buffers{}, ec);
|
||||
detail::maybe_throw(ec, "accept");
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -223,7 +232,8 @@ accept(ConstBufferSequence const& buffers)
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
accept(buffers, ec);
|
||||
detail::maybe_throw(ec, "accept");
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -239,10 +249,11 @@ accept(ConstBufferSequence const& buffers, error_code& ec)
|
||||
"ConstBufferSequence requirements not met");
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
reset();
|
||||
stream_.buffer().commit(buffer_copy(
|
||||
stream_.buffer().prepare(
|
||||
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);
|
||||
if(ec)
|
||||
return;
|
||||
@@ -279,7 +290,8 @@ accept(http::request_v1<Body, Headers> const& request)
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
accept(request, ec);
|
||||
detail::maybe_throw(ec, "accept");
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -291,8 +303,11 @@ accept(http::request_v1<Body, Headers> const& req,
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
reset();
|
||||
auto const res = build_response(req);
|
||||
http::write(stream_, res, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(res.status != 101)
|
||||
{
|
||||
ec = error::handshake_failed;
|
||||
@@ -300,7 +315,7 @@ accept(http::request_v1<Body, Headers> const& req,
|
||||
// teardown if Connection: close.
|
||||
return;
|
||||
}
|
||||
role_ = role_type::server;
|
||||
open(detail::role_type::server);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -316,6 +331,7 @@ async_accept(http::request_v1<Body, Headers> const& req,
|
||||
beast::async_completion<
|
||||
AcceptHandler, void(error_code)
|
||||
> completion(handler);
|
||||
reset();
|
||||
response_op<decltype(completion.handler)>{
|
||||
completion.handler, *this, req,
|
||||
boost_asio_handler_cont_helpers::
|
||||
@@ -333,7 +349,8 @@ handshake(boost::string_ref const& host,
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
handshake(host, resource, ec);
|
||||
detail::maybe_throw(ec, "upgrade");
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -344,6 +361,7 @@ handshake(boost::string_ref const& host,
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
reset();
|
||||
std::string key;
|
||||
http::write(stream_,
|
||||
build_request(host, resource, key), ec);
|
||||
@@ -383,7 +401,8 @@ close(close_reason const& cr)
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
close(cr, ec);
|
||||
detail::maybe_throw(ec, "close");
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -398,7 +417,7 @@ close(close_reason const& cr, error_code& ec)
|
||||
detail::frame_streambuf fb;
|
||||
write_close<static_streambuf>(fb, cr);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
error_ = ec != 0;
|
||||
failed_ = ec != 0;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -418,6 +437,45 @@ async_close(close_reason const& cr, CloseHandler&& handler)
|
||||
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 Streambuf>
|
||||
void
|
||||
@@ -428,7 +486,8 @@ read(opcode& op, Streambuf& streambuf)
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
read(op, streambuf, ec);
|
||||
detail::maybe_throw(ec, "read");
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -481,7 +540,8 @@ read_frame(frame_info& fi, Streambuf& streambuf)
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
read_frame(fi, streambuf, ec);
|
||||
detail::maybe_throw(ec, "read_some");
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -500,8 +560,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
||||
// read header
|
||||
detail::frame_streambuf fb;
|
||||
do_read_fh(fb, code, ec);
|
||||
error_ = ec != 0;
|
||||
if(error_)
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
if(code != close_code::none)
|
||||
break;
|
||||
@@ -513,8 +573,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
||||
auto const mb = fb.prepare(
|
||||
static_cast<std::size_t>(rd_fh_.len));
|
||||
fb.commit(boost::asio::read(stream_, mb, ec));
|
||||
error_ = ec != 0;
|
||||
if(error_)
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
if(rd_fh_.mask)
|
||||
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)
|
||||
{
|
||||
ping_payload_type data;
|
||||
detail::read(data, fb.data(), code);
|
||||
if(code != close_code::none)
|
||||
break;
|
||||
ping_data data;
|
||||
detail::read(data, fb.data());
|
||||
fb.reset();
|
||||
write_ping<static_streambuf>(
|
||||
fb, opcode::pong, data);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
error_ = ec != 0;
|
||||
if(error_)
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
else if(rd_fh_.op == opcode::pong)
|
||||
{
|
||||
ping_payload_type data;
|
||||
detail::read(data, fb.data(), code);
|
||||
if(code != close_code::none)
|
||||
break;
|
||||
// VFALCO How to notify callers using
|
||||
// the synchronous interface?
|
||||
ping_data payload;
|
||||
detail::read(payload, fb.data());
|
||||
if(pong_cb_)
|
||||
pong_cb_(payload);
|
||||
continue;
|
||||
}
|
||||
assert(rd_fh_.op == opcode::close);
|
||||
@@ -560,8 +616,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
||||
wr_close_ = true;
|
||||
write_close<static_streambuf>(fb, cr);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
error_ = ec != 0;
|
||||
if(error_)
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -578,8 +634,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
||||
detail::clamp(rd_need_));
|
||||
auto const bytes_transferred =
|
||||
stream_.read_some(smb, ec);
|
||||
error_ = ec != 0;
|
||||
if(error_)
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
rd_need_ -= bytes_transferred;
|
||||
auto const pb = prepare_buffers(
|
||||
@@ -610,23 +666,23 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
||||
detail::frame_streambuf fb;
|
||||
write_close<static_streambuf>(fb, code);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
error_ = ec != 0;
|
||||
if(error_)
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
}
|
||||
websocket_helpers::call_teardown(next_layer(), ec);
|
||||
error_ = ec != 0;
|
||||
if(error_)
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
ec = error::failed;
|
||||
error_ = true;
|
||||
failed_ = true;
|
||||
return;
|
||||
}
|
||||
if(! ec)
|
||||
websocket_helpers::call_teardown(next_layer(), ec);
|
||||
if(! ec)
|
||||
ec = error::closed;
|
||||
error_ = ec != 0;
|
||||
failed_ = ec != 0;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -658,7 +714,8 @@ write(ConstBufferSequence const& buffers)
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
write(buffers, ec);
|
||||
detail::maybe_throw(ec, "write");
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -719,7 +776,8 @@ write_frame(bool fin, ConstBufferSequence const& buffers)
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
write_frame(fin, buffers, ec);
|
||||
detail::maybe_throw(ec, "write");
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
@@ -744,7 +802,7 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
|
||||
fh.rsv2 = false;
|
||||
fh.rsv3 = false;
|
||||
fh.len = buffer_size(bs);
|
||||
fh.mask = role_ == role_type::client;
|
||||
fh.mask = role_ == detail::role_type::client;
|
||||
if(fh.mask)
|
||||
fh.key = maskgen_();
|
||||
detail::fh_streambuf fh_buf;
|
||||
@@ -754,22 +812,21 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
|
||||
// send header and payload
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(), bs), ec);
|
||||
error_ = ec != 0;
|
||||
failed_ = ec != 0;
|
||||
return;
|
||||
}
|
||||
detail::prepared_key_type key;
|
||||
detail::prepare_key(key, fh.key);
|
||||
auto const tmp_size = detail::clamp(
|
||||
fh.len, wr_buf_size_);
|
||||
auto const tmp_size =
|
||||
detail::clamp(fh.len, mask_buf_size_);
|
||||
std::unique_ptr<std::uint8_t[]> up(
|
||||
new std::uint8_t[tmp_size]);
|
||||
auto const tmp = up.get();
|
||||
std::uint64_t remain = fh.len;
|
||||
consuming_buffers<ConstBufferSequence> cb(bs);
|
||||
{
|
||||
auto const n =
|
||||
detail::clamp(remain, tmp_size);
|
||||
mutable_buffers_1 mb{tmp, n};
|
||||
mutable_buffers_1 mb{up.get(), n};
|
||||
buffer_copy(mb, cb);
|
||||
cb.consume(n);
|
||||
remain -= n;
|
||||
@@ -779,7 +836,7 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
|
||||
buffer_cat(fh_buf.data(), mb), ec);
|
||||
if(ec)
|
||||
{
|
||||
error_ = ec != 0;
|
||||
failed_ = ec != 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -787,7 +844,7 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
|
||||
{
|
||||
auto const n =
|
||||
detail::clamp(remain, tmp_size);
|
||||
mutable_buffers_1 mb{tmp, n};
|
||||
mutable_buffers_1 mb{up.get(), n};
|
||||
buffer_copy(mb, cb);
|
||||
cb.consume(n);
|
||||
remain -= n;
|
||||
@@ -796,7 +853,7 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
|
||||
boost::asio::write(stream_, mb, ec);
|
||||
if(ec)
|
||||
{
|
||||
error_ = ec != 0;
|
||||
failed_ = ec != 0;
|
||||
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>
|
||||
http::request_v1<http::empty_body>
|
||||
stream<NextLayer>::
|
||||
@@ -860,8 +934,11 @@ build_response(http::request_v1<Body, Headers> const& req)
|
||||
res.reason = http::reason_string(res.status);
|
||||
res.version = req.version;
|
||||
res.body = text;
|
||||
// VFALCO TODO respect keep-alive here
|
||||
prepare(res);
|
||||
(*d_)(res);
|
||||
prepare(res,
|
||||
(is_keep_alive(req) && keep_alive_) ?
|
||||
http::connection::keep_alive :
|
||||
http::connection::close);
|
||||
return res;
|
||||
};
|
||||
if(req.version < 11)
|
||||
@@ -874,17 +951,28 @@ build_response(http::request_v1<Body, Headers> const& req)
|
||||
return err("Missing Host");
|
||||
if(! req.headers.exists("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 =
|
||||
req.headers["Sec-WebSocket-Version"];
|
||||
if(version.empty())
|
||||
return err("Missing Sec-WebSocket-Version");
|
||||
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;
|
||||
res.status = 101;
|
||||
res.reason = http::reason_string(res.status);
|
||||
@@ -893,7 +981,6 @@ build_response(http::request_v1<Body, Headers> const& req)
|
||||
{
|
||||
auto const key =
|
||||
req.headers["Sec-WebSocket-Key"];
|
||||
res.headers.insert("Sec-WebSocket-Key", key);
|
||||
res.headers.insert("Sec-WebSocket-Accept",
|
||||
detail::make_sec_ws_accept(key));
|
||||
}
|
||||
@@ -912,6 +999,8 @@ do_response(http::response_v1<Body, Headers> const& res,
|
||||
{
|
||||
// VFALCO Review these error codes
|
||||
auto fail = [&]{ ec = error::response_failed; };
|
||||
if(res.version < 11)
|
||||
return fail();
|
||||
if(res.status != 101)
|
||||
return fail();
|
||||
if(! is_upgrade(res))
|
||||
@@ -924,7 +1013,7 @@ do_response(http::response_v1<Body, Headers> const& res,
|
||||
if(res.headers["Sec-WebSocket-Accept"] !=
|
||||
detail::make_sec_ws_accept(key))
|
||||
return fail();
|
||||
role_ = role_type::client;
|
||||
open(detail::role_type::client);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
|
||||
@@ -56,8 +56,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec, std::size_t, bool again = true);
|
||||
operator()(error_code ec, std::size_t, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(std::size_t size,
|
||||
|
||||
@@ -57,17 +57,17 @@ class stream<NextLayer>::write_frame_op
|
||||
opcode::cont : ws.wr_opcode_;
|
||||
ws.wr_cont_ = ! fin;
|
||||
fh.fin = fin;
|
||||
fh.rsv1 = 0;
|
||||
fh.rsv2 = 0;
|
||||
fh.rsv3 = 0;
|
||||
fh.rsv1 = false;
|
||||
fh.rsv2 = false;
|
||||
fh.rsv3 = false;
|
||||
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)
|
||||
{
|
||||
fh.key = ws.maskgen_();
|
||||
detail::prepare_key(key, fh.key);
|
||||
tmp_size = detail::clamp(
|
||||
fh.len, ws.wr_buf_size_);
|
||||
fh.len, ws.mask_buf_size_);
|
||||
tmp = boost_asio_handler_alloc_helpers::
|
||||
allocate(tmp_size, h);
|
||||
remain = fh.len;
|
||||
@@ -100,18 +100,17 @@ public:
|
||||
std::forward<DeducedHandler>(h), ws,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = false;
|
||||
(*this)(error_code{}, 0, false);
|
||||
(*this)(error_code{});
|
||||
}
|
||||
|
||||
void operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
void operator()(error_code ec, std::size_t);
|
||||
|
||||
void operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
@@ -149,14 +148,28 @@ template<class Buffers, class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_frame_op<Buffers, Handler>::
|
||||
operator()(
|
||||
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 Buffers, class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_frame_op<Buffers, Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::mutable_buffers_1;
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
if(ec)
|
||||
goto upcall;
|
||||
for(;;)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
@@ -164,41 +177,27 @@ operator()(
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 1;
|
||||
d.state = 3;
|
||||
d.ws.wr_op_.template emplace<
|
||||
write_frame_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
if(d.ws.error_)
|
||||
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, 0));
|
||||
boost::asio::error::operation_aborted));
|
||||
return;
|
||||
}
|
||||
assert(! d.ws.wr_close_);
|
||||
d.state = 2;
|
||||
break;
|
||||
// fall through
|
||||
|
||||
// resume
|
||||
case 1:
|
||||
if(d.ws.error_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
break;
|
||||
}
|
||||
d.state = 2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
if(! d.fh.mask)
|
||||
{
|
||||
// send header and payload
|
||||
// send header and entire payload
|
||||
d.state = 99;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
@@ -215,7 +214,7 @@ operator()(
|
||||
d.remain -= n;
|
||||
detail::mask_inplace(mb, d.key);
|
||||
// send header and payload
|
||||
d.state = d.remain > 0 ? 3 : 99;
|
||||
d.state = d.remain > 0 ? 2 : 99;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
@@ -225,7 +224,7 @@ operator()(
|
||||
}
|
||||
|
||||
// sent masked payload
|
||||
case 3:
|
||||
case 2:
|
||||
{
|
||||
auto const n =
|
||||
detail::clamp(d.remain, d.tmp_size);
|
||||
@@ -238,24 +237,41 @@ operator()(
|
||||
// send payload
|
||||
if(d.remain == 0)
|
||||
d.state = 99;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
assert(d.ws.wr_block_ == &d);
|
||||
boost::asio::async_write(
|
||||
d.ws.stream_, mb, std::move(*this));
|
||||
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)
|
||||
d.ws.error_ = true;
|
||||
if(d.ws.wr_block_ == &d)
|
||||
d.ws.wr_block_ = nullptr;
|
||||
upcall:
|
||||
if(d.tmp)
|
||||
{
|
||||
boost_asio_handler_alloc_helpers::
|
||||
deallocate(d.tmp, d.tmp_size, d.h);
|
||||
d.tmp = nullptr;
|
||||
}
|
||||
if(d.ws.wr_block_ == &d)
|
||||
d.ws.wr_block_ = nullptr;
|
||||
d.ws.rd_op_.maybe_invoke();
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
if(! ec)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
@@ -126,6 +126,9 @@ operator()(error_code ec, bool again)
|
||||
d.ws.async_write_frame(fin, pb, std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
case 99:
|
||||
break;
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
#ifndef BEAST_WEBSOCKET_OPTION_HPP
|
||||
#define BEAST_WEBSOCKET_OPTION_HPP
|
||||
|
||||
#include <beast/websocket/rfc6455.hpp>
|
||||
#include <beast/websocket/detail/stream_base.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
@@ -147,6 +149,45 @@ struct keep_alive
|
||||
};
|
||||
#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.
|
||||
|
||||
This controls the opcode set for outgoing messages. Valid
|
||||
@@ -184,6 +225,50 @@ struct message_type
|
||||
};
|
||||
#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.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
@@ -251,44 +337,6 @@ struct read_message_max
|
||||
};
|
||||
#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
|
||||
} // beast
|
||||
|
||||
|
||||
@@ -60,6 +60,13 @@ enum
|
||||
protocol_error = 1002,
|
||||
|
||||
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,
|
||||
policy_error = 1008,
|
||||
too_big = 1009,
|
||||
@@ -80,12 +87,11 @@ enum
|
||||
} // close_code
|
||||
#endif
|
||||
|
||||
#if ! GENERATING_DOCS
|
||||
using reason_string_type =
|
||||
static_string<123, char>;
|
||||
using ping_payload_type =
|
||||
static_string<125, char>;
|
||||
#endif
|
||||
/// The type representing the reason string in a close frame.
|
||||
using reason_string = static_string<123, char>;
|
||||
|
||||
/// The type representing the payload of ping and pong messages.
|
||||
using ping_data = static_string<125, char>;
|
||||
|
||||
/** Description of the close reason.
|
||||
|
||||
@@ -98,7 +104,7 @@ struct close_reason
|
||||
close_code::value code = close_code::none;
|
||||
|
||||
/// The optional utf8-encoded reason string.
|
||||
reason_string_type reason;
|
||||
reason_string reason;
|
||||
|
||||
/** Default constructor.
|
||||
|
||||
@@ -114,17 +120,17 @@ struct close_reason
|
||||
}
|
||||
|
||||
/// Construct from a reason. code is close_code::normal.
|
||||
template<class CharT>
|
||||
close_reason(CharT const* reason_)
|
||||
template<std::size_t N>
|
||||
close_reason(char const (&reason_)[N])
|
||||
: code(close_code::normal)
|
||||
, reason(reason_)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct from a code and reason.
|
||||
template<class CharT>
|
||||
template<std::size_t N>
|
||||
close_reason(close_code::value code_,
|
||||
CharT const* reason_)
|
||||
char const (&reason_)[N])
|
||||
: code(code_)
|
||||
, 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
|
||||
} // beast
|
||||
|
||||
|
||||
@@ -69,8 +69,8 @@ struct frame_info
|
||||
@tparam NextLayer The type representing the next layer, to which
|
||||
data will be read and written during operations. For synchronous
|
||||
operations, the type must support the @b `SyncStream` concept.
|
||||
For asynchronous operations, the type must support the @b `AsyncStream`
|
||||
concept.
|
||||
For asynchronous operations, the type must support the
|
||||
@b `AsyncStream` concept.
|
||||
|
||||
@note A stream object must not be destroyed while there are
|
||||
pending asynchronous operations associated with it.
|
||||
@@ -84,7 +84,7 @@ struct frame_info
|
||||
template<class NextLayer>
|
||||
class stream : public detail::stream_base
|
||||
{
|
||||
friend class ws_test;
|
||||
friend class stream_test;
|
||||
|
||||
streambuf_readstream<NextLayer, streambuf> stream_;
|
||||
|
||||
@@ -124,12 +124,12 @@ public:
|
||||
|
||||
/** Construct a WebSocket stream.
|
||||
|
||||
This constructor creates a websocket stream and initialises
|
||||
This constructor creates a websocket stream and initializes
|
||||
the next layer object.
|
||||
|
||||
@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
|
||||
layer's constructor.
|
||||
*/
|
||||
@@ -198,6 +198,13 @@ public:
|
||||
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
|
||||
void
|
||||
set_option(read_buffer_size const& o)
|
||||
@@ -212,11 +219,11 @@ public:
|
||||
rd_msg_max_ = o.value;
|
||||
}
|
||||
|
||||
/// Set the size of the write buffer
|
||||
/// Set the size of the mask buffer
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -788,7 +795,7 @@ public:
|
||||
|
||||
/** 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:
|
||||
|
||||
@li The close frame finishes sending.
|
||||
@@ -817,7 +824,7 @@ public:
|
||||
|
||||
/** 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:
|
||||
|
||||
@li The close frame finishes sending.
|
||||
@@ -844,7 +851,7 @@ public:
|
||||
void
|
||||
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
|
||||
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
|
||||
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 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_close) until this operation completes.
|
||||
|
||||
@@ -896,6 +903,84 @@ public:
|
||||
#endif
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
asynchronous operation will continue until one of the following
|
||||
is true:
|
||||
@@ -981,7 +1066,7 @@ public:
|
||||
This operation is implemented in terms of one or more calls to the
|
||||
next layer's `async_read_some` and `async_write_some` functions,
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
Control frames encountered while reading frame or message data
|
||||
are handled automatically. Pings are replied to, pongs are noted,
|
||||
and close frames initiate the WebSocket close procedure. When a
|
||||
close frame is received, this call will eventually return
|
||||
@ref error::closed. Because of the need to handle control frames,
|
||||
read operations can cause writes to take place. These writes are
|
||||
managed transparently; callers can still have one active
|
||||
asynchronous read and asynchronous write operation pending
|
||||
simultaneously (a user initiated call to @ref async_close
|
||||
counts as a write).
|
||||
are handled automatically. Pings are replied to, pongs cause
|
||||
an outstanding call to `async_ping` to complete, and close
|
||||
frames initiate the WebSocket close procedure. When a close
|
||||
frame is received, this call will eventually return
|
||||
@ref error::closed. Because of the need to handle control
|
||||
frames, these read operations can cause writes to take place.
|
||||
Despite this, calls to `async_read` and `async_read_frame`
|
||||
only count as read operations.
|
||||
|
||||
@param op A value to receive the message type.
|
||||
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.
|
||||
|
||||
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
|
||||
immediately. The asynchronous operation will continue until
|
||||
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
|
||||
next layer's `async_read_some` and `async_write_some` functions,
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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 asynchronous operation will continue until one of the
|
||||
following conditions is true:
|
||||
@@ -1407,11 +1491,15 @@ private:
|
||||
template<class Handler> class accept_op;
|
||||
template<class Handler> class close_op;
|
||||
template<class Handler> class handshake_op;
|
||||
template<class Handler> class ping_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_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>
|
||||
build_request(boost::string_ref const& host,
|
||||
|
||||
@@ -6,7 +6,16 @@ set -ex
|
||||
|
||||
################################## 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 variant: $VARIANT"
|
||||
@@ -19,10 +28,25 @@ function run_tests_with_gdb {
|
||||
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 {
|
||||
$BOOST_ROOT/bjam toolset=$CC \
|
||||
variant=$VARIANT \
|
||||
address-model=$ADDRESS_MODEL
|
||||
address-model=$ADDRESS_MODEL -j${num_procs}
|
||||
}
|
||||
|
||||
##################################### BUILD ####################################
|
||||
@@ -38,24 +62,24 @@ if [[ $VARIANT == "coverage" ]]; then
|
||||
lcov --no-external -c -i -d . -o baseline.info > /dev/null
|
||||
|
||||
# Perform test
|
||||
run_tests_with_gdb
|
||||
run_tests
|
||||
|
||||
# Run autobahn tests
|
||||
export SERVER=`find . -name "websocket-echo"`
|
||||
nohup scripts/run-with-gdb.sh $SERVER&
|
||||
export SERVER=$(find . -name "websocket-echo")
|
||||
nohup $SERVER&
|
||||
|
||||
# We need to wait a while so wstest can connect!
|
||||
sleep 5
|
||||
# cd scripts && wstest -m fuzzingclient
|
||||
# cd ..
|
||||
cd scripts && wstest -m fuzzingclient
|
||||
cd ..
|
||||
# Show the output
|
||||
cat nohup.out
|
||||
rm nohup.out
|
||||
jobs
|
||||
sleep 5
|
||||
# Kill it gracefully
|
||||
kill -INT %1
|
||||
sleep 1
|
||||
kill -INT %1 || echo "Dead already"
|
||||
wait
|
||||
|
||||
# Create test coverage data file
|
||||
lcov --no-external -c -d . -o testrun.info > /dev/null
|
||||
|
||||
@@ -9,7 +9,7 @@ do
|
||||
test -x $( type -p ${g}-$GCC_VER )
|
||||
ln -sv $(type -p ${g}-$GCC_VER) $HOME/bin/${g}
|
||||
done
|
||||
for c in clang clang++
|
||||
for c in clang clang++ llvm-symbolizer
|
||||
do
|
||||
test -x $( type -p ${c}-$CLANG_VER )
|
||||
ln -sv $(type -p ${c}-$CLANG_VER) $HOME/bin/${c}
|
||||
|
||||
@@ -35,6 +35,7 @@ unit-test core-tests :
|
||||
core/write_streambuf.cpp
|
||||
core/detail/base64.cpp
|
||||
core/detail/empty_base_optimization.cpp
|
||||
core/detail/get_lowest_layer.cpp
|
||||
core/detail/sha1.cpp
|
||||
;
|
||||
|
||||
@@ -43,6 +44,7 @@ unit-test http-tests :
|
||||
http/basic_headers.cpp
|
||||
http/basic_parser_v1.cpp
|
||||
http/body_type.cpp
|
||||
http/concepts.cpp
|
||||
http/empty_body.cpp
|
||||
http/headers.cpp
|
||||
http/message.cpp
|
||||
@@ -57,7 +59,6 @@ unit-test http-tests :
|
||||
http/status.cpp
|
||||
http/streambuf_body.cpp
|
||||
http/string_body.cpp
|
||||
http/type_check.cpp
|
||||
http/write.cpp
|
||||
http/detail/chunk_encode.cpp
|
||||
;
|
||||
@@ -77,6 +78,7 @@ unit-test websocket-tests :
|
||||
websocket/teardown.cpp
|
||||
websocket/detail/frame.cpp
|
||||
websocket/detail/mask.cpp
|
||||
websocket/detail/stream_base.cpp
|
||||
websocket/detail/utf8_checker.cpp
|
||||
;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ add_executable (core-tests
|
||||
write_streambuf.cpp
|
||||
detail/base64.cpp
|
||||
detail/empty_base_optimization.cpp
|
||||
detail/get_lowest_layer.cpp
|
||||
detail/sha1.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -66,6 +66,14 @@ public:
|
||||
expect(buffer_size(buffer_cat(
|
||||
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()
|
||||
|
||||
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()
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::const_buffer;
|
||||
char b[3];
|
||||
std::array<const_buffer, 3> bs{{
|
||||
@@ -98,7 +99,12 @@ public:
|
||||
auto pb = prepare_buffers(2, bs);
|
||||
std::size_t n = 0;
|
||||
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;
|
||||
}
|
||||
expect(n == 2);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ add_executable (http-tests
|
||||
basic_headers.cpp
|
||||
basic_parser_v1.cpp
|
||||
body_type.cpp
|
||||
concepts.cpp
|
||||
empty_body.cpp
|
||||
headers.cpp
|
||||
message.cpp
|
||||
@@ -24,7 +25,6 @@ add_executable (http-tests
|
||||
status.cpp
|
||||
streambuf_body.cpp
|
||||
string_body.cpp
|
||||
type_check.cpp
|
||||
write.cpp
|
||||
detail/chunk_encode.cpp
|
||||
)
|
||||
|
||||
@@ -532,6 +532,7 @@ public:
|
||||
void testInvalidMatrix()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
std::string s;
|
||||
|
||||
for(std::size_t n = 0;; ++n)
|
||||
@@ -549,15 +550,24 @@ public:
|
||||
s[n] = 0;
|
||||
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;
|
||||
error_code ec;
|
||||
p.write(buffer(s.data(), m), ec);
|
||||
p.write(b1, ec);
|
||||
if(ec)
|
||||
{
|
||||
pass();
|
||||
continue;
|
||||
}
|
||||
p.write(buffer(s.data() + m, len - m), ec);
|
||||
p.write(b2, ec);
|
||||
expect(ec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
//
|
||||
|
||||
// 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&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
m.headers.erase("Content-Length");
|
||||
m.headers.insert("Connection", "keep-alive");
|
||||
@@ -69,7 +70,12 @@ public:
|
||||
}
|
||||
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
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
{
|
||||
using namespace std::chrono;
|
||||
using clock_type = std::chrono::high_resolution_clock;
|
||||
log << name;
|
||||
log << name << std::endl;
|
||||
for(std::size_t trial = 1; trial <= repeat; ++trial)
|
||||
{
|
||||
auto const t0 = clock_type::now();
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
auto const elapsed = clock_type::now() - t0;
|
||||
log <<
|
||||
"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;
|
||||
|
||||
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) == " <<
|
||||
sizeof(basic_parser_v1<false, null_parser<true>>);
|
||||
sizeof(basic_parser_v1<false, null_parser<true>>)<< '\n';
|
||||
|
||||
testcase << "Parser speed test, " <<
|
||||
((Repeat * size_ + 512) / 1024) << "KB in " <<
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
#include <beast/http/headers.hpp>
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <beast/test/string_stream.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
@@ -25,16 +27,18 @@ class streambuf_body_test : public beast::unit_test::suite
|
||||
public:
|
||||
void run() override
|
||||
{
|
||||
test::string_stream ss(ios_,
|
||||
std::string const s =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: test\r\n"
|
||||
"Content-Length: 3\r\n"
|
||||
"\r\n"
|
||||
"xyz");
|
||||
"xyz";
|
||||
test::string_stream ss(ios_, s);
|
||||
parser_v1<false, streambuf_body, headers> p;
|
||||
streambuf sb;
|
||||
parse(ss, sb, p);
|
||||
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
|
||||
detail/frame.cpp
|
||||
detail/mask.cpp
|
||||
detail/stream_base.cpp
|
||||
detail/utf8_checker.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -225,7 +225,6 @@ public:
|
||||
testCloseCodes();
|
||||
testFrameHeader();
|
||||
testBadFrameHeaders();
|
||||
pass();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
// 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/unit_test/suite.hpp>
|
||||
|
||||
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;
|
||||
int state = 0;
|
||||
boost::optional<endpoint_type> ep;
|
||||
websocket::stream<socket_type> ws;
|
||||
websocket::opcode op;
|
||||
stream<socket_type> ws;
|
||||
boost::asio::io_service::strand strand;
|
||||
opcode op;
|
||||
beast::streambuf sb;
|
||||
int id;
|
||||
|
||||
data(bool log_, socket_type&& sock_)
|
||||
: log(log_)
|
||||
, ws(std::move(sock_))
|
||||
, strand(ws.get_io_service())
|
||||
, id([]
|
||||
{
|
||||
static int n = 0;
|
||||
@@ -112,6 +114,7 @@ private:
|
||||
: log(log_)
|
||||
, ep(ep_)
|
||||
, ws(std::move(sock_))
|
||||
, strand(ws.get_io_service())
|
||||
, id([]
|
||||
{
|
||||
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)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
auto& d = *d_;
|
||||
switch(d.state)
|
||||
{
|
||||
@@ -191,19 +220,54 @@ private:
|
||||
d.sb.consume(d.sb.size());
|
||||
// read message
|
||||
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;
|
||||
|
||||
// got message
|
||||
case 2:
|
||||
if(ec == websocket::error::closed)
|
||||
if(ec == error::closed)
|
||||
return;
|
||||
if(ec)
|
||||
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
|
||||
d.state = 1;
|
||||
d.ws.set_option(websocket::message_type(d.op));
|
||||
d.ws.async_write(d.sb.data(), std::move(*this));
|
||||
d.ws.set_option(message_type(d.op));
|
||||
d.ws.async_write(d.sb.data(),
|
||||
d.strand.wrap(std::move(*this)));
|
||||
return;
|
||||
|
||||
// connected
|
||||
@@ -214,7 +278,7 @@ private:
|
||||
d.ws.async_handshake(
|
||||
d.ep->address().to_string() + ":" +
|
||||
std::to_string(d.ep->port()),
|
||||
"/", std::move(*this));
|
||||
"/", d.strand.wrap(std::move(*this)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -226,7 +290,7 @@ private:
|
||||
auto& d = *d_;
|
||||
if(d.log)
|
||||
{
|
||||
if(ec != websocket::error::closed)
|
||||
if(ec != error::closed)
|
||||
std::cerr << "#" << d_->id << " " <<
|
||||
what << ": " << ec.message() << std::endl;
|
||||
}
|
||||
@@ -256,6 +320,8 @@ private:
|
||||
{
|
||||
if(! acceptor_.is_open())
|
||||
return;
|
||||
if(ec == boost::asio::error::operation_aborted)
|
||||
return;
|
||||
maybe_throw(ec, "accept");
|
||||
socket_type sock(std::move(sock_));
|
||||
acceptor_.async_accept(sock_,
|
||||
|
||||
@@ -101,15 +101,17 @@ private:
|
||||
{
|
||||
int id;
|
||||
sync_echo_peer& self;
|
||||
socket_type sock;
|
||||
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_,
|
||||
socket_type&& sock_)
|
||||
: id(id_)
|
||||
, self(self_)
|
||||
, work(sock_.get_io_service())
|
||||
, 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
|
||||
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(read_message_max(64 * 1024 * 1024));
|
||||
error_code ec;
|
||||
@@ -164,17 +187,45 @@ private:
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
websocket::opcode op;
|
||||
opcode op;
|
||||
beast::streambuf sb;
|
||||
ws.read(op, sb, ec);
|
||||
if(ec)
|
||||
{
|
||||
auto const s = ec.message();
|
||||
break;
|
||||
ws.set_option(websocket::message_type(op));
|
||||
}
|
||||
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)
|
||||
break;
|
||||
}
|
||||
if(ec && ec != websocket::error::closed)
|
||||
if(ec && ec != error::closed)
|
||||
{
|
||||
fail(id, ec, "read");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user