Merge commit '2f9a8440c2432d8a196571d6300404cb76314125' into develop

This commit is contained in:
Vinnie Falco
2016-09-15 14:21:55 -04:00
134 changed files with 2838 additions and 2670 deletions

30
src/beast/.gitignore vendored
View File

@@ -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/ bin/
node_modules/ bin64/
cov-int/
nohup.out
venv/

View File

@@ -2,6 +2,7 @@ language: cpp
env: env:
global: global:
- LLVM_VERSION=3.8.0
# Maintenance note: to move to a new version # Maintenance note: to move to a new version
# of boost, update both BOOST_ROOT and BOOST_URL. # of boost, update both BOOST_ROOT and BOOST_URL.
# Note that for simplicity, BOOST_ROOT's final # Note that for simplicity, BOOST_ROOT's final
@@ -27,57 +28,50 @@ packages: &gcc5_pkgs
- autotools-dev - autotools-dev
- libc6-dbg - 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: matrix:
include: include:
# GCC/Coverage # GCC/Coverage/Autobahn
- compiler: gcc - 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 addons: &ao_gcc5
apt: apt:
sources: ['ubuntu-toolchain-r-test'] sources: ['ubuntu-toolchain-r-test']
packages: *gcc5_pkgs 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 # Clang/UndefinedBehaviourSanitizer
- compiler: clang - compiler: clang
env: GCC_VER=5 VARIANT=usan CLANG_VER=3.8 ADDRESS_MODEL=64 UBSAN_OPTIONS='print_stacktrace=1' env:
addons: &ao_clang38 - GCC_VER=5
apt: - VARIANT=usan
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8'] - CLANG_VER=3.8
packages: *clang38_pkgs - 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 # Clang/AddressSanitizer
- compiler: clang - compiler: clang
env: GCC_VER=5 VARIANT=asan CLANG_VER=3.8 ADDRESS_MODEL=64 env:
addons: *ao_clang38 - GCC_VER=5
- VARIANT=asan
- CLANG_VER=3.8
- ADDRESS_MODEL=64
- PATH=$PWD/llvm-$LLVM_VERSION/bin:$PATH
addons: *ao_gcc5
cache: cache:
directories: directories:
- $BOOST_ROOT - $BOOST_ROOT
- $VALGRIND_ROOT - $VALGRIND_ROOT
- llvm-$LLVM_VERSION
- cmake
before_install: before_install:
- scripts/install-dependencies.sh - scripts/install-dependencies.sh

View File

@@ -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
View 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
--------------------------------------------------------------------------------

View File

