mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Merge commit '2f9a8440c2432d8a196571d6300404cb76314125' into develop
This commit is contained in:
30
src/beast/.gitignore
vendored
30
src/beast/.gitignore
vendored
@@ -1,30 +1,2 @@
|
||||
docs/
|
||||
._*
|
||||
*.mode1v3
|
||||
*.pbxuser
|
||||
*.perspectivev3
|
||||
*.user
|
||||
*.ncb
|
||||
*.suo
|
||||
*.obj
|
||||
*.ilk
|
||||
*.pch
|
||||
*.pdb
|
||||
*.dep
|
||||
*.idb
|
||||
*.manifest
|
||||
*.manifest.res
|
||||
*.o
|
||||
*.opensdf
|
||||
*.d
|
||||
*.sdf
|
||||
xcuserdata
|
||||
contents.xcworkspacedata
|
||||
.DS_Store
|
||||
.svn
|
||||
profile
|
||||
bin/
|
||||
node_modules/
|
||||
cov-int/
|
||||
nohup.out
|
||||
venv/
|
||||
bin64/
|
||||
|
||||
@@ -2,6 +2,7 @@ language: cpp
|
||||
|
||||
env:
|
||||
global:
|
||||
- LLVM_VERSION=3.8.0
|
||||
# Maintenance note: to move to a new version
|
||||
# of boost, update both BOOST_ROOT and BOOST_URL.
|
||||
# Note that for simplicity, BOOST_ROOT's final
|
||||
@@ -27,57 +28,50 @@ packages: &gcc5_pkgs
|
||||
- autotools-dev
|
||||
- libc6-dbg
|
||||
|
||||
packages: &clang38_pkgs
|
||||
- clang-3.8
|
||||
- g++-5
|
||||
- python-software-properties
|
||||
- libssl-dev
|
||||
- libffi-dev
|
||||
- libstdc++6
|
||||
- binutils-gold
|
||||
# Provides a backtrace if the unittests crash
|
||||
- gdb
|
||||
# Needed for installing valgrind
|
||||
- subversion
|
||||
- automake
|
||||
- autotools-dev
|
||||
- libc6-dbg
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# GCC/Coverage
|
||||
# GCC/Coverage/Autobahn
|
||||
- compiler: gcc
|
||||
env: GCC_VER=5 VARIANT=coverage ADDRESS_MODEL=64
|
||||
env:
|
||||
- GCC_VER=5
|
||||
- VARIANT=coverage
|
||||
- ADDRESS_MODEL=64
|
||||
- BUILD_SYSTEM=cmake
|
||||
- PATH=$PWD/cmake/bin:$PATH
|
||||
addons: &ao_gcc5
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: *gcc5_pkgs
|
||||
|
||||
# # GCC/Debug
|
||||
# - compiler: gcc
|
||||
# env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=64
|
||||
# addons: *ao_gcc5
|
||||
# branches: # NOTE: this does NOT work, though it SHOULD
|
||||
# - master
|
||||
# - develop
|
||||
|
||||
# Clang/UndefinedBehaviourSanitizer
|
||||
- compiler: clang
|
||||
env: GCC_VER=5 VARIANT=usan CLANG_VER=3.8 ADDRESS_MODEL=64 UBSAN_OPTIONS='print_stacktrace=1'
|
||||
addons: &ao_clang38
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8']
|
||||
packages: *clang38_pkgs
|
||||
env:
|
||||
- GCC_VER=5
|
||||
- VARIANT=usan
|
||||
- CLANG_VER=3.8
|
||||
- ADDRESS_MODEL=64
|
||||
- UBSAN_OPTIONS='print_stacktrace=1'
|
||||
- BUILD_SYSTEM=cmake
|
||||
- PATH=$PWD/cmake/bin:$PATH
|
||||
- PATH=$PWD/llvm-$LLVM_VERSION/bin:$PATH
|
||||
addons: *ao_gcc5
|
||||
|
||||
# Clang/AddressSanitizer
|
||||
- compiler: clang
|
||||
env: GCC_VER=5 VARIANT=asan CLANG_VER=3.8 ADDRESS_MODEL=64
|
||||
addons: *ao_clang38
|
||||
env:
|
||||
- GCC_VER=5
|
||||
- VARIANT=asan
|
||||
- CLANG_VER=3.8
|
||||
- ADDRESS_MODEL=64
|
||||
- PATH=$PWD/llvm-$LLVM_VERSION/bin:$PATH
|
||||
addons: *ao_gcc5
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $BOOST_ROOT
|
||||
- $VALGRIND_ROOT
|
||||
- llvm-$LLVM_VERSION
|
||||
- cmake
|
||||
|
||||
before_install:
|
||||
- scripts/install-dependencies.sh
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
1.0.0-b6
|
||||
|
||||
* Use SFINAE on return values
|
||||
* Use beast::error_code instead of nested types
|
||||
* Tidy up use of GENERATING_DOCS
|
||||
* Remove obsolete RFC2616 functions
|
||||
* Add message swap members and free functions
|
||||
* Add HTTP field value parser containers: ext_list, param_list, token_list
|
||||
* Fixes for some corner cases in basic_parser_v1
|
||||
* Configurable limits on headers and body sizes in basic_parser_v1
|
||||
|
||||
API Changes:
|
||||
|
||||
* ci_equal is moved to beast::http namespace, in rfc7230.hpp
|
||||
|
||||
* "DynamicBuffer","dynabuf" renamed from "Streambuf", "streambuf". See:
|
||||
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4478.html#requirements.dynamic_buffers
|
||||
|
||||
* basic_parser_v1 adheres to rfc7230 as strictly as possible
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
86
src/beast/CHANGELOG.md
Normal file
86
src/beast/CHANGELOG.md
Normal file
@@ -0,0 +1,86 @@
|
||||
1.0.0-b13
|
||||
|
||||
* dstream improvements
|
||||
* Remove bin and bin64 directories
|
||||
* Tidy up .vcxproj file groupings
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b12
|
||||
|
||||
* Use -p to print suites from unit test main.
|
||||
* BEAST_EXPECTS to add a reason string to test failures
|
||||
* Fix unit test runner to output all case names
|
||||
* Update README for build requirements
|
||||
* Rename to CHANGELOG.md
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b11
|
||||
|
||||
* Set URI in generated WebSocket Upgrade requests
|
||||
* Rename echo server class and file names
|
||||
* Rename to DynamicBuffer in some code and documentation
|
||||
* Fix integer warnings in Windows builds
|
||||
* Add 32 and 64 bit Windows build support
|
||||
* Update README for build instructions and more
|
||||
* Add repository and documention banners
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b10
|
||||
|
||||
* Fix compilation warnings
|
||||
* Add websocketpp comparison to HTML documentation
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b9
|
||||
|
||||
* Fix CMakeLists.txt
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b8
|
||||
|
||||
* Fix include in example code
|
||||
* Fix basic_headers rfc2616 Section 4.2 compliance
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b7
|
||||
|
||||
* Fix prepare by calling init. prepare() can throw depending on the
|
||||
implementation of Writer. Publicly provided beast::http writers never throw.
|
||||
* Fixes to example HTTP server
|
||||
* Fully qualify ambiguous calls to read and parse
|
||||
* Remove deprecated http::stream wrapper
|
||||
* Example HTTP server now calculates the MIME-type
|
||||
* Fixes and documentation for teardown and use with SSL:
|
||||
* Add example code to rfc7230 javadocs
|
||||
* Remove extraneous header file <beast/http/status.hpp>
|
||||
* Add skip_body parser option
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b6
|
||||
|
||||
* Use SFINAE on return values
|
||||
* Use beast::error_code instead of nested types
|
||||
* Tidy up use of GENERATING_DOCS
|
||||
* Remove obsolete RFC2616 functions
|
||||
* Add message swap members and free functions
|
||||
* Add HTTP field value parser containers: ext_list, param_list, token_list
|
||||
* Fixes for some corner cases in basic_parser_v1
|
||||
* Configurable limits on headers and body sizes in basic_parser_v1
|
||||
|
||||
API Changes:
|
||||
|
||||
* ci_equal is moved to beast::http namespace, in rfc7230.hpp
|
||||
|
||||
* "DynamicBuffer","dynabuf" renamed from "Streambuf", "streambuf". See:
|
||||
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4478.html#requirements.dynamic_buffers
|
||||
|
||||
* basic_parser_v1 adheres to rfc7230 as strictly as possible
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@@ -7,7 +7,7 @@ project (Beast)
|
||||
set_property (GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
if (WIN32)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4 /wd4100 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4 /wd4100 /bigobj /D _WIN32_WINNT=0x0601 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1")
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
|
||||
else()
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
@@ -20,10 +20,29 @@ else()
|
||||
find_package(Threads)
|
||||
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -g -std=c++11 -Wall -Wpedantic")
|
||||
"${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wpedantic")
|
||||
endif()
|
||||
|
||||
message ("cxx Flags: " ${CMAKE_CXX_FLAGS})
|
||||
if ("${VARIANT}" STREQUAL "coverage")
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_BUILD_TYPE RELWITHDEBINFO)
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov")
|
||||
elseif ("${VARIANT}" STREQUAL "asan")
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_BUILD_TYPE RELWITHDEBINFO)
|
||||
elseif ("${VARIANT}" STREQUAL "usan")
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-omit-frame-pointer")
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
|
||||
set(CMAKE_BUILD_TYPE RELWITHDEBINFO)
|
||||
elseif ("${VARIANT}" STREQUAL "debug")
|
||||
set(CMAKE_BUILD_TYPE DEBUG)
|
||||
elseif ("${VARIANT}" STREQUAL "release")
|
||||
set(CMAKE_BUILD_TYPE RELEASE)
|
||||
endif()
|
||||
|
||||
function(DoGroupSources curdir rootdir folder)
|
||||
file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*)
|
||||
@@ -51,6 +70,9 @@ include_directories (include)
|
||||
file(GLOB_RECURSE BEAST_INCLUDES
|
||||
${PROJECT_SOURCE_DIR}/include/beast/*.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/beast/*.ipp
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE EXTRAS_INCLUDES
|
||||
${PROJECT_SOURCE_DIR}/extras/beast/*.hpp
|
||||
${PROJECT_SOURCE_DIR}/extras/beast/*.ipp
|
||||
)
|
||||
@@ -60,5 +82,3 @@ add_subdirectory (test)
|
||||
add_subdirectory (test/core)
|
||||
add_subdirectory (test/http)
|
||||
add_subdirectory (test/websocket)
|
||||
|
||||
#enable_testing()
|
||||
|
||||
@@ -100,6 +100,7 @@ project beast
|
||||
<toolset>clang:<cxxflags>-std=c++11
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
|
||||
<toolset>msvc:<cxxflags>-bigobj
|
||||
<os>LINUX:<define>_XOPEN_SOURCE=600
|
||||
<os>LINUX:<define>_GNU_SOURCE=1
|
||||
<os>SOLARIS:<define>_XOPEN_SOURCE=500
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Beast
|
||||
<img width="880" height = "80" alt = "Beast"
|
||||
src="https://raw.githubusercontent.com/vinniefalco/Beast/master/doc/images/readme.png">
|
||||
|
||||
[](https://gitter.im/vinniefalco/Beast?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status]
|
||||
(https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) [![codecov]
|
||||
@@ -7,23 +8,151 @@
|
||||
(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.
|
||||
# HTTP and WebSocket implementations built on Boost.Asio
|
||||
|
||||
Requirements:
|
||||
---
|
||||
|
||||
* Boost
|
||||
* C++11 or greater
|
||||
* OpenSSL (optional)
|
||||
## CppCon 2016
|
||||
|
||||
This software is currently in beta: interfaces are subject to change. For
|
||||
recent changes see [CHANGELOG](CHANGELOG).
|
||||
I will be giving a lightning talk on Beast at CppCon 2016 in Bellevue,
|
||||
Washington from September 18 to September 22. If you'd like to meet me
|
||||
and hear the talk or ask questions about Beast feel free to approach
|
||||
me in person or send me an email at vinnie.falco@gmail.com to schedule
|
||||
some time.
|
||||
|
||||
About CppCon 2016:
|
||||
http://cppcon.org
|
||||
|
||||
---
|
||||
|
||||
## Contents
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [Description](#description)
|
||||
- [Requirements](#requirements)
|
||||
- [Building](#building)
|
||||
- [Usage](#usage)
|
||||
- [Licence](#licence)
|
||||
- [Contact](#contact)
|
||||
|
||||
## Introduction
|
||||
|
||||
Beast is a header-only, cross-platform C++ library built on Boost.Asio and
|
||||
Boost, containing two modules implementing widely used network protocols.
|
||||
Beast.HTTP offers a universal model for describing, sending, and receiving
|
||||
HTTP messages while Beast.WebSocket provides a complete implementation of
|
||||
the WebSocket protocol. Their design achieves these goals:
|
||||
|
||||
* **Symmetry.** Interfaces are role-agnostic; the same interfaces can be
|
||||
used to build clients, servers, or both.
|
||||
|
||||
* **Ease of Use.** HTTP messages are modeled using simple, readily
|
||||
accessible objects. Functions and classes used to send and receive HTTP
|
||||
or WebSocket messages are designed to resemble Boost.Asio as closely as
|
||||
possible. Users familiar with Boost.Asio will be immediately comfortable
|
||||
using this library.
|
||||
|
||||
* **Flexibility.** Interfaces do not mandate specific implementation
|
||||
strategies; important decisions such as buffer or thread management are
|
||||
left to users of the library.
|
||||
|
||||
* **Performance.** The implementation performs competitively, making it a
|
||||
realistic choice for building high performance network servers.
|
||||
|
||||
* **Scalability.** Development of network applications that scale to thousands
|
||||
of concurrent connections is possible with the implementation.
|
||||
|
||||
* **Basis for further abstraction.** The interfaces facilitate the
|
||||
development of other libraries that provide higher levels of abstraction.
|
||||
|
||||
Beast is used in [rippled](https://github.com/ripple/rippled), an
|
||||
open source server application that implements a decentralized
|
||||
cryptocurrency system.
|
||||
|
||||
## Description
|
||||
|
||||
This software is currently in beta: interfaces may change.
|
||||
For recent changes see the [CHANGELOG](CHANGELOG.md).
|
||||
The library has been submitted to the
|
||||
[Boost Library Incubator](http://rrsd.com/blincubator.com/bi_library/beast-2/?gform_post_id=1579)
|
||||
|
||||
* [Project Site](http://vinniefalco.github.io/)
|
||||
* [Repository](https://github.com/vinniefalco/Beast)
|
||||
* [Project Documentation](http://vinniefalco.github.io/beast/)
|
||||
* [Autobahn.testsuite results](http://vinniefalco.github.io/autobahn/index.html)
|
||||
|
||||
## Requirements
|
||||
|
||||
* Boost 1.58 or higher
|
||||
* C++11 or greater
|
||||
* OpenSSL (optional)
|
||||
|
||||
## Building
|
||||
|
||||
Beast is header-only so there are no libraries to build or link with.
|
||||
To use Beast in your project, simply copy the Beast sources to your
|
||||
project's source tree (alternatively, bring Beast into your Git repository
|
||||
using the `git subtree` or `git submodule` commands). Then, edit your
|
||||
build scripts to add the `include/` directory to the list of paths checked
|
||||
by the C++ compiler when searching for includes. Beast `#include` lines
|
||||
will look like this:
|
||||
```
|
||||
#include <beast/http.hpp>
|
||||
#include <beast/websocket.hpp>
|
||||
```
|
||||
|
||||
To link your program successfully, you'll need to add the Boost.System
|
||||
library to link with. If you use coroutines you'll also need the
|
||||
Boost.Coroutine library. Please visit the Boost documentation for
|
||||
instructions on how to do this for your particular build system.
|
||||
|
||||
For the examples and tests, Beast provides build scripts for Boost.Build (bjam)
|
||||
and CMake. Developers using Microsoft Visual Studio can generate Visual Studio
|
||||
project files by executing these commands from the root of the repository:
|
||||
|
||||
|
||||
```
|
||||
cd bin
|
||||
cmake .. # for 32-bit Windows build
|
||||
|
||||
cd ../bin64
|
||||
cmake .. # for Linux/Mac builds, OR
|
||||
cmake -G"Visual Studio 14 2015 Win64" .. # for 64-bit Windows builds
|
||||
```
|
||||
|
||||
To build with Boost.Build, it is necessary to have the bjam executable
|
||||
in your path. And bjam needs to know how to find the Boost sources. The
|
||||
easiest way to do this is make sure that the version of bjam in your path
|
||||
is the one at the root of the Boost source tree, which is built when
|
||||
running `bootstrap.sh` (or `bootstrap.bat` on Windows).
|
||||
|
||||
Once bjam is in your path, simply run bjam in the root of the Beast
|
||||
repository to automatically build the required Boost libraries if they
|
||||
are not already built, build the examples, then build and run the unit
|
||||
tests.
|
||||
|
||||
The files in the repository are laid out thusly:
|
||||
|
||||
```
|
||||
./
|
||||
bin/ Holds executables and project files
|
||||
bin64/ Holds 64-bit Windows executables and project files
|
||||
include/ Add this to your compiler includes
|
||||
beast/
|
||||
extras/ Additional APIs, may change
|
||||
examples/ Self contained example programs
|
||||
test/ Unit tests and benchmarks
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
These examples are complete, self-contained programs that you can build
|
||||
and run yourself (they are in the `examples` directory).
|
||||
|
||||
Example WebSocket program:
|
||||
```C++
|
||||
#include <beast/to_string.hpp>
|
||||
#include <beast/core/to_string.hpp>
|
||||
#include <beast/websocket.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <iostream>
|
||||
@@ -34,13 +163,13 @@ int main()
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "echo.websocket.org";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r(ios);
|
||||
boost::asio::ip::tcp::socket sock(ios);
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
boost::asio::connect(sock,
|
||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
|
||||
|
||||
// WebSocket connect and send message using beast
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
||||
ws.handshake(host, "/");
|
||||
ws.write(boost::asio::buffer("Hello, world!"));
|
||||
|
||||
@@ -65,8 +194,8 @@ int main()
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "boost.org";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r(ios);
|
||||
boost::asio::ip::tcp::socket sock(ios);
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
boost::asio::connect(sock,
|
||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
|
||||
|
||||
@@ -88,12 +217,13 @@ int main()
|
||||
}
|
||||
```
|
||||
|
||||
Links:
|
||||
## License
|
||||
|
||||
* [Home](http://vinniefalco.github.io/)
|
||||
* [Repository](https://github.com/vinniefalco/Beast)
|
||||
* [Documentation](http://vinniefalco.github.io/beast/)
|
||||
* [Autobahn.testsuite results](http://vinniefalco.github.io/autobahn/index.html)
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file [LICENSE_1_0.txt](LICENSE_1_0.txt) or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
## Contact
|
||||
|
||||
Please report issues or questions here:
|
||||
https://github.com/vinniefalco/Beast/issues
|
||||
|
||||
@@ -15,7 +15,7 @@ using boostbook ;
|
||||
using quickbook ;
|
||||
using doxygen ;
|
||||
|
||||
xml beast_boostbook : beast.qbk ;
|
||||
xml beast_boostbook : master.qbk ;
|
||||
|
||||
path-constant out : . ;
|
||||
|
||||
@@ -56,8 +56,8 @@ boostbook doc
|
||||
<xsl:param>chapter.autolabel=0
|
||||
<xsl:param>boost.image.src=images/beast.png
|
||||
<xsl:param>boost.image.alt="Beast Logo"
|
||||
<xsl:param>boost.image.w=1007
|
||||
<xsl:param>boost.image.h=107
|
||||
<xsl:param>boost.image.w=2400
|
||||
<xsl:param>boost.image.h=80
|
||||
<xsl:param>boost.root=$(broot)
|
||||
<xsl:param>chapter.autolabel=0
|
||||
<xsl:param>chunk.first.sections=1 # Chunk the first top-level section?
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
[section:design Design choices]
|
||||
|
||||
The implementations are driven by business needs of cryptocurrency server
|
||||
applications ([@https://ripple.com Ripple] written in C++. These
|
||||
needs were not met by existing solutions so new code was written. The new
|
||||
code tries to avoid design flaws encountered in the already-existing software
|
||||
applications (e.g. [@https://ripple.com Ripple]) written in C++. These
|
||||
needs were not met by existing solutions so Beast was written from scratch
|
||||
as a solution. Beast's design philosophy avoid flaws exhibited by other
|
||||
libraries:
|
||||
|
||||
* Don't sacrifice performance.
|
||||
@@ -44,11 +44,13 @@ to address those issues.
|
||||
in production. That would give some evidence that the design
|
||||
works in practice.""
|
||||
][
|
||||
Beast.HTTP and Beast.WebSocket will be used in [*rippled], an
|
||||
asynchronous peer to peer server that implements the
|
||||
[*Ripple Consensus Protocol]. These servers are deployed in multiple
|
||||
production environments, with banks in many countries running client
|
||||
applications that connect to [*rippled].
|
||||
Beast.HTTP and Beast.WebSocket are production ready and currently
|
||||
running on public servers receiving traffic and handling millions of
|
||||
dollars worth of financial transactions daily. The servers run [*rippled],
|
||||
open source software ([@https://github.com/ripple/rippled repository])
|
||||
implementing the
|
||||
[@https://ripple.com/files/ripple_consensus_whitepaper.pdf [*Ripple Consensus Protocol]],
|
||||
technology provided by [@http://ripple.com Ripple].
|
||||
]]
|
||||
|
||||
]
|
||||
@@ -171,26 +173,453 @@ start. Other design goals:
|
||||
[section:websocket WebSocket]
|
||||
|
||||
[variablelist
|
||||
|
||||
[[
|
||||
How does this compare to [@https://www.zaphoyd.com/websocketpp websocketpp],
|
||||
an alternate header-only WebSocket implementation?
|
||||
][
|
||||
[variablelist
|
||||
|
||||
[[1. Synchronous Interface][
|
||||
|
||||
Beast offers full support for WebSockets using a synchronous interface. It
|
||||
uses the same style of interfaces found in Boost.Asio: versions that throw
|
||||
exceptions, or versions that return the error code in a reference parameter:
|
||||
|
||||
[table
|
||||
[
|
||||
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
|
||||
[websocketpp]
|
||||
][
|
||||
[```
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
read(opcode& op, DynamicBuffer& dynabuf)
|
||||
```]
|
||||
[
|
||||
/<not available>/
|
||||
]
|
||||
]]]]
|
||||
|
||||
[[2. Connection Model][
|
||||
|
||||
websocketpp supports multiple transports by utilizing a trait, the `config::transport_type`
|
||||
([@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/connection.hpp#L60 asio transport example])
|
||||
To get an idea of the complexity involved with implementing a transport,
|
||||
compare the asio transport to the
|
||||
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59 `iostream` transport]
|
||||
(a layer that allows websocket communication over a std iostream).
|
||||
|
||||
In contrast, Beast abstracts the transport by defining just one [*`NextLayer`]
|
||||
template argument The type requirements for [*`NextLayer`] are
|
||||
already familiar to users as they are documented in Asio:
|
||||
[@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],
|
||||
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html AsyncReadStream], and
|
||||
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html AsyncWriteStream].
|
||||
The type requirements for instantiating `beast::websocket::stream` versus
|
||||
`websocketpp::connection` with user defined types are vastly reduced
|
||||
(18 functions versus 2). Note that websocketpp connections are passed by
|
||||
`shared_ptr`. Beast does not use `shared_ptr` anywhere in its public interface.
|
||||
A `beast::websocket::stream` is constructible and movable in a manner identical
|
||||
`to a boost::asio::ip::tcp::socket`. Callers can put such objects in a
|
||||
`shared_ptr` if they want to, but there is no requirement to do so.
|
||||
|
||||
[table
|
||||
[
|
||||
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp Beast]]
|
||||
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L234 websocketpp]]
|
||||
][
|
||||
[```
|
||||
template<class NextLayer>
|
||||
class stream
|
||||
{
|
||||
NextLayer next_layer_;
|
||||
...
|
||||
}
|
||||
```]
|
||||
[```
|
||||
template <typename config>
|
||||
class connection
|
||||
: public config::transport_type::transport_con_type
|
||||
, public config::connection_base
|
||||
{
|
||||
public:
|
||||
typedef lib::shared_ptr<type> ptr;
|
||||
...
|
||||
}
|
||||
```]
|
||||
]]]]
|
||||
|
||||
[[3. Client and Server Role][
|
||||
|
||||
websocketpp provides multi-role support through a hierarchy of
|
||||
different classes. A `beast::websocket::stream` is role-agnostic, it
|
||||
offers member functions to perform both client and server handshakes
|
||||
in the same class. The same types are used for client and server
|
||||
streams.
|
||||
|
||||
[table
|
||||
[
|
||||
[Beast]
|
||||
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/server_endpoint.hpp#L39 websocketpp],
|
||||
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/client_endpoint.hpp#L42 also]]
|
||||
][
|
||||
[
|
||||
/<not needed>/
|
||||
]
|
||||
[```
|
||||
template <typename config>
|
||||
class client : public endpoint<connection<config>,config>;
|
||||
template <typename config>
|
||||
class server : public endpoint<connection<config>,config>;
|
||||
```]
|
||||
]]]]
|
||||
|
||||
[[4. Thread Safety][
|
||||
|
||||
websocketpp uses mutexes to protect shared data from concurrent
|
||||
access. In contrast, Beast does not use mutexes anywhere in its
|
||||
implementation. Instead, it follows the Asio pattern. Calls to
|
||||
asynchronous initiation functions use the same method to invoke
|
||||
intermediate handlers as the method used to invoke the final handler,
|
||||
through the
|
||||
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/asio_handler_invoke.html asio_handler_invoke] mechanism.
|
||||
|
||||
The only requirement in Beast is that calls to asynchronous initiation
|
||||
functions are made from the same implicit or explicit strand. For
|
||||
example, if the `io_service` associated with a `beast::websocket::stream`
|
||||
is single threaded, this counts as an implicit strand and no performance
|
||||
costs associated with mutexes are incurred.
|
||||
|
||||
[table
|
||||
[
|
||||
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/read_frame_op.ipp#L118 Beast]]
|
||||
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L706 websocketpp]]
|
||||
][
|
||||
[```
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_frame_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::invoke(f, op->d_->h);
|
||||
}
|
||||
```]
|
||||
[```
|
||||
mutex_type m_read_mutex;
|
||||
```]
|
||||
]]]]
|
||||
|
||||
[[5. Callback Model][
|
||||
|
||||
websocketpp requires a one-time call to set the handler for each event
|
||||
in its interface (for example, upon message receipt). The handler is
|
||||
represented by a `std::function equivalent`. Its important to recognize
|
||||
that the websocketpp interface performs type-erasure on this handler.
|
||||
|
||||
In comparison, Beast handlers are specified in a manner identical to
|
||||
Boost.Asio. They are function objects which can be copied or moved but
|
||||
most importantly they are not type erased. The compiler can see
|
||||
through the type directly to the implementation, permitting
|
||||
optimization. Furthermore, Beast follows the Asio rules for treatment
|
||||
of handlers. It respects any allocation, continuation, or invocation
|
||||
customizations associated with the handler through the use of argument
|
||||
dependent lookup overloads of functions such as `asio_handler_allocate`.
|
||||
|
||||
The Beast completion handler is provided at the call site. For each
|
||||
call to an asynchronous initiation function, it is guaranteed that
|
||||
there will be exactly one final call to the handler. This functions
|
||||
exactly the same way as the asynchronous initiation functions found in
|
||||
Boost.Asio, allowing the composition of higher level abstractions.
|
||||
|
||||
[table
|
||||
[
|
||||
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L834 Beast]]
|
||||
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L281 websocketpp],
|
||||
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L473 also]]
|
||||
][
|
||||
[```
|
||||
template<class DynamicBuffer, class ReadHandler>
|
||||
typename async_completion<ReadHandler, void(error_code)>::result_type
|
||||
async_read(opcode& op, DynamicBuffer& dynabuf, ReadHandler&& handler);
|
||||
```]
|
||||
[```
|
||||
typedef lib::function<void(connection_hdl,message_ptr)> message_handler;
|
||||
void set_message_handler(message_handler h);
|
||||
```]
|
||||
]]]]
|
||||
|
||||
[[6. Extensible Asynchronous Model][
|
||||
|
||||
Beast fully supports the
|
||||
[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf Extensible Asynchronous Model]
|
||||
developed by Christopher Kohlhoff, author of Boost.Asio (see Section 8).
|
||||
|
||||
Beast websocket asynchronous interfaces may be used seamlessly with
|
||||
`std::future` stackful/stackless coroutines, or user defined customizations.
|
||||
|
||||
[table
|
||||
[
|
||||
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/stream.ipp#L378 Beast]]
|
||||
[websocketpp]
|
||||
][
|
||||
[```
|
||||
beast::async_completion<ReadHandler, void(error_code)> completion(handler);
|
||||
read_op<DynamicBuffer, decltype(completion.handler)>{
|
||||
completion.handler, *this, op, streambuf};
|
||||
return completion.result.get();
|
||||
```]
|
||||
[
|
||||
/<not available>/
|
||||
]
|
||||
]]]]
|
||||
|
||||
[[7. Message Buffering][
|
||||
|
||||
websocketpp defines a message buffer, passed in arguments by
|
||||
`shared_ptr`, and an associated message manager which permits
|
||||
aggregation and memory reuse of memory. The implementation of
|
||||
`websocketpp::message` uses a `std::string` to hold the payload. If an
|
||||
incoming message is broken up into multiple frames, the string may be
|
||||
reallocated for each continuation frame. The std::string always uses
|
||||
the standard allocator, it is not possible to customize the choice of
|
||||
allocator.
|
||||
|
||||
Beast allows callers to specify the object for receiving the message
|
||||
or frame data, which is of any type meeting the requirements of
|
||||
[@http://vinniefalco.github.io/beast/beast/types/DynamicBuffer.html [*DynamicBuffer]]
|
||||
(modeled after `boost::asio::streambuf`).
|
||||
|
||||
Beast comes with the class `beast::basic_streambuf`, an efficient
|
||||
implementation of the [*DynamicBuffer] concept which makes use of multiple
|
||||
allocated octet arrays. If an incoming message is broken up into
|
||||
multiple pieces, no reallocation occurs. Instead, new allocations are
|
||||
appended to the sequence when existing allocations are filled. Beast
|
||||
does not impose any particular memory management model on callers. The
|
||||
`basic_streambuf` provided by beast supports standard allocators through
|
||||
a template argument. Use the [*DynamicBuffer] that comes with beast,
|
||||
customize the allocator if you desire, or provide your own type that
|
||||
meets the
|
||||
[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/basic_streambuf.hpp#L21 concept requirements].
|
||||
|
||||
[table
|
||||
[
|
||||
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
|
||||
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/message_buffer/message.hpp#L78 websocketpp]]
|
||||
][
|
||||
[```
|
||||
template<class DynamicBuffer>
|
||||
read(opcode& op, DynamicBuffer& dynabuf);
|
||||
```]
|
||||
[```
|
||||
template <template<class> class con_msg_manager>
|
||||
class message {
|
||||
public:
|
||||
typedef lib::shared_ptr<message> ptr;
|
||||
...
|
||||
std::string m_payload;
|
||||
...
|
||||
};
|
||||
```]
|
||||
]]]]
|
||||
|
||||
[[8. Sending Messages][
|
||||
|
||||
When sending a message, websocketpp requires that the payload is
|
||||
packaged in a `websocketpp::message` object using `std::string` as the
|
||||
storage, or it requires a copy of the caller provided buffer by
|
||||
constructing a new message object. Messages are placed onto an
|
||||
outgoing queue. An asynchronous write operation runs in the background
|
||||
to clear the queue. No user facing handler can be registered to be
|
||||
notified when messages or frames have completed sending.
|
||||
|
||||
Beast doesn't allocate or make copies of buffers when sending data. The
|
||||
caller's buffers are sent in-place. You can use any object meeting the
|
||||
requirements of
|
||||
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html ConstBufferSequence],
|
||||
permitting efficient scatter-gather I/O.
|
||||
|
||||
The [*ConstBufferSequence] interface allows callers to send data from
|
||||
memory-mapped regions (not possible in websocketpp). Callers can also
|
||||
use the same buffers to send data to multiple streams, for example
|
||||
broadcasting common subscription data to many clients at once. For
|
||||
each call to `async_write` the completion handler is called once when
|
||||
the data finishes sending, in a manner identical to `boost::asio::async_write`.
|
||||
|
||||
[table
|
||||
[
|
||||
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1048 Beast]]
|
||||
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L672 websocketpp]]
|
||||
][
|
||||
[```
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
write(ConstBufferSequence const& buffers);
|
||||
```]
|
||||
[```
|
||||
lib::error_code send(std::string const & payload,
|
||||
frame::opcode::value op = frame::opcode::text);
|
||||
...
|
||||
lib::error_code send(message_ptr msg);
|
||||
```]
|
||||
]]]]
|
||||
|
||||
[[9. Streaming Messages][
|
||||
|
||||
websocketpp requires that the entire message fit into memory, and that
|
||||
the size is known ahead of time.
|
||||
|
||||
Beast allows callers to compose messages in individual frames. This is
|
||||
useful when the size of the data is not known ahead of time or if it
|
||||
is not desired to buffer the entire message in memory at once before
|
||||
sending it. For example, sending periodic output of a database query
|
||||
running on a coroutine. Or sending the contents of a file in pieces,
|
||||
without bringing it all into memory.
|
||||
|
||||
[table
|
||||
[
|
||||
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1151 Beast]]
|
||||
[websocketpp]
|
||||
][
|
||||
[```
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
write_frame(bool fin,
|
||||
ConstBufferSequence const& buffers);
|
||||
```]
|
||||
[
|
||||
/<not available>/
|
||||
]
|
||||
]]]]
|
||||
|
||||
[[10. Flow Control][
|
||||
|
||||
The websocketpp read implementation continuously reads asynchronously
|
||||
from the network and buffers message data. To prevent unbounded growth
|
||||
and leverage TCP/IP's flow control mechanism, callers can periodically
|
||||
turn this 'read pump' off and back on.
|
||||
|
||||
In contrast a `beast::websocket::stream` does not independently begin
|
||||
background activity, nor does it buffer messages. It receives data only
|
||||
when there is a call to an asynchronous initiation function (for
|
||||
example `beast::websocket::stream::async_read`) with an associated handler.
|
||||
Applications do not need to implement explicit logic to regulate the
|
||||
flow of data. Instead, they follow the traditional model of issuing a
|
||||
read, receiving a read completion, processing the message, then
|
||||
issuing a new read and repeating the process.
|
||||
|
||||
[table
|
||||
[
|
||||
[Beast]
|
||||
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L728 websocketpp]]
|
||||
][
|
||||
[
|
||||
/<implicit>/
|
||||
]
|
||||
[```
|
||||
lib::error_code pause_reading();
|
||||
lib::error_code resume_reading();
|
||||
```]
|
||||
]]]]
|
||||
|
||||
[[11. Connection Establishment][
|
||||
|
||||
websocketpp offers the `endpoint` class which can handle binding and
|
||||
listening to a port, and spawning connection objects.
|
||||
|
||||
Beast does not reinvent the wheel here, callers use the interfaces
|
||||
already in `boost::asio` for receiving incoming connections resolving
|
||||
host names, or establishing outgoing connections. After the socket (or
|
||||
`boost::asio::ssl::stream`) is connected, the `beast::websocket::stream`
|
||||
is constructed around it and the WebSocket handshake can be performed.
|
||||
|
||||
Beast users are free to implement their own "connection manager", but
|
||||
there is no requirement to do so.
|
||||
|
||||
[table
|
||||
[
|
||||
[[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/async_connect.html Beast],
|
||||
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept.html also]]
|
||||
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/endpoint.hpp#L52 websocketpp]]
|
||||
][
|
||||
[```
|
||||
#include <boost/asio.hpp>
|
||||
```]
|
||||
[```
|
||||
template <typename config>
|
||||
class endpoint : public config::socket_type;
|
||||
```]
|
||||
]]]]
|
||||
|
||||
[[12. WebSocket Handshaking][
|
||||
|
||||
Callers invoke `beast::websocket::accept` to perform the WebSocket
|
||||
handshake, but there is no requirement to use this function. Advanced
|
||||
users can perform the WebSocket handshake themselves. Beast WebSocket
|
||||
provides the tools for composing the request or response, and the
|
||||
Beast HTTP interface provides the container and algorithms for sending
|
||||
and receiving HTTP/1 messages including the necessary HTTP Upgrade
|
||||
request for establishing the WebSocket session.
|
||||
|
||||
Beast allows the caller to pass the incoming HTTP Upgrade request for
|
||||
the cases where the caller has already received an HTTP message.
|
||||
This flexibility permits novel and robust implementations. For example,
|
||||
a listening socket that can handshake in multiple protocols on the
|
||||
same port.
|
||||
|
||||
Sometimes callers want to read some bytes on the socket before reading
|
||||
the WebSocket HTTP Upgrade request. Beast allows these already-received
|
||||
bytes to be supplied to an overload of the accepting function to permit
|
||||
sophisticated features. For example, a listening socket that can
|
||||
accept both regular WebSocket and Secure WebSocket (SSL) connections.
|
||||
|
||||
[table
|
||||
[
|
||||
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L501 Beast],
|
||||
[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L401 also]]
|
||||
[websocketpp]
|
||||
][
|
||||
[```
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
accept(ConstBufferSequence const& buffers);
|
||||
|
||||
template<class Body, class Headers>
|
||||
void
|
||||
accept(http::request_v1<Body, Headers> const& request);
|
||||
```]
|
||||
[
|
||||
/<not available>/
|
||||
]
|
||||
]]]]
|
||||
|
||||
]
|
||||
]]
|
||||
|
||||
[[
|
||||
What about message compression?
|
||||
][
|
||||
The feature is not currently present in the library, but the choice
|
||||
of type requirements for buffers passed to the read functions have been
|
||||
made with compression in mind. There is the plan to add this feature;
|
||||
however, we feel that even without compression users can begin taking
|
||||
advantage of the WebSocket protocol immediately with this library.
|
||||
The author is currently porting ZLib 1.2.8 to modern, header-only C++11
|
||||
that does not use macros or try to support ancient architectures. This
|
||||
deflate implementation will be available as its own individually
|
||||
usable interface, and also will be used to power Beast WebSocket's
|
||||
permessage-deflate implementation, due Q4 of 2016.
|
||||
|
||||
However, Beast currently has sufficient functionality that users can
|
||||
begin taking advantage of the WebSocket protocol using this library
|
||||
immediately.
|
||||
]]
|
||||
|
||||
[[
|
||||
Where is the TLS/SSL interface?
|
||||
][
|
||||
The `websocket::stream` just wraps the socket or stream that you
|
||||
provide (for example, a `boost::asio::ip::tcp::socket` or a
|
||||
`boost::asio::ssl::stream`). You establish your TLS connection
|
||||
using the interface on `ssl::stream` like shown in all of the Asio
|
||||
examples, they construct your `websocket::stream` around it.
|
||||
It works perfectly fine - Beast.WebSocket doesn't try to reinvent the
|
||||
wheel or put a fresh coat of interface paint on the `ssl::stream`.
|
||||
The `websocket::stream` wraps the socket or stream that you provide
|
||||
(for example, a `boost::asio::ip::tcp::socket` or a
|
||||
`boost::asio::ssl::stream`). You establish your TLS connection using the
|
||||
interface on `ssl::stream` like shown in all of the Asio examples, they
|
||||
construct your `websocket::stream` around it. It works perfectly fine;
|
||||
Beast.WebSocket doesn't try to reinvent the wheel or put a fresh coat of
|
||||
interface paint on the `ssl::stream`.
|
||||
|
||||
The WebSocket implementation [*does] provides support for shutting down
|
||||
the TLS connection through the use of the ADL compile-time virtual functions
|
||||
@@ -200,14 +629,11 @@ start. Other design goals:
|
||||
for TLS streams. Callers may provide their own overloads of these functions
|
||||
for user-defined next layer types.
|
||||
]]
|
||||
|
||||
]
|
||||
|
||||
[endsect]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -92,19 +92,20 @@ and its customization points in more depth, for advanced applications.
|
||||
[heading Declarations]
|
||||
|
||||
To do anything, a message must be declared. The message class template
|
||||
requires at mininum, a value indicating whether the message is a request
|
||||
requires at minimum, a value indicating whether the message is a request
|
||||
(versus a response), and a `Body` type. The choice of `Body` determines the
|
||||
kind of container used to represent the message body. Here we will
|
||||
declare a request that has a `std::string` for the body container:
|
||||
declare a HTTP/1 request that has a `std::string` for the body container:
|
||||
```
|
||||
http::message<true, http::string_body> req;
|
||||
http::message_v1<true, http::string_body> req;
|
||||
```
|
||||
|
||||
Two type aliases are provided for notational convenience when declaring
|
||||
messages. These two statements declare a request and a response respectively:
|
||||
HTTP/1 messages. These two statements declare a request and a response
|
||||
respectively:
|
||||
```
|
||||
http::request<http::string_body> req;
|
||||
http::response<http::string_body> resp;
|
||||
http::request_v1<http::string_body> req;
|
||||
http::response_v1<http::string_body> resp;
|
||||
```
|
||||
|
||||
[heading Members]
|
||||
@@ -113,14 +114,14 @@ Message objects are default constructible, with public access to data members.
|
||||
Request and response objects have some common members, and some members unique
|
||||
to the message type. These statements set all the members in each message:
|
||||
```
|
||||
http::request<http::string_body> req;
|
||||
req.method = http::method_t::http_get;
|
||||
http::request_v1<http::string_body> req;
|
||||
req.method = "GET";
|
||||
req.url = "/index.html";
|
||||
req.version = 11; // HTTP/1.1
|
||||
req.headers.insert("User-Agent", "hello_world");
|
||||
req.body = "";
|
||||
|
||||
http::response<http::string_body> resp;
|
||||
http::response_v1<http::string_body> resp;
|
||||
resp.status = 404;
|
||||
resp.reason = "Not Found";
|
||||
resp.version = 10; // HTTP/1.0
|
||||
@@ -128,17 +129,6 @@ to the message type. These statements set all the members in each message:
|
||||
resp.body = "The requested resource was not found.";
|
||||
```
|
||||
|
||||
The following statements achieve the same effects as the statements above:
|
||||
```
|
||||
http::request<http::string_body> req({http::method_t::http_get, "/index.html", 11});
|
||||
req.headers.insert("User-Agent", "hello_world");
|
||||
req.body = "";
|
||||
|
||||
http::response<http::string_body> resp({404, "Not Found", 10});
|
||||
resp.headers.insert("Server", "Beast.HTTP");
|
||||
resp.body = "The requested resource was not found.";
|
||||
```
|
||||
|
||||
[heading Headers]
|
||||
|
||||
The `message::headers` member is a container for setting the field/value
|
||||
@@ -146,7 +136,7 @@ pairs in the message. These statements change the values of the headers
|
||||
in the message passed:
|
||||
```
|
||||
template<class Body>
|
||||
void set_fields(http::request<Body>& req)
|
||||
void set_fields(http::request_v1<Body>& req)
|
||||
{
|
||||
if(! req.exists("User-Agent"))
|
||||
req.insert("User-Agent", "myWebClient");
|
||||
@@ -168,7 +158,10 @@ following types, provided by the library, are suitable choices for the
|
||||
* [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
|
||||
Used in GET requests where there is no message body. Example:
|
||||
```
|
||||
http::request<http::empty_body> req({http::method_t::http_get, "/index.html", 11});
|
||||
http::request_v1<http::empty_body> req;
|
||||
req.version = 11;
|
||||
req.method = "GET";
|
||||
req.url = "/index.html";
|
||||
```
|
||||
|
||||
* [link beast.ref.http__string_body [*`string_body`:]] A body with a
|
||||
@@ -177,7 +170,7 @@ or response with simple text in the message body (such as an error message).
|
||||
Has the same insertion complexity of `std::string`. This is the type of body
|
||||
used in the examples:
|
||||
```
|
||||
http::response<http::string_body> resp;
|
||||
http::response_v1<http::string_body> resp;
|
||||
static_assert(std::is_same<decltype(resp.body), std::string>::value);
|
||||
resp.body = "Here is the data you requested";
|
||||
```
|
||||
@@ -197,7 +190,10 @@ functions:
|
||||
```
|
||||
void send_request(boost::asio::ip::tcp::socket& sock)
|
||||
{
|
||||
http::request<http::empty_body> req({http::method_t::http_get, "/index.html", 11});
|
||||
http::request<http::empty_body> req;
|
||||
req.version = 11;
|
||||
req.method = "GET";
|
||||
req.url = "/index.html";
|
||||
...
|
||||
http::write(sock, req); // Throws exception on error
|
||||
...
|
||||
@@ -213,7 +209,7 @@ An asynchronous interface is available:
|
||||
```
|
||||
void handle_write(boost::system::error_code);
|
||||
...
|
||||
http::request<http::empty_body> req;
|
||||
http::request_v1<http::empty_body> req;
|
||||
...
|
||||
http::async_write(sock, req, std::bind(&handle_write, std::placeholders::_1));
|
||||
```
|
||||
@@ -222,13 +218,13 @@ When the implementation reads messages from a socket, it can read bytes lying
|
||||
after the end of the message if they are present (the alternative is to read
|
||||
a single byte at a time which is unsuitable for performance reasons). To
|
||||
store and re-use these extra bytes on subsequent messages, the read interface
|
||||
requires an additional paramter: a [link beast.types.DynamicBuffer [*`DynamicBuffer`]]
|
||||
requires an additional parameter: a [link beast.types.DynamicBuffer [*`DynamicBuffer`]]
|
||||
object. This example reads a message from the socket, with the extra bytes
|
||||
stored in the streambuf parameter for use in a subsequent call to read:
|
||||
```
|
||||
boost::asio::streambuf sb;
|
||||
...
|
||||
http::response<http::string_body> resp;
|
||||
http::response_v1<http::string_body> resp;
|
||||
http::read(sock, sb, resp); // Throws exception on error
|
||||
...
|
||||
// Alternatively
|
||||
@@ -245,7 +241,7 @@ called:
|
||||
void handle_read(boost::system::error_code);
|
||||
...
|
||||
boost::asio::streambuf sb;
|
||||
http::response<http::string_body> resp;
|
||||
http::response_v1<http::string_body> resp;
|
||||
...
|
||||
http::async_read(sock, resp, std::bind(&handle_read, std::placeholders::_1));
|
||||
```
|
||||
@@ -257,7 +253,7 @@ An alternative to using a `boost::asio::streambuf` is to use a
|
||||
void handle_read(boost::system::error_code);
|
||||
...
|
||||
beast::streambuf sb;
|
||||
http::response<http::string_body> resp;
|
||||
http::response_v1<http::string_body> resp;
|
||||
http::read(sock, sb, resp);
|
||||
```
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 241 KiB |
Binary file not shown.
BIN
src/beast/doc/images/readme.png
Normal file
BIN
src/beast/doc/images/readme.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
@@ -6,7 +6,7 @@
|
||||
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
mkdir -p ../bin/doc/xml
|
||||
doxygen beast.dox
|
||||
doxygen source.dox
|
||||
cd ../bin/doc/xml
|
||||
xsltproc combine.xslt index.xml > all.xml
|
||||
cd ../../../doc
|
||||
|
||||
@@ -39,11 +39,11 @@
|
||||
|
||||
[section:intro Introduction]
|
||||
|
||||
Beast is a cross-platform C++ library built on Boost.Asio and Boost, containing
|
||||
two modules implementing widely used network protocols. Beast.HTTP offers a
|
||||
universal model for describing, sending, and receiving HTTP messages while
|
||||
Beast.WebSocket provides a complete implementation of the WebSocket protocol.
|
||||
Their design achieves these goals:
|
||||
Beast is a header-only, cross-platform C++ library built on Boost.Asio and
|
||||
Boost, containing two modules implementing widely used network protocols.
|
||||
Beast.HTTP offers a universal model for describing, sending, and receiving
|
||||
HTTP messages while Beast.WebSocket provides a complete implementation of
|
||||
the WebSocket protocol. Their design achieves these goals:
|
||||
|
||||
* [*Symmetry.] Interfaces are role-agnostic; the same interfaces can be
|
||||
used to build clients, servers, or both.
|
||||
@@ -106,8 +106,8 @@ int main()
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "boost.org";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r(ios);
|
||||
boost::asio::ip::tcp::socket sock(ios);
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
boost::asio::connect(sock,
|
||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
|
||||
|
||||
@@ -131,7 +131,7 @@ int main()
|
||||
|
||||
Establish a WebSocket connection, send a message and receive the reply:
|
||||
```
|
||||
#include <beast/to_string.hpp>
|
||||
#include <beast/core/to_string.hpp>
|
||||
#include <beast/websocket.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <iostream>
|
||||
@@ -142,13 +142,13 @@ int main()
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "echo.websocket.org";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r(ios);
|
||||
boost::asio::ip::tcp::socket sock(ios);
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
boost::asio::connect(sock,
|
||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
|
||||
|
||||
// WebSocket connect and send message using beast
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
||||
ws.handshake(host, "/");
|
||||
ws.write(boost::asio::buffer("Hello, world!"));
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.http__body_max_size">body_max_size</link></member>
|
||||
<member><link linkend="beast.ref.http__headers_max_size">headers_max_size</link></member>
|
||||
<member><link linkend="beast.ref.http__skip_body">skip_body</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
@@ -85,6 +86,7 @@
|
||||
<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>
|
||||
<member><link linkend="beast.ref.websocket__teardown_tag">teardown_tag</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Options</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.beast.org/LICENSE_1_0.txt)
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
-->
|
||||
|
||||
<xsl:output method="text"/>
|
||||
|
||||
@@ -66,7 +66,7 @@ both Boost.Asio and the WebSocket protocol specification described in
|
||||
|
||||
|
||||
|
||||
[section:creating Creating the socket]
|
||||
[section:creation Creation]
|
||||
|
||||
The interface to Beast's WebSocket implementation is a single template
|
||||
class [link beast.ref.websocket__stream `beast::websocket::stream`] which
|
||||
@@ -75,24 +75,40 @@ 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:
|
||||
passed to next layer's constructor. Here we declare a websocket stream over
|
||||
a TCP/IP socket with ownership of the socket:
|
||||
```
|
||||
boost::asio::io_service ios;
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
||||
|
||||
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);
|
||||
```
|
||||
|
||||
[heading Using SSL]
|
||||
|
||||
To use WebSockets over SSL, choose an SSL stream for the next layer template
|
||||
argument when constructing the stream.
|
||||
```
|
||||
#include <beast/websocket/ssl.hpp>
|
||||
#include <beast/websocket.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
boost::asio::io_service ios;
|
||||
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);
|
||||
```
|
||||
|
||||
[note
|
||||
When creating websocket stream objects using SSL, it is necessary
|
||||
to include the file `<beast/websocket/ssl.hpp>`.
|
||||
]
|
||||
|
||||
[heading Non-owning references]
|
||||
|
||||
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:
|
||||
```
|
||||
boost::asio::ip::tcp::socket&& sock;
|
||||
...
|
||||
beast::websocket::stream<
|
||||
boost::asio::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
|
||||
@@ -108,8 +124,7 @@ The layer being wrapped can be accessed through the websocket's "next layer",
|
||||
permitting callers to interact directly with its interface.
|
||||
```
|
||||
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);
|
||||
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws(ios, ctx);
|
||||
...
|
||||
ws.next_layer().shutdown(); // ssl::stream shutdown
|
||||
```
|
||||
|
||||
@@ -1,49 +1,52 @@
|
||||
# Part of Beast
|
||||
|
||||
GroupSources(extras/beast beast)
|
||||
GroupSources(extras/beast extras)
|
||||
GroupSources(include/beast beast)
|
||||
|
||||
GroupSources(examples "/")
|
||||
|
||||
add_executable (http-crawl
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
urls_large_data.hpp
|
||||
urls_large_data.cpp
|
||||
http_crawl.cpp
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(http-crawl ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(http-crawl ${Boost_LIBRARIES} Threads::Threads)
|
||||
endif()
|
||||
|
||||
add_executable (http-server
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
file_body.hpp
|
||||
mime_type.hpp
|
||||
http_async_server.hpp
|
||||
http_stream.hpp
|
||||
http_stream.ipp
|
||||
http_sync_server.hpp
|
||||
http_server.cpp
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(http-server ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(http-server ${Boost_LIBRARIES} Threads::Threads)
|
||||
endif()
|
||||
|
||||
add_executable (http-example
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
http_example.cpp
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(http-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(http-example ${Boost_LIBRARIES} Threads::Threads)
|
||||
endif()
|
||||
|
||||
add_executable (websocket-example
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
websocket_example.cpp
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(websocket-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(websocket-example ${Boost_LIBRARIES} Threads::Threads)
|
||||
endif()
|
||||
|
||||
@@ -23,7 +23,7 @@ struct file_body
|
||||
|
||||
class writer
|
||||
{
|
||||
std::uint64_t size_;
|
||||
std::uint64_t size_ = 0;
|
||||
std::uint64_t offset_ = 0;
|
||||
std::string const& path_;
|
||||
FILE* file_ = nullptr;
|
||||
|
||||
@@ -9,10 +9,13 @@
|
||||
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
|
||||
|
||||
#include "file_body.hpp"
|
||||
#include "http_stream.hpp"
|
||||
#include "mime_type.hpp"
|
||||
|
||||
#include <beast/http.hpp>
|
||||
#include <beast/core/placeholders.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
@@ -32,17 +35,19 @@ class http_async_server
|
||||
using req_type = request_v1<string_body>;
|
||||
using resp_type = response_v1<file_body>;
|
||||
|
||||
std::mutex m_;
|
||||
bool log_ = true;
|
||||
boost::asio::io_service ios_;
|
||||
socket_type sock_;
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
socket_type sock_;
|
||||
std::string root_;
|
||||
std::vector<std::thread> thread_;
|
||||
|
||||
public:
|
||||
http_async_server(endpoint_type const& ep,
|
||||
int threads, std::string const& root)
|
||||
: sock_(ios_)
|
||||
, acceptor_(ios_)
|
||||
std::size_t threads, std::string const& root)
|
||||
: acceptor_(ios_)
|
||||
, sock_(ios_)
|
||||
, root_(root)
|
||||
{
|
||||
acceptor_.open(ep.protocol());
|
||||
@@ -53,7 +58,7 @@ public:
|
||||
std::bind(&http_async_server::on_accept, this,
|
||||
beast::asio::placeholders::error));
|
||||
thread_.reserve(threads);
|
||||
for(int i = 0; i < threads; ++i)
|
||||
for(std::size_t i = 0; i < threads; ++i)
|
||||
thread_.emplace_back(
|
||||
[&] { ios_.run(); });
|
||||
}
|
||||
@@ -67,13 +72,124 @@ public:
|
||||
t.join();
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void
|
||||
log(Args const&... args)
|
||||
{
|
||||
if(log_)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
log_args(args...);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Headers>
|
||||
class write_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
Stream& s;
|
||||
message_v1<isRequest, Body, Headers> m;
|
||||
Handler h;
|
||||
bool cont;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, Stream& s_,
|
||||
message_v1<isRequest, Body, Headers>&& m_)
|
||||
: s(s_)
|
||||
, m(std::move(m_))
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, bool again = true)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
if(! again)
|
||||
{
|
||||
beast::http::async_write(d.s, d.m, std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream,
|
||||
bool isRequest, class Body, class Headers,
|
||||
class DeducedHandler>
|
||||
static
|
||||
void
|
||||
async_write(Stream& stream, message_v1<
|
||||
isRequest, Body, Headers>&& msg,
|
||||
DeducedHandler&& handler)
|
||||
{
|
||||
write_op<Stream, typename std::decay<DeducedHandler>::type,
|
||||
isRequest, Body, Headers>{std::forward<DeducedHandler>(
|
||||
handler), stream, std::move(msg)};
|
||||
}
|
||||
|
||||
class peer : public std::enable_shared_from_this<peer>
|
||||
{
|
||||
int id_;
|
||||
stream<socket_type> stream_;
|
||||
streambuf sb_;
|
||||
socket_type sock_;
|
||||
http_async_server& server_;
|
||||
boost::asio::io_service::strand strand_;
|
||||
std::string root_;
|
||||
req_type req_;
|
||||
|
||||
public:
|
||||
@@ -82,16 +198,22 @@ private:
|
||||
peer& operator=(peer&&) = delete;
|
||||
peer& operator=(peer const&) = delete;
|
||||
|
||||
explicit
|
||||
peer(socket_type&& sock, std::string const& root)
|
||||
: stream_(std::move(sock))
|
||||
, strand_(stream_.get_io_service())
|
||||
, root_(root)
|
||||
peer(socket_type&& sock, http_async_server& server)
|
||||
: sock_(std::move(sock))
|
||||
, server_(server)
|
||||
, strand_(sock_.get_io_service())
|
||||
{
|
||||
static int n = 0;
|
||||
id_ = ++n;
|
||||
}
|
||||
|
||||
void
|
||||
fail(error_code ec, std::string what)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
server_.log("#", id_, " ", what, ": ", ec.message(), "\n");
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
do_read();
|
||||
@@ -99,43 +221,58 @@ private:
|
||||
|
||||
void do_read()
|
||||
{
|
||||
stream_.async_read(req_, strand_.wrap(
|
||||
async_read(sock_, sb_, req_, strand_.wrap(
|
||||
std::bind(&peer::on_read, shared_from_this(),
|
||||
asio::placeholders::error)));
|
||||
}
|
||||
|
||||
void on_read(error_code ec)
|
||||
void on_read(error_code const& ec)
|
||||
{
|
||||
if(ec)
|
||||
return fail(ec, "read");
|
||||
do_read();
|
||||
auto path = req_.url;
|
||||
if(path == "/")
|
||||
path = "/index.html";
|
||||
path = root_ + path;
|
||||
path = server_.root_ + path;
|
||||
if(! boost::filesystem::exists(path))
|
||||
{
|
||||
response_v1<string_body> resp;
|
||||
resp.status = 404;
|
||||
resp.reason = "Not Found";
|
||||
resp.version = req_.version;
|
||||
resp.headers.replace("Server", "http_async_server");
|
||||
resp.body = "The file '" + path + "' was not found";
|
||||
prepare(resp);
|
||||
stream_.async_write(std::move(resp),
|
||||
response_v1<string_body> res;
|
||||
res.status = 404;
|
||||
res.reason = "Not Found";
|
||||
res.version = req_.version;
|
||||
res.headers.insert("Server", "http_async_server");
|
||||
res.headers.insert("Content-Type", "text/html");
|
||||
res.body = "The file '" + path + "' was not found";
|
||||
prepare(res);
|
||||
async_write(sock_, std::move(res),
|
||||
std::bind(&peer::on_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
return;
|
||||
}
|
||||
resp_type resp;
|
||||
resp.status = 200;
|
||||
resp.reason = "OK";
|
||||
resp.version = req_.version;
|
||||
resp.headers.replace("Server", "http_async_server");
|
||||
resp.headers.replace("Content-Type", "text/html");
|
||||
resp.body = path;
|
||||
prepare(resp);
|
||||
stream_.async_write(std::move(resp),
|
||||
resp_type res;
|
||||
res.status = 200;
|
||||
res.reason = "OK";
|
||||
res.version = req_.version;
|
||||
res.headers.insert("Server", "http_async_server");
|
||||
res.headers.insert("Content-Type", mime_type(path));
|
||||
res.body = path;
|
||||
try
|
||||
{
|
||||
prepare(res);
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
res = {};
|
||||
res.status = 500;
|
||||
res.reason = "Internal Error";
|
||||
res.version = req_.version;
|
||||
res.headers.insert("Server", "http_async_server");
|
||||
res.headers.insert("Content-Type", "text/html");
|
||||
res.body =
|
||||
std::string{"An internal error occurred"} + e.what();
|
||||
prepare(res);
|
||||
}
|
||||
async_write(sock_, std::move(res),
|
||||
std::bind(&peer::on_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
@@ -144,36 +281,27 @@ private:
|
||||
{
|
||||
if(ec)
|
||||
fail(ec, "write");
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
fail(error_code ec, std::string what)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
{
|
||||
std::cerr <<
|
||||
"#" << std::to_string(id_) << " " <<
|
||||
what << ": " << ec.message() << std::endl;
|
||||
}
|
||||
do_read();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
fail(error_code ec, std::string what)
|
||||
log_args()
|
||||
{
|
||||
std::cerr <<
|
||||
what << ": " << ec.message() << std::endl;
|
||||
}
|
||||
|
||||
template<class Arg, class... Args>
|
||||
void
|
||||
log_args(Arg const& arg, Args const&... args)
|
||||
{
|
||||
std::cerr << arg;
|
||||
log_args(args...);
|
||||
}
|
||||
|
||||
void
|
||||
maybe_throw(error_code ec, std::string what)
|
||||
fail(error_code ec, std::string what)
|
||||
{
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, what);
|
||||
throw ec;
|
||||
}
|
||||
log(what, ": ", ec.message(), "\n");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -181,12 +309,13 @@ private:
|
||||
{
|
||||
if(! acceptor_.is_open())
|
||||
return;
|
||||
maybe_throw(ec, "accept");
|
||||
if(ec)
|
||||
return fail(ec, "accept");
|
||||
socket_type sock(std::move(sock_));
|
||||
acceptor_.async_accept(sock_,
|
||||
std::bind(&http_async_server::on_accept, this,
|
||||
asio::placeholders::error));
|
||||
std::make_shared<peer>(std::move(sock), root_)->run();
|
||||
std::make_shared<peer>(std::move(sock), *this)->run();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "http_stream.hpp"
|
||||
#include "urls_large_data.hpp"
|
||||
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/http.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <iostream>
|
||||
|
||||
@@ -31,9 +32,9 @@ int main(int, char const*[])
|
||||
ip::tcp::resolver r(ios);
|
||||
auto it = r.resolve(
|
||||
ip::tcp::resolver::query{host, "http"});
|
||||
stream<ip::tcp::socket> hs(ios);
|
||||
connect(hs.lowest_layer(), it);
|
||||
auto ep = hs.lowest_layer().remote_endpoint();
|
||||
ip::tcp::socket sock(ios);
|
||||
connect(sock, it);
|
||||
auto ep = sock.remote_endpoint();
|
||||
request_v1<empty_body> req;
|
||||
req.method = "GET";
|
||||
req.url = "/";
|
||||
@@ -42,10 +43,11 @@ int main(int, char const*[])
|
||||
std::string(":") + std::to_string(ep.port()));
|
||||
req.headers.insert("User-Agent", "beast/http");
|
||||
prepare(req);
|
||||
hs.write(req);
|
||||
response_v1<string_body> resp;
|
||||
hs.read(resp);
|
||||
std::cout << resp;
|
||||
write(sock, req);
|
||||
response_v1<string_body> res;
|
||||
streambuf sb;
|
||||
beast::http::read(sock, sb, res);
|
||||
std::cout << res;
|
||||
}
|
||||
catch(boost::system::system_error const& ec)
|
||||
{
|
||||
|
||||
@@ -15,8 +15,8 @@ int main()
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "boost.org";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r(ios);
|
||||
boost::asio::ip::tcp::socket sock(ios);
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
boost::asio::connect(sock,
|
||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
|
||||
|
||||
|
||||
@@ -57,8 +57,13 @@ int main(int ac, char const* av[])
|
||||
endpoint_type ep{address_type::from_string(ip), port};
|
||||
|
||||
if(sync)
|
||||
{
|
||||
http_sync_server server(ep, root);
|
||||
beast::test::sig_wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
http_async_server server(ep, threads, root);
|
||||
beast::test::sig_wait();
|
||||
beast::test::sig_wait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,480 +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_STREAM_H_INCLUDED
|
||||
#define BEAST_HTTP_STREAM_H_INCLUDED
|
||||
|
||||
#include <beast/core/async_completion.hpp>
|
||||
#include <beast/core/basic_streambuf.hpp>
|
||||
#include <beast/http.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class stream_base
|
||||
{
|
||||
protected:
|
||||
struct op
|
||||
: boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>
|
||||
{
|
||||
virtual ~op() = default;
|
||||
virtual void operator()() = 0;
|
||||
virtual void cancel() = 0;
|
||||
};
|
||||
|
||||
using op_list = typename boost::intrusive::make_list<
|
||||
op, boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
op_list wr_q_;
|
||||
bool wr_active_ = false;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/** Provides message-oriented functionality using HTTP.
|
||||
|
||||
The stream class template provides asynchronous and blocking
|
||||
message-oriented functionality necessary for clients and servers
|
||||
to utilize the HTTP protocol.
|
||||
|
||||
@par Thread Safety
|
||||
@e Distinct @e objects: Safe.@n
|
||||
@e Shared @e objects: Unsafe. The application must ensure that
|
||||
all asynchronous operations are performed within the same
|
||||
implicit or explicit strand.
|
||||
|
||||
@par Example
|
||||
|
||||
To use the class template with an `ip::tcp::socket`, you would write:
|
||||
|
||||
@code
|
||||
http::stream<ip::tcp::socket> hs(io_service);
|
||||
@endcode
|
||||
Alternatively, you can write:
|
||||
@code
|
||||
ip::tcp::socket sock(io_service);
|
||||
http::stream<ip::tcp::socket&> hs(sock);
|
||||
@endcode
|
||||
|
||||
@note A stream object must not be destroyed while there are
|
||||
pending asynchronous operations associated with it.
|
||||
|
||||
@par Concepts
|
||||
AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
|
||||
*/
|
||||
template<class NextLayer,
|
||||
class Allocator = std::allocator<char>>
|
||||
class stream : public detail::stream_base
|
||||
{
|
||||
NextLayer next_layer_;
|
||||
basic_streambuf<Allocator> rd_buf_;
|
||||
|
||||
public:
|
||||
/// The type of the next layer.
|
||||
using next_layer_type =
|
||||
typename std::remove_reference<NextLayer>::type;
|
||||
|
||||
/// The type of the lowest layer.
|
||||
using lowest_layer_type =
|
||||
typename next_layer_type::lowest_layer_type;
|
||||
|
||||
/// The type of endpoint of the lowest layer.
|
||||
using endpoint_type =
|
||||
typename lowest_layer_type::endpoint_type;
|
||||
|
||||
/// The protocol of the next layer.
|
||||
using protocol_type =
|
||||
typename lowest_layer_type::protocol_type;
|
||||
|
||||
/// The type of resolver of the next layer.
|
||||
using resolver_type =
|
||||
typename protocol_type::resolver;
|
||||
|
||||
/** Destructor.
|
||||
|
||||
@note A stream object must not be destroyed while there
|
||||
are pending asynchronous operations associated with it.
|
||||
*/
|
||||
~stream();
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
Undefined behavior if operations are active or pending.
|
||||
*/
|
||||
stream(stream&&) = default;
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
Undefined behavior if operations are active or pending.
|
||||
*/
|
||||
stream& operator=(stream&&) = default;
|
||||
|
||||
/** Construct a HTTP stream.
|
||||
|
||||
This constructor creates a HTTP stream and initialises
|
||||
the next layer.
|
||||
|
||||
@throws Any exceptions thrown by the Stream constructor.
|
||||
|
||||
@param args The arguments to be passed to initialise the
|
||||
next layer. The arguments are forwarded to the next layer's
|
||||
constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
stream(Args&&... args);
|
||||
|
||||
/** Get the io_service associated with the stream.
|
||||
|
||||
This function may be used to obtain the io_service object
|
||||
that the stream uses to dispatch handlers for asynchronous
|
||||
operations.
|
||||
|
||||
@return A reference to the io_service object that the stream
|
||||
will use to dispatch handlers. Ownership is not transferred
|
||||
to the caller.
|
||||
*/
|
||||
boost::asio::io_service&
|
||||
get_io_service()
|
||||
{
|
||||
return next_layer_.lowest_layer().get_io_service();
|
||||
}
|
||||
|
||||
/** Get a reference to the next layer.
|
||||
|
||||
This function returns a reference to the next layer
|
||||
in a stack of stream layers.
|
||||
|
||||
@return A reference to the next layer in the stack of
|
||||
stream layers. Ownership is not transferred to the caller.
|
||||
*/
|
||||
next_layer_type&
|
||||
next_layer()
|
||||
{
|
||||
return next_layer_;
|
||||
}
|
||||
|
||||
/** Get a reference to the next layer.
|
||||
|
||||
This function returns a reference to the next layer in a
|
||||
stack of stream layers.
|
||||
|
||||
@return A reference to the next layer in the stack of
|
||||
stream layers. Ownership is not transferred to the caller.
|
||||
*/
|
||||
next_layer_type const&
|
||||
next_layer() const
|
||||
{
|
||||
return next_layer_;
|
||||
}
|
||||
|
||||
/** Get a reference to the lowest layer.
|
||||
|
||||
This function returns a reference to the lowest layer
|
||||
in a stack of stream layers.
|
||||
|
||||
@return A reference to the lowest layer in the stack of
|
||||
stream layers. Ownership is not transferred to the caller.
|
||||
*/
|
||||
lowest_layer_type&
|
||||
lowest_layer()
|
||||
{
|
||||
return next_layer_.lowest_layer();
|
||||
}
|
||||
|
||||
/** Get a reference to the lowest layer.
|
||||
|
||||
This function returns a reference to the lowest layer
|
||||
in a stack of stream layers.
|
||||
|
||||
@return A reference to the lowest layer in the stack of
|
||||
stream layers. Ownership is not transferred to the caller.
|
||||
*/
|
||||
lowest_layer_type const&
|
||||
lowest_layer() const
|
||||
{
|
||||
return next_layer_.lowest_layer();
|
||||
}
|
||||
|
||||
/** Cancel pending operations.
|
||||
|
||||
This will cancel all of the asynchronous operations pending,
|
||||
including pipelined writes that have not been started. Handlers for
|
||||
canceled writes will be called with
|
||||
`boost::asio::error::operation_aborted`.
|
||||
|
||||
@throws boost::system::system_error Thrown on failure.
|
||||
*/
|
||||
void
|
||||
cancel()
|
||||
{
|
||||
error_code ec;
|
||||
cancel(ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
/** Cancel pending operations.
|
||||
|
||||
This will cancel all of the asynchronous operations pending,
|
||||
including pipelined writes that have not been started. Handlers for
|
||||
canceled writes will be called with
|
||||
`boost::asio::error::operation_aborted`.
|
||||
|
||||
@param ec Set to indicate what error occurred, if any.
|
||||
*/
|
||||
void
|
||||
cancel(error_code& ec);
|
||||
|
||||
/** Read a HTTP message from the stream.
|
||||
|
||||
This function is used to read a single HTTP message from the stream.
|
||||
The call will block until one of the followign conditions is true:
|
||||
|
||||
@li A message has been read.
|
||||
|
||||
@li An error occurred.
|
||||
|
||||
The operation is implemented in terms of zero or more calls to the
|
||||
next layer's `read_some` function.
|
||||
|
||||
@param msg An object used to store the message. The previous
|
||||
contents of the object will be overwritten.
|
||||
|
||||
@throws boost::system::system_error Thrown on failure.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
void
|
||||
read(message_v1<isRequest, Body, Headers>& msg)
|
||||
{
|
||||
error_code ec;
|
||||
read(msg, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
/** Read a HTTP message from the stream.
|
||||
|
||||
This function is used to read a single HTTP message from the stream.
|
||||
The call will block until one of the followign conditions is true:
|
||||
|
||||
@li A message has been read.
|
||||
|
||||
@li An error occurred.
|
||||
|
||||
The operation is implemented in terms of zero or more calls to the
|
||||
next layer's `read_some` function.
|
||||
|
||||
@param msg An object used to store the message. The previous
|
||||
contents of the object will be overwritten.
|
||||
|
||||
@param ec Set to indicate what error occurred, if any.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
void
|
||||
read(message_v1<isRequest, Body, Headers>& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Start reading a HTTP message from the stream asynchronously.
|
||||
|
||||
This function is used to asynchronously read a single HTTP message
|
||||
from the stream. The function call always returns immediately. The
|
||||
asynchronous operation will continue until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li The message has been written.
|
||||
|
||||
@li An error occurred.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the
|
||||
next layer's async_read_some function, and is known as a composed
|
||||
operation. The program must ensure that the stream performs no other
|
||||
read operations or any other composed operations that perform reads
|
||||
until this operation completes.
|
||||
|
||||
@param msg An object used to store the message. The previous
|
||||
contents of the object will be overwritten. Ownership of the message
|
||||
is not transferred; the caller must guarantee that the object remains
|
||||
valid until the handler is called.
|
||||
|
||||
@param handler The handler to be called when the request completes.
|
||||
Copies will be made of the handler as required. The equivalent
|
||||
function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using boost::asio::io_service::post().
|
||||
*/
|
||||
template<bool isRequest, class Body, class Headers,
|
||||
class ReadHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_read(message_v1<isRequest, Body, Headers>& msg,
|
||||
ReadHandler&& handler);
|
||||
|
||||
/** Write a HTTP message to the stream.
|
||||
|
||||
This function is used to write a single HTTP message to the
|
||||
stream. The call will block until one of the following conditions
|
||||
is true:
|
||||
|
||||
@li The entire message is sent.
|
||||
|
||||
@li An error occurred.
|
||||
|
||||
If the semantics of the message require that the connection is
|
||||
closed to indicate the end of the content body,
|
||||
`boost::asio::error::eof` is thrown after the message is sent.
|
||||
successfuly. The caller is responsible for actually closing the
|
||||
connection. For regular TCP/IP streams this means shutting down the
|
||||
send side, while SSL streams may call the SSL shutdown function.
|
||||
|
||||
@param msg The message to send.
|
||||
|
||||
@throws boost::system::system_error Thrown on failure.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
void
|
||||
write(message_v1<isRequest, Body, Headers> const& msg)
|
||||
{
|
||||
error_code ec;
|
||||
write(msg, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
/** Write a HTTP message to the stream.
|
||||
|
||||
This function is used to write a single HTTP message to the
|
||||
stream. The call will block until one of the following conditions
|
||||
is true:
|
||||
|
||||
@li The entire message is sent.
|
||||
|
||||
@li An error occurred.
|
||||
|
||||
If the semantics of the message require that the connection is
|
||||
closed to indicate the end of the content body,
|
||||
`boost::asio::error::eof` is returned after the message is sent.
|
||||
successfuly. The caller is responsible for actually closing the
|
||||
connection. For regular TCP/IP streams this means shutting down the
|
||||
send side, while SSL streams may call the SSL shutdown function.
|
||||
|
||||
@param msg The message to send.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
void
|
||||
write(message_v1<isRequest, Body, Headers> const& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Start pipelining a HTTP message to the stream asynchronously.
|
||||
|
||||
This function is used to queue a message to be sent on the stream.
|
||||
Unlike the free function, this version will place the message on an
|
||||
outgoing message queue if there is already a write pending.
|
||||
|
||||
If the semantics of the message require that the connection is
|
||||
closed to indicate the end of the content body, the handler
|
||||
is called with the error `boost::asio::error::eof` after the message
|
||||
has been sent successfully. The caller is responsible for actually
|
||||
closing the connection. For regular TCP/IP streams this means
|
||||
shutting down the send side, while SSL streams may call the SSL
|
||||
`async_shutdown` function.
|
||||
|
||||
@param msg The message to send. A copy of the message will be made.
|
||||
|
||||
@param handler The handler to be called when the request completes.
|
||||
Copies will be made of the handler as required. The equivalent
|
||||
function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using boost::asio::io_service::post().
|
||||
*/
|
||||
template<bool isRequest, class Body, class Headers,
|
||||
class WriteHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_write(message_v1<isRequest, Body, Headers> const& msg,
|
||||
WriteHandler&& handler);
|
||||
|
||||
/** Start pipelining a HTTP message to the stream asynchronously.
|
||||
|
||||
This function is used to queue a message to be sent on the stream.
|
||||
Unlike the free function, this version will place the message on an
|
||||
outgoing message queue if there is already a write pending.
|
||||
|
||||
If the semantics of the message require that the connection is
|
||||
closed to indicate the end of the content body, the handler
|
||||
is called with the error boost::asio::error::eof. The caller is
|
||||
responsible for actually closing the connection. For regular
|
||||
TCP/IP streams this means shutting down the send side, while SSL
|
||||
streams may call the SSL async_shutdown function.
|
||||
|
||||
@param msg The message to send. Ownership of the message, which
|
||||
must be movable, is transferred to the implementation. The message
|
||||
will not be destroyed until the asynchronous operation completes.
|
||||
|
||||
@param handler The handler to be called when the request completes.
|
||||
Copies will be made of the handler as required. The equivalent
|
||||
function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using boost::asio::io_service::post().
|
||||
*/
|
||||
template<bool isRequest, class Body, class Headers,
|
||||
class WriteHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_write(message_v1<isRequest, Body, Headers>&& msg,
|
||||
WriteHandler&& handler);
|
||||
|
||||
private:
|
||||
template<bool, class, class, class> class read_op;
|
||||
template<bool, class, class, class> class write_op;
|
||||
|
||||
void
|
||||
cancel_all();
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include "http_stream.ipp"
|
||||
|
||||
#endif
|
||||
@@ -1,412 +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_STREAM_IPP_INCLUDED
|
||||
#define BEAST_HTTP_STREAM_IPP_INCLUDED
|
||||
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/handler_alloc.hpp>
|
||||
#include <beast/http/message_v1.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
template<bool isRequest, class Body, class Headers,
|
||||
class Handler>
|
||||
class stream<NextLayer, Allocator>::read_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
stream<NextLayer>& s;
|
||||
message_v1<isRequest, Body, Headers>& m;
|
||||
Handler h;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, stream<NextLayer>& s_,
|
||||
message_v1<isRequest, Body, Headers>& m_)
|
||||
: s(s_)
|
||||
, m(m_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
read_op(read_op&&) = default;
|
||||
read_op(read_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& s, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
template<bool isRequest, class Body, class Headers, class Handler>
|
||||
void
|
||||
stream<NextLayer, Allocator>::
|
||||
read_op<isRequest, Body, Headers, Handler>::
|
||||
operator()(error_code const& ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 99;
|
||||
beast::http::async_read(d.s.next_layer_,
|
||||
d.s.rd_buf_, d.m, std::move(*this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
template<bool isRequest, class Body, class Headers,
|
||||
class Handler>
|
||||
class stream<NextLayer, Allocator>::write_op : public op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
stream<NextLayer>& s;
|
||||
message_v1<isRequest, Body, Headers> m;
|
||||
Handler h;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, stream<NextLayer>& s_,
|
||||
message_v1<isRequest, Body, Headers> const& m_,
|
||||
bool cont_)
|
||||
: s(s_)
|
||||
, m(m_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(cont_)
|
||||
{
|
||||
}
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, stream<NextLayer>& s_,
|
||||
message_v1<isRequest, Body, Headers>&& m_,
|
||||
bool cont_)
|
||||
: s(s_)
|
||||
, m(std::move(m_))
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(cont_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& s, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()() override
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void cancel() override;
|
||||
|
||||
void operator()(error_code const& ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
template<bool isRequest, class Body, class Headers, class Handler>
|
||||
void
|
||||
stream<NextLayer, Allocator>::
|
||||
write_op<isRequest, Body, Headers, Handler>::
|
||||
cancel()
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.s.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
boost::asio::error::operation_aborted));
|
||||
}
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
template<bool isRequest, class Body, class Headers, class Handler>
|
||||
void
|
||||
stream<NextLayer, Allocator>::
|
||||
write_op<isRequest, Body, Headers, Handler>::
|
||||
operator()(error_code const& ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 99;
|
||||
beast::http::async_write(d.s.next_layer_,
|
||||
d.m, std::move(*this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
if(! d.s.wr_q_.empty())
|
||||
{
|
||||
auto& op = d.s.wr_q_.front();
|
||||
op();
|
||||
// VFALCO Use allocator
|
||||
delete &op;
|
||||
d.s.wr_q_.pop_front();
|
||||
}
|
||||
else
|
||||
{
|
||||
d.s.wr_active_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
stream<NextLayer, Allocator>::
|
||||
~stream()
|
||||
{
|
||||
// Can't destroy with pending operations!
|
||||
assert(wr_q_.empty());
|
||||
}
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
template<class... Args>
|
||||
stream<NextLayer, Allocator>::
|
||||
stream(Args&&... args)
|
||||
: next_layer_(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
void
|
||||
stream<NextLayer, Allocator>::
|
||||
cancel(error_code& ec)
|
||||
{
|
||||
cancel_all();
|
||||
lowest_layer().cancel(ec);
|
||||
}
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
void
|
||||
stream<NextLayer, Allocator>::
|
||||
read(message_v1<isRequest, Body, Headers>& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
beast::http::read(next_layer_, rd_buf_, msg, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
template<bool isRequest, class Body, class Headers,
|
||||
class ReadHandler>
|
||||
auto
|
||||
stream<NextLayer, Allocator>::
|
||||
async_read(message_v1<isRequest, Body, Headers>& msg,
|
||||
ReadHandler&& handler) ->
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
{
|
||||
async_completion<
|
||||
ReadHandler, void(error_code)
|
||||
> completion(handler);
|
||||
read_op<isRequest, Body, Headers,
|
||||
decltype(completion.handler)>{
|
||||
completion.handler, *this, msg};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
void
|
||||
stream<NextLayer, Allocator>::
|
||||
write(message_v1<isRequest, Body, Headers> const& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
beast::http::write(next_layer_, msg, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
template<bool isRequest, class Body, class Headers,
|
||||
class WriteHandler>
|
||||
auto
|
||||
stream<NextLayer, Allocator>::
|
||||
async_write(message_v1<isRequest, Body, Headers> const& msg,
|
||||
WriteHandler&& handler) ->
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
{
|
||||
async_completion<
|
||||
WriteHandler, void(error_code)> completion(handler);
|
||||
auto const cont = wr_active_ ||
|
||||
boost_asio_handler_cont_helpers::is_continuation(handler);
|
||||
if(! wr_active_)
|
||||
{
|
||||
wr_active_ = true;
|
||||
write_op<isRequest, Body, Headers,
|
||||
decltype(completion.handler)>{
|
||||
completion.handler, *this, msg, cont }();
|
||||
}
|
||||
else
|
||||
{
|
||||
// VFALCO Use allocator
|
||||
wr_q_.push_back(*new write_op<isRequest, Body, Headers,
|
||||
decltype(completion.handler)>(
|
||||
completion.handler, *this, msg, cont));
|
||||
}
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
template<bool isRequest, class Body, class Headers,
|
||||
class WriteHandler>
|
||||
auto
|
||||
stream<NextLayer, Allocator>::
|
||||
async_write(message_v1<isRequest, Body, Headers>&& msg,
|
||||
WriteHandler&& handler) ->
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
{
|
||||
async_completion<
|
||||
WriteHandler, void(error_code)> completion(handler);
|
||||
auto const cont = wr_active_ ||
|
||||
boost_asio_handler_cont_helpers::is_continuation(handler);
|
||||
if(! wr_active_)
|
||||
{
|
||||
wr_active_ = true;
|
||||
write_op<isRequest, Body, Headers,
|
||||
decltype(completion.handler)>{completion.handler,
|
||||
*this, std::move(msg), cont}();
|
||||
}
|
||||
else
|
||||
{
|
||||
// VFALCO Use allocator
|
||||
wr_q_.push_back(*new write_op<isRequest, Body, Headers,
|
||||
decltype(completion.handler)>(completion.handler,
|
||||
*this, std::move(msg), cont));
|
||||
}
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer, class Allocator>
|
||||
void
|
||||
stream<NextLayer, Allocator>::
|
||||
cancel_all()
|
||||
{
|
||||
for(auto it = wr_q_.begin(); it != wr_q_.end();)
|
||||
{
|
||||
auto& op = *it++;
|
||||
op.cancel();
|
||||
// VFALCO Use allocator
|
||||
delete &op;
|
||||
}
|
||||
wr_q_.clear();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -9,8 +9,9 @@
|
||||
#define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
|
||||
|
||||
#include "file_body.hpp"
|
||||
#include "http_stream.hpp"
|
||||
#include "mime_type.hpp"
|
||||
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
@@ -34,6 +35,8 @@ class http_sync_server
|
||||
using req_type = request_v1<string_body>;
|
||||
using resp_type = response_v1<file_body>;
|
||||
|
||||
bool log_ = true;
|
||||
std::mutex m_;
|
||||
boost::asio::io_service ios_;
|
||||
socket_type sock_;
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
@@ -65,21 +68,43 @@ public:
|
||||
thread_.join();
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void
|
||||
fail(error_code ec, std::string what)
|
||||
log(Args const&... args)
|
||||
{
|
||||
std::cerr <<
|
||||
what << ": " << ec.message() << std::endl;
|
||||
if(log_)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
log_args(args...);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
log_args()
|
||||
{
|
||||
}
|
||||
|
||||
template<class Arg, class... Args>
|
||||
void
|
||||
log_args(Arg const& arg, Args const&... args)
|
||||
{
|
||||
std::cerr << arg;
|
||||
log_args(args...);
|
||||
}
|
||||
|
||||
void
|
||||
maybe_throw(error_code ec, std::string what)
|
||||
fail(error_code ec, std::string what)
|
||||
{
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, what);
|
||||
throw ec;
|
||||
}
|
||||
log(what, ": ", ec.message(), "\n");
|
||||
}
|
||||
|
||||
void
|
||||
fail(int id, error_code const& ec)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted &&
|
||||
ec != boost::asio::error::eof)
|
||||
log("#", id, " ", ec.message(), "\n");
|
||||
}
|
||||
|
||||
struct lambda
|
||||
@@ -109,7 +134,8 @@ public:
|
||||
{
|
||||
if(! acceptor_.is_open())
|
||||
return;
|
||||
maybe_throw(ec, "accept");
|
||||
if(ec)
|
||||
return fail(ec, "accept");
|
||||
static int id_ = 0;
|
||||
std::thread{lambda{++id_, *this, std::move(sock_)}}.detach();
|
||||
acceptor_.async_accept(sock_,
|
||||
@@ -118,23 +144,15 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
fail(int id, error_code const& ec)
|
||||
do_peer(int id, socket_type&& sock0)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted &&
|
||||
ec != boost::asio::error::eof)
|
||||
std::cerr <<
|
||||
"#" << std::to_string(id) << " " << std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
do_peer(int id, socket_type&& sock)
|
||||
{
|
||||
http::stream<socket_type> hs(std::move(sock));
|
||||
socket_type sock(std::move(sock0));
|
||||
streambuf sb;
|
||||
error_code ec;
|
||||
for(;;)
|
||||
{
|
||||
req_type req;
|
||||
hs.read(req, ec);
|
||||
http::read(sock, sb, req, ec);
|
||||
if(ec)
|
||||
break;
|
||||
auto path = req.url;
|
||||
@@ -143,26 +161,42 @@ public:
|
||||
path = root_ + path;
|
||||
if(! boost::filesystem::exists(path))
|
||||
{
|
||||
response_v1<string_body> resp;
|
||||
resp.status = 404;
|
||||
resp.reason = "Not Found";
|
||||
resp.version = req.version;
|
||||
resp.headers.replace("Server", "http_sync_server");
|
||||
resp.body = "The file '" + path + "' was not found";
|
||||
prepare(resp);
|
||||
hs.write(resp, ec);
|
||||
response_v1<string_body> res;
|
||||
res.status = 404;
|
||||
res.reason = "Not Found";
|
||||
res.version = req.version;
|
||||
res.headers.insert("Server", "http_sync_server");
|
||||
res.headers.insert("Content-Type", "text/html");
|
||||
res.body = "The file '" + path + "' was not found";
|
||||
prepare(res);
|
||||
write(sock, res, ec);
|
||||
if(ec)
|
||||
break;
|
||||
}
|
||||
resp_type resp;
|
||||
resp.status = 200;
|
||||
resp.reason = "OK";
|
||||
resp.version = req.version;
|
||||
resp.headers.replace("Server", "http_sync_server");
|
||||
resp.headers.replace("Content-Type", "text/html");
|
||||
resp.body = path;
|
||||
prepare(resp);
|
||||
hs.write(resp, ec);
|
||||
resp_type res;
|
||||
res.status = 200;
|
||||
res.reason = "OK";
|
||||
res.version = req.version;
|
||||
res.headers.insert("Server", "http_sync_server");
|
||||
res.headers.insert("Content-Type", mime_type(path));
|
||||
res.body = path;
|
||||
try
|
||||
{
|
||||
prepare(res);
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
res = {};
|
||||
res.status = 500;
|
||||
res.reason = "Internal Error";
|
||||
res.version = req.version;
|
||||
res.headers.insert("Server", "http_sync_server");
|
||||
res.headers.insert("Content-Type", "text/html");
|
||||
res.body =
|
||||
std::string{"An internal error occurred"} + e.what();
|
||||
prepare(res);
|
||||
}
|
||||
write(sock, res, ec);
|
||||
if(ec)
|
||||
break;
|
||||
}
|
||||
|
||||
51
src/beast/examples/mime_type.hpp
Normal file
51
src/beast/examples/mime_type.hpp
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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_EXAMPLE_HTTP_MIME_TYPE_H_INCLUDED
|
||||
#define BEAST_EXAMPLE_HTTP_MIME_TYPE_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
// Return the Mime-Type for a given file extension
|
||||
template<class = void>
|
||||
std::string
|
||||
mime_type(std::string const& path)
|
||||
{
|
||||
auto const ext =
|
||||
boost::filesystem::path{path}.extension().string();
|
||||
if(ext == ".txt") return "text/plain";
|
||||
if(ext == ".htm") return "text/html";
|
||||
if(ext == ".html") return "text/html";
|
||||
if(ext == ".php") return "text/html";
|
||||
if(ext == ".css") return "text/css";
|
||||
if(ext == ".js") return "application/javascript";
|
||||
if(ext == ".json") return "application/json";
|
||||
if(ext == ".xml") return "application/xml";
|
||||
if(ext == ".swf") return "application/x-shockwave-flash";
|
||||
if(ext == ".flv") return "video/x-flv";
|
||||
if(ext == ".png") return "image/png";
|
||||
if(ext == ".jpe") return "image/jpeg";
|
||||
if(ext == ".jpeg") return "image/jpeg";
|
||||
if(ext == ".jpg") return "image/jpeg";
|
||||
if(ext == ".gif") return "image/gif";
|
||||
if(ext == ".bmp") return "image/bmp";
|
||||
if(ext == ".ico") return "image/vnd.microsoft.icon";
|
||||
if(ext == ".tiff") return "image/tiff";
|
||||
if(ext == ".tif") return "image/tiff";
|
||||
if(ext == ".svg") return "image/svg+xml";
|
||||
if(ext == ".svgz") return "image/svg+xml";
|
||||
return "application/text";
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -16,13 +16,13 @@ int main()
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "echo.websocket.org";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r(ios);
|
||||
boost::asio::ip::tcp::socket sock(ios);
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
boost::asio::connect(sock,
|
||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
|
||||
|
||||
// WebSocket connect and send message using beast
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
||||
ws.handshake(host, "/");
|
||||
ws.write(boost::asio::buffer("Hello, world!"));
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@ namespace test {
|
||||
|
||||
enum error
|
||||
{
|
||||
fail_error = 1
|
||||
success = 0,
|
||||
|
||||
fail_error
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
@@ -161,19 +161,21 @@ public:
|
||||
|
||||
friend
|
||||
void
|
||||
teardown(fail_stream<NextLayer>& stream,
|
||||
boost::system::error_code& ec)
|
||||
teardown(websocket::teardown_tag,
|
||||
fail_stream<NextLayer>& stream,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
if(stream.pfc_->fail(ec))
|
||||
return;
|
||||
websocket_helpers::call_teardown(stream.next_layer(), ec);
|
||||
beast::websocket_helpers::call_teardown(stream.next_layer(), ec);
|
||||
}
|
||||
|
||||
template<class TeardownHandler>
|
||||
friend
|
||||
void
|
||||
async_teardown(fail_stream<NextLayer>& stream,
|
||||
TeardownHandler&& handler)
|
||||
async_teardown(websocket::teardown_tag,
|
||||
fail_stream<NextLayer>& stream,
|
||||
TeardownHandler&& handler)
|
||||
{
|
||||
error_code ec;
|
||||
if(stream.pfc_->fail(ec))
|
||||
@@ -182,7 +184,7 @@ public:
|
||||
bind_handler(std::move(handler), ec));
|
||||
return;
|
||||
}
|
||||
websocket_helpers::call_async_teardown(
|
||||
beast::websocket_helpers::call_async_teardown(
|
||||
stream.next_layer(), std::forward<TeardownHandler>(handler));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,29 +23,29 @@ private:
|
||||
std::string const& what_;
|
||||
|
||||
public:
|
||||
amount (amount const&) = default;
|
||||
amount& operator= (amount const&) = delete;
|
||||
amount(amount const&) = default;
|
||||
amount& operator=(amount const&) = delete;
|
||||
|
||||
template <class = void>
|
||||
amount (std::size_t n, std::string const& what);
|
||||
template<class = void>
|
||||
amount(std::size_t n, std::string const& what);
|
||||
|
||||
friend
|
||||
std::ostream&
|
||||
operator<< (std::ostream& s, amount const& t);
|
||||
operator<<(std::ostream& s, amount const& t);
|
||||
};
|
||||
|
||||
template <class>
|
||||
amount::amount (std::size_t n, std::string const& what)
|
||||
: n_ (n)
|
||||
, what_ (what)
|
||||
template<class>
|
||||
amount::amount(std::size_t n, std::string const& what)
|
||||
: n_(n)
|
||||
, what_(what)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream&
|
||||
operator<< (std::ostream& s, amount const& t)
|
||||
operator<<(std::ostream& s, amount const& t)
|
||||
{
|
||||
s << t.n_ << " " << t.what_ << ((t.n_ != 1) ? "s" : "");
|
||||
s << t.n_ << " " << t.what_ <<((t.n_ != 1) ? "s" : "");
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace detail {
|
||||
The interface allows for limited read only operations. Derived classes
|
||||
provide additional behavior.
|
||||
*/
|
||||
template <class Container>
|
||||
template<class Container>
|
||||
class const_container
|
||||
{
|
||||
private:
|
||||
|
||||
@@ -8,11 +8,10 @@
|
||||
#ifndef BEAST_UNIT_TEST_DSTREAM_HPP
|
||||
#define BEAST_UNIT_TEST_DSTREAM_HPP
|
||||
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
#include <iostream>
|
||||
#include <ios>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <streambuf>
|
||||
#include <string>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@@ -30,15 +29,18 @@
|
||||
namespace beast {
|
||||
namespace unit_test {
|
||||
|
||||
namespace detail {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class CharT, class Traits, class Allocator>
|
||||
class dstream_buf
|
||||
: public std::basic_stringbuf<CharT, Traits, Allocator>
|
||||
{
|
||||
using ostream = std::basic_ostream<CharT, Traits>;
|
||||
|
||||
bool dbg_;
|
||||
ostream& os_;
|
||||
|
||||
template<class T>
|
||||
void write(T const*) = delete;
|
||||
@@ -47,21 +49,21 @@ class dstream_buf
|
||||
{
|
||||
if(dbg_)
|
||||
OutputDebugStringA(s);
|
||||
else
|
||||
std::cout << s;
|
||||
os_ << s;
|
||||
}
|
||||
|
||||
void write(wchar_t const* s)
|
||||
{
|
||||
if(dbg_)
|
||||
OutputDebugStringW(s);
|
||||
else
|
||||
std::wcout << s;
|
||||
os_ << s;
|
||||
}
|
||||
|
||||
public:
|
||||
dstream_buf()
|
||||
: dbg_(IsDebuggerPresent() != FALSE)
|
||||
explicit
|
||||
dstream_buf(ostream& os)
|
||||
: os_(os)
|
||||
, dbg_(IsDebuggerPresent() != FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -79,66 +81,52 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#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.
|
||||
/** std::ostream with Visual Studio IDE redirection.
|
||||
|
||||
Instances of this stream wrap a specified `std::ostream`
|
||||
(such as `std::cout` or `std::cerr`). If the IDE debugger
|
||||
is attached when the stream is created, output will be
|
||||
additionally copied to the Visual Studio Output window.
|
||||
*/
|
||||
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 std::basic_ostream<CharT, Traits>
|
||||
{
|
||||
detail::dstream_buf<
|
||||
CharT, Traits, Allocator> buf_;
|
||||
|
||||
public:
|
||||
basic_dstream()
|
||||
: std::basic_ostream<CharT, Traits>(&this->member)
|
||||
/** Construct a stream.
|
||||
|
||||
@param os The output stream to wrap.
|
||||
*/
|
||||
explicit
|
||||
basic_dstream(std::ostream& os)
|
||||
: std::basic_ostream<CharT, Traits>(&buf_)
|
||||
, buf_(os)
|
||||
{
|
||||
if(os.flags() && std::ios::unitbuf)
|
||||
std::unitbuf(*this);
|
||||
}
|
||||
};
|
||||
|
||||
using dstream = basic_dstream<char>;
|
||||
using dwstream = basic_dstream<wchar_t>;
|
||||
|
||||
} // test
|
||||
#else
|
||||
|
||||
using dstream = std::ostream&;
|
||||
using dwstream = std::wostream&;
|
||||
|
||||
#endif
|
||||
|
||||
} // unit_test
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -33,7 +33,7 @@ static
|
||||
std::string
|
||||
prefix(suite_info const& s)
|
||||
{
|
||||
if (s.manual())
|
||||
if(s.manual())
|
||||
return "|M| ";
|
||||
return " ";
|
||||
}
|
||||
@@ -45,7 +45,7 @@ print(std::ostream& os, suite_list const& c)
|
||||
std::size_t manual = 0;
|
||||
for(auto const& s : c)
|
||||
{
|
||||
os << prefix (s) << s.full_name() << '\n';
|
||||
os << prefix(s) << s.full_name() << '\n';
|
||||
if(s.manual())
|
||||
++manual;
|
||||
}
|
||||
@@ -80,18 +80,18 @@ int main(int ac, char const* av[])
|
||||
|
||||
#ifdef _MSC_VER
|
||||
{
|
||||
int flags = _CrtSetDbgFlag (_CRTDBG_REPORT_FLAG);
|
||||
int flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
|
||||
flags |= _CRTDBG_LEAK_CHECK_DF;
|
||||
_CrtSetDbgFlag (flags);
|
||||
_CrtSetDbgFlag(flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace po = boost::program_options;
|
||||
po::options_description desc("Options");
|
||||
desc.add_options()
|
||||
("help,h", "Produce a help message")
|
||||
("print,r", "Print the list of available test suites")
|
||||
("suites,s", po::value<string>(), "suites to run")
|
||||
("help,h", "Produce a help message")
|
||||
("print,p", "Print the list of available test suites")
|
||||
("suites,s", po::value<string>(), "suites to run")
|
||||
;
|
||||
|
||||
po::positional_options_description p;
|
||||
@@ -99,7 +99,8 @@ int main(int ac, char const* av[])
|
||||
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||
po::notify(vm);
|
||||
|
||||
dstream log;
|
||||
dstream log{std::cerr};
|
||||
std::unitbuf(log);
|
||||
|
||||
if(vm.count("help"))
|
||||
{
|
||||
@@ -121,7 +122,7 @@ int main(int ac, char const* av[])
|
||||
match_auto(suites));
|
||||
else
|
||||
failed = r.run_each(global_suites());
|
||||
if (failed)
|
||||
if(failed)
|
||||
return EXIT_FAILURE;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -45,42 +45,42 @@ private:
|
||||
std::string library_;
|
||||
|
||||
public:
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
explicit
|
||||
selector (mode_t mode, std::string const& pattern = "");
|
||||
selector(mode_t mode, std::string const& pattern = "");
|
||||
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
bool
|
||||
operator() (suite_info const& s);
|
||||
operator()(suite_info const& s);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class>
|
||||
selector::selector (mode_t mode, std::string const& pattern)
|
||||
: mode_ (mode)
|
||||
, pat_ (pattern)
|
||||
template<class>
|
||||
selector::selector(mode_t mode, std::string const& pattern)
|
||||
: mode_(mode)
|
||||
, pat_(pattern)
|
||||
{
|
||||
if (mode_ == automatch && pattern.empty())
|
||||
if(mode_ == automatch && pattern.empty())
|
||||
mode_ = all;
|
||||
}
|
||||
|
||||
template <class>
|
||||
template<class>
|
||||
bool
|
||||
selector::operator() (suite_info const& s)
|
||||
selector::operator()(suite_info const& s)
|
||||
{
|
||||
switch (mode_)
|
||||
switch(mode_)
|
||||
{
|
||||
case automatch:
|
||||
// suite or full name
|
||||
if (s.name() == pat_ || s.full_name() == pat_)
|
||||
if(s.name() == pat_ || s.full_name() == pat_)
|
||||
{
|
||||
mode_ = none;
|
||||
return true;
|
||||
}
|
||||
|
||||
// check module
|
||||
if (pat_ == s.module())
|
||||
if(pat_ == s.module())
|
||||
{
|
||||
mode_ = module;
|
||||
library_ = s.library();
|
||||
@@ -88,7 +88,7 @@ selector::operator() (suite_info const& s)
|
||||
}
|
||||
|
||||
// check library
|
||||
if (pat_ == s.library())
|
||||
if(pat_ == s.library())
|
||||
{
|
||||
mode_ = library;
|
||||
return ! s.manual();
|
||||
@@ -138,9 +138,9 @@ selector::operator() (suite_info const& s)
|
||||
*/
|
||||
inline
|
||||
selector
|
||||
match_auto (std::string const& name)
|
||||
match_auto(std::string const& name)
|
||||
{
|
||||
return selector (selector::automatch, name);
|
||||
return selector(selector::automatch, name);
|
||||
}
|
||||
|
||||
/** Return a predicate that matches all suites not marked manual. */
|
||||
@@ -148,23 +148,23 @@ inline
|
||||
selector
|
||||
match_all()
|
||||
{
|
||||
return selector (selector::all);
|
||||
return selector(selector::all);
|
||||
}
|
||||
|
||||
/** Returns a predicate that matches a specific suite. */
|
||||
inline
|
||||
selector
|
||||
match_suite (std::string const& name)
|
||||
match_suite(std::string const& name)
|
||||
{
|
||||
return selector (selector::suite, name);
|
||||
return selector(selector::suite, name);
|
||||
}
|
||||
|
||||
/** Returns a predicate that matches all suites in a library. */
|
||||
inline
|
||||
selector
|
||||
match_library (std::string const& name)
|
||||
match_library(std::string const& name)
|
||||
{
|
||||
return selector (selector::library, name);
|
||||
return selector(selector::library, name);
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
|
||||
@@ -24,8 +24,8 @@ private:
|
||||
|
||||
public:
|
||||
recorder() = default;
|
||||
recorder (recorder const&) = default;
|
||||
recorder& operator= (recorder const&) = default;
|
||||
recorder(recorder const&) = default;
|
||||
recorder& operator=(recorder const&) = default;
|
||||
|
||||
/** Returns a report with the results of all completed suites. */
|
||||
results const&
|
||||
@@ -37,31 +37,31 @@ public:
|
||||
private:
|
||||
virtual
|
||||
void
|
||||
on_suite_begin (suite_info const& info) override
|
||||
on_suite_begin(suite_info const& info) override
|
||||
{
|
||||
m_suite = suite_results (info.full_name());
|
||||
m_suite = suite_results(info.full_name());
|
||||
}
|
||||
|
||||
virtual
|
||||
void
|
||||
on_suite_end() override
|
||||
{
|
||||
m_results.insert (std::move (m_suite));
|
||||
m_results.insert(std::move(m_suite));
|
||||
}
|
||||
|
||||
virtual
|
||||
void
|
||||
on_case_begin (std::string const& name) override
|
||||
on_case_begin(std::string const& name) override
|
||||
{
|
||||
m_case = case_results (name);
|
||||
m_case = case_results(name);
|
||||
}
|
||||
|
||||
virtual
|
||||
void
|
||||
on_case_end() override
|
||||
{
|
||||
if (m_case.tests.size() > 0)
|
||||
m_suite.insert (std::move (m_case));
|
||||
if(m_case.tests.size() > 0)
|
||||
m_suite.insert(std::move(m_case));
|
||||
}
|
||||
|
||||
virtual
|
||||
@@ -73,16 +73,16 @@ private:
|
||||
|
||||
virtual
|
||||
void
|
||||
on_fail (std::string const& reason) override
|
||||
on_fail(std::string const& reason) override
|
||||
{
|
||||
m_case.tests.fail (reason);
|
||||
m_case.tests.fail(reason);
|
||||
}
|
||||
|
||||
virtual
|
||||
void
|
||||
on_log (std::string const& s) override
|
||||
on_log(std::string const& s) override
|
||||
{
|
||||
m_case.log.insert (s);
|
||||
m_case.log.insert(s);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ private:
|
||||
typename clock_type::time_point start = clock_type::now();
|
||||
|
||||
explicit
|
||||
suite_results(std::string const& name_ = "")
|
||||
suite_results(std::string name_ = "")
|
||||
: name(std::move(name_))
|
||||
{
|
||||
}
|
||||
@@ -83,7 +83,7 @@ private:
|
||||
typename clock_type::time_point start = clock_type::now();
|
||||
|
||||
void
|
||||
add (suite_results const& r);
|
||||
add(suite_results const& r);
|
||||
};
|
||||
|
||||
std::ostream& os_;
|
||||
@@ -107,7 +107,7 @@ private:
|
||||
|
||||
virtual
|
||||
void
|
||||
on_suite_begin (suite_info const& info) override;
|
||||
on_suite_begin(suite_info const& info) override;
|
||||
|
||||
virtual
|
||||
void
|
||||
@@ -115,7 +115,7 @@ private:
|
||||
|
||||
virtual
|
||||
void
|
||||
on_case_begin (std::string const& name) override;
|
||||
on_case_begin(std::string const& name) override;
|
||||
|
||||
virtual
|
||||
void
|
||||
@@ -127,11 +127,11 @@ private:
|
||||
|
||||
virtual
|
||||
void
|
||||
on_fail (std::string const& reason) override;
|
||||
on_fail(std::string const& reason) override;
|
||||
|
||||
virtual
|
||||
void
|
||||
on_log (std::string const& s) override;
|
||||
on_log(std::string const& s) override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -156,7 +156,7 @@ results::add(suite_results const& r)
|
||||
cases += r.cases;
|
||||
failed += r.failed;
|
||||
auto const elapsed = clock_type::now() - r.start;
|
||||
if (elapsed >= std::chrono::seconds{1})
|
||||
if(elapsed >= std::chrono::seconds{1})
|
||||
{
|
||||
auto const iter = std::lower_bound(top.begin(),
|
||||
top.end(), elapsed,
|
||||
@@ -165,13 +165,13 @@ results::add(suite_results const& r)
|
||||
{
|
||||
return t1.second > t2;
|
||||
});
|
||||
if (iter != top.end())
|
||||
if(iter != top.end())
|
||||
{
|
||||
if (top.size() == max_top)
|
||||
if(top.size() == max_top)
|
||||
top.resize(top.size() - 1);
|
||||
top.emplace(iter, r.name, elapsed);
|
||||
}
|
||||
else if (top.size() < max_top)
|
||||
else if(top.size() < max_top)
|
||||
{
|
||||
top.emplace_back(r.name, elapsed);
|
||||
}
|
||||
@@ -213,11 +213,11 @@ reporter<_>::fmtdur(typename clock_type::duration const& d)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto const ms = duration_cast<milliseconds>(d);
|
||||
if (ms < seconds{1})
|
||||
if(ms < seconds{1})
|
||||
return std::to_string(ms.count()) + "ms";
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(1) <<
|
||||
(ms.count()/1000.) << "s";
|
||||
(ms.count()/1000.) << "s";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@@ -241,11 +241,10 @@ void
|
||||
reporter<_>::
|
||||
on_case_begin(std::string const& name)
|
||||
{
|
||||
case_results_ = case_results (name);
|
||||
os_ <<
|
||||
suite_results_.name <<
|
||||
(case_results_.name.empty() ?
|
||||
"" : (" " + case_results_.name)) << std::endl;
|
||||
case_results_ = case_results(name);
|
||||
os_ << suite_results_.name <<
|
||||
(case_results_.name.empty() ? "" :
|
||||
(" " + case_results_.name)) << std::endl;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
@@ -273,7 +272,7 @@ on_fail(std::string const& reason)
|
||||
++case_results_.total;
|
||||
os_ <<
|
||||
"#" << case_results_.total << " failed" <<
|
||||
(reason.empty() ? "" : ": ") << reason << std::endl;
|
||||
(reason.empty() ? "" : ": ") << reason << std::endl;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
|
||||
@@ -23,14 +23,14 @@ public:
|
||||
/** Holds the result of evaluating one test condition. */
|
||||
struct test
|
||||
{
|
||||
explicit test (bool pass_)
|
||||
: pass (pass_)
|
||||
explicit test(bool pass_)
|
||||
: pass(pass_)
|
||||
{
|
||||
}
|
||||
|
||||
test (bool pass_, std::string const& reason_)
|
||||
: pass (pass_)
|
||||
, reason (reason_)
|
||||
test(bool pass_, std::string const& reason_)
|
||||
: pass(pass_)
|
||||
, reason(reason_)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ private:
|
||||
std::size_t failed_;
|
||||
|
||||
public:
|
||||
tests_t ()
|
||||
: failed_ (0)
|
||||
tests_t()
|
||||
: failed_(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -69,15 +69,15 @@ private:
|
||||
void
|
||||
pass()
|
||||
{
|
||||
cont().emplace_back (true);
|
||||
cont().emplace_back(true);
|
||||
}
|
||||
|
||||
/** Register a failed test condition. */
|
||||
void
|
||||
fail (std::string const& reason = "")
|
||||
fail(std::string const& reason = "")
|
||||
{
|
||||
++failed_;
|
||||
cont().emplace_back (false, reason);
|
||||
cont().emplace_back(false, reason);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -87,17 +87,17 @@ private:
|
||||
public:
|
||||
/** Insert a string into the log. */
|
||||
void
|
||||
insert (std::string const& s)
|
||||
insert(std::string const& s)
|
||||
{
|
||||
cont().push_back (s);
|
||||
cont().push_back(s);
|
||||
}
|
||||
};
|
||||
|
||||
std::string name_;
|
||||
|
||||
public:
|
||||
explicit case_results (std::string const& name = "")
|
||||
: name_ (name)
|
||||
explicit case_results(std::string const& name = "")
|
||||
: name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -127,8 +127,8 @@ private:
|
||||
std::size_t failed_ = 0;
|
||||
|
||||
public:
|
||||
explicit suite_results (std::string const& name = "")
|
||||
: name_ (name)
|
||||
explicit suite_results(std::string const& name = "")
|
||||
: name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -156,17 +156,17 @@ public:
|
||||
/** Insert a set of testcase results. */
|
||||
/** @{ */
|
||||
void
|
||||
insert (case_results&& r)
|
||||
insert(case_results&& r)
|
||||
{
|
||||
cont().emplace_back (std::move (r));
|
||||
cont().emplace_back(std::move(r));
|
||||
total_ += r.tests.total();
|
||||
failed_ += r.tests.failed();
|
||||
}
|
||||
|
||||
void
|
||||
insert (case_results const& r)
|
||||
insert(case_results const& r)
|
||||
{
|
||||
cont().push_back (r);
|
||||
cont().push_back(r);
|
||||
total_ += r.tests.total();
|
||||
failed_ += r.tests.failed();
|
||||
}
|
||||
@@ -187,9 +187,9 @@ private:
|
||||
|
||||
public:
|
||||
results()
|
||||
: m_cases (0)
|
||||
, total_ (0)
|
||||
, failed_ (0)
|
||||
: m_cases(0)
|
||||
, total_(0)
|
||||
, failed_(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -217,21 +217,21 @@ public:
|
||||
/** Insert a set of suite results. */
|
||||
/** @{ */
|
||||
void
|
||||
insert (suite_results&& r)
|
||||
insert(suite_results&& r)
|
||||
{
|
||||
m_cases += r.size();
|
||||
total_ += r.total();
|
||||
failed_ += r.failed();
|
||||
cont().emplace_back (std::move (r));
|
||||
cont().emplace_back(std::move(r));
|
||||
}
|
||||
|
||||
void
|
||||
insert (suite_results const& r)
|
||||
insert(suite_results const& r)
|
||||
{
|
||||
m_cases += r.size();
|
||||
total_ += r.total();
|
||||
failed_ += r.failed();
|
||||
cont().push_back (r);
|
||||
cont().push_back(r);
|
||||
}
|
||||
/** @} */
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace beast {
|
||||
namespace unit_test {
|
||||
|
||||
/** Unit test runner interface.
|
||||
|
||||
Derived classes can customize the reporting behavior. This interface is
|
||||
injected into the unit_test class to receive the results of the tests.
|
||||
*/
|
||||
@@ -60,7 +61,7 @@ public:
|
||||
*/
|
||||
template<class = void>
|
||||
bool
|
||||
run (suite_info const& s);
|
||||
run(suite_info const& s);
|
||||
|
||||
/** Run a sequence of suites.
|
||||
The expression
|
||||
@@ -68,44 +69,40 @@ public:
|
||||
must be convertible to `suite_info`.
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class FwdIter>
|
||||
template<class FwdIter>
|
||||
bool
|
||||
run (FwdIter first, FwdIter last);
|
||||
run(FwdIter first, FwdIter last);
|
||||
|
||||
/** Conditionally run a sequence of suites.
|
||||
pred will be called as:
|
||||
@code
|
||||
bool pred (suite_info const&);
|
||||
bool pred(suite_info const&);
|
||||
@endcode
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class FwdIter, class Pred>
|
||||
template<class FwdIter, class Pred>
|
||||
bool
|
||||
run_if (FwdIter first, FwdIter last, Pred pred = Pred{});
|
||||
run_if(FwdIter first, FwdIter last, Pred pred = Pred{});
|
||||
|
||||
/** Run all suites in a container.
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class SequenceContainer>
|
||||
template<class SequenceContainer>
|
||||
bool
|
||||
run_each (SequenceContainer const& c);
|
||||
run_each(SequenceContainer const& c);
|
||||
|
||||
/** Conditionally run suites in a container.
|
||||
pred will be called as:
|
||||
@code
|
||||
bool pred (suite_info const&);
|
||||
bool pred(suite_info const&);
|
||||
@endcode
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class SequenceContainer, class Pred>
|
||||
template<class SequenceContainer, class Pred>
|
||||
bool
|
||||
run_each_if (SequenceContainer const& c, Pred pred = Pred{});
|
||||
run_each_if(SequenceContainer const& c, Pred pred = Pred{});
|
||||
|
||||
protected:
|
||||
//
|
||||
// Overrides
|
||||
//
|
||||
|
||||
/// Called when a new suite starts.
|
||||
virtual
|
||||
void
|
||||
@@ -159,130 +156,130 @@ private:
|
||||
friend class suite;
|
||||
|
||||
// Start a new testcase.
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
void
|
||||
testcase (std::string const& name);
|
||||
testcase(std::string const& name);
|
||||
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
void
|
||||
pass();
|
||||
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
void
|
||||
fail (std::string const& reason);
|
||||
fail(std::string const& reason);
|
||||
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
void
|
||||
log (std::string const& s);
|
||||
log(std::string const& s);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class>
|
||||
template<class>
|
||||
bool
|
||||
runner::run (suite_info const& s)
|
||||
runner::run(suite_info const& s)
|
||||
{
|
||||
// Enable 'default' testcase
|
||||
default_ = true;
|
||||
failed_ = false;
|
||||
on_suite_begin (s);
|
||||
s.run (*this);
|
||||
on_suite_begin(s);
|
||||
s.run(*this);
|
||||
// Forgot to call pass or fail.
|
||||
assert (cond_);
|
||||
assert(cond_);
|
||||
on_case_end();
|
||||
on_suite_end();
|
||||
return failed_;
|
||||
}
|
||||
|
||||
template <class FwdIter>
|
||||
template<class FwdIter>
|
||||
bool
|
||||
runner::run (FwdIter first, FwdIter last)
|
||||
runner::run(FwdIter first, FwdIter last)
|
||||
{
|
||||
bool failed (false);
|
||||
for (;first != last; ++first)
|
||||
failed = run (*first) || failed;
|
||||
bool failed(false);
|
||||
for(;first != last; ++first)
|
||||
failed = run(*first) || failed;
|
||||
return failed;
|
||||
}
|
||||
|
||||
template <class FwdIter, class Pred>
|
||||
template<class FwdIter, class Pred>
|
||||
bool
|
||||
runner::run_if (FwdIter first, FwdIter last, Pred pred)
|
||||
runner::run_if(FwdIter first, FwdIter last, Pred pred)
|
||||
{
|
||||
bool failed (false);
|
||||
for (;first != last; ++first)
|
||||
if (pred (*first))
|
||||
failed = run (*first) || failed;
|
||||
bool failed(false);
|
||||
for(;first != last; ++first)
|
||||
if(pred(*first))
|
||||
failed = run(*first) || failed;
|
||||
return failed;
|
||||
}
|
||||
|
||||
template <class SequenceContainer>
|
||||
template<class SequenceContainer>
|
||||
bool
|
||||
runner::run_each (SequenceContainer const& c)
|
||||
runner::run_each(SequenceContainer const& c)
|
||||
{
|
||||
bool failed (false);
|
||||
for (auto const& s : c)
|
||||
failed = run (s) || failed;
|
||||
bool failed(false);
|
||||
for(auto const& s : c)
|
||||
failed = run(s) || failed;
|
||||
return failed;
|
||||
}
|
||||
|
||||
template <class SequenceContainer, class Pred>
|
||||
template<class SequenceContainer, class Pred>
|
||||
bool
|
||||
runner::run_each_if (SequenceContainer const& c, Pred pred)
|
||||
runner::run_each_if(SequenceContainer const& c, Pred pred)
|
||||
{
|
||||
bool failed (false);
|
||||
for (auto const& s : c)
|
||||
if (pred (s))
|
||||
failed = run (s) || failed;
|
||||
bool failed(false);
|
||||
for(auto const& s : c)
|
||||
if(pred(s))
|
||||
failed = run(s) || failed;
|
||||
return failed;
|
||||
}
|
||||
|
||||
template <class>
|
||||
template<class>
|
||||
void
|
||||
runner::testcase (std::string const& name)
|
||||
runner::testcase(std::string const& name)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
// Name may not be empty
|
||||
assert (default_ || ! name.empty());
|
||||
assert(default_ || ! name.empty());
|
||||
// Forgot to call pass or fail
|
||||
assert (default_ || cond_);
|
||||
if (! default_)
|
||||
assert(default_ || cond_);
|
||||
if(! default_)
|
||||
on_case_end();
|
||||
default_ = false;
|
||||
cond_ = false;
|
||||
on_case_begin (name);
|
||||
on_case_begin(name);
|
||||
}
|
||||
|
||||
template <class>
|
||||
template<class>
|
||||
void
|
||||
runner::pass()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
if (default_)
|
||||
testcase ("");
|
||||
if(default_)
|
||||
testcase("");
|
||||
on_pass();
|
||||
cond_ = true;
|
||||
}
|
||||
|
||||
template <class>
|
||||
template<class>
|
||||
void
|
||||
runner::fail (std::string const& reason)
|
||||
runner::fail(std::string const& reason)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
if (default_)
|
||||
testcase ("");
|
||||
on_fail (reason);
|
||||
if(default_)
|
||||
testcase("");
|
||||
on_fail(reason);
|
||||
failed_ = true;
|
||||
cond_ = true;
|
||||
}
|
||||
|
||||
template <class>
|
||||
template<class>
|
||||
void
|
||||
runner::log (std::string const& s)
|
||||
runner::log(std::string const& s)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
if (default_)
|
||||
testcase ("");
|
||||
on_log (s);
|
||||
if(default_)
|
||||
testcase("");
|
||||
on_log(s);
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define BEAST_UNIT_TEST_SUITE_HPP
|
||||
|
||||
#include <beast/unit_test/runner.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@@ -72,7 +73,7 @@ private:
|
||||
{
|
||||
auto const& s = this->str();
|
||||
if(s.size() > 0)
|
||||
suite_.runner_->on_log(s);
|
||||
suite_.runner_->log(s);
|
||||
this->str("");
|
||||
return 0;
|
||||
}
|
||||
@@ -127,7 +128,7 @@ private:
|
||||
scoped_testcase
|
||||
operator()(abort_t abort);
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
scoped_testcase
|
||||
operator<<(T const& t);
|
||||
};
|
||||
@@ -146,7 +147,9 @@ public:
|
||||
/** Returns the "current" running suite.
|
||||
If no suite is running, nullptr is returned.
|
||||
*/
|
||||
static suite* this_suite()
|
||||
static
|
||||
suite*
|
||||
this_suite()
|
||||
{
|
||||
return *p_this_suite();
|
||||
}
|
||||
@@ -158,6 +161,7 @@ public:
|
||||
}
|
||||
|
||||
/** 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
|
||||
forwarded constructor arguments to the base.
|
||||
@@ -165,67 +169,95 @@ public:
|
||||
*/
|
||||
template<class = void>
|
||||
void
|
||||
operator() (runner& r);
|
||||
operator()(runner& r);
|
||||
|
||||
/** Record a successful test condition. */
|
||||
template<class = void>
|
||||
void
|
||||
pass();
|
||||
|
||||
/** Record a failure.
|
||||
|
||||
@param reason
|
||||
*/
|
||||
template<class = void>
|
||||
void
|
||||
fail(std::string const& reason = "");
|
||||
|
||||
/** Evaluate a test condition.
|
||||
The condition is passed as a template argument instead of `bool` so
|
||||
that implicit conversion is not required. The `reason` argument is
|
||||
logged if the condition is false.
|
||||
|
||||
This function provides improved logging by incorporating the
|
||||
file name and line number into the reported output on failure,
|
||||
as well as additional text specified by the caller.
|
||||
|
||||
@param shouldBeTrue The condition to test. The condition
|
||||
is evaluated in a boolean context.
|
||||
|
||||
@param reason Optional added text to output on a failure.
|
||||
|
||||
@param file The source code file where the test failed.
|
||||
|
||||
@param line The source code line number where the test failed.
|
||||
|
||||
@return `true` if the test condition indicates success.
|
||||
*/
|
||||
template<class Condition, class String>
|
||||
bool
|
||||
expect(Condition const& shouldBeTrue,
|
||||
String const& reason);
|
||||
|
||||
/** @{ */
|
||||
template<class Condition>
|
||||
bool
|
||||
expect(Condition const& shouldBeTrue)
|
||||
{
|
||||
return expect(shouldBeTrue, "");
|
||||
return expect(shouldBeTrue, {});
|
||||
}
|
||||
|
||||
/** Expect an exception from f() */
|
||||
/** @{ */
|
||||
template <class F, class String>
|
||||
template<class Condition>
|
||||
bool
|
||||
except (F&& f, String const& reason);
|
||||
expect(Condition const& shouldBeTrue, std::string const& reason);
|
||||
|
||||
template <class F>
|
||||
template<class Condition>
|
||||
bool
|
||||
except (F&& f)
|
||||
expect(Condition const& shouldBeTrue,
|
||||
char const* file, int line)
|
||||
{
|
||||
return expect(shouldBeTrue, {}, file, line);
|
||||
}
|
||||
|
||||
template<class Condition>
|
||||
bool
|
||||
expect(Condition const& shouldBeTrue,
|
||||
std::string const& reason, char const* file, int line);
|
||||
/** @} */
|
||||
|
||||
//
|
||||
// DEPRECATED
|
||||
//
|
||||
// Expect an exception from f()
|
||||
template<class F, class String>
|
||||
bool
|
||||
except(F&& f, String const& reason);
|
||||
template<class F>
|
||||
bool
|
||||
except(F&& f)
|
||||
{
|
||||
return except(f, "");
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Expect an exception of the given type from f() */
|
||||
/** @{ */
|
||||
template <class E, class F, class String>
|
||||
template<class E, class F, class String>
|
||||
bool
|
||||
except (F&& f, String const& reason);
|
||||
|
||||
template <class E, class F>
|
||||
except(F&& f, String const& reason);
|
||||
template<class E, class F>
|
||||
bool
|
||||
except (F&& f)
|
||||
except(F&& f)
|
||||
{
|
||||
return except<E>(f, "");
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Fail if f() throws */
|
||||
/** @{ */
|
||||
template <class F, class String>
|
||||
template<class F, class String>
|
||||
bool
|
||||
unexcept (F&& f, String const& reason);
|
||||
|
||||
template <class F>
|
||||
unexcept(F&& f, String const& reason);
|
||||
template<class F>
|
||||
bool
|
||||
unexcept (F&& f)
|
||||
unexcept(F&& f)
|
||||
{
|
||||
return unexcept(f, "");
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Return the argument associated with the runner. */
|
||||
std::string const&
|
||||
@@ -235,29 +267,19 @@ public:
|
||||
}
|
||||
|
||||
// DEPRECATED
|
||||
// @return `true` if the test condition indicates success (a false value)
|
||||
template <class Condition, class String>
|
||||
// @return `true` if the test condition indicates success(a false value)
|
||||
template<class Condition, class String>
|
||||
bool
|
||||
unexpected (Condition shouldBeFalse,
|
||||
unexpected(Condition shouldBeFalse,
|
||||
String const& reason);
|
||||
|
||||
template <class Condition>
|
||||
template<class Condition>
|
||||
bool
|
||||
unexpected (Condition shouldBeFalse)
|
||||
unexpected(Condition shouldBeFalse)
|
||||
{
|
||||
return unexpected (shouldBeFalse, "");
|
||||
return unexpected(shouldBeFalse, "");
|
||||
}
|
||||
|
||||
/** Record a successful test condition. */
|
||||
template <class = void>
|
||||
void
|
||||
pass();
|
||||
|
||||
/** Record a failure. */
|
||||
template <class = void>
|
||||
void
|
||||
fail (std::string const& reason = "");
|
||||
|
||||
private:
|
||||
friend class thread;
|
||||
|
||||
@@ -277,9 +299,9 @@ private:
|
||||
void
|
||||
propagate_abort();
|
||||
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
void
|
||||
run (runner& r);
|
||||
run(runner& r);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -298,7 +320,10 @@ public:
|
||||
{
|
||||
auto const& name = ss_.str();
|
||||
if(! name.empty())
|
||||
suite_.runner_->testcase (name);
|
||||
{
|
||||
suite_.log.flush();
|
||||
suite_.runner_->testcase(name);
|
||||
}
|
||||
}
|
||||
|
||||
scoped_testcase(suite& self, std::stringstream& ss)
|
||||
@@ -333,16 +358,17 @@ public:
|
||||
|
||||
inline
|
||||
void
|
||||
suite::testcase_t::operator() (std::string const& name,
|
||||
abort_t abort)
|
||||
suite::testcase_t::operator()(
|
||||
std::string const& name, abort_t abort)
|
||||
{
|
||||
suite_.abort_ = abort == abort_on_fail;
|
||||
suite_.runner_->testcase (name);
|
||||
suite_.log.flush();
|
||||
suite_.runner_->testcase(name);
|
||||
}
|
||||
|
||||
inline
|
||||
suite::scoped_testcase
|
||||
suite::testcase_t::operator() (abort_t abort)
|
||||
suite::testcase_t::operator()(abort_t abort)
|
||||
{
|
||||
suite_.abort_ = abort == abort_on_fail;
|
||||
return { suite_, ss_ };
|
||||
@@ -351,7 +377,7 @@ suite::testcase_t::operator() (abort_t abort)
|
||||
template<class T>
|
||||
inline
|
||||
suite::scoped_testcase
|
||||
suite::testcase_t::operator<< (T const& t)
|
||||
suite::testcase_t::operator<<(T const& t)
|
||||
{
|
||||
return { suite_, ss_, t };
|
||||
}
|
||||
@@ -360,7 +386,8 @@ suite::testcase_t::operator<< (T const& t)
|
||||
|
||||
template<class>
|
||||
void
|
||||
suite::operator()(runner& r)
|
||||
suite::
|
||||
operator()(runner& r)
|
||||
{
|
||||
*p_this_suite() = this;
|
||||
try
|
||||
@@ -375,11 +402,11 @@ suite::operator()(runner& r)
|
||||
}
|
||||
}
|
||||
|
||||
template <class Condition, class String>
|
||||
inline
|
||||
template<class Condition>
|
||||
bool
|
||||
suite::expect(Condition const& shouldBeTrue,
|
||||
String const& reason)
|
||||
suite::
|
||||
expect(
|
||||
Condition const& shouldBeTrue, std::string const& reason)
|
||||
{
|
||||
if(shouldBeTrue)
|
||||
{
|
||||
@@ -390,9 +417,35 @@ suite::expect(Condition const& shouldBeTrue,
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class F, class String>
|
||||
template<class Condition>
|
||||
bool
|
||||
suite::except (F&& f, String const& reason)
|
||||
suite::
|
||||
expect(Condition const& shouldBeTrue,
|
||||
std::string const& reason, char const* file, int line)
|
||||
{
|
||||
if(shouldBeTrue)
|
||||
{
|
||||
pass();
|
||||
return true;
|
||||
}
|
||||
std::string s;
|
||||
if(! reason.empty())
|
||||
{
|
||||
s += reason;
|
||||
s += " ";
|
||||
}
|
||||
s += boost::filesystem::path{file}.filename().string() +
|
||||
"(" + std::to_string(line) + ")";
|
||||
fail(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
// DEPRECATED
|
||||
|
||||
template<class F, class String>
|
||||
bool
|
||||
suite::
|
||||
except(F&& f, String const& reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -407,9 +460,10 @@ suite::except (F&& f, String const& reason)
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class E, class F, class String>
|
||||
template<class E, class F, class String>
|
||||
bool
|
||||
suite::except (F&& f, String const& reason)
|
||||
suite::
|
||||
except(F&& f, String const& reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -424,9 +478,10 @@ suite::except (F&& f, String const& reason)
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class F, class String>
|
||||
template<class F, class String>
|
||||
bool
|
||||
suite::unexcept (F&& f, String const& reason)
|
||||
suite::
|
||||
unexcept(F&& f, String const& reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -441,36 +496,39 @@ suite::unexcept (F&& f, String const& reason)
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Condition, class String>
|
||||
inline
|
||||
template<class Condition, class String>
|
||||
bool
|
||||
suite::unexpected (Condition shouldBeFalse,
|
||||
String const& reason)
|
||||
suite::
|
||||
unexpected(
|
||||
Condition shouldBeFalse, String const& reason)
|
||||
{
|
||||
bool const b =
|
||||
static_cast<bool>(shouldBeFalse);
|
||||
if (! b)
|
||||
if(! b)
|
||||
pass();
|
||||
else
|
||||
fail (reason);
|
||||
fail(reason);
|
||||
return ! b;
|
||||
}
|
||||
|
||||
template <class>
|
||||
template<class>
|
||||
void
|
||||
suite::pass()
|
||||
suite::
|
||||
pass()
|
||||
{
|
||||
propagate_abort();
|
||||
runner_->pass();
|
||||
}
|
||||
|
||||
template <class>
|
||||
// ::fail
|
||||
template<class>
|
||||
void
|
||||
suite::fail (std::string const& reason)
|
||||
suite::
|
||||
fail(std::string const& reason)
|
||||
{
|
||||
propagate_abort();
|
||||
runner_->fail (reason);
|
||||
if (abort_)
|
||||
runner_->fail(reason);
|
||||
if(abort_)
|
||||
{
|
||||
aborted_ = true;
|
||||
throw abort_exception();
|
||||
@@ -479,15 +537,17 @@ suite::fail (std::string const& reason)
|
||||
|
||||
inline
|
||||
void
|
||||
suite::propagate_abort()
|
||||
suite::
|
||||
propagate_abort()
|
||||
{
|
||||
if (abort_ && aborted_)
|
||||
if(abort_ && aborted_)
|
||||
throw abort_exception();
|
||||
}
|
||||
|
||||
template <class>
|
||||
template<class>
|
||||
void
|
||||
suite::run (runner& r)
|
||||
suite::
|
||||
run(runner& r)
|
||||
{
|
||||
runner_ = &r;
|
||||
|
||||
@@ -495,21 +555,37 @@ suite::run (runner& r)
|
||||
{
|
||||
run();
|
||||
}
|
||||
catch (abort_exception const&)
|
||||
catch(abort_exception const&)
|
||||
{
|
||||
// ends the suite
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
runner_->fail ("unhandled exception: " +
|
||||
std::string (e.what()));
|
||||
runner_->fail("unhandled exception: " +
|
||||
std::string(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
catch(...)
|
||||
{
|
||||
runner_->fail ("unhandled exception");
|
||||
runner_->fail("unhandled exception");
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef BEAST_EXPECT
|
||||
/** Check a precondition.
|
||||
|
||||
If the condition is false, the file and line number are reported.
|
||||
*/
|
||||
#define BEAST_EXPECT(cond) expect(cond, __FILE__, __LINE__)
|
||||
#endif
|
||||
|
||||
#ifndef BEAST_EXPECTS
|
||||
/** Check a precondition.
|
||||
|
||||
If the condition is false, the file and line number are reported.
|
||||
*/
|
||||
#define BEAST_EXPECTS(cond, reason) expect(cond, reason, __FILE__, __LINE__)
|
||||
#endif
|
||||
|
||||
} // unit_test
|
||||
} // beast
|
||||
|
||||
@@ -519,7 +595,7 @@ suite::run (runner& r)
|
||||
// This inserts the suite with the given manual flag
|
||||
#define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual) \
|
||||
static beast::unit_test::detail::insert_suite <Class##_test> \
|
||||
Library ## Module ## Class ## _test_instance ( \
|
||||
Library ## Module ## Class ## _test_instance( \
|
||||
#Class, #Module, #Library, manual)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
void
|
||||
run(runner& r) const
|
||||
{
|
||||
run_ (r);
|
||||
run_(r);
|
||||
}
|
||||
|
||||
friend
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
|
||||
The suite must not already exist.
|
||||
*/
|
||||
template <class Suite>
|
||||
template<class Suite>
|
||||
void
|
||||
insert(
|
||||
char const* name,
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class Suite>
|
||||
template<class Suite>
|
||||
void
|
||||
suite_list::insert(
|
||||
char const* name,
|
||||
@@ -56,14 +56,14 @@ suite_list::insert(
|
||||
{
|
||||
std::string s;
|
||||
s = std::string(library) + "." + module + "." + name;
|
||||
auto const result (names_.insert(s));
|
||||
assert (result.second); // Duplicate name
|
||||
auto const result(names_.insert(s));
|
||||
assert(result.second); // Duplicate name
|
||||
}
|
||||
|
||||
{
|
||||
auto const result (classes_.insert (
|
||||
std::type_index (typeid(Suite))));
|
||||
assert (result.second); // Duplicate type
|
||||
auto const result(classes_.insert(
|
||||
std::type_index(typeid(Suite))));
|
||||
assert(result.second); // Duplicate type
|
||||
}
|
||||
#endif
|
||||
cont().emplace(make_suite_info<Suite>(
|
||||
|
||||
@@ -28,31 +28,31 @@ public:
|
||||
using native_handle_type = std::thread::native_handle_type;
|
||||
|
||||
thread() = default;
|
||||
thread (thread const&) = delete;
|
||||
thread& operator= (thread const&) = delete;
|
||||
thread(thread const&) = delete;
|
||||
thread& operator=(thread const&) = delete;
|
||||
|
||||
thread (thread&& other)
|
||||
: s_ (other.s_)
|
||||
, t_ (std::move(other.t_))
|
||||
thread(thread&& other)
|
||||
: s_(other.s_)
|
||||
, t_(std::move(other.t_))
|
||||
{
|
||||
}
|
||||
|
||||
thread& operator= (thread&& other)
|
||||
thread& operator=(thread&& other)
|
||||
{
|
||||
s_ = other.s_;
|
||||
t_ = std::move(other.t_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class F, class... Args>
|
||||
template<class F, class... Args>
|
||||
explicit
|
||||
thread (suite& s, F&& f, Args&&... args)
|
||||
: s_ (&s)
|
||||
thread(suite& s, F&& f, Args&&... args)
|
||||
: s_(&s)
|
||||
{
|
||||
std::function<void(void)> b =
|
||||
std::bind(std::forward<F>(f),
|
||||
std::forward<Args>(args)...);
|
||||
t_ = std::thread (&thread::run, this,
|
||||
t_ = std::thread(&thread::run, this,
|
||||
std::move(b));
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
swap (thread& other)
|
||||
swap(thread& other)
|
||||
{
|
||||
std::swap(s_, other.s_);
|
||||
std::swap(t_, other.t_);
|
||||
@@ -97,23 +97,23 @@ public:
|
||||
|
||||
private:
|
||||
void
|
||||
run (std::function <void(void)> f)
|
||||
run(std::function <void(void)> f)
|
||||
{
|
||||
try
|
||||
{
|
||||
f();
|
||||
}
|
||||
catch (suite::abort_exception const&)
|
||||
catch(suite::abort_exception const&)
|
||||
{
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
s_->fail ("unhandled exception: " +
|
||||
std::string (e.what()));
|
||||
s_->fail("unhandled exception: " +
|
||||
std::string(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
catch(...)
|
||||
{
|
||||
s_->fail ("unhandled exception");
|
||||
s_->fail("unhandled exception");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace beast {
|
||||
@note See <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf">
|
||||
Library Foundations For Asynchronous Operations</a>
|
||||
*/
|
||||
template <class CompletionHandler, class Signature>
|
||||
template<class CompletionHandler, class Signature>
|
||||
struct async_completion
|
||||
{
|
||||
/** The type of the final handler called by the asynchronous initiation function.
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Adapts a @b `MutableBufferSequence` into a @b `Streambuf`.
|
||||
/** Adapts a @b `MutableBufferSequence` into a @b `DynamicBuffer`.
|
||||
|
||||
This class wraps a @b `MutableBufferSequence` to meet the requirements
|
||||
of @b `Streambuf`. Upon construction the input and output sequences are
|
||||
of @b `DynamicBuffer`. Upon construction the input and output sequences are
|
||||
empty. A copy of the mutable buffer sequence object is stored; however,
|
||||
ownership of the underlying memory is not transferred. The caller is
|
||||
responsible for making sure that referenced memory remains valid
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace detail {
|
||||
|
||||
*/
|
||||
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
std::string const&
|
||||
base64_alphabet()
|
||||
{
|
||||
@@ -62,7 +62,7 @@ is_base64(unsigned char c)
|
||||
return (std::isalnum(c) || (c == '+') || (c == '/'));
|
||||
}
|
||||
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
std::string
|
||||
base64_encode (std::uint8_t const* data,
|
||||
std::size_t in_len)
|
||||
@@ -112,7 +112,7 @@ base64_encode (std::uint8_t const* data,
|
||||
|
||||
}
|
||||
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
std::string
|
||||
base64_encode (std::string const& s)
|
||||
{
|
||||
@@ -120,11 +120,11 @@ base64_encode (std::string const& s)
|
||||
std::uint8_t const*> (s.data()), s.size());
|
||||
}
|
||||
|
||||
template <class = void>
|
||||
template<class = void>
|
||||
std::string
|
||||
base64_decode(std::string const& data)
|
||||
{
|
||||
int in_len = data.size();
|
||||
auto in_len = data.size();
|
||||
unsigned char c3[3], c4[4];
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
@@ -54,14 +54,14 @@ public:
|
||||
operator()()
|
||||
{
|
||||
invoke(h_, args_,
|
||||
index_sequence_for<Args...> ());
|
||||
index_sequence_for<Args...>());
|
||||
}
|
||||
|
||||
void
|
||||
operator()() const
|
||||
{
|
||||
invoke(h_, args_,
|
||||
index_sequence_for<Args...> ());
|
||||
index_sequence_for<Args...>());
|
||||
}
|
||||
|
||||
friend
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
struct empty_base_optimization_decide
|
||||
: std::integral_constant <bool,
|
||||
std::is_empty <T>::value
|
||||
@@ -25,7 +25,7 @@ struct empty_base_optimization_decide
|
||||
{
|
||||
};
|
||||
|
||||
template <
|
||||
template<
|
||||
class T,
|
||||
int UniqueID = 0,
|
||||
bool ShouldDeriveFrom =
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
template<
|
||||
class T,
|
||||
int UniqueID
|
||||
>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template <class R, class C, class ...A>
|
||||
template<class R, class C, class ...A>
|
||||
auto
|
||||
is_call_possible_test(C&& c, int, A&& ...a)
|
||||
-> decltype(std::is_convertible<
|
||||
@@ -21,7 +21,7 @@ is_call_possible_test(C&& c, int, A&& ...a)
|
||||
std::is_same<R, void>::value,
|
||||
std::true_type());
|
||||
|
||||
template <class R, class C, class ...A>
|
||||
template<class R, class C, class ...A>
|
||||
std::false_type
|
||||
is_call_possible_test(C&& c, long, A&& ...a);
|
||||
|
||||
@@ -30,13 +30,13 @@ is_call_possible_test(C&& c, long, A&& ...a);
|
||||
is_call_possible<T, void(std::string)>
|
||||
*/
|
||||
/** @{ */
|
||||
template <class C, class F>
|
||||
template<class C, class F>
|
||||
struct is_call_possible
|
||||
: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class C, class R, class ...A>
|
||||
template<class C, class R, class ...A>
|
||||
struct is_call_possible<C, R(A...)>
|
||||
: decltype(is_call_possible_test<R>(
|
||||
std::declval<C>(), 1, std::declval<A>()...))
|
||||
|
||||
@@ -281,10 +281,10 @@ finish(sha1_context& ctx, void* digest) noexcept
|
||||
ctx.buf[ctx.buflen++] = 0x00;
|
||||
std::uint32_t block[BLOCK_INTS];
|
||||
sha1::make_block(ctx.buf, block);
|
||||
if (buflen > BLOCK_BYTES - 8)
|
||||
if(buflen > BLOCK_BYTES - 8)
|
||||
{
|
||||
sha1::transform(ctx.digest, block);
|
||||
for (size_t i = 0; i < BLOCK_INTS - 2; i++)
|
||||
for(size_t i = 0; i < BLOCK_INTS - 2; i++)
|
||||
block[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class has_get_io_service
|
||||
decltype(std::declval<U>().get_io_service()),
|
||||
boost::asio::io_service&>>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
public:
|
||||
using type = decltype(check<T>(0));
|
||||
|
||||
@@ -227,7 +227,7 @@ public:
|
||||
|
||||
/// Write the given data to the stream. Returns the number of bytes written,
|
||||
/// or 0 if an error occurred.
|
||||
template <class ConstBufferSequence>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
|
||||
@@ -34,10 +34,10 @@ namespace beast {
|
||||
caller is still responsible for freeing memory.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template <class T, class CompletionHandler>
|
||||
template<class T, class CompletionHandler>
|
||||
class handler_alloc;
|
||||
#else
|
||||
template <class T, class CompletionHandler>
|
||||
template<class T, class CompletionHandler>
|
||||
class handler_alloc
|
||||
{
|
||||
private:
|
||||
@@ -46,7 +46,7 @@ private:
|
||||
// should produce a compile error if CompletionHandler is not
|
||||
// constructible from H.
|
||||
//
|
||||
template <class U, class H>
|
||||
template<class U, class H>
|
||||
friend class handler_alloc;
|
||||
|
||||
CompletionHandler h_;
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
is_continuation(op->d_->h);
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_some_op* op)
|
||||
{
|
||||
|
||||
@@ -129,7 +129,7 @@ protected:
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A `Streambuf` with a fixed size internal buffer.
|
||||
/** A `DynamicBuffer` with a fixed size internal buffer.
|
||||
|
||||
@tparam N The number of bytes in the internal buffer.
|
||||
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A @b `Streambuf` that uses multiple buffers internally.
|
||||
/** A @b `DynamicBuffer` that uses multiple buffers internally.
|
||||
|
||||
The implementation uses a sequence of one or more character arrays
|
||||
of varying sizes. Additional character array objects are appended to
|
||||
the sequence to accommodate changes in the size of the character
|
||||
sequence.
|
||||
|
||||
@note Meets the requirements of @b `Streambuf`.
|
||||
@note Meets the requirements of @b `DynamicBuffer`.
|
||||
*/
|
||||
using streambuf = basic_streambuf<std::allocator<char>>;
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ protected:
|
||||
using list_t = typename boost::intrusive::make_list<
|
||||
element, boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using set_t = typename boost::intrusive::make_set<
|
||||
using set_t = typename boost::intrusive::make_multiset<
|
||||
element, boost::intrusive::constant_time_size<true>,
|
||||
boost::intrusive::compare<less>>::type;
|
||||
|
||||
@@ -117,7 +117,6 @@ protected:
|
||||
: set_(std::move(set))
|
||||
, list_(std::move(list))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -244,9 +243,10 @@ public:
|
||||
value.
|
||||
|
||||
Field names are stored as-is, but comparison are case-insensitive.
|
||||
The container preserves the order of insertion of fields with
|
||||
different names. For fields with the same name, the implementation
|
||||
concatenates values inserted with duplicate names as per rfc7230.
|
||||
When the container is iterated, the fields are presented in the order
|
||||
of insertion. For fields with the same name, the container behaves
|
||||
as a std::multiset; there will be a separate value for each occurrence
|
||||
of the field name.
|
||||
|
||||
@note Meets the requirements of @b `FieldSequence`.
|
||||
*/
|
||||
@@ -359,27 +359,44 @@ public:
|
||||
return set_.size();
|
||||
}
|
||||
|
||||
/** Returns `true` if the specified field exists. */
|
||||
/// Returns `true` if the specified field exists.
|
||||
bool
|
||||
exists(boost::string_ref const& name) const
|
||||
{
|
||||
return set_.find(name, less{}) != set_.end();
|
||||
}
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching header. */
|
||||
/// Returns the number of values for the specified field.
|
||||
std::size_t
|
||||
count(boost::string_ref const& name) const;
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching field name.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
*/
|
||||
iterator
|
||||
find(boost::string_ref const& name) const;
|
||||
|
||||
/** Returns the value for a case-insensitive matching header, or "" */
|
||||
/** Returns the value for a case-insensitive matching header, or `""`.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
*/
|
||||
boost::string_ref
|
||||
operator[](boost::string_ref const& name) const;
|
||||
|
||||
/** Clear the contents of the basic_headers. */
|
||||
/// Clear the contents of the basic_headers.
|
||||
void
|
||||
clear() noexcept;
|
||||
|
||||
/** Remove a field.
|
||||
|
||||
If more than one field with the specified name exists, all
|
||||
matching fields will be removed.
|
||||
|
||||
@param name The name of the field(s) to remove.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
@@ -387,39 +404,57 @@ public:
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field value already exists the new value will be
|
||||
extended as per RFC2616 Section 4.2.
|
||||
If a field with the same name already exists, the
|
||||
existing field is untouched and a new field value pair
|
||||
is inserted into the container.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@param value A string holding the value of the field.
|
||||
*/
|
||||
// VFALCO TODO Consider allowing rvalue references for std::move?
|
||||
void
|
||||
insert(boost::string_ref const& name, boost::string_ref value);
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field value already exists the new value will be
|
||||
extended as per RFC2616 Section 4.2.
|
||||
If a field with the same name already exists, the
|
||||
existing field is untouched and a new field value pair
|
||||
is inserted into the container.
|
||||
|
||||
@param name The name of the field
|
||||
|
||||
@param value The value of the field. The object will be
|
||||
converted to a string using `boost::lexical_cast`.
|
||||
*/
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_constructible<boost::string_ref, T>::value>::type
|
||||
insert(boost::string_ref name, T const& value)
|
||||
{
|
||||
insert(name,
|
||||
boost::lexical_cast<std::string>(value));
|
||||
insert(name, boost::lexical_cast<std::string>(value));
|
||||
}
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
The current field value, if any, is removed. Then the
|
||||
specified value is inserted as if by `insert(field, value)`.
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@param value A string holding the value of the field.
|
||||
*/
|
||||
void
|
||||
replace(boost::string_ref const& name, boost::string_ref value);
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
The current field value, if any, is removed. Then the
|
||||
specified value is inserted as if by `insert(field, value)`.
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The name of the field
|
||||
|
||||
@param value The value of the field. The object will be
|
||||
converted to a string using `boost::lexical_cast`.
|
||||
*/
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
|
||||
@@ -482,7 +482,7 @@ private:
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
@@ -501,7 +501,7 @@ private:
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
@@ -520,7 +520,7 @@ private:
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
@@ -539,7 +539,7 @@ private:
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
@@ -557,7 +557,7 @@ private:
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
@@ -575,7 +575,7 @@ private:
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
@@ -594,7 +594,7 @@ private:
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
@@ -613,7 +613,7 @@ private:
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
@@ -630,7 +630,7 @@ private:
|
||||
decltype(std::declval<T>().on_headers(
|
||||
std::declval<std::uint64_t>(), std::declval<error_code&>()))>>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
@@ -649,7 +649,7 @@ private:
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
@@ -667,7 +667,7 @@ private:
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
|
||||
@@ -29,7 +29,7 @@ class chunk_encode_text
|
||||
// Storage for the longest hex string we might need, plus delimiters.
|
||||
std::array<char, 2 * sizeof(std::size_t) + 2> buf_;
|
||||
|
||||
template <class OutIter>
|
||||
template<class OutIter>
|
||||
static
|
||||
OutIter
|
||||
to_hex(OutIter last, std::size_t n)
|
||||
|
||||
@@ -22,7 +22,7 @@ class has_content_length_value
|
||||
decltype(std::declval<U>().content_length()),
|
||||
std::uint64_t>>
|
||||
static R check(int);
|
||||
template <class>
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
public:
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
|
||||
|
||||
#include <beast/http/detail/rfc7230.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
@@ -204,6 +205,18 @@ basic_headers(FwdIt first, FwdIt last)
|
||||
insert(first->name(), first->value());
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
basic_headers<Allocator>::
|
||||
count(boost::string_ref const& name) const
|
||||
{
|
||||
auto const it = set_.find(name, less{});
|
||||
if(it == set_.end())
|
||||
return 0;
|
||||
auto const last = set_.upper_bound(name, less{});
|
||||
return static_cast<std::size_t>(std::distance(it, last));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_headers<Allocator>::
|
||||
@@ -221,11 +234,9 @@ boost::string_ref
|
||||
basic_headers<Allocator>::
|
||||
operator[](boost::string_ref const& name) const
|
||||
{
|
||||
// VFALCO This none object looks sketchy
|
||||
static boost::string_ref const none;
|
||||
auto const it = find(name);
|
||||
if(it == end())
|
||||
return none;
|
||||
return {};
|
||||
return it->second;
|
||||
}
|
||||
|
||||
@@ -244,15 +255,23 @@ std::size_t
|
||||
basic_headers<Allocator>::
|
||||
erase(boost::string_ref const& name)
|
||||
{
|
||||
auto const it = set_.find(name, less{});
|
||||
auto it = set_.find(name, less{});
|
||||
if(it == set_.end())
|
||||
return 0;
|
||||
auto& e = *it;
|
||||
set_.erase(set_.iterator_to(e));
|
||||
list_.erase(list_.iterator_to(e));
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(), &e, 1);
|
||||
return 1;
|
||||
auto const last = set_.upper_bound(name, less{});
|
||||
std::size_t n = 1;
|
||||
for(;;)
|
||||
{
|
||||
auto& e = *it++;
|
||||
set_.erase(set_.iterator_to(e));
|
||||
list_.erase(list_.iterator_to(e));
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(), &e, 1);
|
||||
if(it == last)
|
||||
break;
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
@@ -262,25 +281,10 @@ insert(boost::string_ref const& name,
|
||||
boost::string_ref value)
|
||||
{
|
||||
value = detail::trim(value);
|
||||
typename set_t::insert_commit_data d;
|
||||
auto const result =
|
||||
set_.insert_check(name, less{}, d);
|
||||
if(result.second)
|
||||
{
|
||||
auto const p = alloc_traits::allocate(
|
||||
this->member(), 1);
|
||||
alloc_traits::construct(
|
||||
this->member(), p, name, value);
|
||||
list_.push_back(*p);
|
||||
set_.insert_commit(*p, d);
|
||||
return;
|
||||
}
|
||||
// If field already exists, insert comma
|
||||
// separated value as per RFC2616 section 4.2
|
||||
auto& cur = result.first->data.second;
|
||||
cur.reserve(cur.size() + 1 + value.size());
|
||||
cur.append(1, ',');
|
||||
cur.append(value.data(), value.size());
|
||||
auto const p = alloc_traits::allocate(this->member(), 1);
|
||||
alloc_traits::construct(this->member(), p, name, value);
|
||||
set_.insert_before(set_.upper_bound(name, less{}), *p);
|
||||
list_.push_back(*p);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef BEAST_HTTP_IMPL_MESSAGE_V1_IPP
|
||||
#define BEAST_HTTP_IMPL_MESSAGE_V1_IPP
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/http/detail/has_content_length.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
@@ -87,7 +88,11 @@ prepare_content_length(prepare_info& pi,
|
||||
std::true_type)
|
||||
{
|
||||
typename Body::writer w(msg);
|
||||
//w.init(ec); // VFALCO This is a design problem!
|
||||
// VFALCO This is a design problem!
|
||||
error_code ec;
|
||||
w.init(ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
pi.content_length = w.content_length();
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, parse_op* op)
|
||||
{
|
||||
@@ -277,7 +277,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
@@ -412,7 +412,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
static_assert(is_ReadableBody<Body>::value,
|
||||
"ReadableBody requirements not met");
|
||||
error_code ec;
|
||||
read(stream, dynabuf, msg, ec);
|
||||
beast::http::read(stream, dynabuf, msg, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
@@ -431,7 +431,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
static_assert(is_ReadableBody<Body>::value,
|
||||
"ReadableBody requirements not met");
|
||||
parser_v1<isRequest, Body, Headers> p;
|
||||
parse(stream, dynabuf, p, ec);
|
||||
beast::http::parse(stream, dynabuf, p, ec);
|
||||
if(ec)
|
||||
return;
|
||||
assert(p.complete());
|
||||
|
||||
@@ -262,7 +262,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace http {
|
||||
|
||||
enum class parse_error
|
||||
{
|
||||
success = 0,
|
||||
|
||||
connection_closed,
|
||||
|
||||
bad_method,
|
||||
|
||||
@@ -35,6 +35,37 @@ struct parser_response
|
||||
|
||||
} // detail
|
||||
|
||||
/** Skip body option.
|
||||
|
||||
The options controls whether or not the parser expects to see a
|
||||
HTTP body, regardless of the presence or absence of certain fields
|
||||
such as Content-Length.
|
||||
|
||||
Depending on the request, some responses do not carry a body.
|
||||
For example, a 200 response to a CONNECT request from a tunneling
|
||||
proxy. In these cases, callers use the @ref skip_body option to
|
||||
inform the parser that no body is expected. The parser will consider
|
||||
the message complete after the all headers have been received.
|
||||
|
||||
Example:
|
||||
@code
|
||||
parser_v1<true, empty_body, headers> p;
|
||||
p.set_option(skip_body{true});
|
||||
@endcode
|
||||
|
||||
@note Objects of this type are passed to @ref basic_parser_v1::set_option.
|
||||
*/
|
||||
struct skip_body
|
||||
{
|
||||
bool value;
|
||||
|
||||
explicit
|
||||
skip_body(bool v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** A parser for producing HTTP/1 messages.
|
||||
|
||||
This class uses the basic HTTP/1 wire format parser to convert
|
||||
@@ -62,6 +93,7 @@ private:
|
||||
std::string value_;
|
||||
message_type m_;
|
||||
typename message_type::body_type::reader r_;
|
||||
std::uint8_t skip_body_ = 0;
|
||||
|
||||
public:
|
||||
parser_v1(parser_v1&&) = default;
|
||||
@@ -81,6 +113,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// Set the expect body option.
|
||||
void
|
||||
set_option(skip_body const& o)
|
||||
{
|
||||
skip_body_ = o.value ? 1 : 0;
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Only valid if `complete()` would return `true`.
|
||||
@@ -176,7 +215,7 @@ private:
|
||||
{
|
||||
flush();
|
||||
m_.version = 10 * this->http_major() + this->http_minor();
|
||||
return 0;
|
||||
return skip_body_;
|
||||
}
|
||||
|
||||
void on_request(error_code& ec)
|
||||
|
||||
@@ -30,6 +30,16 @@ namespace http {
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
@code
|
||||
for(auto const& param : param_list{";level=9;no_context_takeover;bits=15"})
|
||||
{
|
||||
std::cout << ";" << param.first;
|
||||
if(! param.second.empty())
|
||||
std::cout << "=" << param.second;
|
||||
std::cout << "\n";
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class param_list
|
||||
{
|
||||
@@ -98,6 +108,24 @@ public:
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
To use this class, construct with the string to be parsed and
|
||||
then use @ref begin and @end, or range-for to iterate each
|
||||
item:
|
||||
|
||||
@code
|
||||
for(auto const& ext : ext_list{"none, 7z;level=9, zip;no_context_takeover;bits=15"})
|
||||
{
|
||||
std::cout << ext.first << "\n";
|
||||
for(auto const& param : ext.second)
|
||||
{
|
||||
std::cout << ";" << param.first;
|
||||
if(! param.second.empty())
|
||||
std::cout << "=" << param.second;
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class ext_list
|
||||
{
|
||||
@@ -181,6 +209,15 @@ public:
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
To use this class, construct with the string to be parsed and
|
||||
then use @ref begin and @end, or range-for to iterate each
|
||||
item:
|
||||
|
||||
@code
|
||||
for(auto const& token : token_list{"apple, pear, banana"})
|
||||
std::cout << token << "\n";
|
||||
@endcode
|
||||
*/
|
||||
class token_list
|
||||
{
|
||||
|
||||
@@ -1,71 +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_STATUS_HPP
|
||||
#define BEAST_HTTP_STATUS_HPP
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Returns the string corresponding to the numeric HTTP status code. */
|
||||
template<class = void>
|
||||
char const*
|
||||
status_text(int status)
|
||||
{
|
||||
switch(status)
|
||||
{
|
||||
case 100: return "Continue";
|
||||
case 101: return "Switching Protocols";
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 202: return "Accepted";
|
||||
case 203: return "Non-Authoritative Information";
|
||||
case 204: return "No Content";
|
||||
case 205: return "Reset Content";
|
||||
case 206: return "Partial Content";
|
||||
case 300: return "Multiple Choices";
|
||||
case 301: return "Moved Permanently";
|
||||
case 302: return "Found";
|
||||
case 303: return "See Other";
|
||||
case 304: return "Not Modified";
|
||||
case 305: return "Use Proxy";
|
||||
// case 306: return "<reserved>";
|
||||
case 307: return "Temporary Redirect";
|
||||
case 400: return "Bad Request";
|
||||
case 401: return "Unauthorized";
|
||||
case 402: return "Payment Required";
|
||||
case 403: return "Forbidden";
|
||||
case 404: return "Not Found";
|
||||
case 405: return "Method Not Allowed";
|
||||
case 406: return "Not Acceptable";
|
||||
case 407: return "Proxy Authentication Required";
|
||||
case 408: return "Request Timeout";
|
||||
case 409: return "Conflict";
|
||||
case 410: return "Gone";
|
||||
case 411: return "Length Required";
|
||||
case 412: return "Precondition Failed";
|
||||
case 413: return "Request Entity Too Large";
|
||||
case 414: return "Request-URI Too Long";
|
||||
case 415: return "Unsupported Media Type";
|
||||
case 416: return "Requested Range Not Satisfiable";
|
||||
case 417: return "Expectation Failed";
|
||||
case 500: return "Internal Server Error";
|
||||
case 501: return "Not Implemented";
|
||||
case 502: return "Bad Gateway";
|
||||
case 503: return "Service Unavailable";
|
||||
case 504: return "Gateway Timeout";
|
||||
case 505: return "HTTP Version Not Supported";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "Unknown HTTP status";
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -14,8 +14,8 @@
|
||||
// BEAST_VERSION / 100 % 1000 is the minor version
|
||||
// BEAST_VERSION / 100000 is the major version
|
||||
//
|
||||
#define BEAST_VERSION 100006
|
||||
#define BEAST_VERSION 100000
|
||||
|
||||
#define BEAST_VERSION_STRING "1.0.0-b6"
|
||||
#define BEAST_VERSION_STRING "1.0.0-b13"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -110,7 +110,7 @@ is_valid(close_code::value code)
|
||||
if(v >= 1016 && v <= 2999)
|
||||
return false;
|
||||
// not used
|
||||
if(v >= 0 && v <= 999)
|
||||
if(v <= 999)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -136,12 +136,12 @@ write(DynamicBuffer& db, frame_header const& fh)
|
||||
if(fh.rsv3)
|
||||
b[0] |= 0x10;
|
||||
b[1] = fh.mask ? 0x80 : 0x00;
|
||||
if (fh.len <= 125)
|
||||
if(fh.len <= 125)
|
||||
{
|
||||
b[1] |= fh.len;
|
||||
n = 2;
|
||||
}
|
||||
else if (fh.len <= 65535)
|
||||
else if(fh.len <= 65535)
|
||||
{
|
||||
b[1] |= 126;
|
||||
::new(&b[2]) big_uint16_buf_t{
|
||||
|
||||
@@ -113,7 +113,7 @@ mask_inplace_general(
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto const n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
@@ -122,13 +122,14 @@ mask_inplace_general(
|
||||
*p ^= (key >>16); ++p;
|
||||
*p ^= (key >>24); ++p;
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
auto const m =
|
||||
static_cast<std::uint8_t>(n % sizeof(key));
|
||||
switch(m)
|
||||
{
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
key = ror(key, m*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -144,7 +145,7 @@ mask_inplace_general(
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto const n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
@@ -157,8 +158,9 @@ mask_inplace_general(
|
||||
*p ^= (key >>48); ++p;
|
||||
*p ^= (key >>56); ++p;
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
auto const m =
|
||||
static_cast<std::uint8_t>(n % sizeof(key));
|
||||
switch(m)
|
||||
{
|
||||
case 7: p[6] ^= (key >>16);
|
||||
case 6: p[5] ^= (key >> 8);
|
||||
@@ -167,7 +169,7 @@ mask_inplace_general(
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
key = ror(key, m*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ utf8_checker_t<_>::write(BufferSequence const& bs)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
for (auto const& b : bs)
|
||||
for(auto const& b : bs)
|
||||
if(! write(buffer_cast<void const*>(b),
|
||||
buffer_size(b)))
|
||||
return false;
|
||||
|
||||
@@ -16,8 +16,10 @@ namespace websocket {
|
||||
/// Error codes returned from @ref stream operations.
|
||||
enum class error
|
||||
{
|
||||
success = 0,
|
||||
|
||||
/// Both sides performed a WebSocket close
|
||||
closed = 1,
|
||||
closed,
|
||||
|
||||
/// WebSocket connection failed, protocol violation
|
||||
failed,
|
||||
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, accept_op* op)
|
||||
{
|
||||
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, close_op* op)
|
||||
{
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, handshake_op* op)
|
||||
{
|
||||
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, ping_op* op)
|
||||
{
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_frame_op* op)
|
||||
{
|
||||
@@ -252,7 +252,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||
break;
|
||||
}
|
||||
d.state = do_read_fh + 2;
|
||||
if (n == 0)
|
||||
if(n == 0)
|
||||
{
|
||||
bytes_transferred = 0;
|
||||
break;
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, response_op* op)
|
||||
{
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f,
|
||||
teardown_ssl_op* op)
|
||||
@@ -129,7 +129,7 @@ operator()(error_code ec, bool again)
|
||||
|
||||
template<class AsyncStream>
|
||||
void
|
||||
teardown(
|
||||
teardown(teardown_tag,
|
||||
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||
error_code& ec)
|
||||
{
|
||||
@@ -138,7 +138,7 @@ teardown(
|
||||
|
||||
template<class AsyncStream, class TeardownHandler>
|
||||
void
|
||||
async_teardown(
|
||||
async_teardown(teardown_tag,
|
||||
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||
TeardownHandler&& handler)
|
||||
{
|
||||
|
||||
@@ -920,7 +920,7 @@ build_request(boost::string_ref const& host,
|
||||
boost::string_ref const& resource, std::string& key)
|
||||
{
|
||||
http::request_v1<http::empty_body> req;
|
||||
req.url = "/";
|
||||
req.url = { resource.data(), resource.size() };
|
||||
req.version = 11;
|
||||
req.method = "GET";
|
||||
req.headers.insert("Host", host);
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f,
|
||||
teardown_tcp_op* op)
|
||||
@@ -128,7 +128,7 @@ operator()(error_code ec, std::size_t, bool again)
|
||||
|
||||
inline
|
||||
void
|
||||
teardown(
|
||||
teardown(teardown_tag,
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
error_code& ec)
|
||||
{
|
||||
@@ -151,7 +151,7 @@ teardown(
|
||||
template<class TeardownHandler>
|
||||
inline
|
||||
void
|
||||
async_teardown(
|
||||
async_teardown(teardown_tag,
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
TeardownHandler&& handler)
|
||||
{
|
||||
|
||||
@@ -134,7 +134,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_frame_op* op)
|
||||
{
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace websocket {
|
||||
*/
|
||||
template<class SyncStream>
|
||||
void
|
||||
teardown(
|
||||
teardown(teardown_tag,
|
||||
boost::asio::ssl::stream<SyncStream>& stream,
|
||||
error_code& ec);
|
||||
|
||||
@@ -62,7 +62,7 @@ teardown(
|
||||
template<class AsyncStream, class TeardownHandler>
|
||||
inline
|
||||
void
|
||||
async_teardown(
|
||||
async_teardown(teardown_tag,
|
||||
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||
TeardownHandler&& handler);
|
||||
|
||||
|
||||
@@ -13,8 +13,17 @@
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
namespace websocket {
|
||||
|
||||
/** Tag type used to find teardown and async_teardown overloads
|
||||
|
||||
Overloads of @ref teardown and @async_teardown for user defined
|
||||
types must take a value of type @ref teardown_tag in the first
|
||||
argument in order to be found by the implementation.
|
||||
*/
|
||||
struct teardown_tag {};
|
||||
|
||||
/** Tear down a connection.
|
||||
|
||||
This tears down a connection. The implementation will call
|
||||
@@ -30,7 +39,19 @@ namespace websocket {
|
||||
*/
|
||||
template<class Socket>
|
||||
void
|
||||
teardown(Socket& socket, error_code& ec) = delete;
|
||||
teardown(teardown_tag, Socket& socket, error_code& ec)
|
||||
{
|
||||
/*
|
||||
If you are trying to use OpenSSL and this goes off, you need to
|
||||
add an include for <beast/websocket/ssl.hpp>.
|
||||
|
||||
If you are creating an instance of beast::websocket::stream with your
|
||||
own user defined type, you must provide an overload of teardown with
|
||||
the corresponding signature (including the teardown_tag).
|
||||
*/
|
||||
static_assert(sizeof(Socket)==-1,
|
||||
"Unknown Socket type in teardown.");
|
||||
}
|
||||
|
||||
/** Start tearing down a connection.
|
||||
|
||||
@@ -49,7 +70,8 @@ teardown(Socket& socket, error_code& ec) = delete;
|
||||
function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
);
|
||||
@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
|
||||
@@ -58,57 +80,19 @@ teardown(Socket& socket, error_code& ec) = delete;
|
||||
*/
|
||||
template<class Socket, class TeardownHandler>
|
||||
void
|
||||
async_teardown(Socket& socket, TeardownHandler&& handler) = delete;
|
||||
async_teardown(teardown_tag, Socket& socket, TeardownHandler&& handler)
|
||||
{
|
||||
/*
|
||||
If you are trying to use OpenSSL and this goes off, you need to
|
||||
add an include for <beast/websocket/ssl.hpp>.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Tear down a `boost::asio::ip::tcp::socket`.
|
||||
|
||||
This tears down a connection. The implementation will call
|
||||
the overload of this function based on the `Stream` parameter
|
||||
used to consruct the socket. When `Stream` is a user defined
|
||||
type, and not a `boost::asio::ip::tcp::socket` or any
|
||||
`boost::asio::ssl::stream`, callers are responsible for
|
||||
providing a suitable overload of this function.
|
||||
|
||||
@param socket The socket to tear down.
|
||||
|
||||
@param ec Set to the error if any occurred.
|
||||
If you are creating an instance of beast::websocket::stream with your
|
||||
own user defined type, you must provide an overload of teardown with
|
||||
the corresponding signature (including the teardown_tag).
|
||||
*/
|
||||
void
|
||||
teardown(
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
error_code& ec);
|
||||
|
||||
/** Start tearing down a `boost::asio::ip::tcp::socket`.
|
||||
|
||||
This begins tearing down a connection asynchronously.
|
||||
The implementation will call the overload of this function
|
||||
based on the `Stream` parameter used to consruct the socket.
|
||||
When `Stream` is a user defined type, and not a
|
||||
`boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
|
||||
callers are responsible for providing a suitable overload
|
||||
of this function.
|
||||
|
||||
@param socket The socket to tear down.
|
||||
|
||||
@param handler The handler to be called when the request completes.
|
||||
Copies will be made of the handler as required. The equivalent
|
||||
function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using boost::asio::io_service::post().
|
||||
|
||||
*/
|
||||
template<class TeardownHandler>
|
||||
void
|
||||
async_teardown(
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
TeardownHandler&& handler);
|
||||
static_assert(sizeof(Socket)==-1,
|
||||
"Unknown Socket type in async_teardown.");
|
||||
}
|
||||
|
||||
} // websocket
|
||||
|
||||
@@ -127,7 +111,7 @@ void
|
||||
call_teardown(Socket& socket, error_code& ec)
|
||||
{
|
||||
using websocket::teardown;
|
||||
teardown(socket, ec);
|
||||
teardown(websocket::teardown_tag{}, socket, ec);
|
||||
}
|
||||
|
||||
template<class Socket, class TeardownHandler>
|
||||
@@ -136,12 +120,65 @@ void
|
||||
call_async_teardown(Socket& socket, TeardownHandler&& handler)
|
||||
{
|
||||
using websocket::async_teardown;
|
||||
async_teardown(socket,
|
||||
async_teardown(websocket::teardown_tag{}, socket,
|
||||
std::forward<TeardownHandler>(handler));
|
||||
}
|
||||
|
||||
} // websocket_helpers
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace websocket {
|
||||
|
||||
/** Tear down a `boost::asio::ip::tcp::socket`.
|
||||
|
||||
This tears down a connection. The implementation will call
|
||||
the overload of this function based on the `Stream` parameter
|
||||
used to consruct the socket. When `Stream` is a user defined
|
||||
type, and not a `boost::asio::ip::tcp::socket` or any
|
||||
`boost::asio::ssl::stream`, callers are responsible for
|
||||
providing a suitable overload of this function.
|
||||
|
||||
@param socket The socket to tear down.
|
||||
|
||||
@param ec Set to the error if any occurred.
|
||||
*/
|
||||
void
|
||||
teardown(teardown_tag,
|
||||
boost::asio::ip::tcp::socket& socket, error_code& ec);
|
||||
|
||||
/** Start tearing down a `boost::asio::ip::tcp::socket`.
|
||||
|
||||
This begins tearing down a connection asynchronously.
|
||||
The implementation will call the overload of this function
|
||||
based on the `Stream` parameter used to consruct the socket.
|
||||
When `Stream` is a user defined type, and not a
|
||||
`boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
|
||||
callers are responsible for providing a suitable overload
|
||||
of this function.
|
||||
|
||||
@param socket The socket to tear down.
|
||||
|
||||
@param handler The handler to be called when the request completes.
|
||||
Copies will be made of the handler as required. The equivalent
|
||||
function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
);
|
||||
@endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using boost::asio::io_service::post().
|
||||
|
||||
*/
|
||||
template<class TeardownHandler>
|
||||
void
|
||||
async_teardown(teardown_tag,
|
||||
boost::asio::ip::tcp::socket& socket, TeardownHandler&& handler);
|
||||
|
||||
} // websocket
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/websocket/impl/teardown.ipp>
|
||||
|
||||
@@ -37,8 +37,8 @@ elif [[ $(uname -s) == "Linux" ]]; then
|
||||
num_proc_units=$(nproc)
|
||||
# Physical cores
|
||||
num_jobs=$(lscpu -p | grep -v '^#' | sort -u -t, -k 2,4 | wc -l)
|
||||
if (("$num_proc_units" < "$num_jobs")); then
|
||||
num_jobs=$num_proc_units
|
||||
if ((${num_proc_units} < ${num_jobs})); then
|
||||
num_jobs=$num_proc_units
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,16 @@ function build_beast {
|
||||
-j${num_jobs}
|
||||
}
|
||||
|
||||
function build_beast_cmake {
|
||||
mkdir -p build
|
||||
pushd build > /dev/null
|
||||
cmake -DVARIANT=${VARIANT} ..
|
||||
make -j${num_jobs}
|
||||
mkdir -p ../bin/$VARIANT
|
||||
find . -executable -type f -exec cp {} ../bin/$VARIANT/. \;
|
||||
popd > /dev/null
|
||||
}
|
||||
|
||||
function run_autobahn_test_suite {
|
||||
# Run autobahn tests
|
||||
wsecho=$(find bin -name "websocket-echo" | grep /$VARIANT/)
|
||||
@@ -108,7 +118,11 @@ function run_autobahn_test_suite {
|
||||
|
||||
##################################### BUILD ####################################
|
||||
|
||||
build_beast
|
||||
if [[ ${BUILD_SYSTEM:-} == cmake ]]; then
|
||||
build_beast_cmake
|
||||
else
|
||||
build_beast
|
||||
fi
|
||||
|
||||
##################################### TESTS ####################################
|
||||
|
||||
@@ -123,6 +137,7 @@ if [[ $VARIANT == "coverage" ]]; then
|
||||
run_tests_with_valgrind
|
||||
run_autobahn_test_suite
|
||||
else
|
||||
echo "skipping autobahn tests for feature branch build"
|
||||
run_tests
|
||||
fi
|
||||
|
||||
|
||||
@@ -12,11 +12,32 @@ do
|
||||
test -x $( type -p ${g}-$GCC_VER )
|
||||
ln -sv $(type -p ${g}-$GCC_VER) $HOME/bin/${g}
|
||||
done
|
||||
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}
|
||||
done
|
||||
|
||||
if [[ -n ${CLANG_VER:-} ]]; then
|
||||
# There are cases where the directory exists, but the exe is not available.
|
||||
# Use this workaround for now.
|
||||
if [[ ! -x llvm-${LLVM_VERSION}/bin/llvm-config ]] && [[ -d llvm-${LLVM_VERSION} ]]; then
|
||||
rm -fr llvm-${LLVM_VERSION}
|
||||
fi
|
||||
if [[ ! -d llvm-${LLVM_VERSION} ]]; then
|
||||
mkdir llvm-${LLVM_VERSION}
|
||||
LLVM_URL="http://llvm.org/releases/${LLVM_VERSION}/clang+llvm-${LLVM_VERSION}-x86_64-linux-gnu-ubuntu-14.04.tar.xz"
|
||||
wget -O - ${LLVM_URL} | tar -Jxvf - --strip 1 -C llvm-${LLVM_VERSION}
|
||||
fi
|
||||
llvm-${LLVM_VERSION}/bin/llvm-config --version;
|
||||
export LLVM_CONFIG="llvm-${LLVM_VERSION}/bin/llvm-config";
|
||||
fi
|
||||
|
||||
# There are cases where the directory exists, but the exe is not available.
|
||||
# Use this workaround for now.
|
||||
if [[ ! -x cmake/bin/cmake && -d cmake ]]; then
|
||||
rm -fr cmake
|
||||
fi
|
||||
if [[ ! -d cmake && ${BUILD_SYSTEM:-} == cmake ]]; then
|
||||
CMAKE_URL="http://www.cmake.org/files/v3.5/cmake-3.5.2-Linux-x86_64.tar.gz"
|
||||
mkdir cmake && wget --no-check-certificate -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
|
||||
fi
|
||||
|
||||
# NOTE, changed from PWD -> HOME
|
||||
export PATH=$HOME/bin:$PATH
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# Part of Beast
|
||||
|
||||
GroupSources(extras/beast beast)
|
||||
GroupSources(extras/beast extras)
|
||||
GroupSources(include/beast beast)
|
||||
GroupSources(test "/")
|
||||
|
||||
add_executable (lib-tests
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
../extras/beast/unit_test/main.cpp
|
||||
core.cpp
|
||||
http.cpp
|
||||
|
||||
@@ -33,10 +33,10 @@ unit-test core-tests :
|
||||
core/streambuf.cpp
|
||||
core/to_string.cpp
|
||||
core/write_dynabuf.cpp
|
||||
core/detail/base64.cpp
|
||||
core/detail/empty_base_optimization.cpp
|
||||
core/detail/get_lowest_layer.cpp
|
||||
core/detail/sha1.cpp
|
||||
core/base64.cpp
|
||||
core/empty_base_optimization.cpp
|
||||
core/get_lowest_layer.cpp
|
||||
core/sha1.cpp
|
||||
;
|
||||
|
||||
unit-test http-tests :
|
||||
@@ -56,11 +56,10 @@ unit-test http-tests :
|
||||
http/reason.cpp
|
||||
http/resume_context.cpp
|
||||
http/rfc7230.cpp
|
||||
http/status.cpp
|
||||
http/streambuf_body.cpp
|
||||
http/string_body.cpp
|
||||
http/write.cpp
|
||||
http/detail/chunk_encode.cpp
|
||||
http/chunk_encode.cpp
|
||||
;
|
||||
|
||||
unit-test bench-tests :
|
||||
@@ -76,10 +75,10 @@ unit-test websocket-tests :
|
||||
websocket/rfc6455.cpp
|
||||
websocket/stream.cpp
|
||||
websocket/teardown.cpp
|
||||
websocket/detail/frame.cpp
|
||||
websocket/detail/mask.cpp
|
||||
websocket/detail/stream_base.cpp
|
||||
websocket/detail/utf8_checker.cpp
|
||||
websocket/frame.cpp
|
||||
websocket/mask.cpp
|
||||
websocket/stream_base.cpp
|
||||
websocket/utf8_checker.cpp
|
||||
;
|
||||
|
||||
exe websocket-echo :
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# Part of Beast
|
||||
|
||||
GroupSources(extras/beast beast)
|
||||
GroupSources(extras/beast extras)
|
||||
GroupSources(include/beast beast)
|
||||
GroupSources(test/core "/")
|
||||
|
||||
add_executable (core-tests
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
../../extras/beast/unit_test/main.cpp
|
||||
buffer_test.hpp
|
||||
async_completion.cpp
|
||||
@@ -27,12 +28,12 @@ add_executable (core-tests
|
||||
streambuf.cpp
|
||||
to_string.cpp
|
||||
write_dynabuf.cpp
|
||||
detail/base64.cpp
|
||||
detail/empty_base_optimization.cpp
|
||||
detail/get_lowest_layer.cpp
|
||||
detail/sha1.cpp
|
||||
base64.cpp
|
||||
empty_base_optimization.cpp
|
||||
get_lowest_layer.cpp
|
||||
sha1.cpp
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(core-tests ${Boost_LIBRARIES})
|
||||
target_link_libraries(core-tests ${Boost_LIBRARIES} Threads::Threads)
|
||||
endif()
|
||||
|
||||
@@ -20,8 +20,8 @@ public:
|
||||
check (std::string const& in, std::string const& out)
|
||||
{
|
||||
auto const encoded = base64_encode (in);
|
||||
expect (encoded == out);
|
||||
expect (base64_decode (encoded) == in);
|
||||
BEAST_EXPECT(encoded == out);
|
||||
BEAST_EXPECT(base64_decode (encoded) == in);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -153,10 +153,10 @@ public:
|
||||
void
|
||||
expect_size(std::size_t n, ConstBufferSequence const& buffers)
|
||||
{
|
||||
expect(test::size_pre(buffers) == n);
|
||||
expect(test::size_post(buffers) == n);
|
||||
expect(test::size_rev_pre(buffers) == n);
|
||||
expect(test::size_rev_post(buffers) == n);
|
||||
BEAST_EXPECT(test::size_pre(buffers) == n);
|
||||
BEAST_EXPECT(test::size_post(buffers) == n);
|
||||
BEAST_EXPECT(test::size_rev_pre(buffers) == n);
|
||||
BEAST_EXPECT(test::size_rev_post(buffers) == n);
|
||||
}
|
||||
|
||||
template<class U, class V>
|
||||
@@ -173,7 +173,7 @@ public:
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
std::string const s = "Hello, world";
|
||||
expect(s.size() == 12);
|
||||
BEAST_EXPECT(s.size() == 12);
|
||||
for(std::size_t i = 1; i < 12; ++i) {
|
||||
for(std::size_t x = 1; x < 4; ++x) {
|
||||
for(std::size_t y = 1; y < 4; ++y) {
|
||||
@@ -183,28 +183,28 @@ public:
|
||||
sb.commit(buffer_copy(sb.prepare(x), buffer(s.data(), x)));
|
||||
sb.commit(buffer_copy(sb.prepare(y), buffer(s.data()+x, y)));
|
||||
sb.commit(buffer_copy(sb.prepare(z), buffer(s.data()+x+y, z)));
|
||||
expect(to_string(sb.data()) == s);
|
||||
BEAST_EXPECT(to_string(sb.data()) == s);
|
||||
{
|
||||
streambuf sb2(sb);
|
||||
expect(eq(sb, sb2));
|
||||
BEAST_EXPECT(eq(sb, sb2));
|
||||
}
|
||||
{
|
||||
streambuf sb2;
|
||||
sb2 = sb;
|
||||
expect(eq(sb, sb2));
|
||||
BEAST_EXPECT(eq(sb, sb2));
|
||||
}
|
||||
{
|
||||
streambuf sb2(std::move(sb));
|
||||
expect(to_string(sb2.data()) == s);
|
||||
BEAST_EXPECT(to_string(sb2.data()) == s);
|
||||
expect_size(0, sb.data());
|
||||
sb = std::move(sb2);
|
||||
expect(to_string(sb.data()) == s);
|
||||
BEAST_EXPECT(to_string(sb.data()) == s);
|
||||
expect_size(0, sb2.data());
|
||||
}
|
||||
self_assign(sb, sb);
|
||||
expect(to_string(sb.data()) == s);
|
||||
BEAST_EXPECT(to_string(sb.data()) == s);
|
||||
self_assign(sb, std::move(sb));
|
||||
expect(to_string(sb.data()) == s);
|
||||
BEAST_EXPECT(to_string(sb.data()) == s);
|
||||
}
|
||||
}}}
|
||||
try
|
||||
@@ -226,16 +226,16 @@ public:
|
||||
test_allocator<char, false, false, false, false>;
|
||||
using sb_type = basic_streambuf<alloc_type>;
|
||||
sb_type sb;
|
||||
expect(sb.get_allocator().id() == 1);
|
||||
BEAST_EXPECT(sb.get_allocator().id() == 1);
|
||||
}
|
||||
{
|
||||
using alloc_type =
|
||||
test_allocator<char, false, false, false, false>;
|
||||
using sb_type = basic_streambuf<alloc_type>;
|
||||
sb_type sb;
|
||||
expect(sb.get_allocator().id() == 2);
|
||||
BEAST_EXPECT(sb.get_allocator().id() == 2);
|
||||
sb_type sb2(sb);
|
||||
expect(sb2.get_allocator().id() == 2);
|
||||
BEAST_EXPECT(sb2.get_allocator().id() == 2);
|
||||
sb_type sb3(sb, alloc_type{});
|
||||
}
|
||||
}
|
||||
@@ -246,16 +246,16 @@ public:
|
||||
using boost::asio::buffer_size;
|
||||
{
|
||||
streambuf sb(2);
|
||||
expect(buffer_size(sb.prepare(5)) == 5);
|
||||
expect(buffer_size(sb.prepare(8)) == 8);
|
||||
expect(buffer_size(sb.prepare(7)) == 7);
|
||||
BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5);
|
||||
BEAST_EXPECT(buffer_size(sb.prepare(8)) == 8);
|
||||
BEAST_EXPECT(buffer_size(sb.prepare(7)) == 7);
|
||||
}
|
||||
{
|
||||
streambuf sb(2);
|
||||
sb.prepare(2);
|
||||
expect(test::buffer_count(sb.prepare(5)) == 2);
|
||||
expect(test::buffer_count(sb.prepare(8)) == 3);
|
||||
expect(test::buffer_count(sb.prepare(4)) == 2);
|
||||
BEAST_EXPECT(test::buffer_count(sb.prepare(5)) == 2);
|
||||
BEAST_EXPECT(test::buffer_count(sb.prepare(8)) == 3);
|
||||
BEAST_EXPECT(test::buffer_count(sb.prepare(4)) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ public:
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
std::string const s = "Hello, world";
|
||||
expect(s.size() == 12);
|
||||
BEAST_EXPECT(s.size() == 12);
|
||||
for(std::size_t i = 1; i < 12; ++i) {
|
||||
for(std::size_t x = 1; x < 4; ++x) {
|
||||
for(std::size_t y = 1; y < 4; ++y) {
|
||||
@@ -298,78 +298,78 @@ public:
|
||||
streambuf sb(i);
|
||||
{
|
||||
auto d = sb.prepare(z);
|
||||
expect(buffer_size(d) == z);
|
||||
BEAST_EXPECT(buffer_size(d) == z);
|
||||
}
|
||||
{
|
||||
auto d = sb.prepare(0);
|
||||
expect(buffer_size(d) == 0);
|
||||
BEAST_EXPECT(buffer_size(d) == 0);
|
||||
}
|
||||
{
|
||||
auto d = sb.prepare(y);
|
||||
expect(buffer_size(d) == y);
|
||||
BEAST_EXPECT(buffer_size(d) == y);
|
||||
}
|
||||
{
|
||||
auto d = sb.prepare(x);
|
||||
expect(buffer_size(d) == x);
|
||||
BEAST_EXPECT(buffer_size(d) == x);
|
||||
sb.commit(buffer_copy(d, buffer(s.data(), x)));
|
||||
}
|
||||
expect(sb.size() == x);
|
||||
expect(buffer_size(sb.data()) == sb.size());
|
||||
BEAST_EXPECT(sb.size() == x);
|
||||
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
|
||||
{
|
||||
auto d = sb.prepare(x);
|
||||
expect(buffer_size(d) == x);
|
||||
BEAST_EXPECT(buffer_size(d) == x);
|
||||
}
|
||||
{
|
||||
auto d = sb.prepare(0);
|
||||
expect(buffer_size(d) == 0);
|
||||
BEAST_EXPECT(buffer_size(d) == 0);
|
||||
}
|
||||
{
|
||||
auto d = sb.prepare(z);
|
||||
expect(buffer_size(d) == z);
|
||||
BEAST_EXPECT(buffer_size(d) == z);
|
||||
}
|
||||
{
|
||||
auto d = sb.prepare(y);
|
||||
expect(buffer_size(d) == y);
|
||||
BEAST_EXPECT(buffer_size(d) == y);
|
||||
sb.commit(buffer_copy(d, buffer(s.data()+x, y)));
|
||||
}
|
||||
sb.commit(1);
|
||||
expect(sb.size() == x + y);
|
||||
expect(buffer_size(sb.data()) == sb.size());
|
||||
BEAST_EXPECT(sb.size() == x + y);
|
||||
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
|
||||
{
|
||||
auto d = sb.prepare(x);
|
||||
expect(buffer_size(d) == x);
|
||||
BEAST_EXPECT(buffer_size(d) == x);
|
||||
}
|
||||
{
|
||||
auto d = sb.prepare(y);
|
||||
expect(buffer_size(d) == y);
|
||||
BEAST_EXPECT(buffer_size(d) == y);
|
||||
}
|
||||
{
|
||||
auto d = sb.prepare(0);
|
||||
expect(buffer_size(d) == 0);
|
||||
BEAST_EXPECT(buffer_size(d) == 0);
|
||||
}
|
||||
{
|
||||
auto d = sb.prepare(z);
|
||||
expect(buffer_size(d) == z);
|
||||
BEAST_EXPECT(buffer_size(d) == z);
|
||||
sb.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
|
||||
}
|
||||
sb.commit(2);
|
||||
expect(sb.size() == x + y + z);
|
||||
expect(buffer_size(sb.data()) == sb.size());
|
||||
expect(to_string(sb.data()) == s);
|
||||
BEAST_EXPECT(sb.size() == x + y + z);
|
||||
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
|
||||
BEAST_EXPECT(to_string(sb.data()) == s);
|
||||
sb.consume(t);
|
||||
{
|
||||
auto d = sb.prepare(0);
|
||||
expect(buffer_size(d) == 0);
|
||||
BEAST_EXPECT(buffer_size(d) == 0);
|
||||
}
|
||||
expect(to_string(sb.data()) == s.substr(t, std::string::npos));
|
||||
BEAST_EXPECT(to_string(sb.data()) == s.substr(t, std::string::npos));
|
||||
sb.consume(u);
|
||||
expect(to_string(sb.data()) == s.substr(t + u, std::string::npos));
|
||||
BEAST_EXPECT(to_string(sb.data()) == s.substr(t + u, std::string::npos));
|
||||
sb.consume(v);
|
||||
expect(to_string(sb.data()) == "");
|
||||
BEAST_EXPECT(to_string(sb.data()) == "");
|
||||
sb.consume(1);
|
||||
{
|
||||
auto d = sb.prepare(0);
|
||||
expect(buffer_size(d) == 0);
|
||||
BEAST_EXPECT(buffer_size(d) == 0);
|
||||
}
|
||||
}
|
||||
}}}}}
|
||||
@@ -387,14 +387,14 @@ public:
|
||||
sb.prepare(1);
|
||||
expect_size(3, sb.prepare(3));
|
||||
sb.commit(2);
|
||||
expect(test::buffer_count(sb.data()) == 4);
|
||||
BEAST_EXPECT(test::buffer_count(sb.data()) == 4);
|
||||
}
|
||||
|
||||
void testOutputStream()
|
||||
{
|
||||
streambuf sb;
|
||||
sb << "x";
|
||||
expect(to_string(sb.data()) == "x");
|
||||
BEAST_EXPECT(to_string(sb.data()) == "x");
|
||||
}
|
||||
|
||||
void testReadSizeHelper()
|
||||
@@ -402,44 +402,44 @@ public:
|
||||
using boost::asio::buffer_size;
|
||||
{
|
||||
streambuf sb(10);
|
||||
expect(read_size_helper(sb, 0) == 0);
|
||||
expect(read_size_helper(sb, 1) == 1);
|
||||
expect(read_size_helper(sb, 10) == 10);
|
||||
expect(read_size_helper(sb, 20) == 20);
|
||||
expect(read_size_helper(sb, 1000) == 512);
|
||||
BEAST_EXPECT(read_size_helper(sb, 0) == 0);
|
||||
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
|
||||
BEAST_EXPECT(read_size_helper(sb, 10) == 10);
|
||||
BEAST_EXPECT(read_size_helper(sb, 20) == 20);
|
||||
BEAST_EXPECT(read_size_helper(sb, 1000) == 512);
|
||||
sb.prepare(3);
|
||||
sb.commit(3);
|
||||
expect(read_size_helper(sb, 10) == 7);
|
||||
expect(read_size_helper(sb, 1000) == 7);
|
||||
BEAST_EXPECT(read_size_helper(sb, 10) == 7);
|
||||
BEAST_EXPECT(read_size_helper(sb, 1000) == 7);
|
||||
}
|
||||
{
|
||||
streambuf sb(1000);
|
||||
expect(read_size_helper(sb, 0) == 0);
|
||||
expect(read_size_helper(sb, 1) == 1);
|
||||
expect(read_size_helper(sb, 1000) == 1000);
|
||||
expect(read_size_helper(sb, 2000) == 1000);
|
||||
BEAST_EXPECT(read_size_helper(sb, 0) == 0);
|
||||
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
|
||||
BEAST_EXPECT(read_size_helper(sb, 1000) == 1000);
|
||||
BEAST_EXPECT(read_size_helper(sb, 2000) == 1000);
|
||||
sb.prepare(3);
|
||||
expect(read_size_helper(sb, 0) == 0);
|
||||
expect(read_size_helper(sb, 1) == 1);
|
||||
expect(read_size_helper(sb, 1000) == 1000);
|
||||
expect(read_size_helper(sb, 2000) == 1000);
|
||||
BEAST_EXPECT(read_size_helper(sb, 0) == 0);
|
||||
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
|
||||
BEAST_EXPECT(read_size_helper(sb, 1000) == 1000);
|
||||
BEAST_EXPECT(read_size_helper(sb, 2000) == 1000);
|
||||
sb.commit(3);
|
||||
expect(read_size_helper(sb, 0) == 0);
|
||||
expect(read_size_helper(sb, 1) == 1);
|
||||
expect(read_size_helper(sb, 1000) == 997);
|
||||
expect(read_size_helper(sb, 2000) == 997);
|
||||
BEAST_EXPECT(read_size_helper(sb, 0) == 0);
|
||||
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
|
||||
BEAST_EXPECT(read_size_helper(sb, 1000) == 997);
|
||||
BEAST_EXPECT(read_size_helper(sb, 2000) == 997);
|
||||
sb.consume(2);
|
||||
expect(read_size_helper(sb, 0) == 0);
|
||||
expect(read_size_helper(sb, 1) == 1);
|
||||
expect(read_size_helper(sb, 1000) == 997);
|
||||
expect(read_size_helper(sb, 2000) == 997);
|
||||
BEAST_EXPECT(read_size_helper(sb, 0) == 0);
|
||||
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
|
||||
BEAST_EXPECT(read_size_helper(sb, 1000) == 997);
|
||||
BEAST_EXPECT(read_size_helper(sb, 2000) == 997);
|
||||
}
|
||||
{
|
||||
streambuf sb(2);
|
||||
expect(test::buffer_count(sb.prepare(2)) == 1);
|
||||
expect(test::buffer_count(sb.prepare(3)) == 2);
|
||||
expect(buffer_size(sb.prepare(5)) == 5);
|
||||
expect(read_size_helper(sb, 10) == 6);
|
||||
BEAST_EXPECT(test::buffer_count(sb.prepare(2)) == 1);
|
||||
BEAST_EXPECT(test::buffer_count(sb.prepare(3)) == 2);
|
||||
BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5);
|
||||
BEAST_EXPECT(read_size_helper(sb, 10) == 6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,32 +47,32 @@ public:
|
||||
std::list<const_buffer> b6;
|
||||
auto bs = buffer_cat(
|
||||
b1, b2, b3, b4, b5, b6);
|
||||
expect(buffer_size(bs) == 10);
|
||||
BEAST_EXPECT(buffer_size(bs) == 10);
|
||||
std::vector<const_buffer> v;
|
||||
for(auto iter = make_reverse_iterator(bs.end());
|
||||
iter != make_reverse_iterator(bs.begin()); ++iter)
|
||||
v.emplace_back(*iter);
|
||||
expect(buffer_size(bs) == 10);
|
||||
BEAST_EXPECT(buffer_size(bs) == 10);
|
||||
decltype(bs) bs2(bs);
|
||||
auto bs3(std::move(bs));
|
||||
bs = bs2;
|
||||
bs3 = std::move(bs2);
|
||||
{
|
||||
boost::asio::streambuf sb1, sb2;
|
||||
expect(buffer_size(buffer_cat(
|
||||
BEAST_EXPECT(buffer_size(buffer_cat(
|
||||
sb1.prepare(5), sb2.prepare(7))) == 12);
|
||||
sb1.commit(5);
|
||||
sb2.commit(7);
|
||||
expect(buffer_size(buffer_cat(
|
||||
BEAST_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);
|
||||
BEAST_EXPECT(copy == it);
|
||||
copy = copy;
|
||||
expect(copy == it);
|
||||
BEAST_EXPECT(copy == it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,11 +132,11 @@ public:
|
||||
pass();
|
||||
}
|
||||
auto bs2 = bs;
|
||||
expect(bs.begin() != bs2.begin());
|
||||
expect(bs.end() != bs2.end());
|
||||
BEAST_EXPECT(bs.begin() != bs2.begin());
|
||||
BEAST_EXPECT(bs.end() != bs2.end());
|
||||
decltype(bs)::const_iterator it;
|
||||
decltype(bs2)::const_iterator it2;
|
||||
expect(it == it2);
|
||||
BEAST_EXPECT(it == it2);
|
||||
}
|
||||
|
||||
void run() override
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
using boost::asio::mutable_buffer;
|
||||
char buf[12];
|
||||
std::string const s = "Hello, world";
|
||||
expect(s.size() == sizeof(buf));
|
||||
BEAST_EXPECT(s.size() == sizeof(buf));
|
||||
for(std::size_t i = 1; i < 4; ++i) {
|
||||
for(std::size_t j = 1; j < 4; ++j) {
|
||||
for(std::size_t x = 1; x < 4; ++x) {
|
||||
@@ -60,83 +60,83 @@ public:
|
||||
mutable_buffer{&buf[i], j},
|
||||
mutable_buffer{&buf[i+j], k}}};
|
||||
buffers_adapter<decltype(bs)> ba(std::move(bs));
|
||||
expect(ba.max_size() == sizeof(buf));
|
||||
BEAST_EXPECT(ba.max_size() == sizeof(buf));
|
||||
{
|
||||
auto d = ba.prepare(z);
|
||||
expect(buffer_size(d) == z);
|
||||
BEAST_EXPECT(buffer_size(d) == z);
|
||||
}
|
||||
{
|
||||
auto d = ba.prepare(0);
|
||||
expect(buffer_size(d) == 0);
|
||||
BEAST_EXPECT(buffer_size(d) == 0);
|
||||
}
|
||||
{
|
||||
auto d = ba.prepare(y);
|
||||
expect(buffer_size(d) == y);
|
||||
BEAST_EXPECT(buffer_size(d) == y);
|
||||
}
|
||||
{
|
||||
auto d = ba.prepare(x);
|
||||
expect(buffer_size(d) == x);
|
||||
BEAST_EXPECT(buffer_size(d) == x);
|
||||
ba.commit(buffer_copy(d, buffer(s.data(), x)));
|
||||
}
|
||||
expect(ba.size() == x);
|
||||
expect(ba.max_size() == sizeof(buf) - x);
|
||||
expect(buffer_size(ba.data()) == ba.size());
|
||||
BEAST_EXPECT(ba.size() == x);
|
||||
BEAST_EXPECT(ba.max_size() == sizeof(buf) - x);
|
||||
BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
|
||||
{
|
||||
auto d = ba.prepare(x);
|
||||
expect(buffer_size(d) == x);
|
||||
BEAST_EXPECT(buffer_size(d) == x);
|
||||
}
|
||||
{
|
||||
auto d = ba.prepare(0);
|
||||
expect(buffer_size(d) == 0);
|
||||
BEAST_EXPECT(buffer_size(d) == 0);
|
||||
}
|
||||
{
|
||||
auto d = ba.prepare(z);
|
||||
expect(buffer_size(d) == z);
|
||||
BEAST_EXPECT(buffer_size(d) == z);
|
||||
}
|
||||
{
|
||||
auto d = ba.prepare(y);
|
||||
expect(buffer_size(d) == y);
|
||||
BEAST_EXPECT(buffer_size(d) == y);
|
||||
ba.commit(buffer_copy(d, buffer(s.data()+x, y)));
|
||||
}
|
||||
ba.commit(1);
|
||||
expect(ba.size() == x + y);
|
||||
expect(ba.max_size() == sizeof(buf) - (x + y));
|
||||
expect(buffer_size(ba.data()) == ba.size());
|
||||
BEAST_EXPECT(ba.size() == x + y);
|
||||
BEAST_EXPECT(ba.max_size() == sizeof(buf) - (x + y));
|
||||
BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
|
||||
{
|
||||
auto d = ba.prepare(x);
|
||||
expect(buffer_size(d) == x);
|
||||
BEAST_EXPECT(buffer_size(d) == x);
|
||||
}
|
||||
{
|
||||
auto d = ba.prepare(y);
|
||||
expect(buffer_size(d) == y);
|
||||
BEAST_EXPECT(buffer_size(d) == y);
|
||||
}
|
||||
{
|
||||
auto d = ba.prepare(0);
|
||||
expect(buffer_size(d) == 0);
|
||||
BEAST_EXPECT(buffer_size(d) == 0);
|
||||
}
|
||||
{
|
||||
auto d = ba.prepare(z); expect(buffer_size(d) == z);
|
||||
auto d = ba.prepare(z); BEAST_EXPECT(buffer_size(d) == z);
|
||||
ba.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
|
||||
}
|
||||
ba.commit(2);
|
||||
expect(ba.size() == x + y + z);
|
||||
expect(ba.max_size() == 0);
|
||||
expect(buffer_size(ba.data()) == ba.size());
|
||||
expect(to_string(ba.data()) == s);
|
||||
BEAST_EXPECT(ba.size() == x + y + z);
|
||||
BEAST_EXPECT(ba.max_size() == 0);
|
||||
BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
|
||||
BEAST_EXPECT(to_string(ba.data()) == s);
|
||||
ba.consume(t);
|
||||
{
|
||||
auto d = ba.prepare(0);
|
||||
expect(buffer_size(d) == 0);
|
||||
BEAST_EXPECT(buffer_size(d) == 0);
|
||||
}
|
||||
expect(to_string(ba.data()) == s.substr(t, std::string::npos));
|
||||
BEAST_EXPECT(to_string(ba.data()) == s.substr(t, std::string::npos));
|
||||
ba.consume(u);
|
||||
expect(to_string(ba.data()) == s.substr(t + u, std::string::npos));
|
||||
BEAST_EXPECT(to_string(ba.data()) == s.substr(t + u, std::string::npos));
|
||||
ba.consume(v);
|
||||
expect(to_string(ba.data()) == "");
|
||||
BEAST_EXPECT(to_string(ba.data()) == "");
|
||||
ba.consume(1);
|
||||
{
|
||||
auto d = ba.prepare(0);
|
||||
expect(buffer_size(d) == 0);
|
||||
BEAST_EXPECT(buffer_size(d) == 0);
|
||||
}
|
||||
try
|
||||
{
|
||||
@@ -158,9 +158,9 @@ public:
|
||||
sb_type sb;
|
||||
buffers_adapter<
|
||||
sb_type::mutable_buffers_type> ba(sb.prepare(3));
|
||||
expect(buffer_size(ba.prepare(3)) == 3);
|
||||
BEAST_EXPECT(buffer_size(ba.prepare(3)) == 3);
|
||||
ba.commit(2);
|
||||
expect(buffer_size(ba.data()) == 2);
|
||||
BEAST_EXPECT(buffer_size(ba.data()) == 2);
|
||||
}
|
||||
{
|
||||
using sb_type = beast::streambuf;
|
||||
@@ -168,13 +168,13 @@ public:
|
||||
sb.prepare(3);
|
||||
buffers_adapter<
|
||||
sb_type::mutable_buffers_type> ba(sb.prepare(8));
|
||||
expect(buffer_size(ba.prepare(8)) == 8);
|
||||
BEAST_EXPECT(buffer_size(ba.prepare(8)) == 8);
|
||||
ba.commit(2);
|
||||
expect(buffer_size(ba.data()) == 2);
|
||||
BEAST_EXPECT(buffer_size(ba.data()) == 2);
|
||||
ba.consume(1);
|
||||
ba.commit(6);
|
||||
ba.consume(2);
|
||||
expect(buffer_size(ba.data()) == 5);
|
||||
BEAST_EXPECT(buffer_size(ba.data()) == 5);
|
||||
ba.consume(5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,10 @@ public:
|
||||
void
|
||||
expect_size(std::size_t n, ConstBufferSequence const& buffers)
|
||||
{
|
||||
expect(test::size_pre(buffers) == n);
|
||||
expect(test::size_post(buffers) == n);
|
||||
expect(test::size_rev_pre(buffers) == n);
|
||||
expect(test::size_rev_post(buffers) == n);
|
||||
BEAST_EXPECT(test::size_pre(buffers) == n);
|
||||
BEAST_EXPECT(test::size_post(buffers) == n);
|
||||
BEAST_EXPECT(test::size_rev_pre(buffers) == n);
|
||||
BEAST_EXPECT(test::size_rev_post(buffers) == n);
|
||||
}
|
||||
|
||||
void testMatrix()
|
||||
@@ -43,9 +43,9 @@ public:
|
||||
using boost::asio::const_buffer;
|
||||
char buf[12];
|
||||
std::string const s = "Hello, world";
|
||||
expect(s.size() == sizeof(buf));
|
||||
BEAST_EXPECT(s.size() == sizeof(buf));
|
||||
buffer_copy(buffer(buf), buffer(s));
|
||||
expect(to_string(buffer(buf)) == s);
|
||||
BEAST_EXPECT(to_string(buffer(buf)) == s);
|
||||
for(std::size_t i = 1; i < 4; ++i) {
|
||||
for(std::size_t j = 1; j < 4; ++j) {
|
||||
for(std::size_t x = 1; x < 4; ++x) {
|
||||
@@ -58,24 +58,24 @@ public:
|
||||
const_buffer{&buf[i], j},
|
||||
const_buffer{&buf[i+j], k}}};
|
||||
consuming_buffers<decltype(bs)> cb(bs);
|
||||
expect(to_string(cb) == s);
|
||||
BEAST_EXPECT(to_string(cb) == s);
|
||||
expect_size(s.size(), cb);
|
||||
cb.consume(0);
|
||||
expect(eq(cb, consumed_buffers(bs, 0)));
|
||||
expect(to_string(cb) == s);
|
||||
BEAST_EXPECT(eq(cb, consumed_buffers(bs, 0)));
|
||||
BEAST_EXPECT(to_string(cb) == s);
|
||||
expect_size(s.size(), cb);
|
||||
cb.consume(x);
|
||||
expect(to_string(cb) == s.substr(x));
|
||||
expect(eq(cb, consumed_buffers(bs, x)));
|
||||
BEAST_EXPECT(to_string(cb) == s.substr(x));
|
||||
BEAST_EXPECT(eq(cb, consumed_buffers(bs, x)));
|
||||
cb.consume(y);
|
||||
expect(to_string(cb) == s.substr(x+y));
|
||||
expect(eq(cb, consumed_buffers(bs, x+y)));
|
||||
BEAST_EXPECT(to_string(cb) == s.substr(x+y));
|
||||
BEAST_EXPECT(eq(cb, consumed_buffers(bs, x+y)));
|
||||
cb.consume(z);
|
||||
expect(to_string(cb) == "");
|
||||
expect(eq(cb, consumed_buffers(bs, x+y+z)));
|
||||
BEAST_EXPECT(to_string(cb) == "");
|
||||
BEAST_EXPECT(eq(cb, consumed_buffers(bs, x+y+z)));
|
||||
cb.consume(1);
|
||||
expect(to_string(cb) == "");
|
||||
expect(eq(cb, consumed_buffers(bs, x+y+z)));
|
||||
BEAST_EXPECT(to_string(cb) == "");
|
||||
BEAST_EXPECT(eq(cb, consumed_buffers(bs, x+y+z)));
|
||||
}
|
||||
}}}}
|
||||
}
|
||||
@@ -87,10 +87,10 @@ public:
|
||||
using boost::asio::null_buffers;
|
||||
consuming_buffers<null_buffers> cb(
|
||||
null_buffers{});
|
||||
expect(buffer_size(cb) == 0);
|
||||
BEAST_EXPECT(buffer_size(cb) == 0);
|
||||
consuming_buffers<null_buffers> cb2(
|
||||
null_buffers{});
|
||||
expect(buffer_copy(cb2, cb) == 0);
|
||||
BEAST_EXPECT(buffer_copy(cb2, cb) == 0);
|
||||
}
|
||||
|
||||
void testIterator()
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
std::size_t n = 0;
|
||||
for(auto it = cb.end(); it != cb.begin(); --it)
|
||||
++n;
|
||||
expect(n == 3);
|
||||
BEAST_EXPECT(n == 3);
|
||||
}
|
||||
|
||||
void run() override
|
||||
|
||||
@@ -32,8 +32,8 @@ public:
|
||||
dynabuf_readstream<socket_type, streambuf> srs(ios);
|
||||
dynabuf_readstream<socket_type, streambuf> srs2(std::move(srs));
|
||||
srs = std::move(srs2);
|
||||
expect(&srs.get_io_service() == &ios);
|
||||
expect(&srs.get_io_service() == &srs2.get_io_service());
|
||||
BEAST_EXPECT(&srs.get_io_service() == &ios);
|
||||
BEAST_EXPECT(&srs.get_io_service() == &srs2.get_io_service());
|
||||
}
|
||||
{
|
||||
socket_type sock(ios);
|
||||
@@ -63,11 +63,11 @@ public:
|
||||
boost::asio::read(srs, buffer(&s[0], s.size()), ec);
|
||||
if(! ec)
|
||||
{
|
||||
expect(s == "Hello, world!");
|
||||
BEAST_EXPECT(s == "Hello, world!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(n < limit);
|
||||
BEAST_EXPECT(n < limit);
|
||||
|
||||
for(n = 0; n < limit; ++n)
|
||||
{
|
||||
@@ -82,11 +82,11 @@ public:
|
||||
boost::asio::read(srs, buffer(&s[0], s.size()), ec);
|
||||
if(! ec)
|
||||
{
|
||||
expect(s == "Hello, world!");
|
||||
BEAST_EXPECT(s == "Hello, world!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(n < limit);
|
||||
BEAST_EXPECT(n < limit);
|
||||
|
||||
for(n = 0; n < limit; ++n)
|
||||
{
|
||||
@@ -101,11 +101,11 @@ public:
|
||||
srs, buffer(&s[0], s.size()), do_yield[ec]);
|
||||
if(! ec)
|
||||
{
|
||||
expect(s == "Hello, world!");
|
||||
BEAST_EXPECT(s == "Hello, world!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(n < limit);
|
||||
BEAST_EXPECT(n < limit);
|
||||
|
||||
for(n = 0; n < limit; ++n)
|
||||
{
|
||||
@@ -121,11 +121,11 @@ public:
|
||||
srs, buffer(&s[0], s.size()), do_yield[ec]);
|
||||
if(! ec)
|
||||
{
|
||||
expect(s == "Hello, world!");
|
||||
BEAST_EXPECT(s == "Hello, world!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(n < limit);
|
||||
BEAST_EXPECT(n < limit);
|
||||
}
|
||||
|
||||
void run() override
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user