@@ -7,7 +7,7 @@ project (Beast)
set_property (GLOBAL PROPERTY USE_FOLDERS ON) set_property (GLOBAL PROPERTY USE_FOLDERS ON)
if (WIN32) 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") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
else() else()
set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_LIBS ON)
@@ -20,10 +20,29 @@ else()
find_package(Threads) find_package(Threads)
set(CMAKE_CXX_FLAGS set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -g -std=c++11 -Wall -Wpedantic") "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wpedantic")
endif() 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) function(DoGroupSources curdir rootdir folder)
file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*) file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*)
@@ -51,6 +70,9 @@ include_directories (include)
file(GLOB_RECURSE BEAST_INCLUDES file(GLOB_RECURSE BEAST_INCLUDES
${PROJECT_SOURCE_DIR}/include/beast/*.hpp ${PROJECT_SOURCE_DIR}/include/beast/*.hpp
${PROJECT_SOURCE_DIR}/include/beast/*.ipp ${PROJECT_SOURCE_DIR}/include/beast/*.ipp
)
file(GLOB_RECURSE EXTRAS_INCLUDES
${PROJECT_SOURCE_DIR}/extras/beast/*.hpp ${PROJECT_SOURCE_DIR}/extras/beast/*.hpp
${PROJECT_SOURCE_DIR}/extras/beast/*.ipp ${PROJECT_SOURCE_DIR}/extras/beast/*.ipp
) )
@@ -60,5 +82,3 @@ add_subdirectory (test)
add_subdirectory (test/core) add_subdirectory (test/core)
add_subdirectory (test/http) add_subdirectory (test/http)
add_subdirectory (test/websocket) add_subdirectory (test/websocket)
#enable_testing()

View File

@@ -100,6 +100,7 @@ project beast
<toolset>clang:<cxxflags>-std=c++11 <toolset>clang:<cxxflags>-std=c++11
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1 <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_CRT_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>_XOPEN_SOURCE=600
<os>LINUX:<define>_GNU_SOURCE=1 <os>LINUX:<define>_GNU_SOURCE=1
<os>SOLARIS:<define>_XOPEN_SOURCE=500 <os>SOLARIS:<define>_XOPEN_SOURCE=500

View File

@@ -1,4 +1,5 @@
# Beast <img width="880" height = "80" alt = "Beast"
src="https://raw.githubusercontent.com/vinniefalco/Beast/master/doc/images/readme.png">
[![Join the chat at https://gitter.im/vinniefalco/Beast](https://badges.gitter.im/vinniefalco/Beast.svg)](https://gitter.im/vinniefalco/Beast?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status] [![Join the chat at https://gitter.im/vinniefalco/Beast](https://badges.gitter.im/vinniefalco/Beast.svg)](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] (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/documentation-master-brightgreen.svg)](http://vinniefalco.github.io/beast/) [![License]
(https://img.shields.io/badge/license-boost-brightgreen.svg)](LICENSE_1_0.txt) (https://img.shields.io/badge/license-boost-brightgreen.svg)](LICENSE_1_0.txt)
Beast provides implementations of the HTTP and WebSocket protocols # HTTP and WebSocket implementations built on Boost.Asio
built on top of Boost.Asio and other parts of boost.
Requirements: ---
* Boost ## CppCon 2016
* C++11 or greater
* OpenSSL (optional)
This software is currently in beta: interfaces are subject to change. For I will be giving a lightning talk on Beast at CppCon 2016 in Bellevue,
recent changes see [CHANGELOG](CHANGELOG). 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 The library has been submitted to the
[Boost Library Incubator](http://rrsd.com/blincubator.com/bi_library/beast-2/?gform_post_id=1579) [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: Example WebSocket program:
```C++ ```C++
#include <beast/to_string.hpp> #include <beast/core/to_string.hpp>
#include <beast/websocket.hpp> #include <beast/websocket.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <iostream> #include <iostream>
@@ -34,13 +163,13 @@ int main()
// Normal boost::asio setup // Normal boost::asio setup
std::string const host = "echo.websocket.org"; std::string const host = "echo.websocket.org";
boost::asio::io_service ios; boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r(ios); boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock(ios); boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock, boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"})); r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
// WebSocket connect and send message using beast // 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.handshake(host, "/");
ws.write(boost::asio::buffer("Hello, world!")); ws.write(boost::asio::buffer("Hello, world!"));
@@ -65,8 +194,8 @@ int main()
// Normal boost::asio setup // Normal boost::asio setup
std::string const host = "boost.org"; std::string const host = "boost.org";
boost::asio::io_service ios; boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r(ios); boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock(ios); boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock, boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"})); r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
@@ -88,12 +217,13 @@ int main()
} }
``` ```
Links: ## License
* [Home](http://vinniefalco.github.io/) Distributed under the Boost Software License, Version 1.0.
* [Repository](https://github.com/vinniefalco/Beast) (See accompanying file [LICENSE_1_0.txt](LICENSE_1_0.txt) or copy at
* [Documentation](http://vinniefalco.github.io/beast/) http://www.boost.org/LICENSE_1_0.txt)
* [Autobahn.testsuite results](http://vinniefalco.github.io/autobahn/index.html)
## Contact
Please report issues or questions here: Please report issues or questions here:
https://github.com/vinniefalco/Beast/issues https://github.com/vinniefalco/Beast/issues

View File

@@ -15,7 +15,7 @@ using boostbook ;
using quickbook ; using quickbook ;
using doxygen ; using doxygen ;
xml beast_boostbook : beast.qbk ; xml beast_boostbook : master.qbk ;
path-constant out : . ; path-constant out : . ;
@@ -56,8 +56,8 @@ boostbook doc
<xsl:param>chapter.autolabel=0 <xsl:param>chapter.autolabel=0
<xsl:param>boost.image.src=images/beast.png <xsl:param>boost.image.src=images/beast.png
<xsl:param>boost.image.alt="Beast Logo" <xsl:param>boost.image.alt="Beast Logo"
<xsl:param>boost.image.w=1007 <xsl:param>boost.image.w=2400
<xsl:param>boost.image.h=107 <xsl:param>boost.image.h=80
<xsl:param>boost.root=$(broot) <xsl:param>boost.root=$(broot)
<xsl:param>chapter.autolabel=0 <xsl:param>chapter.autolabel=0
<xsl:param>chunk.first.sections=1 # Chunk the first top-level section? <xsl:param>chunk.first.sections=1 # Chunk the first top-level section?

View File

@@ -8,9 +8,9 @@
[section:design Design choices] [section:design Design choices]
The implementations are driven by business needs of cryptocurrency server The implementations are driven by business needs of cryptocurrency server
applications ([@https://ripple.com Ripple] written in C++. These applications (e.g. [@https://ripple.com Ripple]) written in C++. These
needs were not met by existing solutions so new code was written. The new needs were not met by existing solutions so Beast was written from scratch
code tries to avoid design flaws encountered in the already-existing software as a solution. Beast's design philosophy avoid flaws exhibited by other
libraries: libraries:
* Don't sacrifice performance. * Don't sacrifice performance.
@@ -44,11 +44,13 @@ to address those issues.
in production. That would give some evidence that the design in production. That would give some evidence that the design
works in practice."" works in practice.""
][ ][
Beast.HTTP and Beast.WebSocket will be used in [*rippled], an Beast.HTTP and Beast.WebSocket are production ready and currently
asynchronous peer to peer server that implements the running on public servers receiving traffic and handling millions of
[*Ripple Consensus Protocol]. These servers are deployed in multiple dollars worth of financial transactions daily. The servers run [*rippled],
production environments, with banks in many countries running client open source software ([@https://github.com/ripple/rippled repository])
applications that connect to [*rippled]. 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] [section:websocket WebSocket]
[variablelist [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? What about message compression?
][ ][
The feature is not currently present in the library, but the choice The author is currently porting ZLib 1.2.8 to modern, header-only C++11
of type requirements for buffers passed to the read functions have been that does not use macros or try to support ancient architectures. This
made with compression in mind. There is the plan to add this feature; deflate implementation will be available as its own individually
however, we feel that even without compression users can begin taking usable interface, and also will be used to power Beast WebSocket's
advantage of the WebSocket protocol immediately with this library. 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? Where is the TLS/SSL interface?
][ ][
The `websocket::stream` just wraps the socket or stream that you The `websocket::stream` wraps the socket or stream that you provide
provide (for example, a `boost::asio::ip::tcp::socket` or a (for example, a `boost::asio::ip::tcp::socket` or a
`boost::asio::ssl::stream`). You establish your TLS connection `boost::asio::ssl::stream`). You establish your TLS connection using the
using the interface on `ssl::stream` like shown in all of the Asio interface on `ssl::stream` like shown in all of the Asio examples, they
examples, they construct your `websocket::stream` around it. construct your `websocket::stream` around it. It works perfectly fine;
It works perfectly fine - Beast.WebSocket doesn't try to reinvent the Beast.WebSocket doesn't try to reinvent the wheel or put a fresh coat of
wheel or put a fresh coat of interface paint on the `ssl::stream`. interface paint on the `ssl::stream`.
The WebSocket implementation [*does] provides support for shutting down The WebSocket implementation [*does] provides support for shutting down
the TLS connection through the use of the ADL compile-time virtual functions 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 TLS streams. Callers may provide their own overloads of these functions
for user-defined next layer types. for user-defined next layer types.
]] ]]
] ]
[endsect] [endsect]
[endsect] [endsect]

View File

@@ -92,19 +92,20 @@ and its customization points in more depth, for advanced applications.
[heading Declarations] [heading Declarations]
To do anything, a message must be declared. The message class template 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 (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 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 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::request_v1<http::string_body> req;
http::response<http::string_body> resp; http::response_v1<http::string_body> resp;
``` ```
[heading Members] [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 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: to the message type. These statements set all the members in each message:
``` ```
http::request<http::string_body> req; http::request_v1<http::string_body> req;
req.method = http::method_t::http_get; req.method = "GET";
req.url = "/index.html"; req.url = "/index.html";
req.version = 11; // HTTP/1.1 req.version = 11; // HTTP/1.1
req.headers.insert("User-Agent", "hello_world"); req.headers.insert("User-Agent", "hello_world");
req.body = ""; req.body = "";
http::response<http::string_body> resp; http::response_v1<http::string_body> resp;
resp.status = 404; resp.status = 404;
resp.reason = "Not Found"; resp.reason = "Not Found";
resp.version = 10; // HTTP/1.0 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."; 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] [heading Headers]
The `message::headers` member is a container for setting the field/value 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: in the message passed:
``` ```
template<class Body> template<class Body>
void set_fields(http::request<Body>& req) void set_fields(http::request_v1<Body>& req)
{ {
if(! req.exists("User-Agent")) if(! req.exists("User-Agent"))
req.insert("User-Agent", "myWebClient"); 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. * [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
Used in GET requests where there is no message body. Example: 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 * [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 Has the same insertion complexity of `std::string`. This is the type of body
used in the examples: 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); static_assert(std::is_same<decltype(resp.body), std::string>::value);
resp.body = "Here is the data you requested"; resp.body = "Here is the data you requested";
``` ```
@@ -197,7 +190,10 @@ functions:
``` ```
void send_request(boost::asio::ip::tcp::socket& sock) 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 http::write(sock, req); // Throws exception on error
... ...
@@ -213,7 +209,7 @@ An asynchronous interface is available:
``` ```
void handle_write(boost::system::error_code); 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)); 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 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 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 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 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: stored in the streambuf parameter for use in a subsequent call to read:
``` ```
boost::asio::streambuf sb; 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 http::read(sock, sb, resp); // Throws exception on error
... ...
// Alternatively // Alternatively
@@ -245,7 +241,7 @@ called:
void handle_read(boost::system::error_code); void handle_read(boost::system::error_code);
... ...
boost::asio::streambuf sb; 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)); 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); void handle_read(boost::system::error_code);
... ...
beast::streambuf sb; beast::streambuf sb;
http::response<http::string_body> resp; http::response_v1<http::string_body> resp;
http::read(sock, sb, 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View File

@@ -6,7 +6,7 @@
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
mkdir -p ../bin/doc/xml mkdir -p ../bin/doc/xml
doxygen beast.dox doxygen source.dox
cd ../bin/doc/xml cd ../bin/doc/xml
xsltproc combine.xslt index.xml > all.xml xsltproc combine.xslt index.xml > all.xml
cd ../../../doc cd ../../../doc

View File

@@ -39,11 +39,11 @@
[section:intro Introduction] [section:intro Introduction]
Beast is a cross-platform C++ library built on Boost.Asio and Boost, containing Beast is a header-only, cross-platform C++ library built on Boost.Asio and
two modules implementing widely used network protocols. Beast.HTTP offers a Boost, containing two modules implementing widely used network protocols.
universal model for describing, sending, and receiving HTTP messages while Beast.HTTP offers a universal model for describing, sending, and receiving
Beast.WebSocket provides a complete implementation of the WebSocket protocol. HTTP messages while Beast.WebSocket provides a complete implementation of
Their design achieves these goals: the WebSocket protocol. Their design achieves these goals:
* [*Symmetry.] Interfaces are role-agnostic; the same interfaces can be * [*Symmetry.] Interfaces are role-agnostic; the same interfaces can be
used to build clients, servers, or both. used to build clients, servers, or both.
@@ -106,8 +106,8 @@ int main()
// Normal boost::asio setup // Normal boost::asio setup
std::string const host = "boost.org"; std::string const host = "boost.org";
boost::asio::io_service ios; boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r(ios); boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock(ios); boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock, boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"})); 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: 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 <beast/websocket.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <iostream> #include <iostream>
@@ -142,13 +142,13 @@ int main()
// Normal boost::asio setup // Normal boost::asio setup
std::string const host = "echo.websocket.org"; std::string const host = "echo.websocket.org";
boost::asio::io_service ios; boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r(ios); boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock(ios); boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock, boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"})); r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
// WebSocket connect and send message using beast // 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.handshake(host, "/");
ws.write(boost::asio::buffer("Hello, world!")); ws.write(boost::asio::buffer("Hello, world!"));

View File

@@ -43,6 +43,7 @@
<simplelist type="vert" columns="1"> <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__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__headers_max_size">headers_max_size</link></member>
<member><link linkend="beast.ref.http__skip_body">skip_body</link></member>
</simplelist> </simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead> <bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1"> <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__ping_data">ping_data</link></member>
<member><link linkend="beast.ref.websocket__stream">stream</link></member> <member><link linkend="beast.ref.websocket__stream">stream</link></member>
<member><link linkend="beast.ref.websocket__reason_string">reason_string</link></member> <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> </simplelist>
<bridgehead renderas="sect3">Options</bridgehead> <bridgehead renderas="sect3">Options</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">

View File

@@ -5,7 +5,7 @@
Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying 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"/> <xsl:output method="text"/>

View File

@@ -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 The interface to Beast's WebSocket implementation is a single template
class [link beast.ref.websocket__stream `beast::websocket::stream`] which 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 operations are performed, or
[link beast.types.streams.AsyncStream [*`AsyncStream`]] if asynchronous [link beast.types.streams.AsyncStream [*`AsyncStream`]] if asynchronous
operations are performed, or both. Arguments supplied during construction are operations are performed, or both. Arguments supplied during construction are
passed to next layer's constructor. Here we declare two websockets which have passed to next layer's constructor. Here we declare a websocket stream over
ownership of the next layer: a TCP/IP socket with ownership of the socket:
``` ```
boost::asio::io_service ios; boost::asio::io_service ios;
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(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 For servers that can handshake in multiple protocols, it may be desired
to wrap an object that already exists. This socket can be moved in: to wrap an object that already exists. This socket can be moved in:
``` ```
boost::asio::ip::tcp::socket&& sock; boost::asio::ip::tcp::socket&& sock;
... ...
beast::websocket::stream< beast::websocket::stream<boost::asio::ip::tcp::socket> ws(std::move(sock));
boost::asio::ip::tcp::socket> ws(std::move(sock));
``` ```
Or, the wrapper can be constructed with a non-owning reference. In Or, the wrapper can be constructed with a non-owning reference. In
@@ -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. permitting callers to interact directly with its interface.
``` ```
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
beast::websocket::stream< beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws(ios, ctx);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws(ios, ctx);
... ...
ws.next_layer().shutdown(); // ssl::stream shutdown ws.next_layer().shutdown(); // ssl::stream shutdown
``` ```

View File

@@ -1,49 +1,52 @@
# Part of Beast # Part of Beast
GroupSources(extras/beast beast) GroupSources(extras/beast extras)
GroupSources(include/beast beast) GroupSources(include/beast beast)
GroupSources(examples "/") GroupSources(examples "/")
add_executable (http-crawl add_executable (http-crawl
${BEAST_INCLUDES} ${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
urls_large_data.hpp urls_large_data.hpp
urls_large_data.cpp urls_large_data.cpp
http_crawl.cpp http_crawl.cpp
) )
if (NOT WIN32) if (NOT WIN32)
target_link_libraries(http-crawl ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(http-crawl ${Boost_LIBRARIES} Threads::Threads)
endif() endif()
add_executable (http-server add_executable (http-server
${BEAST_INCLUDES} ${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
file_body.hpp file_body.hpp
mime_type.hpp
http_async_server.hpp http_async_server.hpp
http_stream.hpp
http_stream.ipp
http_sync_server.hpp http_sync_server.hpp
http_server.cpp http_server.cpp
) )
if (NOT WIN32) if (NOT WIN32)
target_link_libraries(http-server ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(http-server ${Boost_LIBRARIES} Threads::Threads)
endif() endif()
add_executable (http-example add_executable (http-example
${BEAST_INCLUDES} ${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
http_example.cpp http_example.cpp
) )
if (NOT WIN32) if (NOT WIN32)
target_link_libraries(http-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(http-example ${Boost_LIBRARIES} Threads::Threads)
endif() endif()
add_executable (websocket-example add_executable (websocket-example
${BEAST_INCLUDES} ${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
websocket_example.cpp websocket_example.cpp
) )
if (NOT WIN32) if (NOT WIN32)
target_link_libraries(websocket-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(websocket-example ${Boost_LIBRARIES} Threads::Threads)
endif() endif()

View File

@@ -23,7 +23,7 @@ struct file_body
class writer class writer
{ {
std::uint64_t size_; std::uint64_t size_ = 0;
std::uint64_t offset_ = 0; std::uint64_t offset_ = 0;
std::string const& path_; std::string const& path_;
FILE* file_ = nullptr; FILE* file_ = nullptr;

View File

@@ -9,10 +9,13 @@
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED #define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#include "file_body.hpp" #include "file_body.hpp"
#include "http_stream.hpp" #include "mime_type.hpp"
#include <beast/http.hpp>
#include <beast/core/placeholders.hpp> #include <beast/core/placeholders.hpp>
#include <beast/core/streambuf.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <cstddef>
#include <cstdio> #include <cstdio>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
@@ -32,17 +35,19 @@ class http_async_server
using req_type = request_v1<string_body>; using req_type = request_v1<string_body>;
using resp_type = response_v1<file_body>; using resp_type = response_v1<file_body>;
std::mutex m_;
bool log_ = true;
boost::asio::io_service ios_; boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_; boost::asio::ip::tcp::acceptor acceptor_;
socket_type sock_;
std::string root_; std::string root_;
std::vector<std::thread> thread_; std::vector<std::thread> thread_;
public: public:
http_async_server(endpoint_type const& ep, http_async_server(endpoint_type const& ep,
int threads, std::string const& root) std::size_t threads, std::string const& root)
: sock_(ios_) : acceptor_(ios_)
, acceptor_(ios_) , sock_(ios_)
, root_(root) , root_(root)
{ {
acceptor_.open(ep.protocol()); acceptor_.open(ep.protocol());
@@ -53,7 +58,7 @@ public:
std::bind(&http_async_server::on_accept, this, std::bind(&http_async_server::on_accept, this,
beast::asio::placeholders::error)); beast::asio::placeholders::error));
thread_.reserve(threads); thread_.reserve(threads);
for(int i = 0; i < threads; ++i) for(std::size_t i = 0; i < threads; ++i)
thread_.emplace_back( thread_.emplace_back(
[&] { ios_.run(); }); [&] { ios_.run(); });
} }
@@ -67,13 +72,124 @@ public:
t.join(); t.join();
} }
template<class... Args>
void
log(Args const&... args)
{
if(log_)
{
std::lock_guard<std::mutex> lock(m_);
log_args(args...);
}
}
private: 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> class peer : public std::enable_shared_from_this<peer>
{ {
int id_; int id_;
stream<socket_type> stream_; streambuf sb_;
socket_type sock_;
http_async_server& server_;
boost::asio::io_service::strand strand_; boost::asio::io_service::strand strand_;
std::string root_;
req_type req_; req_type req_;
public: public:
@@ -82,16 +198,22 @@ private:
peer& operator=(peer&&) = delete; peer& operator=(peer&&) = delete;
peer& operator=(peer const&) = delete; peer& operator=(peer const&) = delete;
explicit peer(socket_type&& sock, http_async_server& server)
peer(socket_type&& sock, std::string const& root) : sock_(std::move(sock))
: stream_(std::move(sock)) , server_(server)
, strand_(stream_.get_io_service()) , strand_(sock_.get_io_service())
, root_(root)
{ {
static int n = 0; static int n = 0;
id_ = ++n; 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() void run()
{ {
do_read(); do_read();
@@ -99,43 +221,58 @@ private:
void do_read() void do_read()
{ {
stream_.async_read(req_, strand_.wrap( async_read(sock_, sb_, req_, strand_.wrap(
std::bind(&peer::on_read, shared_from_this(), std::bind(&peer::on_read, shared_from_this(),
asio::placeholders::error))); asio::placeholders::error)));
} }
void on_read(error_code ec) void on_read(error_code const& ec)
{ {
if(ec) if(ec)
return fail(ec, "read"); return fail(ec, "read");
do_read();
auto path = req_.url; auto path = req_.url;
if(path == "/") if(path == "/")
path = "/index.html"; path = "/index.html";
path = root_ + path; path = server_.root_ + path;
if(! boost::filesystem::exists(path)) if(! boost::filesystem::exists(path))
{ {
response_v1<string_body> resp; response_v1<string_body> res;
resp.status = 404; res.status = 404;
resp.reason = "Not Found"; res.reason = "Not Found";
resp.version = req_.version; res.version = req_.version;
resp.headers.replace("Server", "http_async_server"); res.headers.insert("Server", "http_async_server");
resp.body = "The file '" + path + "' was not found"; res.headers.insert("Content-Type", "text/html");
prepare(resp); res.body = "The file '" + path + "' was not found";
stream_.async_write(std::move(resp), prepare(res);
async_write(sock_, std::move(res),
std::bind(&peer::on_write, shared_from_this(), std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error)); asio::placeholders::error));
return; return;
} }
resp_type resp; resp_type res;
resp.status = 200; res.status = 200;
resp.reason = "OK"; res.reason = "OK";
resp.version = req_.version; res.version = req_.version;
resp.headers.replace("Server", "http_async_server"); res.headers.insert("Server", "http_async_server");
resp.headers.replace("Content-Type", "text/html"); res.headers.insert("Content-Type", mime_type(path));
resp.body = path; res.body = path;
prepare(resp); try
stream_.async_write(std::move(resp), {
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(), std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error)); asio::placeholders::error));
} }
@@ -144,36 +281,27 @@ private:
{ {
if(ec) if(ec)
fail(ec, "write"); fail(ec, "write");
} do_read();
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;
}
} }
}; };
void 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 void
maybe_throw(error_code ec, std::string what) fail(error_code ec, std::string what)
{ {
if(ec) log(what, ": ", ec.message(), "\n");
{
fail(ec, what);
throw ec;
}
} }
void void
@@ -181,12 +309,13 @@ private:
{ {
if(! acceptor_.is_open()) if(! acceptor_.is_open())
return; return;
maybe_throw(ec, "accept"); if(ec)
return fail(ec, "accept");
socket_type sock(std::move(sock_)); socket_type sock(std::move(sock_));
acceptor_.async_accept(sock_, acceptor_.async_accept(sock_,
std::bind(&http_async_server::on_accept, this, std::bind(&http_async_server::on_accept, this,
asio::placeholders::error)); asio::placeholders::error));
std::make_shared<peer>(std::move(sock), root_)->run(); std::make_shared<peer>(std::move(sock), *this)->run();
} }
}; };

View File

@@ -5,9 +5,10 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#include "http_stream.hpp"
#include "urls_large_data.hpp" #include "urls_large_data.hpp"
#include <beast/core/streambuf.hpp>
#include <beast/http.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <iostream> #include <iostream>
@@ -31,9 +32,9 @@ int main(int, char const*[])
ip::tcp::resolver r(ios); ip::tcp::resolver r(ios);
auto it = r.resolve( auto it = r.resolve(
ip::tcp::resolver::query{host, "http"}); ip::tcp::resolver::query{host, "http"});
stream<ip::tcp::socket> hs(ios); ip::tcp::socket sock(ios);
connect(hs.lowest_layer(), it); connect(sock, it);
auto ep = hs.lowest_layer().remote_endpoint(); auto ep = sock.remote_endpoint();
request_v1<empty_body> req; request_v1<empty_body> req;
req.method = "GET"; req.method = "GET";
req.url = "/"; req.url = "/";
@@ -42,10 +43,11 @@ int main(int, char const*[])
std::string(":") + std::to_string(ep.port())); std::string(":") + std::to_string(ep.port()));
req.headers.insert("User-Agent", "beast/http"); req.headers.insert("User-Agent", "beast/http");
prepare(req); prepare(req);
hs.write(req); write(sock, req);
response_v1<string_body> resp; response_v1<string_body> res;
hs.read(resp); streambuf sb;
std::cout << resp; beast::http::read(sock, sb, res);
std::cout << res;
} }
catch(boost::system::system_error const& ec) catch(boost::system::system_error const& ec)
{ {

View File

@@ -15,8 +15,8 @@ int main()
// Normal boost::asio setup // Normal boost::asio setup
std::string const host = "boost.org"; std::string const host = "boost.org";
boost::asio::io_service ios; boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r(ios); boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock(ios); boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock, boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"})); r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));

View File

@@ -57,8 +57,13 @@ int main(int ac, char const* av[])
endpoint_type ep{address_type::from_string(ip), port}; endpoint_type ep{address_type::from_string(ip), port};
if(sync) if(sync)
{
http_sync_server server(ep, root); http_sync_server server(ep, root);
beast::test::sig_wait();
}
else else
{
http_async_server server(ep, threads, root); http_async_server server(ep, threads, root);
beast::test::sig_wait(); beast::test::sig_wait();
}
} }

View File

@@ -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

View File

@@ -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

View File

@@ -9,8 +9,9 @@
#define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED #define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
#include "file_body.hpp" #include "file_body.hpp"
#include "http_stream.hpp" #include "mime_type.hpp"
#include <beast/core/streambuf.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
@@ -34,6 +35,8 @@ class http_sync_server
using req_type = request_v1<string_body>; using req_type = request_v1<string_body>;
using resp_type = response_v1<file_body>; using resp_type = response_v1<file_body>;
bool log_ = true;
std::mutex m_;
boost::asio::io_service ios_; boost::asio::io_service ios_;
socket_type sock_; socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_; boost::asio::ip::tcp::acceptor acceptor_;
@@ -65,21 +68,43 @@ public:
thread_.join(); thread_.join();
} }
template<class... Args>
void void
fail(error_code ec, std::string what) log(Args const&... args)
{ {
std::cerr << if(log_)
what << ": " << ec.message() << std::endl; {
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 void
maybe_throw(error_code ec, std::string what) fail(error_code ec, std::string what)
{ {
if(ec) log(what, ": ", ec.message(), "\n");
{ }
fail(ec, what);
throw ec; 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 struct lambda
@@ -109,7 +134,8 @@ public:
{ {
if(! acceptor_.is_open()) if(! acceptor_.is_open())
return; return;
maybe_throw(ec, "accept"); if(ec)
return fail(ec, "accept");
static int id_ = 0; static int id_ = 0;
std::thread{lambda{++id_, *this, std::move(sock_)}}.detach(); std::thread{lambda{++id_, *this, std::move(sock_)}}.detach();
acceptor_.async_accept(sock_, acceptor_.async_accept(sock_,
@@ -118,23 +144,15 @@ public:
} }
void void
fail(int id, error_code const& ec) do_peer(int id, socket_type&& sock0)
{ {
if(ec != boost::asio::error::operation_aborted && socket_type sock(std::move(sock0));
ec != boost::asio::error::eof) streambuf sb;
std::cerr <<
"#" << std::to_string(id) << " " << std::endl;
}
void
do_peer(int id, socket_type&& sock)
{
http::stream<socket_type> hs(std::move(sock));
error_code ec; error_code ec;
for(;;) for(;;)
{ {
req_type req; req_type req;
hs.read(req, ec); http::read(sock, sb, req, ec);
if(ec) if(ec)
break; break;
auto path = req.url; auto path = req.url;
@@ -143,26 +161,42 @@ public:
path = root_ + path; path = root_ + path;
if(! boost::filesystem::exists(path)) if(! boost::filesystem::exists(path))
{ {
response_v1<string_body> resp; response_v1<string_body> res;
resp.status = 404; res.status = 404;
resp.reason = "Not Found"; res.reason = "Not Found";
resp.version = req.version; res.version = req.version;
resp.headers.replace("Server", "http_sync_server"); res.headers.insert("Server", "http_sync_server");
resp.body = "The file '" + path + "' was not found"; res.headers.insert("Content-Type", "text/html");
prepare(resp); res.body = "The file '" + path + "' was not found";
hs.write(resp, ec); prepare(res);
write(sock, res, ec);
if(ec) if(ec)
break; break;
} }
resp_type resp; resp_type res;
resp.status = 200; res.status = 200;
resp.reason = "OK"; res.reason = "OK";
resp.version = req.version; res.version = req.version;
resp.headers.replace("Server", "http_sync_server"); res.headers.insert("Server", "http_sync_server");
resp.headers.replace("Content-Type", "text/html"); res.headers.insert("Content-Type", mime_type(path));
resp.body = path; res.body = path;
prepare(resp); try
hs.write(resp, ec); {
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) if(ec)
break; break;
} }

View 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

View File

@@ -16,13 +16,13 @@ int main()
// Normal boost::asio setup // Normal boost::asio setup
std::string const host = "echo.websocket.org"; std::string const host = "echo.websocket.org";
boost::asio::io_service ios; boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r(ios); boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock(ios); boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock, boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"})); r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
// WebSocket connect and send message using beast // 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.handshake(host, "/");
ws.write(boost::asio::buffer("Hello, world!")); ws.write(boost::asio::buffer("Hello, world!"));

View File

@@ -15,7 +15,9 @@ namespace test {
enum error enum error
{ {
fail_error = 1 success = 0,
fail_error
}; };
namespace detail { namespace detail {

View File

@@ -161,19 +161,21 @@ public:
friend friend
void void
teardown(fail_stream<NextLayer>& stream, teardown(websocket::teardown_tag,
boost::system::error_code& ec) fail_stream<NextLayer>& stream,
boost::system::error_code& ec)
{ {
if(stream.pfc_->fail(ec)) if(stream.pfc_->fail(ec))
return; return;
websocket_helpers::call_teardown(stream.next_layer(), ec); beast::websocket_helpers::call_teardown(stream.next_layer(), ec);
} }
template<class TeardownHandler> template<class TeardownHandler>
friend friend
void void
async_teardown(fail_stream<NextLayer>& stream, async_teardown(websocket::teardown_tag,
TeardownHandler&& handler) fail_stream<NextLayer>& stream,
TeardownHandler&& handler)
{ {
error_code ec; error_code ec;
if(stream.pfc_->fail(ec)) if(stream.pfc_->fail(ec))
@@ -182,7 +184,7 @@ public:
bind_handler(std::move(handler), ec)); bind_handler(std::move(handler), ec));
return; return;
} }
websocket_helpers::call_async_teardown( beast::websocket_helpers::call_async_teardown(
stream.next_layer(), std::forward<TeardownHandler>(handler)); stream.next_layer(), std::forward<TeardownHandler>(handler));
} }
}; };

View File

@@ -23,29 +23,29 @@ private:
std::string const& what_; std::string const& what_;
public: public:
amount (amount const&) = default; amount(amount const&) = default;
amount& operator= (amount const&) = delete; amount& operator=(amount const&) = delete;
template <class = void> template<class = void>
amount (std::size_t n, std::string const& what); amount(std::size_t n, std::string const& what);
friend friend
std::ostream& std::ostream&
operator<< (std::ostream& s, amount const& t); operator<<(std::ostream& s, amount const& t);
}; };
template <class> template<class>
amount::amount (std::size_t n, std::string const& what) amount::amount(std::size_t n, std::string const& what)
: n_ (n) : n_(n)
, what_ (what) , what_(what)
{ {
} }
inline inline
std::ostream& 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; return s;
} }

View File

@@ -16,7 +16,7 @@ namespace detail {
The interface allows for limited read only operations. Derived classes The interface allows for limited read only operations. Derived classes
provide additional behavior. provide additional behavior.
*/ */
template <class Container> template<class Container>
class const_container class const_container
{ {
private: private:

View File

@@ -8,11 +8,10 @@
#ifndef BEAST_UNIT_TEST_DSTREAM_HPP #ifndef BEAST_UNIT_TEST_DSTREAM_HPP
#define BEAST_UNIT_TEST_DSTREAM_HPP #define BEAST_UNIT_TEST_DSTREAM_HPP
#include <boost/utility/base_from_member.hpp> #include <ios>
#include <iostream>
#include <memory> #include <memory>
#include <ostream> #include <ostream>
#include <sstream> #include <streambuf>
#include <string> #include <string>
#ifdef _MSC_VER #ifdef _MSC_VER
@@ -30,15 +29,18 @@
namespace beast { namespace beast {
namespace unit_test { namespace unit_test {
namespace detail {
#ifdef _MSC_VER #ifdef _MSC_VER
namespace detail {
template<class CharT, class Traits, class Allocator> template<class CharT, class Traits, class Allocator>
class dstream_buf class dstream_buf
: public std::basic_stringbuf<CharT, Traits, Allocator> : public std::basic_stringbuf<CharT, Traits, Allocator>
{ {
using ostream = std::basic_ostream<CharT, Traits>;
bool dbg_; bool dbg_;
ostream& os_;
template<class T> template<class T>
void write(T const*) = delete; void write(T const*) = delete;
@@ -47,21 +49,21 @@ class dstream_buf
{ {
if(dbg_) if(dbg_)
OutputDebugStringA(s); OutputDebugStringA(s);
else os_ << s;
std::cout << s;
} }
void write(wchar_t const* s) void write(wchar_t const* s)
{ {
if(dbg_) if(dbg_)
OutputDebugStringW(s); OutputDebugStringW(s);
else os_ << s;
std::wcout << s;
} }
public: public:
dstream_buf() explicit
: dbg_(IsDebuggerPresent() != FALSE) 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 } // 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< template<
class CharT, class CharT,
class Traits = std::char_traits<CharT>, class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT> class Allocator = std::allocator<CharT>
> >
class basic_dstream class basic_dstream
: private boost::base_from_member< : public std::basic_ostream<CharT, Traits>
detail::dstream_buf<CharT, Traits, Allocator>>
, public std::basic_ostream<CharT, Traits>
{ {
detail::dstream_buf<
CharT, Traits, Allocator> buf_;
public: public:
basic_dstream() /** Construct a stream.
: std::basic_ostream<CharT, Traits>(&this->member)
@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 dstream = basic_dstream<char>;
using dwstream = basic_dstream<wchar_t>; using dwstream = basic_dstream<wchar_t>;
} // test #else
using dstream = std::ostream&;
using dwstream = std::wostream&;
#endif
} // unit_test
} // beast } // beast
#endif #endif

View File

@@ -33,7 +33,7 @@ static
std::string std::string
prefix(suite_info const& s) prefix(suite_info const& s)
{ {
if (s.manual()) if(s.manual())
return "|M| "; return "|M| ";
return " "; return " ";
} }
@@ -45,7 +45,7 @@ print(std::ostream& os, suite_list const& c)
std::size_t manual = 0; std::size_t manual = 0;
for(auto const& s : c) for(auto const& s : c)
{ {
os << prefix (s) << s.full_name() << '\n'; os << prefix(s) << s.full_name() << '\n';
if(s.manual()) if(s.manual())
++manual; ++manual;
} }
@@ -80,18 +80,18 @@ int main(int ac, char const* av[])
#ifdef _MSC_VER #ifdef _MSC_VER
{ {
int flags = _CrtSetDbgFlag (_CRTDBG_REPORT_FLAG); int flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
flags |= _CRTDBG_LEAK_CHECK_DF; flags |= _CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag (flags); _CrtSetDbgFlag(flags);
} }
#endif #endif
namespace po = boost::program_options; namespace po = boost::program_options;
po::options_description desc("Options"); po::options_description desc("Options");
desc.add_options() desc.add_options()
("help,h", "Produce a help message") ("help,h", "Produce a help message")
("print,r", "Print the list of available test suites") ("print,p", "Print the list of available test suites")
("suites,s", po::value<string>(), "suites to run") ("suites,s", po::value<string>(), "suites to run")
; ;
po::positional_options_description p; 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::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm); po::notify(vm);
dstream log; dstream log{std::cerr};
std::unitbuf(log);
if(vm.count("help")) if(vm.count("help"))
{ {
@@ -121,7 +122,7 @@ int main(int ac, char const* av[])
match_auto(suites)); match_auto(suites));
else else
failed = r.run_each(global_suites()); failed = r.run_each(global_suites());
if (failed) if(failed)
return EXIT_FAILURE; return EXIT_FAILURE;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@@ -45,42 +45,42 @@ private:
std::string library_; std::string library_;
public: public:
template <class = void> template<class = void>
explicit explicit
selector (mode_t mode, std::string const& pattern = ""); selector(mode_t mode, std::string const& pattern = "");
template <class = void> template<class = void>
bool bool
operator() (suite_info const& s); operator()(suite_info const& s);
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template <class> template<class>
selector::selector (mode_t mode, std::string const& pattern) selector::selector(mode_t mode, std::string const& pattern)
: mode_ (mode) : mode_(mode)
, pat_ (pattern) , pat_(pattern)
{ {
if (mode_ == automatch && pattern.empty()) if(mode_ == automatch && pattern.empty())
mode_ = all; mode_ = all;
} }
template <class> template<class>
bool bool
selector::operator() (suite_info const& s) selector::operator()(suite_info const& s)
{ {
switch (mode_) switch(mode_)
{ {
case automatch: case automatch:
// suite or full name // suite or full name
if (s.name() == pat_ || s.full_name() == pat_) if(s.name() == pat_ || s.full_name() == pat_)
{ {
mode_ = none; mode_ = none;
return true; return true;
} }
// check module // check module
if (pat_ == s.module()) if(pat_ == s.module())
{ {
mode_ = module; mode_ = module;
library_ = s.library(); library_ = s.library();
@@ -88,7 +88,7 @@ selector::operator() (suite_info const& s)
} }
// check library // check library
if (pat_ == s.library()) if(pat_ == s.library())
{ {
mode_ = library; mode_ = library;
return ! s.manual(); return ! s.manual();
@@ -138,9 +138,9 @@ selector::operator() (suite_info const& s)
*/ */
inline inline
selector 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. */ /** Return a predicate that matches all suites not marked manual. */
@@ -148,23 +148,23 @@ inline
selector selector
match_all() match_all()
{ {
return selector (selector::all); return selector(selector::all);
} }
/** Returns a predicate that matches a specific suite. */ /** Returns a predicate that matches a specific suite. */
inline inline
selector 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. */ /** Returns a predicate that matches all suites in a library. */
inline inline
selector 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 } // unit_test

View File

@@ -24,8 +24,8 @@ private:
public: public:
recorder() = default; recorder() = default;
recorder (recorder const&) = default; recorder(recorder const&) = default;
recorder& operator= (recorder const&) = default; recorder& operator=(recorder const&) = default;
/** Returns a report with the results of all completed suites. */ /** Returns a report with the results of all completed suites. */
results const& results const&
@@ -37,31 +37,31 @@ public:
private: private:
virtual virtual
void 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 virtual
void void
on_suite_end() override on_suite_end() override
{ {
m_results.insert (std::move (m_suite)); m_results.insert(std::move(m_suite));
} }
virtual virtual
void 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 virtual
void void
on_case_end() override on_case_end() override
{ {
if (m_case.tests.size() > 0) if(m_case.tests.size() > 0)
m_suite.insert (std::move (m_case)); m_suite.insert(std::move(m_case));
} }
virtual virtual
@@ -73,16 +73,16 @@ private:
virtual virtual
void 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 virtual
void 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);
} }
}; };

View File

@@ -56,7 +56,7 @@ private:
typename clock_type::time_point start = clock_type::now(); typename clock_type::time_point start = clock_type::now();
explicit explicit
suite_results(std::string const& name_ = "") suite_results(std::string name_ = "")
: name(std::move(name_)) : name(std::move(name_))
{ {
} }
@@ -83,7 +83,7 @@ private:
typename clock_type::time_point start = clock_type::now(); typename clock_type::time_point start = clock_type::now();
void void
add (suite_results const& r); add(suite_results const& r);
}; };
std::ostream& os_; std::ostream& os_;
@@ -107,7 +107,7 @@ private:
virtual virtual
void void
on_suite_begin (suite_info const& info) override; on_suite_begin(suite_info const& info) override;
virtual virtual
void void
@@ -115,7 +115,7 @@ private:
virtual virtual
void void
on_case_begin (std::string const& name) override; on_case_begin(std::string const& name) override;
virtual virtual
void void
@@ -127,11 +127,11 @@ private:
virtual virtual
void void
on_fail (std::string const& reason) override; on_fail(std::string const& reason) override;
virtual virtual
void 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; cases += r.cases;
failed += r.failed; failed += r.failed;
auto const elapsed = clock_type::now() - r.start; 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(), auto const iter = std::lower_bound(top.begin(),
top.end(), elapsed, top.end(), elapsed,
@@ -165,13 +165,13 @@ results::add(suite_results const& r)
{ {
return t1.second > t2; 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.resize(top.size() - 1);
top.emplace(iter, r.name, elapsed); top.emplace(iter, r.name, elapsed);
} }
else if (top.size() < max_top) else if(top.size() < max_top)
{ {
top.emplace_back(r.name, elapsed); top.emplace_back(r.name, elapsed);
} }
@@ -213,11 +213,11 @@ reporter<_>::fmtdur(typename clock_type::duration const& d)
{ {
using namespace std::chrono; using namespace std::chrono;
auto const ms = duration_cast<milliseconds>(d); auto const ms = duration_cast<milliseconds>(d);
if (ms < seconds{1}) if(ms < seconds{1})
return std::to_string(ms.count()) + "ms"; return std::to_string(ms.count()) + "ms";
std::stringstream ss; std::stringstream ss;
ss << std::fixed << std::setprecision(1) << ss << std::fixed << std::setprecision(1) <<
(ms.count()/1000.) << "s"; (ms.count()/1000.) << "s";
return ss.str(); return ss.str();
} }
@@ -241,11 +241,10 @@ void
reporter<_>:: reporter<_>::
on_case_begin(std::string const& name) on_case_begin(std::string const& name)
{ {
case_results_ = case_results (name); case_results_ = case_results(name);
os_ << os_ << suite_results_.name <<
suite_results_.name << (case_results_.name.empty() ? "" :
(case_results_.name.empty() ? (" " + case_results_.name)) << std::endl;
"" : (" " + case_results_.name)) << std::endl;
} }
template<class _> template<class _>
@@ -273,7 +272,7 @@ on_fail(std::string const& reason)
++case_results_.total; ++case_results_.total;
os_ << os_ <<
"#" << case_results_.total << " failed" << "#" << case_results_.total << " failed" <<
(reason.empty() ? "" : ": ") << reason << std::endl; (reason.empty() ? "" : ": ") << reason << std::endl;
} }
template<class _> template<class _>

View File

@@ -23,14 +23,14 @@ public:
/** Holds the result of evaluating one test condition. */ /** Holds the result of evaluating one test condition. */
struct test struct test
{ {
explicit test (bool pass_) explicit test(bool pass_)
: pass (pass_) : pass(pass_)
{ {
} }
test (bool pass_, std::string const& reason_) test(bool pass_, std::string const& reason_)
: pass (pass_) : pass(pass_)
, reason (reason_) , reason(reason_)
{ {
} }
@@ -46,8 +46,8 @@ private:
std::size_t failed_; std::size_t failed_;
public: public:
tests_t () tests_t()
: failed_ (0) : failed_(0)
{ {
} }
@@ -69,15 +69,15 @@ private:
void void
pass() pass()
{ {
cont().emplace_back (true); cont().emplace_back(true);
} }
/** Register a failed test condition. */ /** Register a failed test condition. */
void void
fail (std::string const& reason = "") fail(std::string const& reason = "")
{ {
++failed_; ++failed_;
cont().emplace_back (false, reason); cont().emplace_back(false, reason);
} }
}; };
@@ -87,17 +87,17 @@ private:
public: public:
/** Insert a string into the log. */ /** Insert a string into the log. */
void void
insert (std::string const& s) insert(std::string const& s)
{ {
cont().push_back (s); cont().push_back(s);
} }
}; };
std::string name_; std::string name_;
public: public:
explicit case_results (std::string const& name = "") explicit case_results(std::string const& name = "")
: name_ (name) : name_(name)
{ {
} }
@@ -127,8 +127,8 @@ private:
std::size_t failed_ = 0; std::size_t failed_ = 0;
public: public:
explicit suite_results (std::string const& name = "") explicit suite_results(std::string const& name = "")
: name_ (name) : name_(name)
{ {
} }
@@ -156,17 +156,17 @@ public:
/** Insert a set of testcase results. */ /** Insert a set of testcase results. */
/** @{ */ /** @{ */
void 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(); total_ += r.tests.total();
failed_ += r.tests.failed(); failed_ += r.tests.failed();
} }
void void
insert (case_results const& r) insert(case_results const& r)
{ {
cont().push_back (r); cont().push_back(r);
total_ += r.tests.total(); total_ += r.tests.total();
failed_ += r.tests.failed(); failed_ += r.tests.failed();
} }
@@ -187,9 +187,9 @@ private:
public: public:
results() results()
: m_cases (0) : m_cases(0)
, total_ (0) , total_(0)
, failed_ (0) , failed_(0)
{ {
} }
@@ -217,21 +217,21 @@ public:
/** Insert a set of suite results. */ /** Insert a set of suite results. */
/** @{ */ /** @{ */
void void
insert (suite_results&& r) insert(suite_results&& r)
{ {
m_cases += r.size(); m_cases += r.size();
total_ += r.total(); total_ += r.total();
failed_ += r.failed(); failed_ += r.failed();
cont().emplace_back (std::move (r)); cont().emplace_back(std::move(r));
} }
void void
insert (suite_results const& r) insert(suite_results const& r)
{ {
m_cases += r.size(); m_cases += r.size();
total_ += r.total(); total_ += r.total();
failed_ += r.failed(); failed_ += r.failed();
cont().push_back (r); cont().push_back(r);
} }
/** @} */ /** @} */
}; };

View File

@@ -18,6 +18,7 @@ namespace beast {
namespace unit_test { namespace unit_test {
/** Unit test runner interface. /** Unit test runner interface.
Derived classes can customize the reporting behavior. This interface is Derived classes can customize the reporting behavior. This interface is
injected into the unit_test class to receive the results of the tests. injected into the unit_test class to receive the results of the tests.
*/ */
@@ -60,7 +61,7 @@ public:
*/ */
template<class = void> template<class = void>
bool bool
run (suite_info const& s); run(suite_info const& s);
/** Run a sequence of suites. /** Run a sequence of suites.
The expression The expression
@@ -68,44 +69,40 @@ public:
must be convertible to `suite_info`. must be convertible to `suite_info`.
@return `true` if any conditions failed. @return `true` if any conditions failed.
*/ */
template <class FwdIter> template<class FwdIter>
bool bool
run (FwdIter first, FwdIter last); run(FwdIter first, FwdIter last);
/** Conditionally run a sequence of suites. /** Conditionally run a sequence of suites.
pred will be called as: pred will be called as:
@code @code
bool pred (suite_info const&); bool pred(suite_info const&);
@endcode @endcode
@return `true` if any conditions failed. @return `true` if any conditions failed.
*/ */
template <class FwdIter, class Pred> template<class FwdIter, class Pred>
bool 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. /** Run all suites in a container.
@return `true` if any conditions failed. @return `true` if any conditions failed.
*/ */
template <class SequenceContainer> template<class SequenceContainer>
bool bool
run_each (SequenceContainer const& c); run_each(SequenceContainer const& c);
/** Conditionally run suites in a container. /** Conditionally run suites in a container.
pred will be called as: pred will be called as:
@code @code
bool pred (suite_info const&); bool pred(suite_info const&);
@endcode @endcode
@return `true` if any conditions failed. @return `true` if any conditions failed.
*/ */
template <class SequenceContainer, class Pred> template<class SequenceContainer, class Pred>
bool bool
run_each_if (SequenceContainer const& c, Pred pred = Pred{}); run_each_if(SequenceContainer const& c, Pred pred = Pred{});
protected: protected:
//
// Overrides
//
/// Called when a new suite starts. /// Called when a new suite starts.
virtual virtual
void void
@@ -159,130 +156,130 @@ private:
friend class suite; friend class suite;
// Start a new testcase. // Start a new testcase.
template <class = void> template<class = void>
void void
testcase (std::string const& name); testcase(std::string const& name);
template <class = void> template<class = void>
void void
pass(); pass();
template <class = void> template<class = void>
void void
fail (std::string const& reason); fail(std::string const& reason);
template <class = void> template<class = void>
void void
log (std::string const& s); log(std::string const& s);
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template <class> template<class>
bool bool
runner::run (suite_info const& s) runner::run(suite_info const& s)
{ {
// Enable 'default' testcase // Enable 'default' testcase
default_ = true; default_ = true;
failed_ = false; failed_ = false;
on_suite_begin (s); on_suite_begin(s);
s.run (*this); s.run(*this);
// Forgot to call pass or fail. // Forgot to call pass or fail.
assert (cond_); assert(cond_);
on_case_end(); on_case_end();
on_suite_end(); on_suite_end();
return failed_; return failed_;
} }
template <class FwdIter> template<class FwdIter>
bool bool
runner::run (FwdIter first, FwdIter last) runner::run(FwdIter first, FwdIter last)
{ {
bool failed (false); bool failed(false);
for (;first != last; ++first) for(;first != last; ++first)
failed = run (*first) || failed; failed = run(*first) || failed;
return failed; return failed;
} }
template <class FwdIter, class Pred> template<class FwdIter, class Pred>
bool bool
runner::run_if (FwdIter first, FwdIter last, Pred pred) runner::run_if(FwdIter first, FwdIter last, Pred pred)
{ {
bool failed (false); bool failed(false);
for (;first != last; ++first) for(;first != last; ++first)
if (pred (*first)) if(pred(*first))
failed = run (*first) || failed; failed = run(*first) || failed;
return failed; return failed;
} }
template <class SequenceContainer> template<class SequenceContainer>
bool bool
runner::run_each (SequenceContainer const& c) runner::run_each(SequenceContainer const& c)
{ {
bool failed (false); bool failed(false);
for (auto const& s : c) for(auto const& s : c)
failed = run (s) || failed; failed = run(s) || failed;
return failed; return failed;
} }
template <class SequenceContainer, class Pred> template<class SequenceContainer, class Pred>
bool bool
runner::run_each_if (SequenceContainer const& c, Pred pred) runner::run_each_if(SequenceContainer const& c, Pred pred)
{ {
bool failed (false); bool failed(false);
for (auto const& s : c) for(auto const& s : c)
if (pred (s)) if(pred(s))
failed = run (s) || failed; failed = run(s) || failed;
return failed; return failed;
} }
template <class> template<class>
void void
runner::testcase (std::string const& name) runner::testcase(std::string const& name)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
// Name may not be empty // Name may not be empty
assert (default_ || ! name.empty()); assert(default_ || ! name.empty());
// Forgot to call pass or fail // Forgot to call pass or fail
assert (default_ || cond_); assert(default_ || cond_);
if (! default_) if(! default_)
on_case_end(); on_case_end();
default_ = false; default_ = false;
cond_ = false; cond_ = false;
on_case_begin (name); on_case_begin(name);
} }
template <class> template<class>
void void
runner::pass() runner::pass()
{ {
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
if (default_) if(default_)
testcase (""); testcase("");
on_pass(); on_pass();
cond_ = true; cond_ = true;
} }
template <class> template<class>
void void
runner::fail (std::string const& reason) runner::fail(std::string const& reason)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
if (default_) if(default_)
testcase (""); testcase("");
on_fail (reason); on_fail(reason);
failed_ = true; failed_ = true;
cond_ = true; cond_ = true;
} }
template <class> template<class>
void void
runner::log (std::string const& s) runner::log(std::string const& s)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
if (default_) if(default_)
testcase (""); testcase("");
on_log (s); on_log(s);
} }
} // unit_test } // unit_test

View File

@@ -9,6 +9,7 @@
#define BEAST_UNIT_TEST_SUITE_HPP #define BEAST_UNIT_TEST_SUITE_HPP
#include <beast/unit_test/runner.hpp> #include <beast/unit_test/runner.hpp>
#include <boost/filesystem.hpp>
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
@@ -72,7 +73,7 @@ private:
{ {
auto const& s = this->str(); auto const& s = this->str();
if(s.size() > 0) if(s.size() > 0)
suite_.runner_->on_log(s); suite_.runner_->log(s);
this->str(""); this->str("");
return 0; return 0;
} }
@@ -127,7 +128,7 @@ private:
scoped_testcase scoped_testcase
operator()(abort_t abort); operator()(abort_t abort);
template <class T> template<class T>
scoped_testcase scoped_testcase
operator<<(T const& t); operator<<(T const& t);
}; };
@@ -146,7 +147,9 @@ public:
/** Returns the "current" running suite. /** Returns the "current" running suite.
If no suite is running, nullptr is returned. If no suite is running, nullptr is returned.
*/ */
static suite* this_suite() static
suite*
this_suite()
{ {
return *p_this_suite(); return *p_this_suite();
} }
@@ -158,6 +161,7 @@ public:
} }
/** Invokes the test using the specified runner. /** Invokes the test using the specified runner.
Data members are set up here instead of the constructor as a Data members are set up here instead of the constructor as a
convenience to writing the derived class to avoid repetition of convenience to writing the derived class to avoid repetition of
forwarded constructor arguments to the base. forwarded constructor arguments to the base.
@@ -165,67 +169,95 @@ public:
*/ */
template<class = void> template<class = void>
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. /** 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 This function provides improved logging by incorporating the
logged if the condition is false. 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. @return `true` if the test condition indicates success.
*/ */
template<class Condition, class String> /** @{ */
bool
expect(Condition const& shouldBeTrue,
String const& reason);
template<class Condition> template<class Condition>
bool bool
expect(Condition const& shouldBeTrue) expect(Condition const& shouldBeTrue)
{ {
return expect(shouldBeTrue, ""); return expect(shouldBeTrue, {});
} }
/** Expect an exception from f() */ template<class Condition>
/** @{ */
template <class F, class String>
bool bool
except (F&& f, String const& reason); expect(Condition const& shouldBeTrue, std::string const& reason);
template <class F> template<class Condition>
bool 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, ""); return except(f, "");
} }
/** @} */ template<class E, class F, class String>
/** Expect an exception of the given type from f() */
/** @{ */
template <class E, class F, class String>
bool bool
except (F&& f, String const& reason); except(F&& f, String const& reason);
template<class E, class F>
template <class E, class F>
bool bool
except (F&& f) except(F&& f)
{ {
return except<E>(f, ""); return except<E>(f, "");
} }
/** @} */ template<class F, class String>
/** Fail if f() throws */
/** @{ */
template <class F, class String>
bool bool
unexcept (F&& f, String const& reason); unexcept(F&& f, String const& reason);
template<class F>
template <class F>
bool bool
unexcept (F&& f) unexcept(F&& f)
{ {
return unexcept(f, ""); return unexcept(f, "");
} }
/** @} */
/** Return the argument associated with the runner. */ /** Return the argument associated with the runner. */
std::string const& std::string const&
@@ -235,29 +267,19 @@ public:
} }
// DEPRECATED // DEPRECATED
// @return `true` if the test condition indicates success (a false value) // @return `true` if the test condition indicates success(a false value)
template <class Condition, class String> template<class Condition, class String>
bool bool
unexpected (Condition shouldBeFalse, unexpected(Condition shouldBeFalse,
String const& reason); String const& reason);
template <class Condition> template<class Condition>
bool 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: private:
friend class thread; friend class thread;
@@ -277,9 +299,9 @@ private:
void void
propagate_abort(); propagate_abort();
template <class = void> template<class = void>
void void
run (runner& r); run(runner& r);
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -298,7 +320,10 @@ public:
{ {
auto const& name = ss_.str(); auto const& name = ss_.str();
if(! name.empty()) if(! name.empty())
suite_.runner_->testcase (name); {
suite_.log.flush();
suite_.runner_->testcase(name);
}
} }
scoped_testcase(suite& self, std::stringstream& ss) scoped_testcase(suite& self, std::stringstream& ss)
@@ -333,16 +358,17 @@ public:
inline inline
void void
suite::testcase_t::operator() (std::string const& name, suite::testcase_t::operator()(
abort_t abort) std::string const& name, abort_t abort)
{ {
suite_.abort_ = abort == abort_on_fail; suite_.abort_ = abort == abort_on_fail;
suite_.runner_->testcase (name); suite_.log.flush();
suite_.runner_->testcase(name);
} }
inline inline
suite::scoped_testcase suite::scoped_testcase
suite::testcase_t::operator() (abort_t abort) suite::testcase_t::operator()(abort_t abort)
{ {
suite_.abort_ = abort == abort_on_fail; suite_.abort_ = abort == abort_on_fail;
return { suite_, ss_ }; return { suite_, ss_ };
@@ -351,7 +377,7 @@ suite::testcase_t::operator() (abort_t abort)
template<class T> template<class T>
inline inline
suite::scoped_testcase suite::scoped_testcase
suite::testcase_t::operator<< (T const& t) suite::testcase_t::operator<<(T const& t)
{ {
return { suite_, ss_, t }; return { suite_, ss_, t };
} }
@@ -360,7 +386,8 @@ suite::testcase_t::operator<< (T const& t)
template<class> template<class>
void void
suite::operator()(runner& r) suite::
operator()(runner& r)
{ {
*p_this_suite() = this; *p_this_suite() = this;
try try
@@ -375,11 +402,11 @@ suite::operator()(runner& r)
} }
} }
template <class Condition, class String> template<class Condition>
inline
bool bool
suite::expect(Condition const& shouldBeTrue, suite::
String const& reason) expect(
Condition const& shouldBeTrue, std::string const& reason)
{ {
if(shouldBeTrue) if(shouldBeTrue)
{ {
@@ -390,9 +417,35 @@ suite::expect(Condition const& shouldBeTrue,
return false; return false;
} }
template <class F, class String> template<class Condition>
bool 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 try
{ {
@@ -407,9 +460,10 @@ suite::except (F&& f, String const& reason)
return true; return true;
} }
template <class E, class F, class String> template<class E, class F, class String>
bool bool
suite::except (F&& f, String const& reason) suite::
except(F&& f, String const& reason)
{ {
try try
{ {
@@ -424,9 +478,10 @@ suite::except (F&& f, String const& reason)
return true; return true;
} }
template <class F, class String> template<class F, class String>
bool bool
suite::unexcept (F&& f, String const& reason) suite::
unexcept(F&& f, String const& reason)
{ {
try try
{ {
@@ -441,36 +496,39 @@ suite::unexcept (F&& f, String const& reason)
return false; return false;
} }
template <class Condition, class String> template<class Condition, class String>
inline
bool bool
suite::unexpected (Condition shouldBeFalse, suite::
String const& reason) unexpected(
Condition shouldBeFalse, String const& reason)
{ {
bool const b = bool const b =
static_cast<bool>(shouldBeFalse); static_cast<bool>(shouldBeFalse);
if (! b) if(! b)
pass(); pass();
else else
fail (reason); fail(reason);
return ! b; return ! b;
} }
template <class> template<class>
void void
suite::pass() suite::
pass()
{ {
propagate_abort(); propagate_abort();
runner_->pass(); runner_->pass();
} }
template <class> // ::fail
template<class>
void void
suite::fail (std::string const& reason) suite::
fail(std::string const& reason)
{ {
propagate_abort(); propagate_abort();
runner_->fail (reason); runner_->fail(reason);
if (abort_) if(abort_)
{ {
aborted_ = true; aborted_ = true;
throw abort_exception(); throw abort_exception();
@@ -479,15 +537,17 @@ suite::fail (std::string const& reason)
inline inline
void void
suite::propagate_abort() suite::
propagate_abort()
{ {
if (abort_ && aborted_) if(abort_ && aborted_)
throw abort_exception(); throw abort_exception();
} }
template <class> template<class>
void void
suite::run (runner& r) suite::
run(runner& r)
{ {
runner_ = &r; runner_ = &r;
@@ -495,21 +555,37 @@ suite::run (runner& r)
{ {
run(); run();
} }
catch (abort_exception const&) catch(abort_exception const&)
{ {
// ends the suite // ends the suite
} }
catch (std::exception const& e) catch(std::exception const& e)
{ {
runner_->fail ("unhandled exception: " + runner_->fail("unhandled exception: " +
std::string (e.what())); 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 } // unit_test
} // beast } // beast
@@ -519,7 +595,7 @@ suite::run (runner& r)
// This inserts the suite with the given manual flag // This inserts the suite with the given manual flag
#define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual) \ #define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual) \
static beast::unit_test::detail::insert_suite <Class##_test> \ static beast::unit_test::detail::insert_suite <Class##_test> \
Library ## Module ## Class ## _test_instance ( \ Library ## Module ## Class ## _test_instance( \
#Class, #Module, #Library, manual) #Class, #Module, #Library, manual)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -80,7 +80,7 @@ public:
void void
run(runner& r) const run(runner& r) const
{ {
run_ (r); run_(r);
} }
friend friend

View File

@@ -33,7 +33,7 @@ public:
The suite must not already exist. The suite must not already exist.
*/ */
template <class Suite> template<class Suite>
void void
insert( insert(
char const* name, char const* name,
@@ -44,7 +44,7 @@ public:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template <class Suite> template<class Suite>
void void
suite_list::insert( suite_list::insert(
char const* name, char const* name,
@@ -56,14 +56,14 @@ suite_list::insert(
{ {
std::string s; std::string s;
s = std::string(library) + "." + module + "." + name; s = std::string(library) + "." + module + "." + name;
auto const result (names_.insert(s)); auto const result(names_.insert(s));
assert (result.second); // Duplicate name assert(result.second); // Duplicate name
} }
{ {
auto const result (classes_.insert ( auto const result(classes_.insert(
std::type_index (typeid(Suite)))); std::type_index(typeid(Suite))));
assert (result.second); // Duplicate type assert(result.second); // Duplicate type
} }
#endif #endif
cont().emplace(make_suite_info<Suite>( cont().emplace(make_suite_info<Suite>(

View File

@@ -28,31 +28,31 @@ public:
using native_handle_type = std::thread::native_handle_type; using native_handle_type = std::thread::native_handle_type;
thread() = default; thread() = default;
thread (thread const&) = delete; thread(thread const&) = delete;
thread& operator= (thread const&) = delete; thread& operator=(thread const&) = delete;
thread (thread&& other) thread(thread&& other)
: s_ (other.s_) : s_(other.s_)
, t_ (std::move(other.t_)) , t_(std::move(other.t_))
{ {
} }
thread& operator= (thread&& other) thread& operator=(thread&& other)
{ {
s_ = other.s_; s_ = other.s_;
t_ = std::move(other.t_); t_ = std::move(other.t_);
return *this; return *this;
} }
template <class F, class... Args> template<class F, class... Args>
explicit explicit
thread (suite& s, F&& f, Args&&... args) thread(suite& s, F&& f, Args&&... args)
: s_ (&s) : s_(&s)
{ {
std::function<void(void)> b = std::function<void(void)> b =
std::bind(std::forward<F>(f), std::bind(std::forward<F>(f),
std::forward<Args>(args)...); std::forward<Args>(args)...);
t_ = std::thread (&thread::run, this, t_ = std::thread(&thread::run, this,
std::move(b)); std::move(b));
} }
@@ -89,7 +89,7 @@ public:
} }
void void
swap (thread& other) swap(thread& other)
{ {
std::swap(s_, other.s_); std::swap(s_, other.s_);
std::swap(t_, other.t_); std::swap(t_, other.t_);
@@ -97,23 +97,23 @@ public:
private: private:
void void
run (std::function <void(void)> f) run(std::function <void(void)> f)
{ {
try try
{ {
f(); 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: " + s_->fail("unhandled exception: " +
std::string (e.what())); std::string(e.what()));
} }
catch (...) catch(...)
{ {
s_->fail ("unhandled exception"); s_->fail("unhandled exception");
} }
} }
}; };

View File

@@ -47,7 +47,7 @@ namespace beast {
@note See <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf"> @note See <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf">
Library Foundations For Asynchronous Operations</a> Library Foundations For Asynchronous Operations</a>
*/ */
template <class CompletionHandler, class Signature> template<class CompletionHandler, class Signature>
struct async_completion struct async_completion
{ {
/** The type of the final handler called by the asynchronous initiation function. /** The type of the final handler called by the asynchronous initiation function.

View File

@@ -14,10 +14,10 @@
namespace beast { 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 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, empty. A copy of the mutable buffer sequence object is stored; however,
ownership of the underlying memory is not transferred. The caller is ownership of the underlying memory is not transferred. The caller is
responsible for making sure that referenced memory remains valid responsible for making sure that referenced memory remains valid

View File

@@ -44,7 +44,7 @@ namespace detail {
*/ */
template <class = void> template<class = void>
std::string const& std::string const&
base64_alphabet() base64_alphabet()
{ {
@@ -62,7 +62,7 @@ is_base64(unsigned char c)
return (std::isalnum(c) || (c == '+') || (c == '/')); return (std::isalnum(c) || (c == '+') || (c == '/'));
} }
template <class = void> template<class = void>
std::string std::string
base64_encode (std::uint8_t const* data, base64_encode (std::uint8_t const* data,
std::size_t in_len) std::size_t in_len)
@@ -112,7 +112,7 @@ base64_encode (std::uint8_t const* data,
} }
template <class = void> template<class = void>
std::string std::string
base64_encode (std::string const& s) base64_encode (std::string const& s)
{ {
@@ -120,11 +120,11 @@ base64_encode (std::string const& s)
std::uint8_t const*> (s.data()), s.size()); std::uint8_t const*> (s.data()), s.size());
} }
template <class = void> template<class = void>
std::string std::string
base64_decode(std::string const& data) base64_decode(std::string const& data)
{ {
int in_len = data.size(); auto in_len = data.size();
unsigned char c3[3], c4[4]; unsigned char c3[3], c4[4];
int i = 0; int i = 0;
int j = 0; int j = 0;

View File

@@ -54,14 +54,14 @@ public:
operator()() operator()()
{ {
invoke(h_, args_, invoke(h_, args_,
index_sequence_for<Args...> ()); index_sequence_for<Args...>());
} }
void void
operator()() const operator()() const
{ {
invoke(h_, args_, invoke(h_, args_,
index_sequence_for<Args...> ()); index_sequence_for<Args...>());
} }
friend friend

View File

@@ -14,7 +14,7 @@
namespace beast { namespace beast {
namespace detail { namespace detail {
template <class T> template<class T>
struct empty_base_optimization_decide struct empty_base_optimization_decide
: std::integral_constant <bool, : std::integral_constant <bool,
std::is_empty <T>::value std::is_empty <T>::value
@@ -25,7 +25,7 @@ struct empty_base_optimization_decide
{ {
}; };
template < template<
class T, class T,
int UniqueID = 0, int UniqueID = 0,
bool ShouldDeriveFrom = bool ShouldDeriveFrom =
@@ -57,7 +57,7 @@ public:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template < template<
class T, class T,
int UniqueID int UniqueID
> >

View File

@@ -13,7 +13,7 @@
namespace beast { namespace beast {
namespace detail { namespace detail {
template <class R, class C, class ...A> template<class R, class C, class ...A>
auto auto
is_call_possible_test(C&& c, int, A&& ...a) is_call_possible_test(C&& c, int, A&& ...a)
-> decltype(std::is_convertible< -> decltype(std::is_convertible<
@@ -21,7 +21,7 @@ is_call_possible_test(C&& c, int, A&& ...a)
std::is_same<R, void>::value, std::is_same<R, void>::value,
std::true_type()); std::true_type());
template <class R, class C, class ...A> template<class R, class C, class ...A>
std::false_type std::false_type
is_call_possible_test(C&& c, long, A&& ...a); 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)> is_call_possible<T, void(std::string)>
*/ */
/** @{ */ /** @{ */
template <class C, class F> template<class C, class F>
struct is_call_possible struct is_call_possible
: std::false_type : std::false_type
{ {
}; };
template <class C, class R, class ...A> template<class C, class R, class ...A>
struct is_call_possible<C, R(A...)> struct is_call_possible<C, R(A...)>
: decltype(is_call_possible_test<R>( : decltype(is_call_possible_test<R>(
std::declval<C>(), 1, std::declval<A>()...)) std::declval<C>(), 1, std::declval<A>()...))

View File

@@ -281,10 +281,10 @@ finish(sha1_context& ctx, void* digest) noexcept
ctx.buf[ctx.buflen++] = 0x00; ctx.buf[ctx.buflen++] = 0x00;
std::uint32_t block[BLOCK_INTS]; std::uint32_t block[BLOCK_INTS];
sha1::make_block(ctx.buf, block); sha1::make_block(ctx.buf, block);
if (buflen > BLOCK_BYTES - 8) if(buflen > BLOCK_BYTES - 8)
{ {
sha1::transform(ctx.digest, block); 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; block[i] = 0;
} }

View File

@@ -34,7 +34,7 @@ class has_get_io_service
decltype(std::declval<U>().get_io_service()), decltype(std::declval<U>().get_io_service()),
boost::asio::io_service&>> boost::asio::io_service&>>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
public: public:
using type = decltype(check<T>(0)); using type = decltype(check<T>(0));

View File

@@ -227,7 +227,7 @@ public:
/// Write the given data to the stream. Returns the number of bytes written, /// Write the given data to the stream. Returns the number of bytes written,
/// or 0 if an error occurred. /// or 0 if an error occurred.
template <class ConstBufferSequence> template<class ConstBufferSequence>
std::size_t std::size_t
write_some(ConstBufferSequence const& buffers, write_some(ConstBufferSequence const& buffers,
error_code& ec) error_code& ec)

View File

@@ -34,10 +34,10 @@ namespace beast {
caller is still responsible for freeing memory. caller is still responsible for freeing memory.
*/ */
#if GENERATING_DOCS #if GENERATING_DOCS
template <class T, class CompletionHandler> template<class T, class CompletionHandler>
class handler_alloc; class handler_alloc;
#else #else
template <class T, class CompletionHandler> template<class T, class CompletionHandler>
class handler_alloc class handler_alloc
{ {
private: private:
@@ -46,7 +46,7 @@ private:
// should produce a compile error if CompletionHandler is not // should produce a compile error if CompletionHandler is not
// constructible from H. // constructible from H.
// //
template <class U, class H> template<class U, class H>
friend class handler_alloc; friend class handler_alloc;
CompletionHandler h_; CompletionHandler h_;

View File

@@ -85,7 +85,7 @@ public:
is_continuation(op->d_->h); is_continuation(op->d_->h);
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, read_some_op* op) void asio_handler_invoke(Function&& f, read_some_op* op)
{ {

View File

@@ -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. @tparam N The number of bytes in the internal buffer.

View File

@@ -12,14 +12,14 @@
namespace beast { 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 The implementation uses a sequence of one or more character arrays
of varying sizes. Additional character array objects are appended to of varying sizes. Additional character array objects are appended to
the sequence to accommodate changes in the size of the character the sequence to accommodate changes in the size of the character
sequence. sequence.
@note Meets the requirements of @b `Streambuf`. @note Meets the requirements of @b `DynamicBuffer`.
*/ */
using streambuf = basic_streambuf<std::allocator<char>>; using streambuf = basic_streambuf<std::allocator<char>>;

View File

@@ -105,7 +105,7 @@ protected:
using list_t = typename boost::intrusive::make_list< using list_t = typename boost::intrusive::make_list<
element, boost::intrusive::constant_time_size<false>>::type; 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>, element, boost::intrusive::constant_time_size<true>,
boost::intrusive::compare<less>>::type; boost::intrusive::compare<less>>::type;
@@ -117,7 +117,6 @@ protected:
: set_(std::move(set)) : set_(std::move(set))
, list_(std::move(list)) , list_(std::move(list))
{ {
} }
public: public:
@@ -244,9 +243,10 @@ public:
value. value.
Field names are stored as-is, but comparison are case-insensitive. Field names are stored as-is, but comparison are case-insensitive.
The container preserves the order of insertion of fields with When the container is iterated, the fields are presented in the order
different names. For fields with the same name, the implementation of insertion. For fields with the same name, the container behaves
concatenates values inserted with duplicate names as per rfc7230. as a std::multiset; there will be a separate value for each occurrence
of the field name.
@note Meets the requirements of @b `FieldSequence`. @note Meets the requirements of @b `FieldSequence`.
*/ */
@@ -359,27 +359,44 @@ public:
return set_.size(); return set_.size();
} }
/** Returns `true` if the specified field exists. */ /// Returns `true` if the specified field exists.
bool bool
exists(boost::string_ref const& name) const exists(boost::string_ref const& name) const
{ {
return set_.find(name, less{}) != set_.end(); 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 iterator
find(boost::string_ref const& name) const; 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 boost::string_ref
operator[](boost::string_ref const& name) const; operator[](boost::string_ref const& name) const;
/** Clear the contents of the basic_headers. */ /// Clear the contents of the basic_headers.
void void
clear() noexcept; clear() noexcept;
/** Remove a field. /** 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. @return The number of fields removed.
*/ */
std::size_t std::size_t
@@ -387,39 +404,57 @@ public:
/** Insert a field value. /** Insert a field value.
If a field value already exists the new value will be If a field with the same name already exists, the
extended as per RFC2616 Section 4.2. 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 void
insert(boost::string_ref const& name, boost::string_ref value); insert(boost::string_ref const& name, boost::string_ref value);
/** Insert a field value. /** Insert a field value.
If a field value already exists the new value will be If a field with the same name already exists, the
extended as per RFC2616 Section 4.2. 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> template<class T>
typename std::enable_if< typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type ! std::is_constructible<boost::string_ref, T>::value>::type
insert(boost::string_ref name, T const& value) insert(boost::string_ref name, T const& value)
{ {
insert(name, insert(name, boost::lexical_cast<std::string>(value));
boost::lexical_cast<std::string>(value));
} }
/** Replace a field value. /** Replace a field value.
The current field value, if any, is removed. Then the First removes any values with matching field names, then
specified value is inserted as if by `insert(field, value)`. inserts the new field value.
@param name The name of the field.
@param value A string holding the value of the field.
*/ */
void void
replace(boost::string_ref const& name, boost::string_ref value); replace(boost::string_ref const& name, boost::string_ref value);
/** Replace a field value. /** Replace a field value.
The current field value, if any, is removed. Then the First removes any values with matching field names, then
specified value is inserted as if by `insert(field, value)`. 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> template<class T>
typename std::enable_if< typename std::enable_if<

View File

@@ -482,7 +482,7 @@ private:
std::declval<error_code&>()), std::declval<error_code&>()),
std::true_type{})> std::true_type{})>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<C>(0)); using type = decltype(check<C>(0));
public: public:
@@ -501,7 +501,7 @@ private:
std::declval<error_code&>()), std::declval<error_code&>()),
std::true_type{})> std::true_type{})>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<C>(0)); using type = decltype(check<C>(0));
public: public:
@@ -520,7 +520,7 @@ private:
std::declval<error_code&>()), std::declval<error_code&>()),
std::true_type{})> std::true_type{})>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<C>(0)); using type = decltype(check<C>(0));
public: public:
@@ -539,7 +539,7 @@ private:
std::declval<error_code&>()), std::declval<error_code&>()),
std::true_type{})> std::true_type{})>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<C>(0)); using type = decltype(check<C>(0));
public: public:
@@ -557,7 +557,7 @@ private:
std::declval<error_code&>()), std::declval<error_code&>()),
std::true_type{})> std::true_type{})>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<C>(0)); using type = decltype(check<C>(0));
public: public:
@@ -575,7 +575,7 @@ private:
std::declval<error_code&>()), std::declval<error_code&>()),
std::true_type{})> std::true_type{})>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<C>(0)); using type = decltype(check<C>(0));
public: public:
@@ -594,7 +594,7 @@ private:
std::declval<error_code&>()), std::declval<error_code&>()),
std::true_type{})> std::true_type{})>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<C>(0)); using type = decltype(check<C>(0));
public: public:
@@ -613,7 +613,7 @@ private:
std::declval<error_code&>()), std::declval<error_code&>()),
std::true_type{})> std::true_type{})>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<C>(0)); using type = decltype(check<C>(0));
public: public:
@@ -630,7 +630,7 @@ private:
decltype(std::declval<T>().on_headers( decltype(std::declval<T>().on_headers(
std::declval<std::uint64_t>(), std::declval<error_code&>()))>> std::declval<std::uint64_t>(), std::declval<error_code&>()))>>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<C>(0)); using type = decltype(check<C>(0));
public: public:
@@ -649,7 +649,7 @@ private:
std::declval<error_code&>()), std::declval<error_code&>()),
std::true_type{})> std::true_type{})>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<C>(0)); using type = decltype(check<C>(0));
public: public:
@@ -667,7 +667,7 @@ private:
std::declval<error_code&>()), std::declval<error_code&>()),
std::true_type{})> std::true_type{})>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<C>(0)); using type = decltype(check<C>(0));
public: public:

View File

@@ -29,7 +29,7 @@ class chunk_encode_text
// Storage for the longest hex string we might need, plus delimiters. // Storage for the longest hex string we might need, plus delimiters.
std::array<char, 2 * sizeof(std::size_t) + 2> buf_; std::array<char, 2 * sizeof(std::size_t) + 2> buf_;
template <class OutIter> template<class OutIter>
static static
OutIter OutIter
to_hex(OutIter last, std::size_t n) to_hex(OutIter last, std::size_t n)

View File

@@ -22,7 +22,7 @@ class has_content_length_value
decltype(std::declval<U>().content_length()), decltype(std::declval<U>().content_length()),
std::uint64_t>> std::uint64_t>>
static R check(int); static R check(int);
template <class> template<class>
static std::false_type check(...); static std::false_type check(...);
using type = decltype(check<T>(0)); using type = decltype(check<T>(0));
public: public:

View File

@@ -9,6 +9,7 @@
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP #define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#include <beast/http/detail/rfc7230.hpp> #include <beast/http/detail/rfc7230.hpp>
#include <algorithm>
namespace beast { namespace beast {
namespace http { namespace http {
@@ -204,6 +205,18 @@ basic_headers(FwdIt first, FwdIt last)
insert(first->name(), first->value()); 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> template<class Allocator>
auto auto
basic_headers<Allocator>:: basic_headers<Allocator>::
@@ -221,11 +234,9 @@ boost::string_ref
basic_headers<Allocator>:: basic_headers<Allocator>::
operator[](boost::string_ref const& name) const operator[](boost::string_ref const& name) const
{ {
// VFALCO This none object looks sketchy
static boost::string_ref const none;
auto const it = find(name); auto const it = find(name);
if(it == end()) if(it == end())
return none; return {};
return it->second; return it->second;
} }
@@ -244,15 +255,23 @@ std::size_t
basic_headers<Allocator>:: basic_headers<Allocator>::
erase(boost::string_ref const& name) erase(boost::string_ref const& name)
{ {
auto const it = set_.find(name, less{}); auto it = set_.find(name, less{});
if(it == set_.end()) if(it == set_.end())
return 0; return 0;
auto& e = *it; auto const last = set_.upper_bound(name, less{});
set_.erase(set_.iterator_to(e)); std::size_t n = 1;
list_.erase(list_.iterator_to(e)); for(;;)
alloc_traits::destroy(this->member(), &e); {
alloc_traits::deallocate(this->member(), &e, 1); auto& e = *it++;
return 1; 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> template<class Allocator>
@@ -262,25 +281,10 @@ insert(boost::string_ref const& name,
boost::string_ref value) boost::string_ref value)
{ {
value = detail::trim(value); value = detail::trim(value);
typename set_t::insert_commit_data d; auto const p = alloc_traits::allocate(this->member(), 1);
auto const result = alloc_traits::construct(this->member(), p, name, value);
set_.insert_check(name, less{}, d); set_.insert_before(set_.upper_bound(name, less{}), *p);
if(result.second) list_.push_back(*p);
{
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());
} }
template<class Allocator> template<class Allocator>

View File

@@ -8,6 +8,7 @@
#ifndef BEAST_HTTP_IMPL_MESSAGE_V1_IPP #ifndef BEAST_HTTP_IMPL_MESSAGE_V1_IPP
#define 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/rfc7230.hpp>
#include <beast/http/detail/has_content_length.hpp> #include <beast/http/detail/has_content_length.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
@@ -87,7 +88,11 @@ prepare_content_length(prepare_info& pi,
std::true_type) std::true_type)
{ {
typename Body::writer w(msg); 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(); pi.content_length = w.content_length();
} }

View File

@@ -91,7 +91,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, parse_op* op) void asio_handler_invoke(Function&& f, parse_op* op)
{ {
@@ -277,7 +277,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, read_op* op) void asio_handler_invoke(Function&& f, read_op* op)
{ {
@@ -412,7 +412,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
static_assert(is_ReadableBody<Body>::value, static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met"); "ReadableBody requirements not met");
error_code ec; error_code ec;
read(stream, dynabuf, msg, ec); beast::http::read(stream, dynabuf, msg, ec);
if(ec) if(ec)
throw system_error{ec}; throw system_error{ec};
} }
@@ -431,7 +431,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
static_assert(is_ReadableBody<Body>::value, static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met"); "ReadableBody requirements not met");
parser_v1<isRequest, Body, Headers> p; parser_v1<isRequest, Body, Headers> p;
parse(stream, dynabuf, p, ec); beast::http::parse(stream, dynabuf, p, ec);
if(ec) if(ec)
return; return;
assert(p.complete()); assert(p.complete());

View File

@@ -262,7 +262,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, write_op* op) void asio_handler_invoke(Function&& f, write_op* op)
{ {

View File

@@ -15,6 +15,8 @@ namespace http {
enum class parse_error enum class parse_error
{ {
success = 0,
connection_closed, connection_closed,
bad_method, bad_method,

View File

@@ -35,6 +35,37 @@ struct parser_response
} // detail } // 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. /** A parser for producing HTTP/1 messages.
This class uses the basic HTTP/1 wire format parser to convert This class uses the basic HTTP/1 wire format parser to convert
@@ -62,6 +93,7 @@ private:
std::string value_; std::string value_;
message_type m_; message_type m_;
typename message_type::body_type::reader r_; typename message_type::body_type::reader r_;
std::uint8_t skip_body_ = 0;
public: public:
parser_v1(parser_v1&&) = default; 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. /** Returns the parsed message.
Only valid if `complete()` would return `true`. Only valid if `complete()` would return `true`.
@@ -176,7 +215,7 @@ private:
{ {
flush(); flush();
m_.version = 10 * this->http_major() + this->http_minor(); m_.version = 10 * this->http_major() + this->http_minor();
return 0; return skip_body_;
} }
void on_request(error_code& ec) void on_request(error_code& ec)

View File

@@ -30,6 +30,16 @@ namespace http {
the behavior of the container will be as if a string containing the behavior of the container will be as if a string containing
only characters up to but excluding the first invalid character only characters up to but excluding the first invalid character
was used to construct the list. 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 class param_list
{ {
@@ -98,6 +108,24 @@ public:
the behavior of the container will be as if a string containing the behavior of the container will be as if a string containing
only characters up to but excluding the first invalid character only characters up to but excluding the first invalid character
was used to construct the list. 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 class ext_list
{ {
@@ -181,6 +209,15 @@ public:
the behavior of the container will be as if a string containing the behavior of the container will be as if a string containing
only characters up to but excluding the first invalid character only characters up to but excluding the first invalid character
was used to construct the list. 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 class token_list
{ {

View File

@@ -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

View File

@@ -14,8 +14,8 @@
// BEAST_VERSION / 100 % 1000 is the minor version // BEAST_VERSION / 100 % 1000 is the minor version
// BEAST_VERSION / 100000 is the major 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 #endif

View File

@@ -110,7 +110,7 @@ is_valid(close_code::value code)
if(v >= 1016 && v <= 2999) if(v >= 1016 && v <= 2999)
return false; return false;
// not used // not used
if(v >= 0 && v <= 999) if(v <= 999)
return false; return false;
return true; return true;
} }
@@ -136,12 +136,12 @@ write(DynamicBuffer& db, frame_header const& fh)
if(fh.rsv3) if(fh.rsv3)
b[0] |= 0x10; b[0] |= 0x10;
b[1] = fh.mask ? 0x80 : 0x00; b[1] = fh.mask ? 0x80 : 0x00;
if (fh.len <= 125) if(fh.len <= 125)
{ {
b[1] |= fh.len; b[1] |= fh.len;
n = 2; n = 2;
} }
else if (fh.len <= 65535) else if(fh.len <= 65535)
{ {
b[1] |= 126; b[1] |= 126;
::new(&b[2]) big_uint16_buf_t{ ::new(&b[2]) big_uint16_buf_t{

View File

@@ -113,7 +113,7 @@ mask_inplace_general(
{ {
using boost::asio::buffer_cast; using boost::asio::buffer_cast;
using boost::asio::buffer_size; using boost::asio::buffer_size;
auto n = buffer_size(b); auto const n = buffer_size(b);
auto p = buffer_cast<std::uint8_t*>(b); auto p = buffer_cast<std::uint8_t*>(b);
for(auto i = n / sizeof(key); i; --i) for(auto i = n / sizeof(key); i; --i)
{ {
@@ -122,13 +122,14 @@ mask_inplace_general(
*p ^= (key >>16); ++p; *p ^= (key >>16); ++p;
*p ^= (key >>24); ++p; *p ^= (key >>24); ++p;
} }
n %= sizeof(key); auto const m =
switch(n) static_cast<std::uint8_t>(n % sizeof(key));
switch(m)
{ {
case 3: p[2] ^= (key >>16); case 3: p[2] ^= (key >>16);
case 2: p[1] ^= (key >> 8); case 2: p[1] ^= (key >> 8);
case 1: p[0] ^= key; case 1: p[0] ^= key;
key = ror(key, n*8); key = ror(key, m*8);
default: default:
break; break;
} }
@@ -144,7 +145,7 @@ mask_inplace_general(
{ {
using boost::asio::buffer_cast; using boost::asio::buffer_cast;
using boost::asio::buffer_size; using boost::asio::buffer_size;
auto n = buffer_size(b); auto const n = buffer_size(b);
auto p = buffer_cast<std::uint8_t*>(b); auto p = buffer_cast<std::uint8_t*>(b);
for(auto i = n / sizeof(key); i; --i) for(auto i = n / sizeof(key); i; --i)
{ {
@@ -157,8 +158,9 @@ mask_inplace_general(
*p ^= (key >>48); ++p; *p ^= (key >>48); ++p;
*p ^= (key >>56); ++p; *p ^= (key >>56); ++p;
} }
n %= sizeof(key); auto const m =
switch(n) static_cast<std::uint8_t>(n % sizeof(key));
switch(m)
{ {
case 7: p[6] ^= (key >>16); case 7: p[6] ^= (key >>16);
case 6: p[5] ^= (key >> 8); case 6: p[5] ^= (key >> 8);
@@ -167,7 +169,7 @@ mask_inplace_general(
case 3: p[2] ^= (key >>16); case 3: p[2] ^= (key >>16);
case 2: p[1] ^= (key >> 8); case 2: p[1] ^= (key >> 8);
case 1: p[0] ^= key; case 1: p[0] ^= key;
key = ror(key, n*8); key = ror(key, m*8);
default: default:
break; break;
} }

View File

@@ -131,7 +131,7 @@ utf8_checker_t<_>::write(BufferSequence const& bs)
{ {
using boost::asio::buffer_cast; using boost::asio::buffer_cast;
using boost::asio::buffer_size; using boost::asio::buffer_size;
for (auto const& b : bs) for(auto const& b : bs)
if(! write(buffer_cast<void const*>(b), if(! write(buffer_cast<void const*>(b),
buffer_size(b))) buffer_size(b)))
return false; return false;

View File

@@ -16,8 +16,10 @@ namespace websocket {
/// Error codes returned from @ref stream operations. /// Error codes returned from @ref stream operations.
enum class error enum class error
{ {
success = 0,
/// Both sides performed a WebSocket close /// Both sides performed a WebSocket close
closed = 1, closed,
/// WebSocket connection failed, protocol violation /// WebSocket connection failed, protocol violation
failed, failed,

View File

@@ -101,7 +101,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, accept_op* op) void asio_handler_invoke(Function&& f, accept_op* op)
{ {

View File

@@ -97,7 +97,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, close_op* op) void asio_handler_invoke(Function&& f, close_op* op)
{ {

View File

@@ -93,7 +93,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, handshake_op* op) void asio_handler_invoke(Function&& f, handshake_op* op)
{ {

View File

@@ -95,7 +95,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, ping_op* op) void asio_handler_invoke(Function&& f, ping_op* op)
{ {

View File

@@ -117,7 +117,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, read_frame_op* op) 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; break;
} }
d.state = do_read_fh + 2; d.state = do_read_fh + 2;
if (n == 0) if(n == 0)
{ {
bytes_transferred = 0; bytes_transferred = 0;
break; break;

View File

@@ -88,7 +88,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, read_op* op) void asio_handler_invoke(Function&& f, read_op* op)
{ {

View File

@@ -93,7 +93,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, response_op* op) void asio_handler_invoke(Function&& f, response_op* op)
{ {

View File

@@ -93,7 +93,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, void asio_handler_invoke(Function&& f,
teardown_ssl_op* op) teardown_ssl_op* op)
@@ -129,7 +129,7 @@ operator()(error_code ec, bool again)
template<class AsyncStream> template<class AsyncStream>
void void
teardown( teardown(teardown_tag,
boost::asio::ssl::stream<AsyncStream>& stream, boost::asio::ssl::stream<AsyncStream>& stream,
error_code& ec) error_code& ec)
{ {
@@ -138,7 +138,7 @@ teardown(
template<class AsyncStream, class TeardownHandler> template<class AsyncStream, class TeardownHandler>
void void
async_teardown( async_teardown(teardown_tag,
boost::asio::ssl::stream<AsyncStream>& stream, boost::asio::ssl::stream<AsyncStream>& stream,
TeardownHandler&& handler) TeardownHandler&& handler)
{ {

View File

@@ -920,7 +920,7 @@ build_request(boost::string_ref const& host,
boost::string_ref const& resource, std::string& key) boost::string_ref const& resource, std::string& key)
{ {
http::request_v1<http::empty_body> req; http::request_v1<http::empty_body> req;
req.url = "/"; req.url = { resource.data(), resource.size() };
req.version = 11; req.version = 11;
req.method = "GET"; req.method = "GET";
req.headers.insert("Host", host); req.headers.insert("Host", host);

View File

@@ -80,7 +80,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, void asio_handler_invoke(Function&& f,
teardown_tcp_op* op) teardown_tcp_op* op)
@@ -128,7 +128,7 @@ operator()(error_code ec, std::size_t, bool again)
inline inline
void void
teardown( teardown(teardown_tag,
boost::asio::ip::tcp::socket& socket, boost::asio::ip::tcp::socket& socket,
error_code& ec) error_code& ec)
{ {
@@ -151,7 +151,7 @@ teardown(
template<class TeardownHandler> template<class TeardownHandler>
inline inline
void void
async_teardown( async_teardown(teardown_tag,
boost::asio::ip::tcp::socket& socket, boost::asio::ip::tcp::socket& socket,
TeardownHandler&& handler) TeardownHandler&& handler)
{ {

View File

@@ -134,7 +134,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, write_frame_op* op) void asio_handler_invoke(Function&& f, write_frame_op* op)
{ {

View File

@@ -91,7 +91,7 @@ public:
return op->d_->cont; return op->d_->cont;
} }
template <class Function> template<class Function>
friend friend
void asio_handler_invoke(Function&& f, write_op* op) void asio_handler_invoke(Function&& f, write_op* op)
{ {

View File

@@ -31,7 +31,7 @@ namespace websocket {
*/ */
template<class SyncStream> template<class SyncStream>
void void
teardown( teardown(teardown_tag,
boost::asio::ssl::stream<SyncStream>& stream, boost::asio::ssl::stream<SyncStream>& stream,
error_code& ec); error_code& ec);
@@ -62,7 +62,7 @@ teardown(
template<class AsyncStream, class TeardownHandler> template<class AsyncStream, class TeardownHandler>
inline inline
void void
async_teardown( async_teardown(teardown_tag,
boost::asio::ssl::stream<AsyncStream>& stream, boost::asio::ssl::stream<AsyncStream>& stream,
TeardownHandler&& handler); TeardownHandler&& handler);

View File

@@ -13,8 +13,17 @@
#include <type_traits> #include <type_traits>
namespace beast { namespace beast {
namespace websocket { 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. /** Tear down a connection.
This tears down a connection. The implementation will call This tears down a connection. The implementation will call
@@ -30,7 +39,19 @@ namespace websocket {
*/ */
template<class Socket> template<class Socket>
void 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. /** Start tearing down a connection.
@@ -49,7 +70,8 @@ teardown(Socket& socket, error_code& ec) = delete;
function signature of the handler must be: function signature of the handler must be:
@code void handler( @code void handler(
error_code const& error // result of operation error_code const& error // result of operation
); @endcode );
@endcode
Regardless of whether the asynchronous operation completes Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a 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> template<class Socket, class TeardownHandler>
void 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>.
//------------------------------------------------------------------------------ If you are creating an instance of beast::websocket::stream with your
own user defined type, you must provide an overload of teardown with
/** Tear down a `boost::asio::ip::tcp::socket`. the corresponding signature (including the teardown_tag).
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 static_assert(sizeof(Socket)==-1,
teardown( "Unknown Socket type in async_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);
} // websocket } // websocket
@@ -127,7 +111,7 @@ void
call_teardown(Socket& socket, error_code& ec) call_teardown(Socket& socket, error_code& ec)
{ {
using websocket::teardown; using websocket::teardown;
teardown(socket, ec); teardown(websocket::teardown_tag{}, socket, ec);
} }
template<class Socket, class TeardownHandler> template<class Socket, class TeardownHandler>
@@ -136,12 +120,65 @@ void
call_async_teardown(Socket& socket, TeardownHandler&& handler) call_async_teardown(Socket& socket, TeardownHandler&& handler)
{ {
using websocket::async_teardown; using websocket::async_teardown;
async_teardown(socket, async_teardown(websocket::teardown_tag{}, socket,
std::forward<TeardownHandler>(handler)); std::forward<TeardownHandler>(handler));
} }
} // websocket_helpers } // 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 } // beast
#include <beast/websocket/impl/teardown.ipp> #include <beast/websocket/impl/teardown.ipp>

View File

@@ -37,8 +37,8 @@ elif [[ $(uname -s) == "Linux" ]]; then
num_proc_units=$(nproc) num_proc_units=$(nproc)
# Physical cores # Physical cores
num_jobs=$(lscpu -p | grep -v '^#' | sort -u -t, -k 2,4 | wc -l) num_jobs=$(lscpu -p | grep -v '^#' | sort -u -t, -k 2,4 | wc -l)
if (("$num_proc_units" < "$num_jobs")); then if ((${num_proc_units} < ${num_jobs})); then
num_jobs=$num_proc_units num_jobs=$num_proc_units
fi fi
fi fi
@@ -82,6 +82,16 @@ function build_beast {
-j${num_jobs} -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 { function run_autobahn_test_suite {
# Run autobahn tests # Run autobahn tests
wsecho=$(find bin -name "websocket-echo" | grep /$VARIANT/) wsecho=$(find bin -name "websocket-echo" | grep /$VARIANT/)
@@ -108,7 +118,11 @@ function run_autobahn_test_suite {
##################################### BUILD #################################### ##################################### BUILD ####################################
build_beast if [[ ${BUILD_SYSTEM:-} == cmake ]]; then
build_beast_cmake
else
build_beast
fi
##################################### TESTS #################################### ##################################### TESTS ####################################
@@ -123,6 +137,7 @@ if [[ $VARIANT == "coverage" ]]; then
run_tests_with_valgrind run_tests_with_valgrind
run_autobahn_test_suite run_autobahn_test_suite
else else
echo "skipping autobahn tests for feature branch build"
run_tests run_tests
fi fi

View File

@@ -12,11 +12,32 @@ do
test -x $( type -p ${g}-$GCC_VER ) test -x $( type -p ${g}-$GCC_VER )
ln -sv $(type -p ${g}-$GCC_VER) $HOME/bin/${g} ln -sv $(type -p ${g}-$GCC_VER) $HOME/bin/${g}
done done
for c in clang clang++ llvm-symbolizer
do if [[ -n ${CLANG_VER:-} ]]; then
test -x $( type -p ${c}-$CLANG_VER ) # There are cases where the directory exists, but the exe is not available.
ln -sv $(type -p ${c}-$CLANG_VER) $HOME/bin/${c} # Use this workaround for now.
done 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 # NOTE, changed from PWD -> HOME
export PATH=$HOME/bin:$PATH export PATH=$HOME/bin:$PATH

View File

@@ -1,11 +1,12 @@
# Part of Beast # Part of Beast
GroupSources(extras/beast beast) GroupSources(extras/beast extras)
GroupSources(include/beast beast) GroupSources(include/beast beast)
GroupSources(test "/") GroupSources(test "/")
add_executable (lib-tests add_executable (lib-tests
${BEAST_INCLUDES} ${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
../extras/beast/unit_test/main.cpp ../extras/beast/unit_test/main.cpp
core.cpp core.cpp
http.cpp http.cpp

View File

@@ -33,10 +33,10 @@ unit-test core-tests :
core/streambuf.cpp core/streambuf.cpp
core/to_string.cpp core/to_string.cpp
core/write_dynabuf.cpp core/write_dynabuf.cpp
core/detail/base64.cpp core/base64.cpp
core/detail/empty_base_optimization.cpp core/empty_base_optimization.cpp
core/detail/get_lowest_layer.cpp core/get_lowest_layer.cpp
core/detail/sha1.cpp core/sha1.cpp
; ;
unit-test http-tests : unit-test http-tests :
@@ -56,11 +56,10 @@ unit-test http-tests :
http/reason.cpp http/reason.cpp
http/resume_context.cpp http/resume_context.cpp
http/rfc7230.cpp http/rfc7230.cpp
http/status.cpp
http/streambuf_body.cpp http/streambuf_body.cpp
http/string_body.cpp http/string_body.cpp
http/write.cpp http/write.cpp
http/detail/chunk_encode.cpp http/chunk_encode.cpp
; ;
unit-test bench-tests : unit-test bench-tests :
@@ -76,10 +75,10 @@ unit-test websocket-tests :
websocket/rfc6455.cpp websocket/rfc6455.cpp
websocket/stream.cpp websocket/stream.cpp
websocket/teardown.cpp websocket/teardown.cpp
websocket/detail/frame.cpp websocket/frame.cpp
websocket/detail/mask.cpp websocket/mask.cpp
websocket/detail/stream_base.cpp websocket/stream_base.cpp
websocket/detail/utf8_checker.cpp websocket/utf8_checker.cpp
; ;
exe websocket-echo : exe websocket-echo :

View File

@@ -1,11 +1,12 @@
# Part of Beast # Part of Beast
GroupSources(extras/beast beast) GroupSources(extras/beast extras)
GroupSources(include/beast beast) GroupSources(include/beast beast)
GroupSources(test/core "/") GroupSources(test/core "/")
add_executable (core-tests add_executable (core-tests
${BEAST_INCLUDES} ${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
../../extras/beast/unit_test/main.cpp ../../extras/beast/unit_test/main.cpp
buffer_test.hpp buffer_test.hpp
async_completion.cpp async_completion.cpp
@@ -27,12 +28,12 @@ add_executable (core-tests
streambuf.cpp streambuf.cpp
to_string.cpp to_string.cpp
write_dynabuf.cpp write_dynabuf.cpp
detail/base64.cpp base64.cpp
detail/empty_base_optimization.cpp empty_base_optimization.cpp
detail/get_lowest_layer.cpp get_lowest_layer.cpp
detail/sha1.cpp sha1.cpp
) )
if (NOT WIN32) if (NOT WIN32)
target_link_libraries(core-tests ${Boost_LIBRARIES}) target_link_libraries(core-tests ${Boost_LIBRARIES} Threads::Threads)
endif() endif()

View File

@@ -20,8 +20,8 @@ public:
check (std::string const& in, std::string const& out) check (std::string const& in, std::string const& out)
{ {
auto const encoded = base64_encode (in); auto const encoded = base64_encode (in);
expect (encoded == out); BEAST_EXPECT(encoded == out);
expect (base64_decode (encoded) == in); BEAST_EXPECT(base64_decode (encoded) == in);
} }
void void

View File

@@ -153,10 +153,10 @@ public:
void void
expect_size(std::size_t n, ConstBufferSequence const& buffers) expect_size(std::size_t n, ConstBufferSequence const& buffers)
{ {
expect(test::size_pre(buffers) == n); BEAST_EXPECT(test::size_pre(buffers) == n);
expect(test::size_post(buffers) == n); BEAST_EXPECT(test::size_post(buffers) == n);
expect(test::size_rev_pre(buffers) == n); BEAST_EXPECT(test::size_rev_pre(buffers) == n);
expect(test::size_rev_post(buffers) == n); BEAST_EXPECT(test::size_rev_post(buffers) == n);
} }
template<class U, class V> template<class U, class V>
@@ -173,7 +173,7 @@ public:
using boost::asio::buffer_cast; using boost::asio::buffer_cast;
using boost::asio::buffer_size; using boost::asio::buffer_size;
std::string const s = "Hello, world"; 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 i = 1; i < 12; ++i) {
for(std::size_t x = 1; x < 4; ++x) { for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) { 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(x), buffer(s.data(), x)));
sb.commit(buffer_copy(sb.prepare(y), buffer(s.data()+x, y))); 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))); 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); streambuf sb2(sb);
expect(eq(sb, sb2)); BEAST_EXPECT(eq(sb, sb2));
} }
{ {
streambuf sb2; streambuf sb2;
sb2 = sb; sb2 = sb;
expect(eq(sb, sb2)); BEAST_EXPECT(eq(sb, sb2));
} }
{ {
streambuf sb2(std::move(sb)); streambuf sb2(std::move(sb));
expect(to_string(sb2.data()) == s); BEAST_EXPECT(to_string(sb2.data()) == s);
expect_size(0, sb.data()); expect_size(0, sb.data());
sb = std::move(sb2); sb = std::move(sb2);
expect(to_string(sb.data()) == s); BEAST_EXPECT(to_string(sb.data()) == s);
expect_size(0, sb2.data()); expect_size(0, sb2.data());
} }
self_assign(sb, sb); self_assign(sb, sb);
expect(to_string(sb.data()) == s); BEAST_EXPECT(to_string(sb.data()) == s);
self_assign(sb, std::move(sb)); self_assign(sb, std::move(sb));
expect(to_string(sb.data()) == s); BEAST_EXPECT(to_string(sb.data()) == s);
} }
}}} }}}
try try
@@ -226,16 +226,16 @@ public:
test_allocator<char, false, false, false, false>; test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>; using sb_type = basic_streambuf<alloc_type>;
sb_type sb; sb_type sb;
expect(sb.get_allocator().id() == 1); BEAST_EXPECT(sb.get_allocator().id() == 1);
} }
{ {
using alloc_type = using alloc_type =
test_allocator<char, false, false, false, false>; test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>; using sb_type = basic_streambuf<alloc_type>;
sb_type sb; sb_type sb;
expect(sb.get_allocator().id() == 2); BEAST_EXPECT(sb.get_allocator().id() == 2);
sb_type sb2(sb); sb_type sb2(sb);
expect(sb2.get_allocator().id() == 2); BEAST_EXPECT(sb2.get_allocator().id() == 2);
sb_type sb3(sb, alloc_type{}); sb_type sb3(sb, alloc_type{});
} }
} }
@@ -246,16 +246,16 @@ public:
using boost::asio::buffer_size; using boost::asio::buffer_size;
{ {
streambuf sb(2); streambuf sb(2);
expect(buffer_size(sb.prepare(5)) == 5); BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5);
expect(buffer_size(sb.prepare(8)) == 8); BEAST_EXPECT(buffer_size(sb.prepare(8)) == 8);
expect(buffer_size(sb.prepare(7)) == 7); BEAST_EXPECT(buffer_size(sb.prepare(7)) == 7);
} }
{ {
streambuf sb(2); streambuf sb(2);
sb.prepare(2); sb.prepare(2);
expect(test::buffer_count(sb.prepare(5)) == 2); BEAST_EXPECT(test::buffer_count(sb.prepare(5)) == 2);
expect(test::buffer_count(sb.prepare(8)) == 3); BEAST_EXPECT(test::buffer_count(sb.prepare(8)) == 3);
expect(test::buffer_count(sb.prepare(4)) == 2); BEAST_EXPECT(test::buffer_count(sb.prepare(4)) == 2);
} }
} }
@@ -286,7 +286,7 @@ public:
using boost::asio::buffer_cast; using boost::asio::buffer_cast;
using boost::asio::buffer_size; using boost::asio::buffer_size;
std::string const s = "Hello, world"; 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 i = 1; i < 12; ++i) {
for(std::size_t x = 1; x < 4; ++x) { for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) { for(std::size_t y = 1; y < 4; ++y) {
@@ -298,78 +298,78 @@ public:
streambuf sb(i); streambuf sb(i);
{ {
auto d = sb.prepare(z); auto d = sb.prepare(z);
expect(buffer_size(d) == z); BEAST_EXPECT(buffer_size(d) == z);
} }
{ {
auto d = sb.prepare(0); auto d = sb.prepare(0);
expect(buffer_size(d) == 0); BEAST_EXPECT(buffer_size(d) == 0);
} }
{ {
auto d = sb.prepare(y); auto d = sb.prepare(y);
expect(buffer_size(d) == y); BEAST_EXPECT(buffer_size(d) == y);
} }
{ {
auto d = sb.prepare(x); 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))); sb.commit(buffer_copy(d, buffer(s.data(), x)));
} }
expect(sb.size() == x); BEAST_EXPECT(sb.size() == x);
expect(buffer_size(sb.data()) == sb.size()); BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
{ {
auto d = sb.prepare(x); auto d = sb.prepare(x);
expect(buffer_size(d) == x); BEAST_EXPECT(buffer_size(d) == x);
} }
{ {
auto d = sb.prepare(0); auto d = sb.prepare(0);
expect(buffer_size(d) == 0); BEAST_EXPECT(buffer_size(d) == 0);
} }
{ {
auto d = sb.prepare(z); auto d = sb.prepare(z);
expect(buffer_size(d) == z); BEAST_EXPECT(buffer_size(d) == z);
} }
{ {
auto d = sb.prepare(y); 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(buffer_copy(d, buffer(s.data()+x, y)));
} }
sb.commit(1); sb.commit(1);
expect(sb.size() == x + y); BEAST_EXPECT(sb.size() == x + y);
expect(buffer_size(sb.data()) == sb.size()); BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
{ {
auto d = sb.prepare(x); auto d = sb.prepare(x);
expect(buffer_size(d) == x); BEAST_EXPECT(buffer_size(d) == x);
} }
{ {
auto d = sb.prepare(y); auto d = sb.prepare(y);
expect(buffer_size(d) == y); BEAST_EXPECT(buffer_size(d) == y);
} }
{ {
auto d = sb.prepare(0); auto d = sb.prepare(0);
expect(buffer_size(d) == 0); BEAST_EXPECT(buffer_size(d) == 0);
} }
{ {
auto d = sb.prepare(z); 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(buffer_copy(d, buffer(s.data()+x+y, z)));
} }
sb.commit(2); sb.commit(2);
expect(sb.size() == x + y + z); BEAST_EXPECT(sb.size() == x + y + z);
expect(buffer_size(sb.data()) == sb.size()); BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
expect(to_string(sb.data()) == s); BEAST_EXPECT(to_string(sb.data()) == s);
sb.consume(t); sb.consume(t);
{ {
auto d = sb.prepare(0); 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); 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); sb.consume(v);
expect(to_string(sb.data()) == ""); BEAST_EXPECT(to_string(sb.data()) == "");
sb.consume(1); sb.consume(1);
{ {
auto d = sb.prepare(0); auto d = sb.prepare(0);
expect(buffer_size(d) == 0); BEAST_EXPECT(buffer_size(d) == 0);
} }
} }
}}}}} }}}}}
@@ -387,14 +387,14 @@ public:
sb.prepare(1); sb.prepare(1);
expect_size(3, sb.prepare(3)); expect_size(3, sb.prepare(3));
sb.commit(2); sb.commit(2);
expect(test::buffer_count(sb.data()) == 4); BEAST_EXPECT(test::buffer_count(sb.data()) == 4);
} }
void testOutputStream() void testOutputStream()
{ {
streambuf sb; streambuf sb;
sb << "x"; sb << "x";
expect(to_string(sb.data()) == "x"); BEAST_EXPECT(to_string(sb.data()) == "x");
} }
void testReadSizeHelper() void testReadSizeHelper()
@@ -402,44 +402,44 @@ public:
using boost::asio::buffer_size; using boost::asio::buffer_size;
{ {
streambuf sb(10); streambuf sb(10);
expect(read_size_helper(sb, 0) == 0); BEAST_EXPECT(read_size_helper(sb, 0) == 0);
expect(read_size_helper(sb, 1) == 1); BEAST_EXPECT(read_size_helper(sb, 1) == 1);
expect(read_size_helper(sb, 10) == 10); BEAST_EXPECT(read_size_helper(sb, 10) == 10);
expect(read_size_helper(sb, 20) == 20); BEAST_EXPECT(read_size_helper(sb, 20) == 20);
expect(read_size_helper(sb, 1000) == 512); BEAST_EXPECT(read_size_helper(sb, 1000) == 512);
sb.prepare(3); sb.prepare(3);
sb.commit(3); sb.commit(3);
expect(read_size_helper(sb, 10) == 7); BEAST_EXPECT(read_size_helper(sb, 10) == 7);
expect(read_size_helper(sb, 1000) == 7); BEAST_EXPECT(read_size_helper(sb, 1000) == 7);
} }
{ {
streambuf sb(1000); streambuf sb(1000);
expect(read_size_helper(sb, 0) == 0); BEAST_EXPECT(read_size_helper(sb, 0) == 0);
expect(read_size_helper(sb, 1) == 1); BEAST_EXPECT(read_size_helper(sb, 1) == 1);
expect(read_size_helper(sb, 1000) == 1000); BEAST_EXPECT(read_size_helper(sb, 1000) == 1000);
expect(read_size_helper(sb, 2000) == 1000); BEAST_EXPECT(read_size_helper(sb, 2000) == 1000);
sb.prepare(3); sb.prepare(3);
expect(read_size_helper(sb, 0) == 0); BEAST_EXPECT(read_size_helper(sb, 0) == 0);
expect(read_size_helper(sb, 1) == 1); BEAST_EXPECT(read_size_helper(sb, 1) == 1);
expect(read_size_helper(sb, 1000) == 1000); BEAST_EXPECT(read_size_helper(sb, 1000) == 1000);
expect(read_size_helper(sb, 2000) == 1000); BEAST_EXPECT(read_size_helper(sb, 2000) == 1000);
sb.commit(3); sb.commit(3);
expect(read_size_helper(sb, 0) == 0); BEAST_EXPECT(read_size_helper(sb, 0) == 0);
expect(read_size_helper(sb, 1) == 1); BEAST_EXPECT(read_size_helper(sb, 1) == 1);
expect(read_size_helper(sb, 1000) == 997); BEAST_EXPECT(read_size_helper(sb, 1000) == 997);
expect(read_size_helper(sb, 2000) == 997); BEAST_EXPECT(read_size_helper(sb, 2000) == 997);
sb.consume(2); sb.consume(2);
expect(read_size_helper(sb, 0) == 0); BEAST_EXPECT(read_size_helper(sb, 0) == 0);
expect(read_size_helper(sb, 1) == 1); BEAST_EXPECT(read_size_helper(sb, 1) == 1);
expect(read_size_helper(sb, 1000) == 997); BEAST_EXPECT(read_size_helper(sb, 1000) == 997);
expect(read_size_helper(sb, 2000) == 997); BEAST_EXPECT(read_size_helper(sb, 2000) == 997);
} }
{ {
streambuf sb(2); streambuf sb(2);
expect(test::buffer_count(sb.prepare(2)) == 1); BEAST_EXPECT(test::buffer_count(sb.prepare(2)) == 1);
expect(test::buffer_count(sb.prepare(3)) == 2); BEAST_EXPECT(test::buffer_count(sb.prepare(3)) == 2);
expect(buffer_size(sb.prepare(5)) == 5); BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5);
expect(read_size_helper(sb, 10) == 6); BEAST_EXPECT(read_size_helper(sb, 10) == 6);
} }
} }

View File

@@ -47,32 +47,32 @@ public:
std::list<const_buffer> b6; std::list<const_buffer> b6;
auto bs = buffer_cat( auto bs = buffer_cat(
b1, b2, b3, b4, b5, b6); b1, b2, b3, b4, b5, b6);
expect(buffer_size(bs) == 10); BEAST_EXPECT(buffer_size(bs) == 10);
std::vector<const_buffer> v; std::vector<const_buffer> v;
for(auto iter = make_reverse_iterator(bs.end()); for(auto iter = make_reverse_iterator(bs.end());
iter != make_reverse_iterator(bs.begin()); ++iter) iter != make_reverse_iterator(bs.begin()); ++iter)
v.emplace_back(*iter); v.emplace_back(*iter);
expect(buffer_size(bs) == 10); BEAST_EXPECT(buffer_size(bs) == 10);
decltype(bs) bs2(bs); decltype(bs) bs2(bs);
auto bs3(std::move(bs)); auto bs3(std::move(bs));
bs = bs2; bs = bs2;
bs3 = std::move(bs2); bs3 = std::move(bs2);
{ {
boost::asio::streambuf sb1, sb2; boost::asio::streambuf sb1, sb2;
expect(buffer_size(buffer_cat( BEAST_EXPECT(buffer_size(buffer_cat(
sb1.prepare(5), sb2.prepare(7))) == 12); sb1.prepare(5), sb2.prepare(7))) == 12);
sb1.commit(5); sb1.commit(5);
sb2.commit(7); sb2.commit(7);
expect(buffer_size(buffer_cat( BEAST_EXPECT(buffer_size(buffer_cat(
sb1.data(), sb2.data())) == 12); sb1.data(), sb2.data())) == 12);
} }
for(auto it = bs.begin(); it != bs.end(); ++it) for(auto it = bs.begin(); it != bs.end(); ++it)
{ {
decltype(bs)::const_iterator copy; decltype(bs)::const_iterator copy;
copy = it; copy = it;
expect(copy == it); BEAST_EXPECT(copy == it);
copy = copy; copy = copy;
expect(copy == it); BEAST_EXPECT(copy == it);
} }
} }
@@ -132,11 +132,11 @@ public:
pass(); pass();
} }
auto bs2 = bs; auto bs2 = bs;
expect(bs.begin() != bs2.begin()); BEAST_EXPECT(bs.begin() != bs2.begin());
expect(bs.end() != bs2.end()); BEAST_EXPECT(bs.end() != bs2.end());
decltype(bs)::const_iterator it; decltype(bs)::const_iterator it;
decltype(bs2)::const_iterator it2; decltype(bs2)::const_iterator it2;
expect(it == it2); BEAST_EXPECT(it == it2);
} }
void run() override void run() override

View File

@@ -43,7 +43,7 @@ public:
using boost::asio::mutable_buffer; using boost::asio::mutable_buffer;
char buf[12]; char buf[12];
std::string const s = "Hello, world"; 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 i = 1; i < 4; ++i) {
for(std::size_t j = 1; j < 4; ++j) { for(std::size_t j = 1; j < 4; ++j) {
for(std::size_t x = 1; x < 4; ++x) { for(std::size_t x = 1; x < 4; ++x) {
@@ -60,83 +60,83 @@ public:
mutable_buffer{&buf[i], j}, mutable_buffer{&buf[i], j},
mutable_buffer{&buf[i+j], k}}}; mutable_buffer{&buf[i+j], k}}};
buffers_adapter<decltype(bs)> ba(std::move(bs)); 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); auto d = ba.prepare(z);
expect(buffer_size(d) == z); BEAST_EXPECT(buffer_size(d) == z);
} }
{ {
auto d = ba.prepare(0); auto d = ba.prepare(0);
expect(buffer_size(d) == 0); BEAST_EXPECT(buffer_size(d) == 0);
} }
{ {
auto d = ba.prepare(y); auto d = ba.prepare(y);
expect(buffer_size(d) == y); BEAST_EXPECT(buffer_size(d) == y);
} }
{ {
auto d = ba.prepare(x); 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))); ba.commit(buffer_copy(d, buffer(s.data(), x)));
} }
expect(ba.size() == x); BEAST_EXPECT(ba.size() == x);
expect(ba.max_size() == sizeof(buf) - x); BEAST_EXPECT(ba.max_size() == sizeof(buf) - x);
expect(buffer_size(ba.data()) == ba.size()); BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
{ {
auto d = ba.prepare(x); auto d = ba.prepare(x);
expect(buffer_size(d) == x); BEAST_EXPECT(buffer_size(d) == x);
} }
{ {
auto d = ba.prepare(0); auto d = ba.prepare(0);
expect(buffer_size(d) == 0); BEAST_EXPECT(buffer_size(d) == 0);
} }
{ {
auto d = ba.prepare(z); auto d = ba.prepare(z);
expect(buffer_size(d) == z); BEAST_EXPECT(buffer_size(d) == z);
} }
{ {
auto d = ba.prepare(y); 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(buffer_copy(d, buffer(s.data()+x, y)));
} }
ba.commit(1); ba.commit(1);
expect(ba.size() == x + y); BEAST_EXPECT(ba.size() == x + y);
expect(ba.max_size() == sizeof(buf) - (x + y)); BEAST_EXPECT(ba.max_size() == sizeof(buf) - (x + y));
expect(buffer_size(ba.data()) == ba.size()); BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
{ {
auto d = ba.prepare(x); auto d = ba.prepare(x);
expect(buffer_size(d) == x); BEAST_EXPECT(buffer_size(d) == x);
} }
{ {
auto d = ba.prepare(y); auto d = ba.prepare(y);
expect(buffer_size(d) == y); BEAST_EXPECT(buffer_size(d) == y);
} }
{ {
auto d = ba.prepare(0); 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(buffer_copy(d, buffer(s.data()+x+y, z)));
} }
ba.commit(2); ba.commit(2);
expect(ba.size() == x + y + z); BEAST_EXPECT(ba.size() == x + y + z);
expect(ba.max_size() == 0); BEAST_EXPECT(ba.max_size() == 0);
expect(buffer_size(ba.data()) == ba.size()); BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
expect(to_string(ba.data()) == s); BEAST_EXPECT(to_string(ba.data()) == s);
ba.consume(t); ba.consume(t);
{ {
auto d = ba.prepare(0); 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); 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); ba.consume(v);
expect(to_string(ba.data()) == ""); BEAST_EXPECT(to_string(ba.data()) == "");
ba.consume(1); ba.consume(1);
{ {
auto d = ba.prepare(0); auto d = ba.prepare(0);
expect(buffer_size(d) == 0); BEAST_EXPECT(buffer_size(d) == 0);
} }
try try
{ {
@@ -158,9 +158,9 @@ public:
sb_type sb; sb_type sb;
buffers_adapter< buffers_adapter<
sb_type::mutable_buffers_type> ba(sb.prepare(3)); 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); ba.commit(2);
expect(buffer_size(ba.data()) == 2); BEAST_EXPECT(buffer_size(ba.data()) == 2);
} }
{ {
using sb_type = beast::streambuf; using sb_type = beast::streambuf;
@@ -168,13 +168,13 @@ public:
sb.prepare(3); sb.prepare(3);
buffers_adapter< buffers_adapter<
sb_type::mutable_buffers_type> ba(sb.prepare(8)); 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); ba.commit(2);
expect(buffer_size(ba.data()) == 2); BEAST_EXPECT(buffer_size(ba.data()) == 2);
ba.consume(1); ba.consume(1);
ba.commit(6); ba.commit(6);
ba.consume(2); ba.consume(2);
expect(buffer_size(ba.data()) == 5); BEAST_EXPECT(buffer_size(ba.data()) == 5);
ba.consume(5); ba.consume(5);
} }
} }

View File

@@ -31,10 +31,10 @@ public:
void void
expect_size(std::size_t n, ConstBufferSequence const& buffers) expect_size(std::size_t n, ConstBufferSequence const& buffers)
{ {
expect(test::size_pre(buffers) == n); BEAST_EXPECT(test::size_pre(buffers) == n);
expect(test::size_post(buffers) == n); BEAST_EXPECT(test::size_post(buffers) == n);
expect(test::size_rev_pre(buffers) == n); BEAST_EXPECT(test::size_rev_pre(buffers) == n);
expect(test::size_rev_post(buffers) == n); BEAST_EXPECT(test::size_rev_post(buffers) == n);
} }
void testMatrix() void testMatrix()
@@ -43,9 +43,9 @@ public:
using boost::asio::const_buffer; using boost::asio::const_buffer;
char buf[12]; char buf[12];
std::string const s = "Hello, world"; std::string const s = "Hello, world";
expect(s.size() == sizeof(buf)); BEAST_EXPECT(s.size() == sizeof(buf));
buffer_copy(buffer(buf), buffer(s)); 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 i = 1; i < 4; ++i) {
for(std::size_t j = 1; j < 4; ++j) { for(std::size_t j = 1; j < 4; ++j) {
for(std::size_t x = 1; x < 4; ++x) { for(std::size_t x = 1; x < 4; ++x) {
@@ -58,24 +58,24 @@ public:
const_buffer{&buf[i], j}, const_buffer{&buf[i], j},
const_buffer{&buf[i+j], k}}}; const_buffer{&buf[i+j], k}}};
consuming_buffers<decltype(bs)> cb(bs); consuming_buffers<decltype(bs)> cb(bs);
expect(to_string(cb) == s); BEAST_EXPECT(to_string(cb) == s);
expect_size(s.size(), cb); expect_size(s.size(), cb);
cb.consume(0); cb.consume(0);
expect(eq(cb, consumed_buffers(bs, 0))); BEAST_EXPECT(eq(cb, consumed_buffers(bs, 0)));
expect(to_string(cb) == s); BEAST_EXPECT(to_string(cb) == s);
expect_size(s.size(), cb); expect_size(s.size(), cb);
cb.consume(x); cb.consume(x);
expect(to_string(cb) == s.substr(x)); BEAST_EXPECT(to_string(cb) == s.substr(x));
expect(eq(cb, consumed_buffers(bs, x))); BEAST_EXPECT(eq(cb, consumed_buffers(bs, x)));
cb.consume(y); cb.consume(y);
expect(to_string(cb) == s.substr(x+y)); BEAST_EXPECT(to_string(cb) == s.substr(x+y));
expect(eq(cb, consumed_buffers(bs, x+y))); BEAST_EXPECT(eq(cb, consumed_buffers(bs, x+y)));
cb.consume(z); cb.consume(z);
expect(to_string(cb) == ""); BEAST_EXPECT(to_string(cb) == "");
expect(eq(cb, consumed_buffers(bs, x+y+z))); BEAST_EXPECT(eq(cb, consumed_buffers(bs, x+y+z)));
cb.consume(1); cb.consume(1);
expect(to_string(cb) == ""); BEAST_EXPECT(to_string(cb) == "");
expect(eq(cb, consumed_buffers(bs, x+y+z))); BEAST_EXPECT(eq(cb, consumed_buffers(bs, x+y+z)));
} }
}}}} }}}}
} }
@@ -87,10 +87,10 @@ public:
using boost::asio::null_buffers; using boost::asio::null_buffers;
consuming_buffers<null_buffers> cb( consuming_buffers<null_buffers> cb(
null_buffers{}); null_buffers{});
expect(buffer_size(cb) == 0); BEAST_EXPECT(buffer_size(cb) == 0);
consuming_buffers<null_buffers> cb2( consuming_buffers<null_buffers> cb2(
null_buffers{}); null_buffers{});
expect(buffer_copy(cb2, cb) == 0); BEAST_EXPECT(buffer_copy(cb2, cb) == 0);
} }
void testIterator() void testIterator()
@@ -101,7 +101,7 @@ public:
std::size_t n = 0; std::size_t n = 0;
for(auto it = cb.end(); it != cb.begin(); --it) for(auto it = cb.end(); it != cb.begin(); --it)
++n; ++n;
expect(n == 3); BEAST_EXPECT(n == 3);
} }
void run() override void run() override

View File

@@ -32,8 +32,8 @@ public:
dynabuf_readstream<socket_type, streambuf> srs(ios); dynabuf_readstream<socket_type, streambuf> srs(ios);
dynabuf_readstream<socket_type, streambuf> srs2(std::move(srs)); dynabuf_readstream<socket_type, streambuf> srs2(std::move(srs));
srs = std::move(srs2); srs = std::move(srs2);
expect(&srs.get_io_service() == &ios); BEAST_EXPECT(&srs.get_io_service() == &ios);
expect(&srs.get_io_service() == &srs2.get_io_service()); BEAST_EXPECT(&srs.get_io_service() == &srs2.get_io_service());
} }
{ {
socket_type sock(ios); socket_type sock(ios);
@@ -63,11 +63,11 @@ public:
boost::asio::read(srs, buffer(&s[0], s.size()), ec); boost::asio::read(srs, buffer(&s[0], s.size()), ec);
if(! ec) if(! ec)
{ {
expect(s == "Hello, world!"); BEAST_EXPECT(s == "Hello, world!");
break; break;
} }
} }
expect(n < limit); BEAST_EXPECT(n < limit);
for(n = 0; n < limit; ++n) for(n = 0; n < limit; ++n)
{ {
@@ -82,11 +82,11 @@ public:
boost::asio::read(srs, buffer(&s[0], s.size()), ec); boost::asio::read(srs, buffer(&s[0], s.size()), ec);
if(! ec) if(! ec)
{ {
expect(s == "Hello, world!"); BEAST_EXPECT(s == "Hello, world!");
break; break;
} }
} }
expect(n < limit); BEAST_EXPECT(n < limit);
for(n = 0; n < limit; ++n) for(n = 0; n < limit; ++n)
{ {
@@ -101,11 +101,11 @@ public:
srs, buffer(&s[0], s.size()), do_yield[ec]); srs, buffer(&s[0], s.size()), do_yield[ec]);
if(! ec) if(! ec)
{ {
expect(s == "Hello, world!"); BEAST_EXPECT(s == "Hello, world!");
break; break;
} }
} }
expect(n < limit); BEAST_EXPECT(n < limit);
for(n = 0; n < limit; ++n) for(n = 0; n < limit; ++n)
{ {
@@ -121,11 +121,11 @@ public:
srs, buffer(&s[0], s.size()), do_yield[ec]); srs, buffer(&s[0], s.size()), do_yield[ec]);
if(! ec) if(! ec)
{ {
expect(s == "Hello, world!"); BEAST_EXPECT(s == "Hello, world!");
break; break;
} }
} }
expect(n < limit); BEAST_EXPECT(n < limit);
} }
void run() override void run() override

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