Add 'src/beast/' from commit '2f9a8440c2432d8a196571d6300404cb76314125'

git-subtree-dir: src/beast
git-subtree-mainline: 7c90b9ef88
git-subtree-split: 2f9a8440c2
This commit is contained in:
Vinnie Falco
2016-09-15 15:07:45 -04:00
251 changed files with 55530 additions and 0 deletions

12
src/beast/.gitattributes vendored Normal file
View File

@@ -0,0 +1,12 @@
# Set default behaviour, in case users don't have core.autocrlf set.
* text=auto
# Github
.md text eol=lf
# Visual Studio
*.sln text eol=crlf
*.vcproj text eol=crlf
*.vcxproj text eol=crlf
*.props text eol=crlf
*.filters text eol=crlf

2
src/beast/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
bin/
bin64/

87
src/beast/.travis.yml Normal file
View File

@@ -0,0 +1,87 @@
language: cpp
env:
global:
- LLVM_VERSION=3.8.0
# Maintenance note: to move to a new version
# of boost, update both BOOST_ROOT and BOOST_URL.
# Note that for simplicity, BOOST_ROOT's final
# namepart must match the folder name internal
# to boost's .tar.gz.
- LCOV_ROOT=$HOME/lcov
- VALGRIND_ROOT=$HOME/valgrind-install
- BOOST_ROOT=$HOME/boost_1_60_0
- BOOST_URL='http://downloads.sourceforge.net/project/boost/boost/1.60.0/boost_1_60_0.tar.gz?r=https%3A%2F%2Fsourceforge.net%2Fprojects%2Fboost%2Ffiles%2Fboost%2F1.60.0%2Fboost_1_60_0.tar.gz&ts=1460417589&use_mirror=netix'
packages: &gcc5_pkgs
- gcc-5
- g++-5
- python-software-properties
- libssl-dev
- libffi-dev
- libstdc++6
- binutils-gold
# Provides a backtrace if the unittests crash
- gdb
# Needed for installing valgrind
- subversion
- automake
- autotools-dev
- libc6-dbg
matrix:
include:
# GCC/Coverage/Autobahn
- compiler: gcc
env:
- GCC_VER=5
- VARIANT=coverage
- ADDRESS_MODEL=64
- BUILD_SYSTEM=cmake
- PATH=$PWD/cmake/bin:$PATH
addons: &ao_gcc5
apt:
sources: ['ubuntu-toolchain-r-test']
packages: *gcc5_pkgs
# Clang/UndefinedBehaviourSanitizer
- compiler: clang
env:
- GCC_VER=5
- VARIANT=usan
- CLANG_VER=3.8
- ADDRESS_MODEL=64
- UBSAN_OPTIONS='print_stacktrace=1'
- BUILD_SYSTEM=cmake
- PATH=$PWD/cmake/bin:$PATH
- PATH=$PWD/llvm-$LLVM_VERSION/bin:$PATH
addons: *ao_gcc5
# Clang/AddressSanitizer
- compiler: clang
env:
- GCC_VER=5
- VARIANT=asan
- CLANG_VER=3.8
- ADDRESS_MODEL=64
- PATH=$PWD/llvm-$LLVM_VERSION/bin:$PATH
addons: *ao_gcc5
cache:
directories:
- $BOOST_ROOT
- $VALGRIND_ROOT
- llvm-$LLVM_VERSION
- cmake
before_install:
- scripts/install-dependencies.sh
script:
- scripts/build-and-test.sh
after_script:
- cat nohup.out || echo "nohup.out already deleted"
notifications:
email:
false

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

84
src/beast/CMakeLists.txt Normal file
View File

@@ -0,0 +1,84 @@
# Part of Beast
cmake_minimum_required (VERSION 3.2)
project (Beast)
set_property (GLOBAL PROPERTY USE_FOLDERS ON)
if (WIN32)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4 /wd4100 /bigobj /D _WIN32_WINNT=0x0601 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
else()
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
find_package(Boost REQUIRED COMPONENTS coroutine context thread filesystem program_options system)
include_directories(${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIR})
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads)
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wpedantic")
endif()
if ("${VARIANT}" STREQUAL "coverage")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
set(CMAKE_BUILD_TYPE RELWITHDEBINFO)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov")
elseif ("${VARIANT}" STREQUAL "asan")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
set(CMAKE_BUILD_TYPE RELWITHDEBINFO)
elseif ("${VARIANT}" STREQUAL "usan")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-omit-frame-pointer")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
set(CMAKE_BUILD_TYPE RELWITHDEBINFO)
elseif ("${VARIANT}" STREQUAL "debug")
set(CMAKE_BUILD_TYPE DEBUG)
elseif ("${VARIANT}" STREQUAL "release")
set(CMAKE_BUILD_TYPE RELEASE)
endif()
function(DoGroupSources curdir rootdir folder)
file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*)
foreach(child ${children})
if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${curdir}/${child})
DoGroupSources(${curdir}/${child} ${rootdir} ${folder})
elseif(${child} STREQUAL "CMakeLists.txt")
source_group("" FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
else()
string(REGEX REPLACE ^${rootdir} ${folder} groupname ${curdir})
#set(groupname ${curdir})
string(REPLACE "/" "\\" groupname ${groupname})
source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
endif()
endforeach()
endfunction()
function(GroupSources curdir folder)
DoGroupSources(${curdir} ${curdir} ${folder})
endfunction()
include_directories (extras)
include_directories (include)
file(GLOB_RECURSE BEAST_INCLUDES
${PROJECT_SOURCE_DIR}/include/beast/*.hpp
${PROJECT_SOURCE_DIR}/include/beast/*.ipp
)
file(GLOB_RECURSE EXTRAS_INCLUDES
${PROJECT_SOURCE_DIR}/extras/beast/*.hpp
${PROJECT_SOURCE_DIR}/extras/beast/*.ipp
)
add_subdirectory (examples)
add_subdirectory (test)
add_subdirectory (test/core)
add_subdirectory (test/http)
add_subdirectory (test/websocket)

127
src/beast/Jamroot Normal file
View File

@@ -0,0 +1,127 @@
#
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
import os ;
import feature ;
import boost ;
boost.use-project ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = QNXNTO
{
lib socket ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
if [ os.name ] = NT
{
lib ssl : : <name>ssleay32 ;
lib crypto : : <name>libeay32 ;
}
else
{
lib ssl ;
lib crypto ;
}
variant coverage
:
debug
:
<cxxflags>"-fprofile-arcs -ftest-coverage"
<linkflags>"-lgcov"
;
variant asan
:
release
:
<cxxflags>"-fsanitize=address -fno-omit-frame-pointer"
<linkflags>"-fsanitize=address"
;
variant msan
:
debug
:
<cxxflags>"-fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 -fsanitize-memory-use-after-dtor"
<linkflags>"-fsanitize=memory"
;
variant usan
:
debug
:
<cxxflags>"-fsanitize=undefined -fno-omit-frame-pointer"
<linkflags>"-fsanitize=undefined"
;
project beast
: requirements
<include>.
<include>./extras
<include>./include
#<use>/boost//headers
<library>/boost/system//boost_system
<library>/boost/coroutine//boost_coroutine
<library>/boost/filesystem//boost_filesystem
<library>/boost/program_options//boost_program_options
# <library>ssl
# <library>crypto
<define>BOOST_ALL_NO_LIB=1
<define>BOOST_SYSTEM_NO_DEPRECATED=1
<threading>multi
<link>static
<runtime-link>shared
<debug-symbols>on
<toolset>gcc:<cxxflags>-std=c++11
<toolset>gcc:<cxxflags>-Wno-unused-variable
<toolset>clang:<cxxflags>-std=c++11
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
<toolset>msvc:<cxxflags>-bigobj
<os>LINUX:<define>_XOPEN_SOURCE=600
<os>LINUX:<define>_GNU_SOURCE=1
<os>SOLARIS:<define>_XOPEN_SOURCE=500
<os>SOLARIS:<define>__EXTENSIONS__
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0601
<os>NT,<toolset>cw:<library>ws2_32
<os>NT,<toolset>cw:<library>mswsock
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>QNXNTO:<library>socket
<os>HAIKU:<library>network
: usage-requirements
<include>.
:
build-dir bin
;
build-project test ;
build-project examples ;

23
src/beast/LICENSE_1_0.txt Normal file
View File

@@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

229
src/beast/README.md Normal file
View File

@@ -0,0 +1,229 @@
<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]
(https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) [![codecov]
(https://codecov.io/gh/vinniefalco/Beast/branch/master/graph/badge.svg)](https://codecov.io/gh/vinniefalco/Beast) [![coveralls]
(https://coveralls.io/repos/github/vinniefalco/Beast/badge.svg?branch=master)](https://coveralls.io/github/vinniefalco/Beast?branch=master) [![Documentation]
(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)
# HTTP and WebSocket implementations built on Boost.Asio
---
## CppCon 2016
I will be giving a lightning talk on Beast at CppCon 2016 in Bellevue,
Washington from September 18 to September 22. If you'd like to meet me
and hear the talk or ask questions about Beast feel free to approach
me in person or send me an email at vinnie.falco@gmail.com to schedule
some time.
About CppCon 2016:
http://cppcon.org
---
## Contents
- [Introduction](#introduction)
- [Description](#description)
- [Requirements](#requirements)
- [Building](#building)
- [Usage](#usage)
- [Licence](#licence)
- [Contact](#contact)
## Introduction
Beast is a header-only, cross-platform C++ library built on Boost.Asio and
Boost, containing two modules implementing widely used network protocols.
Beast.HTTP offers a universal model for describing, sending, and receiving
HTTP messages while Beast.WebSocket provides a complete implementation of
the WebSocket protocol. Their design achieves these goals:
* **Symmetry.** Interfaces are role-agnostic; the same interfaces can be
used to build clients, servers, or both.
* **Ease of Use.** HTTP messages are modeled using simple, readily
accessible objects. Functions and classes used to send and receive HTTP
or WebSocket messages are designed to resemble Boost.Asio as closely as
possible. Users familiar with Boost.Asio will be immediately comfortable
using this library.
* **Flexibility.** Interfaces do not mandate specific implementation
strategies; important decisions such as buffer or thread management are
left to users of the library.
* **Performance.** The implementation performs competitively, making it a
realistic choice for building high performance network servers.
* **Scalability.** Development of network applications that scale to thousands
of concurrent connections is possible with the implementation.
* **Basis for further abstraction.** The interfaces facilitate the
development of other libraries that provide higher levels of abstraction.
Beast is used in [rippled](https://github.com/ripple/rippled), an
open source server application that implements a decentralized
cryptocurrency system.
## Description
This software is currently in beta: interfaces may change.
For recent changes see the [CHANGELOG](CHANGELOG.md).
The library has been submitted to the
[Boost Library Incubator](http://rrsd.com/blincubator.com/bi_library/beast-2/?gform_post_id=1579)
* [Project Site](http://vinniefalco.github.io/)
* [Repository](https://github.com/vinniefalco/Beast)
* [Project Documentation](http://vinniefalco.github.io/beast/)
* [Autobahn.testsuite results](http://vinniefalco.github.io/autobahn/index.html)
## Requirements
* Boost 1.58 or higher
* C++11 or greater
* OpenSSL (optional)
## Building
Beast is header-only so there are no libraries to build or link with.
To use Beast in your project, simply copy the Beast sources to your
project's source tree (alternatively, bring Beast into your Git repository
using the `git subtree` or `git submodule` commands). Then, edit your
build scripts to add the `include/` directory to the list of paths checked
by the C++ compiler when searching for includes. Beast `#include` lines
will look like this:
```
#include <beast/http.hpp>
#include <beast/websocket.hpp>
```
To link your program successfully, you'll need to add the Boost.System
library to link with. If you use coroutines you'll also need the
Boost.Coroutine library. Please visit the Boost documentation for
instructions on how to do this for your particular build system.
For the examples and tests, Beast provides build scripts for Boost.Build (bjam)
and CMake. Developers using Microsoft Visual Studio can generate Visual Studio
project files by executing these commands from the root of the repository:
```
cd bin
cmake .. # for 32-bit Windows build
cd ../bin64
cmake .. # for Linux/Mac builds, OR
cmake -G"Visual Studio 14 2015 Win64" .. # for 64-bit Windows builds
```
To build with Boost.Build, it is necessary to have the bjam executable
in your path. And bjam needs to know how to find the Boost sources. The
easiest way to do this is make sure that the version of bjam in your path
is the one at the root of the Boost source tree, which is built when
running `bootstrap.sh` (or `bootstrap.bat` on Windows).
Once bjam is in your path, simply run bjam in the root of the Beast
repository to automatically build the required Boost libraries if they
are not already built, build the examples, then build and run the unit
tests.
The files in the repository are laid out thusly:
```
./
bin/ Holds executables and project files
bin64/ Holds 64-bit Windows executables and project files
include/ Add this to your compiler includes
beast/
extras/ Additional APIs, may change
examples/ Self contained example programs
test/ Unit tests and benchmarks
```
## Usage
These examples are complete, self-contained programs that you can build
and run yourself (they are in the `examples` directory).
Example WebSocket program:
```C++
#include <beast/core/to_string.hpp>
#include <beast/websocket.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
int main()
{
// Normal boost::asio setup
std::string const host = "echo.websocket.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
// WebSocket connect and send message using beast
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
ws.handshake(host, "/");
ws.write(boost::asio::buffer("Hello, world!"));
// Receive WebSocket message, print and close using beast
beast::streambuf sb;
beast::websocket::opcode op;
ws.read(op, sb);
ws.close(beast::websocket::close_code::normal);
std::cout << to_string(sb.data()) << "\n";
}
```
Example HTTP program:
```C++
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
int main()
{
// Normal boost::asio setup
std::string const host = "boost.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
// Send HTTP request using beast
beast::http::request_v1<beast::http::empty_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast");
beast::http::prepare(req);
beast::http::write(sock, req);
// Receive and print HTTP response using beast
beast::streambuf sb;
beast::http::response_v1<beast::http::streambuf_body> resp;
beast::http::read(sock, sb, resp);
std::cout << resp;
}
```
## License
Distributed under the Boost Software License, Version 1.0.
(See accompanying file [LICENSE_1_0.txt](LICENSE_1_0.txt) or copy at
http://www.boost.org/LICENSE_1_0.txt)
## Contact
Please report issues or questions here:
https://github.com/vinniefalco/Beast/issues

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

@@ -0,0 +1,56 @@
* Add writer::prepare(msg&) interface to set Content-Type
Boost.Http
* Use enum instead of bool in isRequest
Docs:
* Include Example program listings in the docs
* Fix index in docs
* melpon sandbox?
* Implement cleanup-param to remove spaces around template arguments
e.g. in basic_streambuf move constructor members
* Don't put using namespace at file scope in examples,
do something like "using ba = boost::asio" instead.
Core:
* Replace Jamroot with Jamfile
* Fix bidirectional buffers iterators operator->()
* Complete allocator testing in basic_streambuf
WebSocket:
* more invokable unit test coverage
* More control over the HTTP request and response during handshakes
* optimized versions of key/masking, choose prepared_key size
* Give callers control over the http request/response used during handshake
* Investigate poor autobahn results in Debug builds
* Fall through composed operation switch cases
* Use close_code::no_code instead of close_code::none
* Make request_type, response_type public APIs,
use in stream member function signatures
HTTP:
* Define Parser concept in HTTP
- Need parse version of read() so caller can set parser options
like maximum size of headers, maximum body size, etc
* add bool should_close(message_v1 const&) to replace the use
of eof return value from write and async_write
* More fine grained parser errors
* HTTP parser size limit with test (configurable?)
* HTTP parser trailers with test
* Decode chunk encoding parameters
* URL parser, strong URL character checking in HTTP parser
* Fix prepare() calling content_length() without init()
* Complete allocator testing in basic_streambuf, basic_headers
* Custom HTTP error codes for various situations
* Branch prediction hints in parser
* Check basic_parser_v1 against rfc7230 for leading message whitespace
* Fix the order of message constructor parameters:
body first then headers (since body is constructed with arguments more often)
* Unit tests for char tables
* Remove status_code() from API when isRequest==true, et. al.
* Permit sending trailers and parameters in chunk-encoding chunks
Future:
* SOCKS proxy client and server implementations

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

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

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

@@ -0,0 +1,83 @@
#
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
import os ;
local broot = [ os.environ BOOST_ROOT ] ;
project beast/doc ;
using boostbook ;
using quickbook ;
using doxygen ;
xml beast_boostbook : master.qbk ;
path-constant out : . ;
install stylesheets
:
$(broot)/doc/src/boostbook.css
:
<location>$(out)/html
;
explicit stylesheets ;
install images
:
[ glob $(broot)/doc/src/images/*.png ]
images/beast.png
images/body.png
images/message.png
:
<location>$(out)/html/images
;
explicit images ;
install callouts
:
[ glob $(broot)/doc/src/images/callouts/*.png ]
:
<location>$(out)/html/images/callouts
;
explicit callout ;
boostbook doc
:
beast_boostbook
:
<xsl:param>chapter.autolabel=0
<xsl:param>boost.image.src=images/beast.png
<xsl:param>boost.image.alt="Beast Logo"
<xsl:param>boost.image.w=2400
<xsl:param>boost.image.h=80
<xsl:param>boost.root=$(broot)
<xsl:param>chapter.autolabel=0
<xsl:param>chunk.first.sections=1 # Chunk the first top-level section?
<xsl:param>chunk.section.depth=8 # Depth to which sections should be chunked
<xsl:param>generate.section.toc.level=1 # Control depth of TOC generation in sections
<xsl:param>toc.max.depth=2 # How many levels should be created for each TOC?
<xsl:param>toc.section.depth=2 # How deep should recursive sections appear in the TOC?
:
<location>temp
<dependency>stylesheets
<dependency>images
;
#explicit doc ;
# <xsl:param>nav.layout=none
# <format>html:<xsl:param>location=../bin/doc/html
# <xsl:param>generate.toc="chapter nop section nop"
# <xsl:param>root.filename=index
# <xsl:param>output-root="../bin/html"
#[include reference.qbk]
#[xinclude index.xml]

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

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

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

@@ -0,0 +1,639 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:design Design choices]
The implementations are driven by business needs of cryptocurrency server
applications (e.g. [@https://ripple.com Ripple]) written in C++. These
needs were not met by existing solutions so Beast was written from scratch
as a solution. Beast's design philosophy avoid flaws exhibited by other
libraries:
* Don't sacrifice performance.
* Don't do too much, otherwise interfaces become rigid.
* Symmetric interfaces (client and server the same, or close to it).
* Emulate Boost.Asio interfaces as much as possible, since Asio is
proven and it is familiar to users.
* Let library users make the important decisions such as how to
allocate memory or how to leverage flow control.
Beast uses the [link beast.types.DynamicBuffer [*`DynamicBuffer`]] concept
presented in the Netwoking TS, and relies heavily on the Boost.Asio
[*`ConstBufferSequence`] and [*`MutableBufferSequence`] concepts for passing
buffers to functions. The authors have found the dynamic buffer and buffer
sequence interfaces to be optimal for interacting with Asio, and for other
tasks such as incremental parsing of data in buffers (for example, parsing
websocket frames stored in a [link beast.ref.static_streambuf `static_streambuf`]).
During the development of Beast the authors have studied other software
packages and in particular the comments left during the Boost Review process
of other packages offering similar functionality. In this section we attempt
to address those issues.
[variablelist
[[
"I would also like to see instances of this library being used
in production. That would give some evidence that the design
works in practice.""
][
Beast.HTTP and Beast.WebSocket are production ready and currently
running on public servers receiving traffic and handling millions of
dollars worth of financial transactions daily. The servers run [*rippled],
open source software ([@https://github.com/ripple/rippled repository])
implementing the
[@https://ripple.com/files/ripple_consensus_whitepaper.pdf [*Ripple Consensus Protocol]],
technology provided by [@http://ripple.com Ripple].
]]
]
[section:http HTTP]
For HTTP we to model the message to maximize flexibility of implementation
strategies while allowing familiar verbs such as [*`read`] and [*`write`].
The HTTP interface is further driven by the needs of the WebSocket module,
as a WebSocket session requires a HTTP Upgrade handshake exchange at the
start. Other design goals:
* Don't try to invent a complete web server or client
* Have simple free functions to send and receive messages.
* Allow the message object to be customized,
[variablelist
[[
"Some more advanced examples, e.g. including TLS with client/server
certificates would help.""
][
The HTTP interface doesn't try to reinvent the wheel, it just uses
the `boost::asio::ip::tcp::socket` or `boost::asio::ssl::stream` that
you set up beforehand. Callers use the interfaces already existing
on those objects to make outgoing connections, accept incoming connections,
or establish TLS sessions with certificates. We find the available Asio
examples for performing these tasks sufficient.
]]
[[
"A built-in router?"
][
We presume this means a facility to match expressions against the URI
in HTTP requests, and dispatch them to calling code. The authors feel
that this is a responsibility of higher level code. Beast.HTTP does
not try to offer a web server.
]]
[[
"Cookies? Forms/File Uploads?""
][
Cookies, or managing these types of HTTP headers in general, is the
responsibility of higher levels. Beast.HTTP just tries to get complete
messages to and from the calling code. It deals in the HTTP headers just
enough to process the message body and leaves the rest to callers. However,
for forms and file uploads the symmetric interface of the message class
allows HTTP requests to include arbitrary body types including those needed
to upload a file or fill out a form.
]]
[[
"...supporting TLS (is this a feature? If not this would be a show-stopper),
etc.
][
Beast.HTTP does not provide direct facilities for implementing TLS
connections; however, the interfaces already existing on the
`boost::asio::ssl::stream` are available and can be used to establish
secure connections. Then, functions like `http::read` or `http::async_write`
can work with those encrypted connections with no problem.
]]
[[
"There should also be more examples of how to integrate the http service
with getting files from the file system, generating responses CGI-style"
][
The design goal for the library is to not try to invent a web server.
We feel that there is a strong need for a basic implementation that
models the HTTP message and provides functions to send and receive them
over Asio. Such an implementation should serve as a building block upon
which higher abstractions such as the aforementioned HTTP service or
cgi-gateway can be built.
]]
[[
"You should send a 100-continue to ask for the rest of the body if required."
][
These behaviors are best left to the calling software. A future library
can build on Beast.HTTP to provide these behaviors.
]]
[[
"What about HTTP/2?""
][
Many reviewers feel that HTTP/2 support is an essential feature of
a HTTP library. The authors agree that HTTP/2 is important but also
feel that the most sensible implementation is one that does not re-use
the same network reading and writing interface for 2 as that for 1.0
and 1.1.
The Beast.HTTP message model is suitable for HTTP/2 and can be re-used.
The IEFT HTTP Working Group adopted message compatiblity with HTTP/1.x
as an explicit goal. A parser can simply emit full headers after
decoding the compressed HTTP/2 headers. The stream ID is not logicaly
part of the message but rather message metadata and should be
communicated out-of-band (see below). HTTP/2 sessions begin with a
traditional HTTP/1.1 Upgrade similar in fashion to the WebSocket
upgrade. A HTTP/2 implementation can use existing Beast.HTTP primitives
to perform this handshake.
Free functions for HTTP/2 sessions are not possible because of the
requirement to maintain per-session state. For example, to decode the
compressed headers. Or to remember and respect the remote peer's window
settings. The authors propose that a HTTP/2 implementation be written
as a separate class template, similar to the `websocket::stream` but with
additional interfaces to support version 2 features. We feel that
Beast.HTTP offers enough useful functionality to justify inclusion,
so that developers can take advantage of it right away instead of
waiting.
]]
]
[endsect]
[section:websocket WebSocket]
[variablelist
[[
How does this compare to [@https://www.zaphoyd.com/websocketpp websocketpp],
an alternate header-only WebSocket implementation?
][
[variablelist
[[1. Synchronous Interface][
Beast offers full support for WebSockets using a synchronous interface. It
uses the same style of interfaces found in Boost.Asio: versions that throw
exceptions, or versions that return the error code in a reference parameter:
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
[websocketpp]
][
[```
template<class DynamicBuffer>
void
read(opcode& op, DynamicBuffer& dynabuf)
```]
[
/<not available>/
]
]]]]
[[2. Connection Model][
websocketpp supports multiple transports by utilizing a trait, the `config::transport_type`
([@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/connection.hpp#L60 asio transport example])
To get an idea of the complexity involved with implementing a transport,
compare the asio transport to the
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59 `iostream` transport]
(a layer that allows websocket communication over a std iostream).
In contrast, Beast abstracts the transport by defining just one [*`NextLayer`]
template argument The type requirements for [*`NextLayer`] are
already familiar to users as they are documented in Asio:
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html SyncReadStream],
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html SyncWriteStream],
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html AsyncReadStream], and
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html AsyncWriteStream].
The type requirements for instantiating `beast::websocket::stream` versus
`websocketpp::connection` with user defined types are vastly reduced
(18 functions versus 2). Note that websocketpp connections are passed by
`shared_ptr`. Beast does not use `shared_ptr` anywhere in its public interface.
A `beast::websocket::stream` is constructible and movable in a manner identical
`to a boost::asio::ip::tcp::socket`. Callers can put such objects in a
`shared_ptr` if they want to, but there is no requirement to do so.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp Beast]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L234 websocketpp]]
][
[```
template<class NextLayer>
class stream
{
NextLayer next_layer_;
...
}
```]
[```
template <typename config>
class connection
: public config::transport_type::transport_con_type
, public config::connection_base
{
public:
typedef lib::shared_ptr<type> ptr;
...
}
```]
]]]]
[[3. Client and Server Role][
websocketpp provides multi-role support through a hierarchy of
different classes. A `beast::websocket::stream` is role-agnostic, it
offers member functions to perform both client and server handshakes
in the same class. The same types are used for client and server
streams.
[table
[
[Beast]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/server_endpoint.hpp#L39 websocketpp],
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/client_endpoint.hpp#L42 also]]
][
[
/<not needed>/
]
[```
template <typename config>
class client : public endpoint<connection<config>,config>;
template <typename config>
class server : public endpoint<connection<config>,config>;
```]
]]]]
[[4. Thread Safety][
websocketpp uses mutexes to protect shared data from concurrent
access. In contrast, Beast does not use mutexes anywhere in its
implementation. Instead, it follows the Asio pattern. Calls to
asynchronous initiation functions use the same method to invoke
intermediate handlers as the method used to invoke the final handler,
through the
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/asio_handler_invoke.html asio_handler_invoke] mechanism.
The only requirement in Beast is that calls to asynchronous initiation
functions are made from the same implicit or explicit strand. For
example, if the `io_service` associated with a `beast::websocket::stream`
is single threaded, this counts as an implicit strand and no performance
costs associated with mutexes are incurred.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/read_frame_op.ipp#L118 Beast]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L706 websocketpp]]
][
[```
template <class Function>
friend
void asio_handler_invoke(Function&& f, read_frame_op* op)
{
return boost_asio_handler_invoke_helpers::invoke(f, op->d_->h);
}
```]
[```
mutex_type m_read_mutex;
```]
]]]]
[[5. Callback Model][
websocketpp requires a one-time call to set the handler for each event
in its interface (for example, upon message receipt). The handler is
represented by a `std::function equivalent`. Its important to recognize
that the websocketpp interface performs type-erasure on this handler.
In comparison, Beast handlers are specified in a manner identical to
Boost.Asio. They are function objects which can be copied or moved but
most importantly they are not type erased. The compiler can see
through the type directly to the implementation, permitting
optimization. Furthermore, Beast follows the Asio rules for treatment
of handlers. It respects any allocation, continuation, or invocation
customizations associated with the handler through the use of argument
dependent lookup overloads of functions such as `asio_handler_allocate`.
The Beast completion handler is provided at the call site. For each
call to an asynchronous initiation function, it is guaranteed that
there will be exactly one final call to the handler. This functions
exactly the same way as the asynchronous initiation functions found in
Boost.Asio, allowing the composition of higher level abstractions.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L834 Beast]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L281 websocketpp],
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L473 also]]
][
[```
template<class DynamicBuffer, class ReadHandler>
typename async_completion<ReadHandler, void(error_code)>::result_type
async_read(opcode& op, DynamicBuffer& dynabuf, ReadHandler&& handler);
```]
[```
typedef lib::function<void(connection_hdl,message_ptr)> message_handler;
void set_message_handler(message_handler h);
```]
]]]]
[[6. Extensible Asynchronous Model][
Beast fully supports the
[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf Extensible Asynchronous Model]
developed by Christopher Kohlhoff, author of Boost.Asio (see Section 8).
Beast websocket asynchronous interfaces may be used seamlessly with
`std::future` stackful/stackless coroutines, or user defined customizations.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/stream.ipp#L378 Beast]]
[websocketpp]
][
[```
beast::async_completion<ReadHandler, void(error_code)> completion(handler);
read_op<DynamicBuffer, decltype(completion.handler)>{
completion.handler, *this, op, streambuf};
return completion.result.get();
```]
[
/<not available>/
]
]]]]
[[7. Message Buffering][
websocketpp defines a message buffer, passed in arguments by
`shared_ptr`, and an associated message manager which permits
aggregation and memory reuse of memory. The implementation of
`websocketpp::message` uses a `std::string` to hold the payload. If an
incoming message is broken up into multiple frames, the string may be
reallocated for each continuation frame. The std::string always uses
the standard allocator, it is not possible to customize the choice of
allocator.
Beast allows callers to specify the object for receiving the message
or frame data, which is of any type meeting the requirements of
[@http://vinniefalco.github.io/beast/beast/types/DynamicBuffer.html [*DynamicBuffer]]
(modeled after `boost::asio::streambuf`).
Beast comes with the class `beast::basic_streambuf`, an efficient
implementation of the [*DynamicBuffer] concept which makes use of multiple
allocated octet arrays. If an incoming message is broken up into
multiple pieces, no reallocation occurs. Instead, new allocations are
appended to the sequence when existing allocations are filled. Beast
does not impose any particular memory management model on callers. The
`basic_streambuf` provided by beast supports standard allocators through
a template argument. Use the [*DynamicBuffer] that comes with beast,
customize the allocator if you desire, or provide your own type that
meets the
[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/basic_streambuf.hpp#L21 concept requirements].
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/message_buffer/message.hpp#L78 websocketpp]]
][
[```
template<class DynamicBuffer>
read(opcode& op, DynamicBuffer& dynabuf);
```]
[```
template <template<class> class con_msg_manager>
class message {
public:
typedef lib::shared_ptr<message> ptr;
...
std::string m_payload;
...
};
```]
]]]]
[[8. Sending Messages][
When sending a message, websocketpp requires that the payload is
packaged in a `websocketpp::message` object using `std::string` as the
storage, or it requires a copy of the caller provided buffer by
constructing a new message object. Messages are placed onto an
outgoing queue. An asynchronous write operation runs in the background
to clear the queue. No user facing handler can be registered to be
notified when messages or frames have completed sending.
Beast doesn't allocate or make copies of buffers when sending data. The
caller's buffers are sent in-place. You can use any object meeting the
requirements of
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html ConstBufferSequence],
permitting efficient scatter-gather I/O.
The [*ConstBufferSequence] interface allows callers to send data from
memory-mapped regions (not possible in websocketpp). Callers can also
use the same buffers to send data to multiple streams, for example
broadcasting common subscription data to many clients at once. For
each call to `async_write` the completion handler is called once when
the data finishes sending, in a manner identical to `boost::asio::async_write`.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1048 Beast]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L672 websocketpp]]
][
[```
template<class ConstBufferSequence>
void
write(ConstBufferSequence const& buffers);
```]
[```
lib::error_code send(std::string const & payload,
frame::opcode::value op = frame::opcode::text);
...
lib::error_code send(message_ptr msg);
```]
]]]]
[[9. Streaming Messages][
websocketpp requires that the entire message fit into memory, and that
the size is known ahead of time.
Beast allows callers to compose messages in individual frames. This is
useful when the size of the data is not known ahead of time or if it
is not desired to buffer the entire message in memory at once before
sending it. For example, sending periodic output of a database query
running on a coroutine. Or sending the contents of a file in pieces,
without bringing it all into memory.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1151 Beast]]
[websocketpp]
][
[```
template<class ConstBufferSequence>
void
write_frame(bool fin,
ConstBufferSequence const& buffers);
```]
[
/<not available>/
]
]]]]
[[10. Flow Control][
The websocketpp read implementation continuously reads asynchronously
from the network and buffers message data. To prevent unbounded growth
and leverage TCP/IP's flow control mechanism, callers can periodically
turn this 'read pump' off and back on.
In contrast a `beast::websocket::stream` does not independently begin
background activity, nor does it buffer messages. It receives data only
when there is a call to an asynchronous initiation function (for
example `beast::websocket::stream::async_read`) with an associated handler.
Applications do not need to implement explicit logic to regulate the
flow of data. Instead, they follow the traditional model of issuing a
read, receiving a read completion, processing the message, then
issuing a new read and repeating the process.
[table
[
[Beast]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L728 websocketpp]]
][
[
/<implicit>/
]
[```
lib::error_code pause_reading();
lib::error_code resume_reading();
```]
]]]]
[[11. Connection Establishment][
websocketpp offers the `endpoint` class which can handle binding and
listening to a port, and spawning connection objects.
Beast does not reinvent the wheel here, callers use the interfaces
already in `boost::asio` for receiving incoming connections resolving
host names, or establishing outgoing connections. After the socket (or
`boost::asio::ssl::stream`) is connected, the `beast::websocket::stream`
is constructed around it and the WebSocket handshake can be performed.
Beast users are free to implement their own "connection manager", but
there is no requirement to do so.
[table
[
[[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/async_connect.html Beast],
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept.html also]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/endpoint.hpp#L52 websocketpp]]
][
[```
#include <boost/asio.hpp>
```]
[```
template <typename config>
class endpoint : public config::socket_type;
```]
]]]]
[[12. WebSocket Handshaking][
Callers invoke `beast::websocket::accept` to perform the WebSocket
handshake, but there is no requirement to use this function. Advanced
users can perform the WebSocket handshake themselves. Beast WebSocket
provides the tools for composing the request or response, and the
Beast HTTP interface provides the container and algorithms for sending
and receiving HTTP/1 messages including the necessary HTTP Upgrade
request for establishing the WebSocket session.
Beast allows the caller to pass the incoming HTTP Upgrade request for
the cases where the caller has already received an HTTP message.
This flexibility permits novel and robust implementations. For example,
a listening socket that can handshake in multiple protocols on the
same port.
Sometimes callers want to read some bytes on the socket before reading
the WebSocket HTTP Upgrade request. Beast allows these already-received
bytes to be supplied to an overload of the accepting function to permit
sophisticated features. For example, a listening socket that can
accept both regular WebSocket and Secure WebSocket (SSL) connections.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L501 Beast],
[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L401 also]]
[websocketpp]
][
[```
template<class ConstBufferSequence>
void
accept(ConstBufferSequence const& buffers);
template<class Body, class Headers>
void
accept(http::request_v1<Body, Headers> const& request);
```]
[
/<not available>/
]
]]]]
]
]]
[[
What about message compression?
][
The author is currently porting ZLib 1.2.8 to modern, header-only C++11
that does not use macros or try to support ancient architectures. This
deflate implementation will be available as its own individually
usable interface, and also will be used to power Beast WebSocket's
permessage-deflate implementation, due Q4 of 2016.
However, Beast currently has sufficient functionality that users can
begin taking advantage of the WebSocket protocol using this library
immediately.
]]
[[
Where is the TLS/SSL interface?
][
The `websocket::stream` wraps the socket or stream that you provide
(for example, a `boost::asio::ip::tcp::socket` or a
`boost::asio::ssl::stream`). You establish your TLS connection using the
interface on `ssl::stream` like shown in all of the Asio examples, they
construct your `websocket::stream` around it. It works perfectly fine;
Beast.WebSocket doesn't try to reinvent the wheel or put a fresh coat of
interface paint on the `ssl::stream`.
The WebSocket implementation [*does] provides support for shutting down
the TLS connection through the use of the ADL compile-time virtual functions
[link beast.ref.websocket__teardown `teardown`] and
[link beast.ref.websocket__async_teardown `async_teardown`]. These will
properly close the connection as per rfc6455 and overloads are available
for TLS streams. Callers may provide their own overloads of these functions
for user-defined next layer types.
]]
]
[endsect]
[endsect]

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

@@ -0,0 +1,356 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:http HTTP]
Beast.HTTP offers programmers simple and performant models of HTTP messages and
their associated operations including synchronous and asynchronous reading and
writing of messages in the HTTP/1 wire format using Boost.Asio.
The HTTP protocol is described fully in
[@https://tools.ietf.org/html/rfc7230 rfc7230]
[section:motivation Motivation]
The HTTP protocol is pervasive in network applications. As C++ is a logical
choice for high performance network servers, there is great utility in solid
building blocks for manipulating, sending, and receiving HTTP messages
compliant with the Hypertext Transfer Protocol and the supplements that
follow. Unfortunately reliable implementations or industry standards do not
exist in C++.
Beast.HTTP is built on Boost.Asio and uses its own robust header-only HTTP/1
message parser modeled after the nodejs http-parser (written in C). A proposal
to add networking functionality to the C++ standard library, based on
Boost.Asio, is under consideration by the standards committee. Since the final
approved networking interface for the C++ standard library will likely closely
resemble the current interface of Boost.Asio, it is logical for Beast.HTTP to
use Boost.Asio as its network transport.
[endsect]
[section:scope Scope]
This library is designed to be a building block for creating higher level
libraries. It is not designed to be end-user facing. There is no convenient
class that implements the core of a web server, nor is there a convenient
class to quickly perform common operations such as fetching a file or
connecting and retrieving a document from a secure connection. These
use-cases are important, but this library does not try to do that. Instead,
it offers primitives that can be used to build those user-facing algorithms.
A HTTP message (referred to hereafter as "message") contains request or
response specific attributes, a series of zero or more name/value pairs
(collectively termed "headers"), and a series of octets called the message
body which may be zero in length. The HTTP protocol defines the client and
server roles: clients send messages called requests and servers send back
messages called responses. `http::message` models both requests and responses.
Beast aims to offer this functionality:
* [*Model]: Provide a universal HTTP message class model.
* [*Build]: Construct a new message and manipulate its contents.
* [*Parse]: Deserialize a message from a network or memory stream in HTTP/1 wire format.
* [*Serialize]: Serialize a message into a network or memory stream in HTTP/1 wire format.
[note The documentation which follows assumes familiarity with
both Boost.Asio and the HTTP protocol specification described in
[@https://tools.ietf.org/html/rfc7230 rfc7230] ]
[endsect]
[section:usage Usage]
[note
Sample code and identifiers mentioned in this section are written
as if the following declarations are in effect:
```
#include <beast/http.hpp>
using namespace beast;
```
]
In the paragraphs that follow we describe the available interfaces for
performing typical operations such as interacting with a HTTP server
or handling simple requests. Subsequent sections cover the message model
and its customization points in more depth, for advanced applications.
[heading Declarations]
To do anything, a message must be declared. The message class template
requires at minimum, a value indicating whether the message is a request
(versus a response), and a `Body` type. The choice of `Body` determines the
kind of container used to represent the message body. Here we will
declare a HTTP/1 request that has a `std::string` for the body container:
```
http::message_v1<true, http::string_body> req;
```
Two type aliases are provided for notational convenience when declaring
HTTP/1 messages. These two statements declare a request and a response
respectively:
```
http::request_v1<http::string_body> req;
http::response_v1<http::string_body> resp;
```
[heading Members]
Message objects are default constructible, with public access to data members.
Request and response objects have some common members, and some members unique
to the message type. These statements set all the members in each message:
```
http::request_v1<http::string_body> req;
req.method = "GET";
req.url = "/index.html";
req.version = 11; // HTTP/1.1
req.headers.insert("User-Agent", "hello_world");
req.body = "";
http::response_v1<http::string_body> resp;
resp.status = 404;
resp.reason = "Not Found";
resp.version = 10; // HTTP/1.0
resp.headers.insert("Server", "Beast.HTTP");
resp.body = "The requested resource was not found.";
```
[heading Headers]
The `message::headers` member is a container for setting the field/value
pairs in the message. These statements change the values of the headers
in the message passed:
```
template<class Body>
void set_fields(http::request_v1<Body>& req)
{
if(! req.exists("User-Agent"))
req.insert("User-Agent", "myWebClient");
if(req.exists("Accept-Charset"))
req.erase("Accept-Charset");
req.replace("Accept", "text/plain");
}
```
[heading Body]
The `message::body` member represents the message body. Depending on the
`Body` template argument type, this could be a writable container. The
following types, provided by the library, are suitable choices for the
`Body` type:
* [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
Used in GET requests where there is no message body. Example:
```
http::request_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
`value_type` as `std::string`. Useful for quickly putting together a request
or response with simple text in the message body (such as an error message).
Has the same insertion complexity of `std::string`. This is the type of body
used in the examples:
```
http::response_v1<http::string_body> resp;
static_assert(std::is_same<decltype(resp.body), std::string>::value);
resp.body = "Here is the data you requested";
```
* [link beast.ref.http__streambuf_body [*`streambuf_body`:]] A body with a
`value_type` of [link beast.ref.streambuf `streambuf`]: an efficient storage
object which uses multiple octet arrays of varying lengths to represent data.
[heading Sockets]
The library provides simple free functions modeled after Boost.Asio to
send and receive messages on TCP/IP sockets, SSL streams, or any object
which meets the Boost.Asio type requirements (SyncReadStream, SyncWriteStream,
AsyncReadStream, and AsyncWriteStream depending on the types of operations
performed). To send messages synchronously, use one of the `http:write`
functions:
```
void send_request(boost::asio::ip::tcp::socket& sock)
{
http::request<http::empty_body> req;
req.version = 11;
req.method = "GET";
req.url = "/index.html";
...
http::write(sock, req); // Throws exception on error
...
// Alternatively
boost::system::error:code ec;
http::write(sock, req, ec);
if(ec)
std::cerr << "error writing http message: " << ec.message();
}
```
An asynchronous interface is available:
```
void handle_write(boost::system::error_code);
...
http::request_v1<http::empty_body> req;
...
http::async_write(sock, req, std::bind(&handle_write, std::placeholders::_1));
```
When the implementation reads messages from a socket, it can read bytes lying
after the end of the message if they are present (the alternative is to read
a single byte at a time which is unsuitable for performance reasons). To
store and re-use these extra bytes on subsequent messages, the read interface
requires an additional parameter: a [link beast.types.DynamicBuffer [*`DynamicBuffer`]]
object. This example reads a message from the socket, with the extra bytes
stored in the streambuf parameter for use in a subsequent call to read:
```
boost::asio::streambuf sb;
...
http::response_v1<http::string_body> resp;
http::read(sock, sb, resp); // Throws exception on error
...
// Alternatively
boost::system::error:code ec;
http::read(sock, sb, resp, ec);
if(ec)
std::cerr << "error reading http message: " << ec.message();
```
As with the write function, an asynchronous interface is available. The
stream buffer parameter must remain valid until the completion handler is
called:
```
void handle_read(boost::system::error_code);
...
boost::asio::streambuf sb;
http::response_v1<http::string_body> resp;
...
http::async_read(sock, resp, std::bind(&handle_read, std::placeholders::_1));
```
An alternative to using a `boost::asio::streambuf` is to use a
[link beast.ref.streambuf `beast::streambuf`], which meets the requirements of
[*`DynamicBuffer`] and is optimized for performance:
```
void handle_read(boost::system::error_code);
...
beast::streambuf sb;
http::response_v1<http::string_body> resp;
http::read(sock, sb, resp);
```
The `read` implementation can use any object meeting the requirements of
[link beast.types.DynamicBuffer [*`DynamicBuffer`]], allowing callers to define custom
memory management strategies used by the implementation.
[endsect]
[section:advanced Advanced]
The spectrum of hardware and software platforms which perform these typical
HTTP operations is vast, ranging from powerful servers in large datacenters
to tiny resource-limited embedded devices. No single concrete implementation
of a class intended to model messages can efficiently serve all needs.
For example, an object that minimizes resources during parsing may not be
able to edit and change headers dynamically. A message that represents the
message body as a disk file may support sending but not parsing. Many efficient
and correct models of messages exist, supporting some or all of the
operations listed above.
[heading Message model]
The message class template and provided Body types are suitable for casual
library users. This section explains the message model for advanced users
who wish to take control over aspects of the implementation. We introduce
customization points for the header and body via class template arguments.
This illustration shows more detail about the
[link beast.ref.http__message [*`message`]] class template (boilerplate
present in the actual declaration has been removed for clarity):
[$images/message.png [width 580px] [height 225px]]
The default constructor, move special members, and copy special members are
all defaulted. A message is movable, copyable, or default constructible based
on the capabilities of its template arguments.
Messages modeled in this fashion are ['complete], containing all of the
information required to perform the supported set of operations. They are
['first-class types], returnable from functions and composable. HTTP
requests and responses are distinct types, allowing functions to be
overloaded on the type of message.
[endsect]
[section:headers Headers Type]
The `Headers` type represents the field/value pairs present in every HTTP
message. These types implement the
[link beast.types.FieldSequence [*`FieldSequence`]]
concept. The value type of a field sequence is an object meeting the
requirements of [link beast.types.Field [*`Field`]]. The implementation can
serialize any instance of `Headers` that meets the field sequence requirements.
This example shows a function which returns `true` if the specified field
sequence has a connect field:
```
template<class FieldSequence>
bool
has_connect(FieldSequence const& fs)
{
return std::find_if(fs.begin(), fs.end(),
[&](auto const& field)
{
return ci_equal(field.name(), "Connect");
});
}
```
[endsect]
[section:body Body Type]
The `Body` template argument in the `message` class must meet the
[link beast.types.Body [*`Body`] requirements]. It provides customization
of the data member in the message, the algorithm for parsing, and the
algorithm for serialization:
[$images/body.png [width 510px] [height 210px]]
Instances of the optional nested types `writer` and `reader` perform
serialization and deserialization of the message body. If either or
both of these types are present, the message becomes serializable, parsable,
or both. They model [link beast.types.Reader [*`Reader`]] and
[link beast.types.Writer [*`Writer`]] respectively.
For specialized applications, users may implement their own types which
meet the requirements. The examples included with this library provide a
Body implementation used to serve files in a HTTP server.
[endsect]
[endsect]

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

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

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" "boostbook.dtd">
<!--
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-->
<section id="index">
<index/>
</section>

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

@@ -0,0 +1,13 @@
#!/usr/bin/bash
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
mkdir -p ../bin/doc/xml
doxygen source.dox
cd ../bin/doc/xml
xsltproc combine.xslt index.xml > all.xml
cd ../../../doc
xsltproc reference.xsl ../bin/doc/xml/all.xml > reference.qbk

211
src/beast/doc/master.qbk Normal file
View File

@@ -0,0 +1,211 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[library Beast
[quickbook 1.6]
[copyright 2013 - 2016 Vinnie Falco]
[purpose C++ Library]
[license
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
[@http://www.boost.org/LICENSE_1_0.txt])
]
[authors [Falco, Vinnie]]
[category template]
[category generic]
]
[template mdash[] '''&mdash; ''']
[template indexterm1[term1] '''<indexterm><primary>'''[term1]'''</primary></indexterm>''']
[template indexterm2[term1 term2] '''<indexterm><primary>'''[term1]'''</primary><secondary>'''[term2]'''</secondary></indexterm>''']
[def __POSIX__ /POSIX/]
[def __Windows__ /Windows/]
[def __accept__ [@http://www.opengroup.org/onlinepubs/000095399/functions/accept.html `accept()`]]
[def __connect__ [@http://www.opengroup.org/onlinepubs/000095399/functions/connect.html `connect()`]]
[def __getpeername__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getpeername.html `getpeername()`]]
[def __getsockname__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getsockname.html `getsockname()`]]
[def __getsockopt__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getsockopt.html `getsockopt()`]]
[def __ioctl__ [@http://www.opengroup.org/onlinepubs/000095399/functions/ioctl.html `ioctl()`]]
[def __recvfrom__ [@http://www.opengroup.org/onlinepubs/000095399/functions/recvfrom.html `recvfrom()`]]
[def __sendto__ [@http://www.opengroup.org/onlinepubs/000095399/functions/sendto.html `sendto()`]]
[def __setsockopt__ [@http://www.opengroup.org/onlinepubs/000095399/functions/setsockopt.html `setsockopt()`]]
[def __socket__ [@http://www.opengroup.org/onlinepubs/000095399/functions/socket.html `socket()`]]
[section:intro Introduction]
Beast is a 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.
[section:requirements Requirements]
Beast requires:
* [*C++11.] A minimum of C++11 is needed.
* [*Boost.] Beast is built on Boost, especially Boost.Asio.
* [*OpenSSL.] If using TLS/Secure sockets (optional).
[note Tested compilers: msvc-14+, gcc 5+, clang 3.6+]
The library is [*header-only]. It is not necessary to add any .cpp files,
or to edit your existing build script or project file except to provide
that the include/ directory for beast is searched for include files.
[endsect]
[section:example Examples]
These usage examples are intended to quickly impress upon readers the
flavor of the library. They are complete programs which may be built
and run. Source code and build scripts for these programs may be found
in the examples directory.
Use HTTP to request the root page from a website and print the response:
```
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
int main()
{
// Normal boost::asio setup
std::string const host = "boost.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
// Send HTTP request using beast
beast::http::request_v1<beast::http::empty_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast");
beast::http::prepare(req);
beast::http::write(sock, req);
// Receive and print HTTP response using beast
beast::streambuf sb;
beast::http::response_v1<beast::http::streambuf_body> resp;
beast::http::read(sock, sb, resp);
std::cout << resp;
}
```
Establish a WebSocket connection, send a message and receive the reply:
```
#include <beast/core/to_string.hpp>
#include <beast/websocket.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
int main()
{
// Normal boost::asio setup
std::string const host = "echo.websocket.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
// WebSocket connect and send message using beast
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
ws.handshake(host, "/");
ws.write(boost::asio::buffer("Hello, world!"));
// Receive WebSocket message, print and close using beast
beast::streambuf sb;
beast::websocket::opcode op;
ws.read(op, sb);
ws.close(beast::websocket::close_code::normal);
std::cout << to_string(sb.data()) << "\n";
}
```
[endsect]
[section:credits Credits]
Boost.Asio is the inspiration behind which all of the interfaces and
implementation strategies are built. Some parts of the documentation are
written to closely resemble the wording and presentation of Boost.Asio
documentation. Credit goes to Christopher Kohloff for the wonderful
Asio library and the ideas upon which Beast is built.
Beast would not be possible without the considerable time and patience
contributed by David Schwartz, Edward Hennis, Howard Hinnant, Miguel Portilla,
Nikolaos Bougalis, Scott Determan, Scott Schurr, and Ripple Labs for
supporting its development.
[endsect]
[endsect]
[include http.qbk]
[include websocket.qbk]
[section:types Type Requirements]
[include types/Body.qbk]
[include types/BufferSequence.qbk]
[include types/DynamicBuffer.qbk]
[include types/Field.qbk]
[include types/FieldSequence.qbk]
[include types/Parser.qbk]
[include types/Reader.qbk]
[include types/Streams.qbk]
[include types/Writer.qbk]
[endsect]
[include design.qbk]
[section:quickref Quick Reference]
[xinclude quickref.xml]
[endsect]
[include reference.qbk]
[section:idx Index]
[xinclude index.xml]
[endsect]

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

@@ -0,0 +1,214 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" "boostbook.dtd">
<!--
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-->
<informaltable frame="all">
<tgroup cols="4">
<colspec colname="a"/>
<colspec colname="b"/>
<colspec colname="c"/>
<colspec colname="d"/>
<thead>
<row>
<entry valign="center" namest="a" nameend="b">
<bridgehead renderas="sect2">HTTP</bridgehead>
</entry>
<entry valign="center" namest="c" nameend="d">
<bridgehead renderas="sect2">WebSocket</bridgehead>
</entry>
</row>
</thead>
<tbody>
<row>
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__basic_dynabuf_body">basic_dynabuf_body</link></member>
<member><link linkend="beast.ref.http__basic_headers">basic_headers</link></member>
<member><link linkend="beast.ref.http__basic_parser_v1">basic_parser_v1</link></member>
<member><link linkend="beast.ref.http__empty_body">empty_body</link></member>
<member><link linkend="beast.ref.http__headers">headers</link></member>
<member><link linkend="beast.ref.http__message">message</link></member>
<member><link linkend="beast.ref.http__resume_context">resume_context</link></member>
<member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member>
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
</simplelist>
<bridgehead renderas="sect3">Options</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__body_max_size">body_max_size</link></member>
<member><link linkend="beast.ref.http__headers_max_size">headers_max_size</link></member>
<member><link linkend="beast.ref.http__skip_body">skip_body</link></member>
</simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
<member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member>
<member><link linkend="beast.ref.http__is_ReadableBody">is_ReadableBody</link></member>
<member><link linkend="beast.ref.http__is_WritableBody">is_WritableBody</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__async_parse">async_parse</link></member>
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
<member><link linkend="beast.ref.http__async_write">async_write</link></member>
<member><link linkend="beast.ref.http__parse">parse</link></member>
<member><link linkend="beast.ref.http__prepare">prepare</link></member>
<member><link linkend="beast.ref.http__read">read</link></member>
<member><link linkend="beast.ref.http__swap">swap</link></member>
<member><link linkend="beast.ref.http__write">write</link></member>
</simplelist>
<bridgehead renderas="sect3">Constants</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__connection">connection</link></member>
</simplelist>
<bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.types.Body">Body</link></member>
<member><link linkend="beast.types.Field">Field</link></member>
<member><link linkend="beast.types.FieldSequence">FieldSequence</link></member>
<member><link linkend="beast.types.Parser">Parser</link></member>
<member><link linkend="beast.types.Reader">Reader</link></member>
<member><link linkend="beast.types.Writer">Writer</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.websocket__close_reason">close_reason</link></member>
<member><link linkend="beast.ref.websocket__ping_data">ping_data</link></member>
<member><link linkend="beast.ref.websocket__stream">stream</link></member>
<member><link linkend="beast.ref.websocket__reason_string">reason_string</link></member>
<member><link linkend="beast.ref.websocket__teardown_tag">teardown_tag</link></member>
</simplelist>
<bridgehead renderas="sect3">Options</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.websocket__auto_fragment_size">auto_fragment_size</link></member>
<member><link linkend="beast.ref.websocket__decorate">decorate</link></member>
<member><link linkend="beast.ref.websocket__keep_alive">keep_alive</link></member>
<member><link linkend="beast.ref.websocket__mask_buffer_size">mask_buffer_size</link></member>
<member><link linkend="beast.ref.websocket__message_type">message_type</link></member>
<member><link linkend="beast.ref.websocket__pong_callback">pong_callback</link></member>
<member><link linkend="beast.ref.websocket__read_buffer_size">read_buffer_size</link></member>
<member><link linkend="beast.ref.websocket__read_message_max">read_message_max</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.websocket__async_teardown">async_teardown</link></member>
<member><link linkend="beast.ref.websocket__teardown">teardown</link></member>
</simplelist>
<bridgehead renderas="sect3">Constants</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.websocket__close_code">close_code</link></member>
<member><link linkend="beast.ref.websocket__error">error</link></member>
<member><link linkend="beast.ref.websocket__opcode">opcode</link></member>
</simplelist>
</entry>
</row>
</tbody>
</tgroup>
<tgroup cols="4">
<colspec colname="a"/>
<colspec colname="b"/>
<colspec colname="c"/>
<colspec colname="d"/>
<thead>
<row>
<entry valign="center" namest="a" nameend="d">
<bridgehead renderas="sect2">Core</bridgehead>
</entry>
</row>
</thead>
<tbody>
<row>
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.async_completion">async_completion</link></member>
<member><link linkend="beast.ref.basic_streambuf">basic_streambuf</link></member>
<member><link linkend="beast.ref.buffers_adapter">buffers_adapter</link></member>
<member><link linkend="beast.ref.consuming_buffers">consuming_buffers</link></member>
<member><link linkend="beast.ref.dynabuf_readstream">dynabuf_readstream</link></member>
<member><link linkend="beast.ref.error_code">error_code</link></member>
<member><link linkend="beast.ref.handler_alloc">handler_alloc</link></member>
<member><link linkend="beast.ref.prepared_buffers">prepared_buffers</link></member>
<member><link linkend="beast.ref.static_streambuf">static_streambuf</link></member>
<member><link linkend="beast.ref.static_streambuf_n">static_streambuf_n</link></member>
<member><link linkend="beast.ref.static_string">static_string</link></member>
<member><link linkend="beast.ref.streambuf">streambuf</link></member>
<member><link linkend="beast.ref.system_error">system_error</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.bind_handler">bind_handler</link></member>
<member><link linkend="beast.ref.buffer_cat">buffer_cat</link></member>
<member><link linkend="beast.ref.consumed_buffers">consumed_buffers</link></member>
<member><link linkend="beast.ref.prepare_buffer">prepare_buffer</link></member>
<member><link linkend="beast.ref.prepare_buffers">prepare_buffers</link></member>
<member><link linkend="beast.ref.to_string">to_string</link></member>
<member><link linkend="beast.ref.write">write</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.is_AsyncReadStream">is_AsyncReadStream</link></member>
<member><link linkend="beast.ref.is_AsyncWriteStream">is_AsyncWriteStream</link></member>
<member><link linkend="beast.ref.is_AsyncStream">is_AsyncStream</link></member>
<member><link linkend="beast.ref.is_BufferSequence">is_BufferSequence</link></member>
<member><link linkend="beast.ref.is_CompletionHandler">is_CompletionHandler</link></member>
<member><link linkend="beast.ref.is_ConstBufferSequence">is_ConstBufferSequence</link></member>
<member><link linkend="beast.ref.is_DynamicBuffer">is_DynamicBuffer</link></member>
<member><link linkend="beast.ref.is_MutableBufferSequence">is_MutableBufferSequence</link></member>
<member><link linkend="beast.ref.is_SyncReadStream">is_SyncReadStream</link></member>
<member><link linkend="beast.ref.is_SyncStream">is_SyncStream</link></member>
<member><link linkend="beast.ref.is_SyncWriteStream">is_SyncWriteStream</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.types.streams.AsyncStream">AsyncStream</link></member>
<member><link linkend="beast.types.BufferSequence">BufferSequence</link></member>
<member><link linkend="beast.types.DynamicBuffer">DynamicBuffer</link></member>
<member><link linkend="beast.types.streams.Stream">Stream</link></member>
<member><link linkend="beast.types.streams.SyncStream">SyncStream</link></member>
</simplelist>
</entry>
</row>
</tbody>
</tgroup>
<tgroup cols="1">
<colspec colname="a"/>
<thead>
<row>
<entry valign="center" namest="a" nameend="a">
<bridgehead renderas="sect2">Diagnostic</bridgehead>
</entry>
</row>
</thead>
<tbody>
<row>
<entry valign="top">
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.doc_debug">doc_debug</link></member>
<member><link linkend="beast.ref.nested__nested_doc_debug">nested_doc_debug</link></member>
</simplelist>
</entry>
</row>
</tbody>
</tgroup>
</informaltable>

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

File diff suppressed because it is too large Load Diff

381
src/beast/doc/source.dox Normal file
View File

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

View File

@@ -0,0 +1,50 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:Body Body]
In this table:
* `X` is a type meeting the requirements of [*`Body`].
[table Body requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X::value_type`]
[]
[
The type of the `message::body` member.
If this is not movable or not copyable, the containing message
will be not movable or not copyable.
]
]
[
[`X:value_type{}`]
[]
[`DefaultConstructible`]
]
[
[`Body::reader`]
[]
[
If present, a type meeting the requirements of
[link beast.types.Reader [*`Reader`]].
Provides an implementation to parse the body.
]
]
[
[`Body::writer`]
[]
[
If present, a type meeting the requirements of
[link beast.types.Writer [*`Writer`]].
Provides an implementation to serialize the body.
]
]
]
[endsect]

View File

@@ -0,0 +1,15 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:BufferSequence BufferSequence]
A `BufferSequence` is a type meeting either of the following requirements:
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*`ConstBufferSequence`]]
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*`MutableBufferSequence`]]
[endsect]

View File

@@ -0,0 +1,125 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:DynamicBuffer DynamicBuffer]
A dynamic buffer encapsulates memory storage that may be automatically resized
as required, where the memory is divided into an input sequence followed by an
output sequence. These memory regions are internal to the dynamic buffer, but
direct access to the elements is provided to permit them to be efficiently used
with I/O operations, such as the send or receive operations of a socket. Data
written to the output sequence of a dynamic buffer object is appended to the
input sequence of the same object.
The interface to this concept is intended to permit the following
implementation strategies:
* A single contiguous octet array, which is reallocated as necessary to
accommodate changes in the size of the octet sequence.
* A sequence of one or more octet arrays, where each array is of the same
size. Additional octet array objects are appended to the sequence to
accommodate changes in the size of the octet sequence.
* A sequence of one or more octet arrays of varying sizes. Additional octet
array objects are appended to the sequence to accommodate changes in the
size of the character sequence. This is the implementation approached
currently offered by [link beast.ref.basic_streambuf `basic_streambuf`].
In the table below:
* `X` denotes a dynamic buffer class.
* `a` denotes a value of type `X`.
* `c` denotes a (possibly const) value of type `X`.
* `n` denotes a value of type `std::size_t`.
* `T` denotes a type meeting the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html `ConstBufferSequence`].
* `U` denotes a type meeting the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html `MutableBufferSequence`].
[table DynamicBuffer requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X::const_buffers_type`]
[`T`]
[
This type represents the memory associated with the input sequence.
]
]
[
[`X::mutable_buffers_type`]
[`U`]
[
This type represents the memory associated with the output sequence.
]
]
[
[`c.size()`]
[`std::size_t`]
[
Returns the size, in bytes, of the input sequence.
]
]
[
[`c.max_size()`]
[`std::size_t`]
[
Returns the permitted maximum of the sum of the sizes of the input
sequence and output sequence.
]
]
[
[`c.capacity()`]
[`std::size_t`]
[
Returns the maximum sum of the sizes of the input sequence and output
sequence that the dynamic buffer can hold without requiring reallocation.
]
]
[
[`c.data()`]
[`X::const_buffers_type`]
[
Returns a constant buffer sequence u that represents the memory
associated with the input sequence, and where `buffer_size(u) == size()`.
]
]
[
[`a.prepare(n)`]
[`X:mutable_buffers_type`]
[
Returns a mutable buffer sequence u representing the output sequence,
and where `buffer_size(u) == n`. The dynamic buffer reallocates memory
as required. All constant or mutable buffer sequences previously
obtained using `data()` or `prepare()` are invalidated.
Throws: `length_error` if `size() + n` exceeds `max_size()`.
]
]
[
[`a.commit(n)`]
[ ]
[
Appends `n` bytes from the start of the output sequence to the end of
the input sequence. The remainder of the output sequence is discarded.
If `n` is greater than the size of the output sequence, the entire
output sequence is appended to the input sequence. All constant or
mutable buffer sequences previously obtained using `data()` or
`prepare()` are invalidated.
]
]
[
[`a.consume(n)`]
[ ]
[
Removes `n` bytes from beginning of the input sequence. If `n` is
greater than the size of the input sequence, the entire input sequence
is removed. All constant or mutable buffer sequences previously
obtained using `data()` or `prepare()` are invalidated.
]
]
]
[endsect]

View File

@@ -0,0 +1,41 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:Field Field]
A [*`Field`] represents a single HTTP header field/value pair.
In this table:
* `X` denotes a type meeting the requirements of [*`Field`].
* `a` denotes a value of type `X`.
[table Field requirements
[[operation][type][semantics, pre/post-conditions]]
[
[`a.name()`]
[`boost::string_ref`]
[
This function returns a value implicitly convertible to
`boost::string_ref` containing the case-insensitive field
name, without leading or trailing white space.
]
]
[
[`a.value()`]
[`boost::string_ref`]
[
This function returns a value implicitly convertible to
`boost::string_ref` containing the value for the field. The
value is considered canonical if there is no leading or
trailing whitespace.
]
]
]
[endsect]

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)
]
[section:FieldSequence FieldSequence]
A [*`FieldSequence`] is an iterable container whose value type meets
the requirements of [link beast.types.Field [*`Field`]].
In this table:
* `X` denotes a type that meets the requirements of [*`FieldSequence`].
* `a` is a value of type `X`.
[table FieldSequence requirements
[[operation][type][semantics, pre/post-conditions]]
[
[`X::value_type`]
[]
[
A type that meets the requirements of `Field`.
]
]
[
[`X::const_iterator`]
[]
[
A type that meets the requirements of `ForwardIterator`.
]
]
[
[`a.begin()`]
[`X::const_iterator`]
[
Returns an iterator to the beginning of the field sequence.
]
]
[
[`a.end()`]
[`X::const_iterator`]
[
Returns an iterator to the end of the field sequence.
]
]
]
[endsect]

View File

@@ -0,0 +1,58 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:Parser Parser]
A [*`Parser`] is used to deserialize HTTP/1 messages from [link beast.types.streams streams].
Objects of this type are used with [link beast.ref.http__parse http::parse] and
[link beast.ref.http__async_parse http::async_parse].
In this table:
* `X` denotes a type meeting the requirements of [*`Parser`].
* `a` denotes a value of type `X`.
* `b` is a value meeting the requirements of [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConvertibleToConstBuffer.html [*`ConvertibleToConstBuffer`]].
* `ec` is a value of type [link beast.ref.error_code `error_code&`].
[table Parser requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`a.complete()`]
[`bool`]
[
Returns `true` when a complete HTTP/1 message has been parsed.
]
]
[
[`a.write(b, ec)`]
[`std::size_t`]
[
Parses the octets in the specified input buffer sequentially until
an error occurs, the end of the buffer is reached, or a complete
HTTP/1 message has been parsed. If an error occurs, `ec` is set
to the error code and parsing stops. This function returns the
number of bytes consumed from the input buffer.
]
]
[
[`a.write_eof(ec)`]
[`void`]
[
Indicates to the parser that no more octets will be available.
Typically this function is called when the end of stream is reached.
For example, if a call to `boost::asio::ip::tcp::socket::read_some`
generates a `boost::asio::error::eof` error. Some HTTP/1 messages
determine the end of the message body by an end of file marker or
closing of the connection.
]
]
]
[endsect]

View File

@@ -0,0 +1,54 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:Reader Reader]
Parser implementations will construct the corresponding `reader` object
during the parse. This customization point allows the Body to determine
the strategy for storing incoming message body data.
In this table:
* `X` denotes a type meeting the requirements of [*`Reader`].
* `a` denotes a value of type `X`.
* `p` is any pointer.
* `n` is a value convertible to `std::size_t`.
* `ec` is a value of type `error_code&`.
* `m` denotes a value of type `message const&` where
`std::is_same<decltype(m.body), Body::value_type>:value == true`
[table Reader requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X a(m);`]
[]
[
`a` is constructible from `m`. The lifetime of `m` is
guaranteed to end no earlier than after `a` is destroyed.
]
]
[
[`a.write(p, n, ec)`]
[`void`]
[
Deserializes the input sequence into the body.
If `ec` is set, the deserialization is aborted and the error
is returned to the caller.
]
]
]
[note Definitions for required `Reader` member functions should be declared
inline so the generated code becomes part of the implementation. ]
[endsect]

View File

@@ -0,0 +1,34 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:streams Streams]
Stream types represent objects capable of performing synchronous or
asynchronous I/O. They are based on concepts from `boost::asio`.
[heading:Stream Stream]
A type modeling [*`Stream`] meets either or both of the following requirements:
* [*`AsyncStream`]
* [*`SyncStream`]
[heading:AsyncStream AsyncStream]
A type modeling [*`AsyncStream`] meets the following requirements:
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncReadStream.html [*`AsyncReadStream`]]
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*`AsyncWriteStream`]]
[heading:SyncStream SyncStream]
A type modeling [*`SyncStream`] meets the following requirements:
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncReadStream.html [*`SyncReadStream`]]
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncWriteStream.html [*`SyncWriteStream`]]
[endsect]

View File

@@ -0,0 +1,179 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:Writer Writer]
A `Writer` serializes the message body. The implementation creates an instance
of this type when serializing a message, and calls into it zero or more times
to provide buffers containing the data. The interface of `Writer` is intended
to allow serialization in these scenarios:
* A body that does not entirely fit in memory.
* A body produced incrementally from coroutine output.
* A body represented by zero or more buffers already in memory.
* A body as a series of buffers when the content size is not known ahead of time.
* Body data generated on demand from other threads.
* Body data computed algorithmically.
In this table:
* `X` denotes a type meeting the requirements of `Writer`.
* `a` denotes a value of type `X`.
* `m` denotes a value of type `message const&` where
`std::is_same<decltype(m.body), Body::value_type>:value == true`.
* `rc` is an object of type [link beast.ref.http__resume_context resume_context].
* `ec` is a value of type `error_code&`.
* `wf` is a [*write function]: a function object of unspecified type provided
by the implementation which accepts any value meeting the requirements
of `ConstBufferSequence` as its single parameter.
[table Writer requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X a(m);`]
[]
[
`a` is constructible from `m`. The lifetime of `m` is
guaranteed to end no earlier than after `a` is destroyed.
]
]
[
[`a.init(ec)`]
[`void`]
[
Called immediately after construction.
If `ec` is set, the serialization is aborted and the error
is propagated to the caller.
]
]
[
[`a.content_length()`]
[`std::uint64_t`]
[
If this member is present, it is called after initialization
and before calls to provide buffers. The serialized message will
have the Content-Length field set to the value returned from
this function. If this member is absent, the serialized message
body will be chunk-encoded for HTTP versions 1.1 and later, else
the serialized message body will be sent unmodified, with the
error `boost::asio::error::eof` returned to the caller, to notify
they should close the connection to indicate the end of the message.
]
]
[
[`a(rc, ec, wf)`]
[`boost::tribool`]
[
Called repeatedly after `init` succeeds.
`wf` is a function object which takes as its single parameter,
any value meeting the requirements of `ConstBufferSequence`.
Buffers provided by the `writer` to this [*write function] must
remain valid until the next member function of `writer` is
invoked (which may be the destructor). This function returns `true`
to indicate all message body data has been written, or `false`
if there is more body data. If the return value is
`boost::indeterminate`, the implementation will suspend the operation
until the writer invokes `rc`. It is the writers responsibility when
returning `boost::indeterminate`, to acquire ownership of the
`resume_context` via move construction and eventually call it or else
undefined behavior results.
]
]
]
[note Definitions for required `Writer` member functions should be declared
inline so the generated code becomes part of the implementation. ]
Exemplar:
```
struct writer
{
public:
/** Construct the writer.
The msg object is guaranteed to exist for the lifetime of the writer.
Exceptions:
No-throw guarantee.
@param msg The message whose body is to be written.
*/
template<bool isRequest, class Body, class Headers>
explicit
writer(message<isRequest, Body, Headers> const& msg);
/** Initialize the writer.
Called once immediately after construction.
The writer can perform initialization which may fail.
@param ec Contains the error code if any errors occur.
*/
void
init(error_code& ec);
/** Returns the content length.
If this member is present, the implementation will set the
Content-Length field accordingly. If absent, the implementation will
use chunk-encoding or terminate the connection to indicate the end
of the message.
*/
std::size_t
content_length() const;
/** Write zero or one buffer representing the message body.
Postconditions:
If return value is `true`:
* Callee does not take ownership of resume.
* Callee made zero or one calls to `write`.
* There is no more data remaining to write.
If return value is `false`:
* Callee does not take ownership of resume.
* Callee made one call to `write`.
If return value is boost::indeterminate:
* Callee takes ownership of `resume`.
* Caller suspends the write operation
until `resume` is invoked.
When the caller takes ownership of resume, the
asynchronous operation will not complete until the
caller destroys the object.
@param resume A functor to call to resume the write operation
after the writer has returned boost::indeterminate.
@param ec Set to indicate an error. This will cause an
asynchronous write operation to complete with the error.
@param write A functor the writer will call to provide the next
set of buffers. Ownership of the buffers is not transferred,
the writer must guarantee that the buffers remain valid until the
next member function is invoked, which may be the destructor.
@return `true` if there is data, `false` when done,
boost::indeterminate to suspend.
@note Undefined behavior if the callee takes ownership
of resume but does not return boost::indeterminate.
*/
template<class WriteFunction>
boost::tribool
operator()(resume_context&&, error_code&, WriteFunction&& write);
};
```
[endsect]

452
src/beast/doc/websocket.qbk Normal file
View File

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

View File

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

View File

@@ -0,0 +1,26 @@
#
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
import os ;
exe http-crawl :
http_crawl.cpp
urls_large_data.cpp
;
exe http-server :
http_server.cpp
;
exe http-example :
http_example.cpp
;
exe websocket-example :
websocket_example.cpp
;

View File

@@ -0,0 +1,87 @@
//
// 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_FILE_BODY_H_INCLUDED
#define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
#include <beast/http/body_type.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/filesystem.hpp>
#include <cstdio>
#include <cstdint>
namespace beast {
namespace http {
struct file_body
{
using value_type = std::string;
class writer
{
std::uint64_t size_ = 0;
std::uint64_t offset_ = 0;
std::string const& path_;
FILE* file_ = nullptr;
char buf_[4096];
std::size_t buf_len_;
public:
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
writer(message<isRequest, file_body, Headers> const& m) noexcept
: path_(m.body)
{
}
~writer()
{
if(file_)
fclose(file_);
}
void
init(error_code& ec) noexcept
{
file_ = fopen(path_.c_str(), "rb");
if(! file_)
ec = boost::system::errc::make_error_code(
static_cast<boost::system::errc::errc_t>(errno));
else
size_ = boost::filesystem::file_size(path_);
}
std::uint64_t
content_length() const
{
return size_;
}
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
if(size_ - offset_ < sizeof(buf_))
buf_len_ = static_cast<std::size_t>(
size_ - offset_);
else
buf_len_ = sizeof(buf_);
auto const nread = fread(buf_, 1, sizeof(buf_), file_);
(void)nread;
offset_ += buf_len_;
write(boost::asio::buffer(buf_, buf_len_));
return offset_ >= size_;
}
};
};
} // http
} // beast
#endif

View File

@@ -0,0 +1,325 @@
//
// 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_ASYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#include "file_body.hpp"
#include "mime_type.hpp"
#include <beast/http.hpp>
#include <beast/core/placeholders.hpp>
#include <beast/core/streambuf.hpp>
#include <boost/asio.hpp>
#include <cstddef>
#include <cstdio>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
namespace beast {
namespace http {
class http_async_server
{
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
using req_type = request_v1<string_body>;
using resp_type = response_v1<file_body>;
std::mutex m_;
bool log_ = true;
boost::asio::io_service ios_;
boost::asio::ip::tcp::acceptor acceptor_;
socket_type sock_;
std::string root_;
std::vector<std::thread> thread_;
public:
http_async_server(endpoint_type const& ep,
std::size_t threads, std::string const& root)
: acceptor_(ios_)
, sock_(ios_)
, root_(root)
{
acceptor_.open(ep.protocol());
acceptor_.bind(ep);
acceptor_.listen(
boost::asio::socket_base::max_connections);
acceptor_.async_accept(sock_,
std::bind(&http_async_server::on_accept, this,
beast::asio::placeholders::error));
thread_.reserve(threads);
for(std::size_t i = 0; i < threads; ++i)
thread_.emplace_back(
[&] { ios_.run(); });
}
~http_async_server()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
for(auto& t : thread_)
t.join();
}
template<class... Args>
void
log(Args const&... args)
{
if(log_)
{
std::lock_guard<std::mutex> lock(m_);
log_args(args...);
}
}
private:
template<class Stream, class Handler,
bool isRequest, class Body, class Headers>
class write_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
Stream& s;
message_v1<isRequest, Body, Headers> m;
Handler h;
bool cont;
template<class DeducedHandler>
data(DeducedHandler&& h_, Stream& s_,
message_v1<isRequest, Body, Headers>&& m_)
: s(s_)
, m(std::move(m_))
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
write_op(write_op&&) = default;
write_op(write_op const&) = default;
template<class DeducedHandler, class... Args>
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), s,
std::forward<Args>(args)...))
{
(*this)(error_code{}, false);
}
void
operator()(error_code ec, bool again = true)
{
auto& d = *d_;
d.cont = d.cont || again;
if(! again)
{
beast::http::async_write(d.s, d.m, std::move(*this));
return;
}
d.h(ec);
}
friend
void* asio_handler_allocate(
std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
bool asio_handler_is_continuation(write_op* op)
{
return op->d_->cont;
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, write_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class Stream,
bool isRequest, class Body, class Headers,
class DeducedHandler>
static
void
async_write(Stream& stream, message_v1<
isRequest, Body, Headers>&& msg,
DeducedHandler&& handler)
{
write_op<Stream, typename std::decay<DeducedHandler>::type,
isRequest, Body, Headers>{std::forward<DeducedHandler>(
handler), stream, std::move(msg)};
}
class peer : public std::enable_shared_from_this<peer>
{
int id_;
streambuf sb_;
socket_type sock_;
http_async_server& server_;
boost::asio::io_service::strand strand_;
req_type req_;
public:
peer(peer&&) = default;
peer(peer const&) = default;
peer& operator=(peer&&) = delete;
peer& operator=(peer const&) = delete;
peer(socket_type&& sock, http_async_server& server)
: sock_(std::move(sock))
, server_(server)
, strand_(sock_.get_io_service())
{
static int n = 0;
id_ = ++n;
}
void
fail(error_code ec, std::string what)
{
if(ec != boost::asio::error::operation_aborted)
server_.log("#", id_, " ", what, ": ", ec.message(), "\n");
}
void run()
{
do_read();
}
void do_read()
{
async_read(sock_, sb_, req_, strand_.wrap(
std::bind(&peer::on_read, shared_from_this(),
asio::placeholders::error)));
}
void on_read(error_code const& ec)
{
if(ec)
return fail(ec, "read");
auto path = req_.url;
if(path == "/")
path = "/index.html";
path = server_.root_ + path;
if(! boost::filesystem::exists(path))
{
response_v1<string_body> res;
res.status = 404;
res.reason = "Not Found";
res.version = req_.version;
res.headers.insert("Server", "http_async_server");
res.headers.insert("Content-Type", "text/html");
res.body = "The file '" + path + "' was not found";
prepare(res);
async_write(sock_, std::move(res),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
return;
}
resp_type res;
res.status = 200;
res.reason = "OK";
res.version = req_.version;
res.headers.insert("Server", "http_async_server");
res.headers.insert("Content-Type", mime_type(path));
res.body = path;
try
{
prepare(res);
}
catch(std::exception const& e)
{
res = {};
res.status = 500;
res.reason = "Internal Error";
res.version = req_.version;
res.headers.insert("Server", "http_async_server");
res.headers.insert("Content-Type", "text/html");
res.body =
std::string{"An internal error occurred"} + e.what();
prepare(res);
}
async_write(sock_, std::move(res),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
}
void on_write(error_code ec)
{
if(ec)
fail(ec, "write");
do_read();
}
};
void
log_args()
{
}
template<class Arg, class... Args>
void
log_args(Arg const& arg, Args const&... args)
{
std::cerr << arg;
log_args(args...);
}
void
fail(error_code ec, std::string what)
{
log(what, ": ", ec.message(), "\n");
}
void
on_accept(error_code ec)
{
if(! acceptor_.is_open())
return;
if(ec)
return fail(ec, "accept");
socket_type sock(std::move(sock_));
acceptor_.async_accept(sock_,
std::bind(&http_async_server::on_accept, this,
asio::placeholders::error));
std::make_shared<peer>(std::move(sock), *this)->run();
}
};
} // http
} // beast
#endif

View File

@@ -0,0 +1,61 @@
//
// 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)
//
#include "urls_large_data.hpp"
#include <beast/core/streambuf.hpp>
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <iostream>
using namespace beast::http;
using namespace boost::asio;
template<class String>
void
err(beast::error_code const& ec, String const& what)
{
std::cerr << what << ": " << ec.message() << std::endl;
}
int main(int, char const*[])
{
io_service ios;
for(auto const& host : urls_large_data())
{
try
{
ip::tcp::resolver r(ios);
auto it = r.resolve(
ip::tcp::resolver::query{host, "http"});
ip::tcp::socket sock(ios);
connect(sock, it);
auto ep = sock.remote_endpoint();
request_v1<empty_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;
req.headers.insert("Host", host +
std::string(":") + std::to_string(ep.port()));
req.headers.insert("User-Agent", "beast/http");
prepare(req);
write(sock, req);
response_v1<string_body> res;
streambuf sb;
beast::http::read(sock, sb, res);
std::cout << res;
}
catch(boost::system::system_error const& ec)
{
std::cerr << host << ": " << ec.what();
}
catch(...)
{
std::cerr << host << ": unknown exception" << std::endl;
}
}
}

View File

@@ -0,0 +1,38 @@
//
// 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)
//
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
int main()
{
// Normal boost::asio setup
std::string const host = "boost.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
// Send HTTP request using beast
beast::http::request_v1<beast::http::empty_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast");
beast::http::prepare(req);
beast::http::write(sock, req);
// Receive and print HTTP response using beast
beast::streambuf sb;
beast::http::response_v1<beast::http::streambuf_body> resp;
beast::http::read(sock, sb, resp);
std::cout << resp;
}

View File

@@ -0,0 +1,69 @@
//
// 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)
//
#include "http_async_server.hpp"
#include "http_sync_server.hpp"
#include <beast/test/sig_wait.hpp>
#include <boost/program_options.hpp>
#include <iostream>
int main(int ac, char const* av[])
{
using namespace beast::http;
namespace po = boost::program_options;
po::options_description desc("Options");
desc.add_options()
("root,r", po::value<std::string>()->implicit_value("."),
"Set the root directory for serving files")
("port,p", po::value<std::uint16_t>()->implicit_value(8080),
"Set the port number for the server")
("ip", po::value<std::string>()->implicit_value("0.0.0.0"),
"Set the IP address to bind to, \"0.0.0.0\" for all")
("threads,n", po::value<std::size_t>()->implicit_value(4),
"Set the number of threads to use")
("sync,s", "Launch a synchronous server")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
std::string root = ".";
if(vm.count("root"))
root = vm["root"].as<std::string>();
std::uint16_t port = 8080;
if(vm.count("port"))
port = vm["port"].as<std::uint16_t>();
std::string ip = "0.0.0.0";
if(vm.count("ip"))
ip = vm["ip"].as<std::string>();
std::size_t threads = 4;
if(vm.count("threads"))
threads = vm["threads"].as<std::size_t>();
bool sync = vm.count("sync") > 0;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
endpoint_type ep{address_type::from_string(ip), port};
if(sync)
{
http_sync_server server(ep, root);
beast::test::sig_wait();
}
else
{
http_async_server server(ep, threads, root);
beast::test::sig_wait();
}
}

View File

@@ -0,0 +1,210 @@
//
// 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_SYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
#include "file_body.hpp"
#include "mime_type.hpp"
#include <beast/core/streambuf.hpp>
#include <boost/asio.hpp>
#include <cstdint>
#include <cstdio>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <utility>
#include <iostream>
namespace beast {
namespace http {
class http_sync_server
{
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
using req_type = request_v1<string_body>;
using resp_type = response_v1<file_body>;
bool log_ = true;
std::mutex m_;
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
std::string root_;
std::thread thread_;
public:
http_sync_server(endpoint_type const& ep,
std::string const& root)
: sock_(ios_)
, acceptor_(ios_)
, root_(root)
{
acceptor_.open(ep.protocol());
acceptor_.bind(ep);
acceptor_.listen(
boost::asio::socket_base::max_connections);
acceptor_.async_accept(sock_,
std::bind(&http_sync_server::on_accept, this,
beast::asio::placeholders::error));
thread_ = std::thread{[&]{ ios_.run(); }};
}
~http_sync_server()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
thread_.join();
}
template<class... Args>
void
log(Args const&... args)
{
if(log_)
{
std::lock_guard<std::mutex> lock(m_);
log_args(args...);
}
}
private:
void
log_args()
{
}
template<class Arg, class... Args>
void
log_args(Arg const& arg, Args const&... args)
{
std::cerr << arg;
log_args(args...);
}
void
fail(error_code ec, std::string what)
{
log(what, ": ", ec.message(), "\n");
}
void
fail(int id, error_code const& ec)
{
if(ec != boost::asio::error::operation_aborted &&
ec != boost::asio::error::eof)
log("#", id, " ", ec.message(), "\n");
}
struct lambda
{
int id;
http_sync_server& self;
socket_type sock;
boost::asio::io_service::work work;
lambda(int id_, http_sync_server& self_,
socket_type&& sock_)
: id(id_)
, self(self_)
, sock(std::move(sock_))
, work(sock.get_io_service())
{
}
void operator()()
{
self.do_peer(id, std::move(sock));
}
};
void
on_accept(error_code ec)
{
if(! acceptor_.is_open())
return;
if(ec)
return fail(ec, "accept");
static int id_ = 0;
std::thread{lambda{++id_, *this, std::move(sock_)}}.detach();
acceptor_.async_accept(sock_,
std::bind(&http_sync_server::on_accept, this,
asio::placeholders::error));
}
void
do_peer(int id, socket_type&& sock0)
{
socket_type sock(std::move(sock0));
streambuf sb;
error_code ec;
for(;;)
{
req_type req;
http::read(sock, sb, req, ec);
if(ec)
break;
auto path = req.url;
if(path == "/")
path = "/index.html";
path = root_ + path;
if(! boost::filesystem::exists(path))
{
response_v1<string_body> res;
res.status = 404;
res.reason = "Not Found";
res.version = req.version;
res.headers.insert("Server", "http_sync_server");
res.headers.insert("Content-Type", "text/html");
res.body = "The file '" + path + "' was not found";
prepare(res);
write(sock, res, ec);
if(ec)
break;
}
resp_type res;
res.status = 200;
res.reason = "OK";
res.version = req.version;
res.headers.insert("Server", "http_sync_server");
res.headers.insert("Content-Type", mime_type(path));
res.body = path;
try
{
prepare(res);
}
catch(std::exception const& e)
{
res = {};
res.status = 500;
res.reason = "Internal Error";
res.version = req.version;
res.headers.insert("Server", "http_sync_server");
res.headers.insert("Content-Type", "text/html");
res.body =
std::string{"An internal error occurred"} + e.what();
prepare(res);
}
write(sock, res, ec);
if(ec)
break;
}
fail(id, ec);
}
};
} // http
} // beast
#endif

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
//
// 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 URLS_LARGE_DATA_H_INCLUDED
#define URLS_LARGE_DATA_H_INCLUDED
#include <vector>
std::vector<char const*> const&
urls_large_data();
#endif

View File

@@ -0,0 +1,35 @@
//
// 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)
//
#include <beast/core/to_string.hpp>
#include <beast/websocket.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
int main()
{
// Normal boost::asio setup
std::string const host = "echo.websocket.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
// WebSocket connect and send message using beast
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
ws.handshake(host, "/");
ws.write(boost::asio::buffer("Hello, world!"));
// Receive WebSocket message, print and close using beast
beast::streambuf sb;
beast::websocket::opcode op;
ws.read(op, sb);
ws.close(beast::websocket::close_code::normal);
std::cout << to_string(sb.data()) << "\n";
}

View File

@@ -0,0 +1,3 @@
# Extras
These are not part of the official public Beast interface but they are used by the tests and some third party programs.

View File

@@ -0,0 +1,149 @@
//
// 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_TEST_FAIL_COUNTER_HPP
#define BEAST_TEST_FAIL_COUNTER_HPP
#include <beast/core/error.hpp>
namespace beast {
namespace test {
enum error
{
success = 0,
fail_error
};
namespace detail {
class fail_error_category : public boost::system::error_category
{
public:
const char*
name() const noexcept override
{
return "test";
}
std::string
message(int ev) const override
{
switch(static_cast<error>(ev))
{
default:
case error::fail_error:
return "test error";
}
}
boost::system::error_condition
default_error_condition(int ev) const noexcept override
{
return boost::system::error_condition{ev, *this};
}
bool
equivalent(int ev,
boost::system::error_condition const& condition
) const noexcept override
{
return condition.value() == ev &&
&condition.category() == this;
}
bool
equivalent(error_code const& error, int ev) const noexcept override
{
return error.value() == ev &&
&error.category() == this;
}
};
inline
boost::system::error_category const&
get_error_category()
{
static fail_error_category const cat{};
return cat;
}
} // detail
inline
error_code
make_error_code(error ev)
{
return error_code{static_cast<int>(ev),
detail::get_error_category()};
}
/** A countdown to simulated failure.
On the Nth operation, the class will fail with the specified
error code, or the default error code of @ref fail_error.
*/
class fail_counter
{
std::size_t n_;
error_code ec_;
public:
fail_counter(fail_counter&&) = default;
/** Construct a counter.
@param The 0-based index of the operation to fail on or after.
*/
explicit
fail_counter(std::size_t n,
error_code ev = make_error_code(fail_error))
: n_(n)
, ec_(ev)
{
}
/// Throw an exception on the Nth failure
void
fail()
{
if(n_ > 0)
--n_;
if(! n_)
throw system_error{ec_};
}
/// Set an error code on the Nth failure
bool
fail(error_code& ec)
{
if(n_ > 0)
--n_;
if(! n_)
{
ec = ec_;
return true;
}
return false;
}
};
} // test
} // beast
namespace boost {
namespace system {
template<>
struct is_error_code_enum<beast::test::error>
{
static bool const value = true;
};
} // system
} // boost
#endif

View File

@@ -0,0 +1,195 @@
//
// 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_TEST_FAIL_STREAM_HPP
#define BEAST_TEST_FAIL_STREAM_HPP
#include <beast/core/async_completion.hpp>
#include <beast/core/bind_handler.hpp>
#include <beast/core/error.hpp>
#include <beast/core/detail/get_lowest_layer.hpp>
#include <beast/websocket/teardown.hpp>
#include <beast/test/fail_counter.hpp>
#include <boost/optional.hpp>
namespace beast {
namespace test {
/** A stream wrapper that fails.
On the Nth operation, the stream will fail with the specified
error code, or the default error code of invalid_argument.
*/
template<class NextLayer>
class fail_stream
{
boost::optional<fail_counter> fc_;
fail_counter* pfc_;
NextLayer next_layer_;
public:
using next_layer_type =
typename std::remove_reference<NextLayer>::type;
using lowest_layer_type =
typename beast::detail::get_lowest_layer<
next_layer_type>::type;
fail_stream(fail_stream&&) = delete;
fail_stream(fail_stream const&) = delete;
fail_stream& operator=(fail_stream&&) = delete;
fail_stream& operator=(fail_stream const&) = delete;
template<class... Args>
explicit
fail_stream(std::size_t n, Args&&... args)
: fc_(n)
, pfc_(&*fc_)
, next_layer_(std::forward<Args>(args)...)
{
}
template<class... Args>
explicit
fail_stream(fail_counter& fc, Args&&... args)
: pfc_(&fc)
, next_layer_(std::forward<Args>(args)...)
{
}
next_layer_type&
next_layer()
{
return next_layer_;
}
lowest_layer_type&
lowest_layer()
{
return next_layer_.lowest_layer();
}
lowest_layer_type const&
lowest_layer() const
{
return next_layer_.lowest_layer();
}
boost::asio::io_service&
get_io_service()
{
return next_layer_.get_io_service();
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
pfc_->fail();
return next_layer_.read_some(buffers);
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers, error_code& ec)
{
if(pfc_->fail(ec))
return 0;
return next_layer_.read_some(buffers, ec);
}
template<class MutableBufferSequence, class ReadHandler>
typename async_completion<
ReadHandler, void(error_code)>::result_type
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
error_code ec;
if(pfc_->fail(ec))
{
async_completion<
ReadHandler, void(error_code, std::size_t)
> completion(handler);
next_layer_.get_io_service().post(
bind_handler(completion.handler, ec, 0));
return completion.result.get();
}
return next_layer_.async_read_some(buffers,
std::forward<ReadHandler>(handler));
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
pfc_->fail();
return next_layer_.write_some(buffers);
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers, error_code& ec)
{
if(pfc_->fail(ec))
return 0;
return next_layer_.write_some(buffers, ec);
}
template<class ConstBufferSequence, class WriteHandler>
typename async_completion<
WriteHandler, void(error_code)>::result_type
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
error_code ec;
if(pfc_->fail(ec))
{
async_completion<
WriteHandler, void(error_code, std::size_t)
> completion(handler);
next_layer_.get_io_service().post(
bind_handler(completion.handler, ec, 0));
return completion.result.get();
}
return next_layer_.async_write_some(buffers,
std::forward<WriteHandler>(handler));
}
friend
void
teardown(websocket::teardown_tag,
fail_stream<NextLayer>& stream,
boost::system::error_code& ec)
{
if(stream.pfc_->fail(ec))
return;
beast::websocket_helpers::call_teardown(stream.next_layer(), ec);
}
template<class TeardownHandler>
friend
void
async_teardown(websocket::teardown_tag,
fail_stream<NextLayer>& stream,
TeardownHandler&& handler)
{
error_code ec;
if(stream.pfc_->fail(ec))
{
stream.get_io_service().post(
bind_handler(std::move(handler), ec));
return;
}
beast::websocket_helpers::call_async_teardown(
stream.next_layer(), std::forward<TeardownHandler>(handler));
}
};
} // test
} // beast
#endif

View File

@@ -0,0 +1,36 @@
//
// 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_TEST_SIG_WAIT_HPP
#define BEAST_TEST_SIG_WAIT_HPP
#include <boost/asio.hpp>
#include <condition_variable>
#include <mutex>
namespace beast {
namespace test {
/// Block until SIGINT or SIGTERM is received.
inline
void
sig_wait()
{
boost::asio::io_service ios;
boost::asio::signal_set signals(
ios, SIGINT, SIGTERM);
signals.async_wait(
[&](boost::system::error_code const&, int)
{
});
ios.run();
}
} // test
} // beast
#endif

View File

@@ -0,0 +1,126 @@
//
// 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_TEST_STRING_STREAM_HPP
#define BEAST_TEST_STRING_STREAM_HPP
#include <beast/core/bind_handler.hpp>
#include <beast/core/error.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <string>
namespace beast {
namespace test {
/** A SyncStream and AsyncStream that reads from a string.
This class behaves like a socket, except that written data is simply
discarded, and when data is read it comes from a string provided
at construction.
*/
class string_stream
{
std::string s_;
boost::asio::io_service& ios_;
public:
string_stream(boost::asio::io_service& ios,
std::string s)
: s_(std::move(s))
, ios_(ios)
{
}
boost::asio::io_service&
get_io_service()
{
return ios_;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
error_code ec;
auto const n = read_some(buffers, ec);
if(ec)
throw system_error{ec};
return n;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
auto const n = boost::asio::buffer_copy(
buffers, boost::asio::buffer(s_));
if(n > 0)
s_.erase(0, n);
else
ec = boost::asio::error::eof;
return n;
}
template<class MutableBufferSequence, class ReadHandler>
typename async_completion<ReadHandler,
void(error_code, std::size_t)>::result_type
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
auto const n = boost::asio::buffer_copy(
buffers, boost::asio::buffer(s_));
error_code ec;
if(n > 0)
s_.erase(0, n);
else
ec = boost::asio::error::eof;
async_completion<ReadHandler,
void(error_code, std::size_t)> completion(handler);
ios_.post(bind_handler(
completion.handler, ec, n));
return completion.result.get();
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
error_code ec;
auto const n = write_some(buffers, ec);
if(ec)
throw system_error{ec};
return n;
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers,
error_code&)
{
return boost::asio::buffer_size(buffers);
}
template<class ConstBuffeSequence, class WriteHandler>
typename async_completion<WriteHandler,
void(error_code, std::size_t)>::result_type
async_write_some(ConstBuffeSequence const& buffers,
WriteHandler&& handler)
{
async_completion<WriteHandler,
void(error_code, std::size_t)> completion(handler);
ios_.post(bind_handler(completion.handler,
error_code{}, boost::asio::buffer_size(buffers)));
return completion.result.get();
}
};
} // test
} // beast
#endif

View File

@@ -0,0 +1,108 @@
//
// 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_TEST_YIELD_TO_HPP
#define BEAST_TEST_YIELD_TO_HPP
#include <boost/asio/io_service.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/optional.hpp>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
namespace beast {
namespace test {
/** Mix-in to support tests using asio coroutines.
Derive from this class and use yield_to to launch test functions
inside coroutines. This is handy for testing asynchronous asio
code.
*/
class enable_yield_to
{
protected:
boost::asio::io_service ios_;
private:
boost::optional<boost::asio::io_service::work> work_;
std::thread thread_;
std::mutex m_;
std::condition_variable cv_;
bool running_ = false;
public:
/// The type of yield context passed to functions.
using yield_context =
boost::asio::yield_context;
enable_yield_to()
: work_(ios_)
, thread_([&]
{
ios_.run();
}
)
{
}
~enable_yield_to()
{
work_ = boost::none;
thread_.join();
}
/// Return the `io_service` associated with the object
boost::asio::io_service&
get_io_service()
{
return ios_;
}
/** Run a function in a coroutine.
This call will block until the coroutine terminates.
Function will be called with this signature:
@code
void f(yield_context);
@endcode
*/
template<class Function>
void
yield_to(Function&& f);
};
template<class Function>
void
enable_yield_to::yield_to(Function&& f)
{
{
std::lock_guard<std::mutex> lock(m_);
running_ = true;
}
boost::asio::spawn(ios_,
[&](boost::asio::yield_context do_yield)
{
f(do_yield);
std::lock_guard<std::mutex> lock(m_);
running_ = false;
cv_.notify_all();
}
, boost::coroutines::attributes(2 * 1024 * 1024));
std::unique_lock<std::mutex> lock(m_);
cv_.wait(lock, [&]{ return ! running_; });
}
} // test
} // beast
#endif

View File

@@ -0,0 +1,55 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_AMOUNT_HPP
#define BEAST_UNIT_TEST_AMOUNT_HPP
#include <cstddef>
#include <ostream>
#include <string>
namespace beast {
namespace unit_test {
/** Utility for producing nicely composed output of amounts with units. */
class amount
{
private:
std::size_t n_;
std::string const& what_;
public:
amount(amount const&) = default;
amount& operator=(amount const&) = delete;
template<class = void>
amount(std::size_t n, std::string const& what);
friend
std::ostream&
operator<<(std::ostream& s, amount const& t);
};
template<class>
amount::amount(std::size_t n, std::string const& what)
: n_(n)
, what_(what)
{
}
inline
std::ostream&
operator<<(std::ostream& s, amount const& t)
{
s << t.n_ << " " << t.what_ <<((t.n_ != 1) ? "s" : "");
return s;
}
} // unit_test
} // beast
#endif

View File

@@ -0,0 +1,91 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_DETAIL_CONST_CONTAINER_HPP
#define BEAST_UNIT_TEST_DETAIL_CONST_CONTAINER_HPP
namespace beast {
namespace unit_test {
namespace detail {
/** Adapter to constrain a container interface.
The interface allows for limited read only operations. Derived classes
provide additional behavior.
*/
template<class Container>
class const_container
{
private:
using cont_type = Container;
cont_type m_cont;
protected:
cont_type& cont()
{
return m_cont;
}
cont_type const& cont() const
{
return m_cont;
}
public:
using value_type = typename cont_type::value_type;
using size_type = typename cont_type::size_type;
using difference_type = typename cont_type::difference_type;
using iterator = typename cont_type::const_iterator;
using const_iterator = typename cont_type::const_iterator;
/** Returns `true` if the container is empty. */
bool
empty() const
{
return m_cont.empty();
}
/** Returns the number of items in the container. */
size_type
size() const
{
return m_cont.size();
}
/** Returns forward iterators for traversal. */
/** @{ */
const_iterator
begin() const
{
return m_cont.cbegin();
}
const_iterator
cbegin() const
{
return m_cont.cbegin();
}
const_iterator
end() const
{
return m_cont.cend();
}
const_iterator
cend() const
{
return m_cont.cend();
}
/** @} */
};
} // detail
} // unit_test
} // beast
#endif

View File

@@ -0,0 +1,132 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_DSTREAM_HPP
#define BEAST_UNIT_TEST_DSTREAM_HPP
#include <ios>
#include <memory>
#include <ostream>
#include <streambuf>
#include <string>
#ifdef _MSC_VER
# ifndef NOMINMAX
# define NOMINMAX 1
# endif
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# undef WIN32_LEAN_AND_MEAN
# undef NOMINMAX
#endif
namespace beast {
namespace unit_test {
#ifdef _MSC_VER
namespace detail {
template<class CharT, class Traits, class Allocator>
class dstream_buf
: public std::basic_stringbuf<CharT, Traits, Allocator>
{
using ostream = std::basic_ostream<CharT, Traits>;
bool dbg_;
ostream& os_;
template<class T>
void write(T const*) = delete;
void write(char const* s)
{
if(dbg_)
OutputDebugStringA(s);
os_ << s;
}
void write(wchar_t const* s)
{
if(dbg_)
OutputDebugStringW(s);
os_ << s;
}
public:
explicit
dstream_buf(ostream& os)
: os_(os)
, dbg_(IsDebuggerPresent() != FALSE)
{
}
~dstream_buf()
{
sync();
}
int
sync() override
{
write(this->str().c_str());
this->str("");
return 0;
}
};
} // detail
/** std::ostream with Visual Studio IDE redirection.
Instances of this stream wrap a specified `std::ostream`
(such as `std::cout` or `std::cerr`). If the IDE debugger
is attached when the stream is created, output will be
additionally copied to the Visual Studio Output window.
*/
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
>
class basic_dstream
: public std::basic_ostream<CharT, Traits>
{
detail::dstream_buf<
CharT, Traits, Allocator> buf_;
public:
/** Construct a stream.
@param os The output stream to wrap.
*/
explicit
basic_dstream(std::ostream& os)
: std::basic_ostream<CharT, Traits>(&buf_)
, buf_(os)
{
if(os.flags() && std::ios::unitbuf)
std::unitbuf(*this);
}
};
using dstream = basic_dstream<char>;
using dwstream = basic_dstream<wchar_t>;
#else
using dstream = std::ostream&;
using dwstream = std::wostream&;
#endif
} // unit_test
} // beast
#endif

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_UNIT_TEST_GLOBAL_SUITES_HPP
#define BEAST_UNIT_TEST_GLOBAL_SUITES_HPP
#include <beast/unit_test/suite_list.hpp>
namespace beast {
namespace unit_test {
namespace detail {
/// Holds test suites registered during static initialization.
inline
suite_list&
global_suites()
{
static suite_list s;
return s;
}
template<class Suite>
struct insert_suite
{
insert_suite(char const* name, char const* module,
char const* library, bool manual)
{
global_suites().insert<Suite>(
name, module, library, manual);
}
};
} // detail
/// Holds test suites registered during static initialization.
inline
suite_list const&
global_suites()
{
return detail::global_suites();
}
} // unit_test
} // beast
#endif

View File

@@ -0,0 +1,129 @@
//
// 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)
//
#include <beast/unit_test/amount.hpp>
#include <beast/unit_test/dstream.hpp>
#include <beast/unit_test/global_suites.hpp>
#include <beast/unit_test/match.hpp>
#include <beast/unit_test/reporter.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/program_options.hpp>
#include <cstdlib>
#include <iostream>
#include <vector>
#ifdef _MSC_VER
# ifndef WIN32_LEAN_AND_MEAN // VC_EXTRALEAN
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# undef WIN32_LEAN_AND_MEAN
# else
# include <windows.h>
# endif
#endif
namespace beast {
namespace unit_test {
static
std::string
prefix(suite_info const& s)
{
if(s.manual())
return "|M| ";
return " ";
}
static
void
print(std::ostream& os, suite_list const& c)
{
std::size_t manual = 0;
for(auto const& s : c)
{
os << prefix(s) << s.full_name() << '\n';
if(s.manual())
++manual;
}
os <<
amount(c.size(), "suite") << " total, " <<
amount(manual, "manual suite") <<
'\n'
;
}
// Print the list of suites
// Used with the --print command line option
static
void
print(std::ostream& os)
{
os << "------------------------------------------\n";
print(os, global_suites());
os << "------------------------------------------" <<
std::endl;
}
} // unit_test
} // beast
// Simple main used to produce stand
// alone executables that run unit tests.
int main(int ac, char const* av[])
{
using namespace std;
using namespace beast::unit_test;
#ifdef _MSC_VER
{
int flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
flags |= _CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag(flags);
}
#endif
namespace po = boost::program_options;
po::options_description desc("Options");
desc.add_options()
("help,h", "Produce a help message")
("print,p", "Print the list of available test suites")
("suites,s", po::value<string>(), "suites to run")
;
po::positional_options_description p;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
dstream log{std::cerr};
std::unitbuf(log);
if(vm.count("help"))
{
log << desc << std::endl;
}
else if(vm.count("print"))
{
print(log);
}
else
{
std::string suites;
if(vm.count("suites") > 0)
suites = vm["suites"].as<string>();
reporter r(log);
bool failed;
if(! suites.empty())
failed = r.run_each_if(global_suites(),
match_auto(suites));
else
failed = r.run_each(global_suites());
if(failed)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
}

View File

@@ -0,0 +1,173 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_MATCH_HPP
#define BEAST_UNIT_TEST_MATCH_HPP
#include <beast/unit_test/suite_info.hpp>
#include <string>
namespace beast {
namespace unit_test {
// Predicate for implementing matches
class selector
{
public:
enum mode_t
{
// Run all tests except manual ones
all,
// Run tests that match in any field
automatch,
// Match on suite
suite,
// Match on library
library,
// Match on module (used internally)
module,
// Match nothing (used internally)
none
};
private:
mode_t mode_;
std::string pat_;
std::string library_;
public:
template<class = void>
explicit
selector(mode_t mode, std::string const& pattern = "");
template<class = void>
bool
operator()(suite_info const& s);
};
//------------------------------------------------------------------------------
template<class>
selector::selector(mode_t mode, std::string const& pattern)
: mode_(mode)
, pat_(pattern)
{
if(mode_ == automatch && pattern.empty())
mode_ = all;
}
template<class>
bool
selector::operator()(suite_info const& s)
{
switch(mode_)
{
case automatch:
// suite or full name
if(s.name() == pat_ || s.full_name() == pat_)
{
mode_ = none;
return true;
}
// check module
if(pat_ == s.module())
{
mode_ = module;
library_ = s.library();
return ! s.manual();
}
// check library
if(pat_ == s.library())
{
mode_ = library;
return ! s.manual();
}
return false;
case suite:
return pat_ == s.name();
case module:
return pat_ == s.module() && ! s.manual();
case library:
return pat_ == s.library() && ! s.manual();
case none:
return false;
case all:
default:
// fall through
break;
};
return ! s.manual();
}
//------------------------------------------------------------------------------
// Utility functions for producing predicates to select suites.
/** Returns a predicate that implements a smart matching rule.
The predicate checks the suite, module, and library fields of the
suite_info in that order. When it finds a match, it changes modes
depending on what was found:
If a suite is matched first, then only the suite is selected. The
suite may be marked manual.
If a module is matched first, then only suites from that module
and library not marked manual are selected from then on.
If a library is matched first, then only suites from that library
not marked manual are selected from then on.
*/
inline
selector
match_auto(std::string const& name)
{
return selector(selector::automatch, name);
}
/** Return a predicate that matches all suites not marked manual. */
inline
selector
match_all()
{
return selector(selector::all);
}
/** Returns a predicate that matches a specific suite. */
inline
selector
match_suite(std::string const& name)
{
return selector(selector::suite, name);
}
/** Returns a predicate that matches all suites in a library. */
inline
selector
match_library(std::string const& name)
{
return selector(selector::library, name);
}
} // unit_test
} // beast
#endif

View File

@@ -0,0 +1,92 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_RECORDER_HPP
#define BEAST_UNIT_TEST_RECORDER_HPP
#include <beast/unit_test/results.hpp>
#include <beast/unit_test/runner.hpp>
namespace beast {
namespace unit_test {
/** A test runner that stores the results. */
class recorder : public runner
{
private:
results m_results;
suite_results m_suite;
case_results m_case;
public:
recorder() = default;
recorder(recorder const&) = default;
recorder& operator=(recorder const&) = default;
/** Returns a report with the results of all completed suites. */
results const&
report() const
{
return m_results;
}
private:
virtual
void
on_suite_begin(suite_info const& info) override
{
m_suite = suite_results(info.full_name());
}
virtual
void
on_suite_end() override
{
m_results.insert(std::move(m_suite));
}
virtual
void
on_case_begin(std::string const& name) override
{
m_case = case_results(name);
}
virtual
void
on_case_end() override
{
if(m_case.tests.size() > 0)
m_suite.insert(std::move(m_case));
}
virtual
void
on_pass() override
{
m_case.tests.pass();
}
virtual
void
on_fail(std::string const& reason) override
{
m_case.tests.fail(reason);
}
virtual
void
on_log(std::string const& s) override
{
m_case.log.insert(s);
}
};
} // unit_test
} // beast
#endif

View File

@@ -0,0 +1,294 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_REPORTER_HPP
#define BEAST_UNIT_TEST_REPORTER_HPP
#include <beast/unit_test/amount.hpp>
#include <beast/unit_test/recorder.hpp>
#include <boost/optional.hpp>
#include <algorithm>
#include <chrono>
#include <functional>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
namespace beast {
namespace unit_test {
namespace detail {
/** A simple test runner that writes everything to a stream in real time.
The totals are output when the object is destroyed.
*/
template<class = void>
class reporter : public runner
{
private:
using clock_type = std::chrono::steady_clock;
struct case_results
{
std::string name;
std::size_t total = 0;
std::size_t failed = 0;
explicit
case_results(std::string name_ = "")
: name(std::move(name_))
{
}
};
struct suite_results
{
std::string name;
std::size_t cases = 0;
std::size_t total = 0;
std::size_t failed = 0;
typename clock_type::time_point start = clock_type::now();
explicit
suite_results(std::string name_ = "")
: name(std::move(name_))
{
}
void
add(case_results const& r);
};
struct results
{
using run_time = std::pair<std::string,
typename clock_type::duration>;
enum
{
max_top = 10
};
std::size_t suites = 0;
std::size_t cases = 0;
std::size_t total = 0;
std::size_t failed = 0;
std::vector<run_time> top;
typename clock_type::time_point start = clock_type::now();
void
add(suite_results const& r);
};
std::ostream& os_;
results results_;
suite_results suite_results_;
case_results case_results_;
public:
reporter(reporter const&) = delete;
reporter& operator=(reporter const&) = delete;
~reporter();
explicit
reporter(std::ostream& os = std::cout);
private:
static
std::string
fmtdur(typename clock_type::duration const& d);
virtual
void
on_suite_begin(suite_info const& info) override;
virtual
void
on_suite_end() override;
virtual
void
on_case_begin(std::string const& name) override;
virtual
void
on_case_end() override;
virtual
void
on_pass() override;
virtual
void
on_fail(std::string const& reason) override;
virtual
void
on_log(std::string const& s) override;
};
//------------------------------------------------------------------------------
template<class _>
void
reporter<_>::
suite_results::add(case_results const& r)
{
++cases;
total += r.total;
failed += r.failed;
}
template<class _>
void
reporter<_>::
results::add(suite_results const& r)
{
++suites;
total += r.total;
cases += r.cases;
failed += r.failed;
auto const elapsed = clock_type::now() - r.start;
if(elapsed >= std::chrono::seconds{1})
{
auto const iter = std::lower_bound(top.begin(),
top.end(), elapsed,
[](run_time const& t1,
typename clock_type::duration const& t2)
{
return t1.second > t2;
});
if(iter != top.end())
{
if(top.size() == max_top)
top.resize(top.size() - 1);
top.emplace(iter, r.name, elapsed);
}
else if(top.size() < max_top)
{
top.emplace_back(r.name, elapsed);
}
}
}
//------------------------------------------------------------------------------
template<class _>
reporter<_>::
reporter(std::ostream& os)
: os_(os)
{
}
template<class _>
reporter<_>::~reporter()
{
if(results_.top.size() > 0)
{
os_ << "Longest suite times:\n";
for(auto const& i : results_.top)
os_ << std::setw(8) <<
fmtdur(i.second) << " " << i.first << '\n';
}
auto const elapsed = clock_type::now() - results_.start;
os_ <<
fmtdur(elapsed) << ", " <<
amount{results_.suites, "suite"} << ", " <<
amount{results_.cases, "case"} << ", " <<
amount{results_.total, "test"} << " total, " <<
amount{results_.failed, "failure"} <<
std::endl;
}
template<class _>
std::string
reporter<_>::fmtdur(typename clock_type::duration const& d)
{
using namespace std::chrono;
auto const ms = duration_cast<milliseconds>(d);
if(ms < seconds{1})
return std::to_string(ms.count()) + "ms";
std::stringstream ss;
ss << std::fixed << std::setprecision(1) <<
(ms.count()/1000.) << "s";
return ss.str();
}
template<class _>
void
reporter<_>::
on_suite_begin(suite_info const& info)
{
suite_results_ = suite_results{info.full_name()};
}
template<class _>
void
reporter<_>::on_suite_end()
{
results_.add(suite_results_);
}
template<class _>
void
reporter<_>::
on_case_begin(std::string const& name)
{
case_results_ = case_results(name);
os_ << suite_results_.name <<
(case_results_.name.empty() ? "" :
(" " + case_results_.name)) << std::endl;
}
template<class _>
void
reporter<_>::
on_case_end()
{
suite_results_.add(case_results_);
}
template<class _>
void
reporter<_>::
on_pass()
{
++case_results_.total;
}
template<class _>
void
reporter<_>::
on_fail(std::string const& reason)
{
++case_results_.failed;
++case_results_.total;
os_ <<
"#" << case_results_.total << " failed" <<
(reason.empty() ? "" : ": ") << reason << std::endl;
}
template<class _>
void
reporter<_>::
on_log(std::string const& s)
{
os_ << s;
os_.flush();
}
} // detail
using reporter = detail::reporter<>;
} // unit_test
} // beast
#endif

View File

@@ -0,0 +1,242 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_RESULTS_HPP
#define BEAST_UNIT_TEST_RESULTS_HPP
#include <beast/unit_test/detail/const_container.hpp>
#include <string>
#include <vector>
namespace beast {
namespace unit_test {
/** Holds a set of test condition outcomes in a testcase. */
class case_results
{
public:
/** Holds the result of evaluating one test condition. */
struct test
{
explicit test(bool pass_)
: pass(pass_)
{
}
test(bool pass_, std::string const& reason_)
: pass(pass_)
, reason(reason_)
{
}
bool pass;
std::string reason;
};
private:
class tests_t
: public detail::const_container <std::vector <test>>
{
private:
std::size_t failed_;
public:
tests_t()
: failed_(0)
{
}
/** Returns the total number of test conditions. */
std::size_t
total() const
{
return cont().size();
}
/** Returns the number of failed test conditions. */
std::size_t
failed() const
{
return failed_;
}
/** Register a successful test condition. */
void
pass()
{
cont().emplace_back(true);
}
/** Register a failed test condition. */
void
fail(std::string const& reason = "")
{
++failed_;
cont().emplace_back(false, reason);
}
};
class log_t
: public detail::const_container <std::vector <std::string>>
{
public:
/** Insert a string into the log. */
void
insert(std::string const& s)
{
cont().push_back(s);
}
};
std::string name_;
public:
explicit case_results(std::string const& name = "")
: name_(name)
{
}
/** Returns the name of this testcase. */
std::string const&
name() const
{
return name_;
}
/** Memberspace for a container of test condition outcomes. */
tests_t tests;
/** Memberspace for a container of testcase log messages. */
log_t log;
};
//--------------------------------------------------------------------------
/** Holds the set of testcase results in a suite. */
class suite_results
: public detail::const_container <std::vector <case_results>>
{
private:
std::string name_;
std::size_t total_ = 0;
std::size_t failed_ = 0;
public:
explicit suite_results(std::string const& name = "")
: name_(name)
{
}
/** Returns the name of this suite. */
std::string const&
name() const
{
return name_;
}
/** Returns the total number of test conditions. */
std::size_t
total() const
{
return total_;
}
/** Returns the number of failures. */
std::size_t
failed() const
{
return failed_;
}
/** Insert a set of testcase results. */
/** @{ */
void
insert(case_results&& r)
{
cont().emplace_back(std::move(r));
total_ += r.tests.total();
failed_ += r.tests.failed();
}
void
insert(case_results const& r)
{
cont().push_back(r);
total_ += r.tests.total();
failed_ += r.tests.failed();
}
/** @} */
};
//------------------------------------------------------------------------------
// VFALCO TODO Make this a template class using scoped allocators
/** Holds the results of running a set of testsuites. */
class results
: public detail::const_container <std::vector <suite_results>>
{
private:
std::size_t m_cases;
std::size_t total_;
std::size_t failed_;
public:
results()
: m_cases(0)
, total_(0)
, failed_(0)
{
}
/** Returns the total number of test cases. */
std::size_t
cases() const
{
return m_cases;
}
/** Returns the total number of test conditions. */
std::size_t
total() const
{
return total_;
}
/** Returns the number of failures. */
std::size_t
failed() const
{
return failed_;
}
/** Insert a set of suite results. */
/** @{ */
void
insert(suite_results&& r)
{
m_cases += r.size();
total_ += r.total();
failed_ += r.failed();
cont().emplace_back(std::move(r));
}
void
insert(suite_results const& r)
{
m_cases += r.size();
total_ += r.total();
failed_ += r.failed();
cont().push_back(r);
}
/** @} */
};
} // unit_test
} // beast
#endif

View File

@@ -0,0 +1,288 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_RUNNER_H_INCLUDED
#define BEAST_UNIT_TEST_RUNNER_H_INCLUDED
#include <beast/unit_test/suite_info.hpp>
#include <cassert>
#include <mutex>
#include <ostream>
#include <string>
namespace beast {
namespace unit_test {
/** Unit test runner interface.
Derived classes can customize the reporting behavior. This interface is
injected into the unit_test class to receive the results of the tests.
*/
class runner
{
std::string arg_;
bool default_ = false;
bool failed_ = false;
bool cond_ = false;
std::recursive_mutex mutex_;
public:
runner() = default;
virtual ~runner() = default;
runner(runner const&) = delete;
runner& operator=(runner const&) = delete;
/** Set the argument string.
The argument string is available to suites and
allows for customization of the test. Each suite
defines its own syntax for the argumnet string.
The same argument is passed to all suites.
*/
void
arg(std::string const& s)
{
arg_ = s;
}
/** Returns the argument string. */
std::string const&
arg() const
{
return arg_;
}
/** Run the specified suite.
@return `true` if any conditions failed.
*/
template<class = void>
bool
run(suite_info const& s);
/** Run a sequence of suites.
The expression
`FwdIter::value_type`
must be convertible to `suite_info`.
@return `true` if any conditions failed.
*/
template<class FwdIter>
bool
run(FwdIter first, FwdIter last);
/** Conditionally run a sequence of suites.
pred will be called as:
@code
bool pred(suite_info const&);
@endcode
@return `true` if any conditions failed.
*/
template<class FwdIter, class Pred>
bool
run_if(FwdIter first, FwdIter last, Pred pred = Pred{});
/** Run all suites in a container.
@return `true` if any conditions failed.
*/
template<class SequenceContainer>
bool
run_each(SequenceContainer const& c);
/** Conditionally run suites in a container.
pred will be called as:
@code
bool pred(suite_info const&);
@endcode
@return `true` if any conditions failed.
*/
template<class SequenceContainer, class Pred>
bool
run_each_if(SequenceContainer const& c, Pred pred = Pred{});
protected:
/// Called when a new suite starts.
virtual
void
on_suite_begin(suite_info const&)
{
}
/// Called when a suite ends.
virtual
void
on_suite_end()
{
}
/// Called when a new case starts.
virtual
void
on_case_begin(std::string const&)
{
}
/// Called when a new case ends.
virtual
void
on_case_end()
{
}
/// Called for each passing condition.
virtual
void
on_pass()
{
}
/// Called for each failing condition.
virtual
void
on_fail(std::string const&)
{
}
/// Called when a test logs output.
virtual
void
on_log(std::string const&)
{
}
private:
friend class suite;
// Start a new testcase.
template<class = void>
void
testcase(std::string const& name);
template<class = void>
void
pass();
template<class = void>
void
fail(std::string const& reason);
template<class = void>
void
log(std::string const& s);
};
//------------------------------------------------------------------------------
template<class>
bool
runner::run(suite_info const& s)
{
// Enable 'default' testcase
default_ = true;
failed_ = false;
on_suite_begin(s);
s.run(*this);
// Forgot to call pass or fail.
assert(cond_);
on_case_end();
on_suite_end();
return failed_;
}
template<class FwdIter>
bool
runner::run(FwdIter first, FwdIter last)
{
bool failed(false);
for(;first != last; ++first)
failed = run(*first) || failed;
return failed;
}
template<class FwdIter, class Pred>
bool
runner::run_if(FwdIter first, FwdIter last, Pred pred)
{
bool failed(false);
for(;first != last; ++first)
if(pred(*first))
failed = run(*first) || failed;
return failed;
}
template<class SequenceContainer>
bool
runner::run_each(SequenceContainer const& c)
{
bool failed(false);
for(auto const& s : c)
failed = run(s) || failed;
return failed;
}
template<class SequenceContainer, class Pred>
bool
runner::run_each_if(SequenceContainer const& c, Pred pred)
{
bool failed(false);
for(auto const& s : c)
if(pred(s))
failed = run(s) || failed;
return failed;
}
template<class>
void
runner::testcase(std::string const& name)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
// Name may not be empty
assert(default_ || ! name.empty());
// Forgot to call pass or fail
assert(default_ || cond_);
if(! default_)
on_case_end();
default_ = false;
cond_ = false;
on_case_begin(name);
}
template<class>
void
runner::pass()
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
if(default_)
testcase("");
on_pass();
cond_ = true;
}
template<class>
void
runner::fail(std::string const& reason)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
if(default_)
testcase("");
on_fail(reason);
failed_ = true;
cond_ = true;
}
template<class>
void
runner::log(std::string const& s)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
if(default_)
testcase("");
on_log(s);
}
} // unit_test
} // beast
#endif

View File

@@ -0,0 +1,657 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_SUITE_HPP
#define BEAST_UNIT_TEST_SUITE_HPP
#include <beast/unit_test/runner.hpp>
#include <boost/filesystem.hpp>
#include <ostream>
#include <sstream>
#include <string>
namespace beast {
namespace unit_test {
class thread;
enum abort_t
{
no_abort_on_fail,
abort_on_fail
};
/** A testsuite class.
Derived classes execute a series of testcases, where each testcase is
a series of pass/fail tests. To provide a unit test using this class,
derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a
translation unit.
*/
class suite
{
private:
bool abort_ = false;
bool aborted_ = false;
runner* runner_ = nullptr;
// This exception is thrown internally to stop the current suite
// in the event of a failure, if the option to stop is set.
struct abort_exception : public std::exception
{
char const*
what() const noexcept override
{
return "test suite aborted";
}
};
template<class CharT, class Traits, class Allocator>
class log_buf
: public std::basic_stringbuf<CharT, Traits, Allocator>
{
suite& suite_;
public:
explicit
log_buf(suite& self)
: suite_(self)
{
}
~log_buf()
{
sync();
}
int
sync() override
{
auto const& s = this->str();
if(s.size() > 0)
suite_.runner_->log(s);
this->str("");
return 0;
}
};
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
>
class log_os : public std::basic_ostream<CharT, Traits>
{
log_buf<CharT, Traits, Allocator> buf_;
public:
explicit
log_os(suite& self)
: std::basic_ostream<CharT, Traits>(&buf_)
, buf_(self)
{
}
};
class scoped_testcase;
class testcase_t
{
suite& suite_;
std::stringstream ss_;
public:
explicit
testcase_t(suite& self)
: suite_(self)
{
}
/** Open a new testcase.
A testcase is a series of evaluated test conditions. A test
suite may have multiple test cases. A test is associated with
the last opened testcase. When the test first runs, a default
unnamed case is opened. Tests with only one case may omit the
call to testcase.
@param abort Determines if suite continues running after a failure.
*/
void
operator()(std::string const& name,
abort_t abort = no_abort_on_fail);
scoped_testcase
operator()(abort_t abort);
template<class T>
scoped_testcase
operator<<(T const& t);
};
public:
/** Logging output stream.
Text sent to the log output stream will be forwarded to
the output stream associated with the runner.
*/
log_os<char> log;
/** Memberspace for declaring test cases. */
testcase_t testcase;
/** Returns the "current" running suite.
If no suite is running, nullptr is returned.
*/
static
suite*
this_suite()
{
return *p_this_suite();
}
suite()
: log(*this)
, testcase(*this)
{
}
/** Invokes the test using the specified runner.
Data members are set up here instead of the constructor as a
convenience to writing the derived class to avoid repetition of
forwarded constructor arguments to the base.
Normally this is called by the framework for you.
*/
template<class = void>
void
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.
This function provides improved logging by incorporating the
file name and line number into the reported output on failure,
as well as additional text specified by the caller.
@param shouldBeTrue The condition to test. The condition
is evaluated in a boolean context.
@param reason Optional added text to output on a failure.
@param file The source code file where the test failed.
@param line The source code line number where the test failed.
@return `true` if the test condition indicates success.
*/
/** @{ */
template<class Condition>
bool
expect(Condition const& shouldBeTrue)
{
return expect(shouldBeTrue, {});
}
template<class Condition>
bool
expect(Condition const& shouldBeTrue, std::string const& reason);
template<class Condition>
bool
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, "");
}
template<class E, class F, class String>
bool
except(F&& f, String const& reason);
template<class E, class F>
bool
except(F&& f)
{
return except<E>(f, "");
}
template<class F, class String>
bool
unexcept(F&& f, String const& reason);
template<class F>
bool
unexcept(F&& f)
{
return unexcept(f, "");
}
/** Return the argument associated with the runner. */
std::string const&
arg() const
{
return runner_->arg();
}
// DEPRECATED
// @return `true` if the test condition indicates success(a false value)
template<class Condition, class String>
bool
unexpected(Condition shouldBeFalse,
String const& reason);
template<class Condition>
bool
unexpected(Condition shouldBeFalse)
{
return unexpected(shouldBeFalse, "");
}
private:
friend class thread;
static
suite**
p_this_suite()
{
static suite* pts = nullptr;
return &pts;
}
/** Runs the suite. */
virtual
void
run() = 0;
void
propagate_abort();
template<class = void>
void
run(runner& r);
};
//------------------------------------------------------------------------------
// Helper for streaming testcase names
class suite::scoped_testcase
{
private:
suite& suite_;
std::stringstream& ss_;
public:
scoped_testcase& operator=(scoped_testcase const&) = delete;
~scoped_testcase()
{
auto const& name = ss_.str();
if(! name.empty())
{
suite_.log.flush();
suite_.runner_->testcase(name);
}
}
scoped_testcase(suite& self, std::stringstream& ss)
: suite_(self)
, ss_(ss)
{
ss_.clear();
ss_.str({});
}
template<class T>
scoped_testcase(suite& self,
std::stringstream& ss, T const& t)
: suite_(self)
, ss_(ss)
{
ss_.clear();
ss_.str({});
ss_ << t;
}
template<class T>
scoped_testcase&
operator<<(T const& t)
{
ss_ << t;
return *this;
}
};
//------------------------------------------------------------------------------
inline
void
suite::testcase_t::operator()(
std::string const& name, abort_t abort)
{
suite_.abort_ = abort == abort_on_fail;
suite_.log.flush();
suite_.runner_->testcase(name);
}
inline
suite::scoped_testcase
suite::testcase_t::operator()(abort_t abort)
{
suite_.abort_ = abort == abort_on_fail;
return { suite_, ss_ };
}
template<class T>
inline
suite::scoped_testcase
suite::testcase_t::operator<<(T const& t)
{
return { suite_, ss_, t };
}
//------------------------------------------------------------------------------
template<class>
void
suite::
operator()(runner& r)
{
*p_this_suite() = this;
try
{
run(r);
*p_this_suite() = nullptr;
}
catch(...)
{
*p_this_suite() = nullptr;
throw;
}
}
template<class Condition>
bool
suite::
expect(
Condition const& shouldBeTrue, std::string const& reason)
{
if(shouldBeTrue)
{
pass();
return true;
}
fail(reason);
return false;
}
template<class Condition>
bool
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
{
f();
fail(reason);
return false;
}
catch(...)
{
pass();
}
return true;
}
template<class E, class F, class String>
bool
suite::
except(F&& f, String const& reason)
{
try
{
f();
fail(reason);
return false;
}
catch(E const&)
{
pass();
}
return true;
}
template<class F, class String>
bool
suite::
unexcept(F&& f, String const& reason)
{
try
{
f();
pass();
return true;
}
catch(...)
{
fail(reason);
}
return false;
}
template<class Condition, class String>
bool
suite::
unexpected(
Condition shouldBeFalse, String const& reason)
{
bool const b =
static_cast<bool>(shouldBeFalse);
if(! b)
pass();
else
fail(reason);
return ! b;
}
template<class>
void
suite::
pass()
{
propagate_abort();
runner_->pass();
}
// ::fail
template<class>
void
suite::
fail(std::string const& reason)
{
propagate_abort();
runner_->fail(reason);
if(abort_)
{
aborted_ = true;
throw abort_exception();
}
}
inline
void
suite::
propagate_abort()
{
if(abort_ && aborted_)
throw abort_exception();
}
template<class>
void
suite::
run(runner& r)
{
runner_ = &r;
try
{
run();
}
catch(abort_exception const&)
{
// ends the suite
}
catch(std::exception const& e)
{
runner_->fail("unhandled exception: " +
std::string(e.what()));
}
catch(...)
{
runner_->fail("unhandled exception");
}
}
#ifndef BEAST_EXPECT
/** Check a precondition.
If the condition is false, the file and line number are reported.
*/
#define BEAST_EXPECT(cond) expect(cond, __FILE__, __LINE__)
#endif
#ifndef BEAST_EXPECTS
/** Check a precondition.
If the condition is false, the file and line number are reported.
*/
#define BEAST_EXPECTS(cond, reason) expect(cond, reason, __FILE__, __LINE__)
#endif
} // unit_test
} // beast
//------------------------------------------------------------------------------
// detail:
// This inserts the suite with the given manual flag
#define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual) \
static beast::unit_test::detail::insert_suite <Class##_test> \
Library ## Module ## Class ## _test_instance( \
#Class, #Module, #Library, manual)
//------------------------------------------------------------------------------
// Preprocessor directives for controlling unit test definitions.
// If this is already defined, don't redefine it. This allows
// programs to provide custom behavior for testsuite definitions
//
#ifndef BEAST_DEFINE_TESTSUITE
/** Enables insertion of test suites into the global container.
The default is to insert all test suite definitions into the global
container. If BEAST_DEFINE_TESTSUITE is user defined, this macro
has no effect.
*/
#ifndef BEAST_NO_UNIT_TEST_INLINE
#define BEAST_NO_UNIT_TEST_INLINE 0
#endif
/** Define a unit test suite.
Class The type representing the class being tested.
Module Identifies the module.
Library Identifies the library.
The declaration for the class implementing the test should be the same
as Class ## _test. For example, if Class is aged_ordered_container, the
test class must be declared as:
@code
struct aged_ordered_container_test : beast::unit_test::suite
{
//...
};
@endcode
The macro invocation must appear in the same namespace as the test class.
*/
#if BEAST_NO_UNIT_TEST_INLINE
#define BEAST_DEFINE_TESTSUITE(Class,Module,Library)
#else
#include <beast/unit_test/global_suites.hpp>
#define BEAST_DEFINE_TESTSUITE(Class,Module,Library) \
BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,false)
#define BEAST_DEFINE_TESTSUITE_MANUAL(Class,Module,Library) \
BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,true)
#endif
#endif
//------------------------------------------------------------------------------
#endif

View File

@@ -0,0 +1,122 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_SUITE_INFO_HPP
#define BEAST_UNIT_TEST_SUITE_INFO_HPP
#include <cstring>
#include <functional>
#include <string>
#include <utility>
namespace beast {
namespace unit_test {
class runner;
/** Associates a unit test type with metadata. */
class suite_info
{
using run_type = std::function<void(runner&)>;
std::string name_;
std::string module_;
std::string library_;
bool manual_;
run_type run_;
public:
suite_info(
std::string name,
std::string module,
std::string library,
bool manual,
run_type run)
: name_(std::move(name))
, module_(std::move(module))
, library_(std::move(library))
, manual_(manual)
, run_(std::move(run))
{
}
std::string const&
name() const
{
return name_;
}
std::string const&
module() const
{
return module_;
}
std::string const&
library() const
{
return library_;
}
/// Returns `true` if this suite only runs manually.
bool
manual() const
{
return manual_;
}
/// Return the canonical suite name as a string.
std::string
full_name() const
{
return library_ + "." + module_ + "." + name_;
}
/// Run a new instance of the associated test suite.
void
run(runner& r) const
{
run_(r);
}
friend
bool
operator<(suite_info const& lhs, suite_info const& rhs)
{
return
std::tie(lhs.library_, lhs.module_, lhs.name_) <
std::tie(rhs.library_, rhs.module_, rhs.name_);
}
};
//------------------------------------------------------------------------------
/// Convenience for producing suite_info for a given test type.
template<class Suite>
suite_info
make_suite_info(
std::string name,
std::string module,
std::string library,
bool manual)
{
return suite_info(
std::move(name),
std::move(module),
std::move(library),
manual,
[](runner& r)
{
Suite{}(r);
}
);
}
} // unit_test
} // beast
#endif

View File

@@ -0,0 +1,77 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_SUITE_LIST_HPP
#define BEAST_UNIT_TEST_SUITE_LIST_HPP
#include <beast/unit_test/suite_info.hpp>
#include <beast/unit_test/detail/const_container.hpp>
#include <cassert>
#include <typeindex>
#include <set>
#include <unordered_set>
namespace beast {
namespace unit_test {
/// A container of test suites.
class suite_list
: public detail::const_container <std::set <suite_info>>
{
private:
#ifndef NDEBUG
std::unordered_set<std::string> names_;
std::unordered_set<std::type_index> classes_;
#endif
public:
/** Insert a suite into the set.
The suite must not already exist.
*/
template<class Suite>
void
insert(
char const* name,
char const* module,
char const* library,
bool manual);
};
//------------------------------------------------------------------------------
template<class Suite>
void
suite_list::insert(
char const* name,
char const* module,
char const* library,
bool manual)
{
#ifndef NDEBUG
{
std::string s;
s = std::string(library) + "." + module + "." + name;
auto const result(names_.insert(s));
assert(result.second); // Duplicate name
}
{
auto const result(classes_.insert(
std::type_index(typeid(Suite))));
assert(result.second); // Duplicate type
}
#endif
cont().emplace(make_suite_info<Suite>(
name, module, library, manual));
}
} // unit_test
} // beast
#endif

View File

@@ -0,0 +1,124 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_THREAD_HPP
#define BEAST_UNIT_TEST_THREAD_HPP
#include <beast/unit_test/suite.hpp>
#include <functional>
#include <thread>
#include <utility>
namespace beast {
namespace unit_test {
/** Replacement for std::thread that handles exceptions in unit tests. */
class thread
{
private:
suite* s_ = nullptr;
std::thread t_;
public:
using id = std::thread::id;
using native_handle_type = std::thread::native_handle_type;
thread() = default;
thread(thread const&) = delete;
thread& operator=(thread const&) = delete;
thread(thread&& other)
: s_(other.s_)
, t_(std::move(other.t_))
{
}
thread& operator=(thread&& other)
{
s_ = other.s_;
t_ = std::move(other.t_);
return *this;
}
template<class F, class... Args>
explicit
thread(suite& s, F&& f, Args&&... args)
: s_(&s)
{
std::function<void(void)> b =
std::bind(std::forward<F>(f),
std::forward<Args>(args)...);
t_ = std::thread(&thread::run, this,
std::move(b));
}
bool
joinable() const
{
return t_.joinable();
}
std::thread::id
get_id() const
{
return t_.get_id();
}
static
unsigned
hardware_concurrency() noexcept
{
return std::thread::hardware_concurrency();
}
void
join()
{
t_.join();
s_->propagate_abort();
}
void
detach()
{
t_.detach();
}
void
swap(thread& other)
{
std::swap(s_, other.s_);
std::swap(t_, other.t_);
}
private:
void
run(std::function <void(void)> f)
{
try
{
f();
}
catch(suite::abort_exception const&)
{
}
catch(std::exception const& e)
{
s_->fail("unhandled exception: " +
std::string(e.what()));
}
catch(...)
{
s_->fail("unhandled exception");
}
}
};
} // unit_test
} // beast
#endif

View File

@@ -0,0 +1,31 @@
//
// 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_CORE_HPP
#define BEAST_CORE_HPP
#include <beast/core/async_completion.hpp>
#include <beast/core/basic_streambuf.hpp>
#include <beast/core/bind_handler.hpp>
#include <beast/core/buffer_cat.hpp>
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/buffers_adapter.hpp>
#include <beast/core/consuming_buffers.hpp>
#include <beast/core/error.hpp>
#include <beast/core/handler_alloc.hpp>
#include <beast/core/handler_concepts.hpp>
#include <beast/core/placeholders.hpp>
#include <beast/core/prepare_buffers.hpp>
#include <beast/core/static_streambuf.hpp>
#include <beast/core/static_string.hpp>
#include <beast/core/stream_concepts.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/core/dynabuf_readstream.hpp>
#include <beast/core/to_string.hpp>
#include <beast/core/write_dynabuf.hpp>
#endif

View File

@@ -0,0 +1,87 @@
//
// 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_ASYNC_COMPLETION_HPP
#define BEAST_ASYNC_COMPLETION_HPP
#include <beast/core/handler_concepts.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/handler_type.hpp>
#include <type_traits>
#include <utility>
namespace beast {
/** Helper for customizing the return type of asynchronous initiation functions.
This class template is used to transform caller-provided completion
handlers in calls to asynchronous initiation functions. The transformation
allows customization of the return type of the initiating function, and the
function signature of the final handler.
@tparam CompletionHandler A completion handler, or a user defined type
with specializations for customizing the return type (for example,
`boost::asio::use_future` or `boost::asio::yield_context`).
@tparam Signature The callable signature of the final completion handler.
Example:
@code
...
template<class CompletionHandler>
typename async_completion<CompletionHandler,
void(boost::system::error_code)>::result_type
async_initfn(..., CompletionHandler&& handler)
{
async_completion<CompletionHandler,
void(boost::system::error_code)> completion(handler);
...
return completion.result.get();
}
@endcode
@note See <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf">
Library Foundations For Asynchronous Operations</a>
*/
template<class CompletionHandler, class Signature>
struct async_completion
{
/** The type of the final handler called by the asynchronous initiation function.
Objects of this type will be callable with the specified signature.
*/
using handler_type =
typename boost::asio::handler_type<
CompletionHandler, Signature>::type;
/// The type of the value returned by the asynchronous initiation function.
using result_type = typename
boost::asio::async_result<handler_type>::type;
/** Construct the helper.
@param token The completion handler. Copies will be made as
required. If `CompletionHandler` is movable, it may also be moved.
*/
async_completion(typename std::remove_reference<CompletionHandler>::type& token)
: handler(std::forward<CompletionHandler>(token))
, result(handler)
{
static_assert(is_CompletionHandler<handler_type, Signature>::value,
"Handler requirements not met");
}
/// The final completion handler, callable with the specified signature.
handler_type handler;
/// The return value of the asynchronous initiation function.
boost::asio::async_result<handler_type> result;
};
} // beast
#endif

View File

@@ -0,0 +1,303 @@
//
// 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_BASIC_STREAMBUF_HPP
#define BEAST_BASIC_STREAMBUF_HPP
#include <beast/core/detail/empty_base_optimization.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/intrusive/list.hpp>
#include <iterator>
#include <limits>
#include <memory>
#include <type_traits>
namespace beast {
/** A @b `DynamicBuffer` that uses multiple buffers internally.
The implementation uses a sequence of one or more character arrays
of varying sizes. Additional character array objects are appended to
the sequence to accommodate changes in the size of the character
sequence.
@note Meets the requirements of @b `DynamicBuffer`.
@tparam Allocator The allocator to use for managing memory.
*/
template<class Allocator>
class basic_streambuf
#if ! GENERATING_DOCS
: private detail::empty_base_optimization<
typename std::allocator_traits<Allocator>::
template rebind_alloc<std::uint8_t>>
#endif
{
public:
#if GENERATING_DOCS
/// The type of allocator used.
using allocator_type = Allocator;
#else
using allocator_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<std::uint8_t>;
#endif
private:
// Storage for the list of buffers representing the input
// and output sequences. The allocation for each element
// contains `element` followed by raw storage bytes.
class element;
using alloc_traits = std::allocator_traits<allocator_type>;
using list_type = typename boost::intrusive::make_list<element,
boost::intrusive::constant_time_size<true>>::type;
using iterator = typename list_type::iterator;
using const_iterator = typename list_type::const_iterator;
using size_type = typename std::allocator_traits<Allocator>::size_type;
using const_buffer = boost::asio::const_buffer;
using mutable_buffer = boost::asio::mutable_buffer;
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<iterator>::iterator_category>::value,
"BidirectionalIterator requirements not met");
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<const_iterator>::iterator_category>::value,
"BidirectionalIterator requirements not met");
list_type list_; // list of allocated buffers
iterator out_; // element that contains out_pos_
size_type alloc_size_; // min amount to allocate
size_type in_size_ = 0; // size of the input sequence
size_type in_pos_ = 0; // input offset in list_.front()
size_type out_pos_ = 0; // output offset in *out_
size_type out_end_ = 0; // output end offset in list_.back()
public:
#if GENERATING_DOCS
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = implementation_defined;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = implementation_defined;
#else
class const_buffers_type;
class mutable_buffers_type;
#endif
/// Destructor.
~basic_streambuf();
/** Move constructor.
The new object will have the input sequence of
the other stream buffer, and an empty output sequence.
@note After the move, the moved-from object will have
an empty input and output sequence, with no internal
buffers allocated.
*/
basic_streambuf(basic_streambuf&&);
/** Move constructor.
The new object will have the input sequence of
the other stream buffer, and an empty output sequence.
@note After the move, the moved-from object will have
an empty input and output sequence, with no internal
buffers allocated.
@param alloc The allocator to associate with the
stream buffer.
*/
basic_streambuf(basic_streambuf&&,
allocator_type const& alloc);
/** Move assignment.
This object will have the input sequence of
the other stream buffer, and an empty output sequence.
@note After the move, the moved-from object will have
an empty input and output sequence, with no internal
buffers allocated.
*/
basic_streambuf&
operator=(basic_streambuf&&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
basic_streambuf(basic_streambuf const&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
@param alloc The allocator to associate with the
stream buffer.
*/
basic_streambuf(basic_streambuf const&,
allocator_type const& alloc);
/** Copy assignment.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
basic_streambuf& operator=(basic_streambuf const&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
template<class OtherAlloc>
basic_streambuf(basic_streambuf<OtherAlloc> const&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
@param alloc The allocator to associate with the
stream buffer.
*/
template<class OtherAlloc>
basic_streambuf(basic_streambuf<OtherAlloc> const&,
allocator_type const& alloc);
/** Copy assignment.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
template<class OtherAlloc>
basic_streambuf& operator=(basic_streambuf<OtherAlloc> const&);
/** Construct a stream buffer.
@param alloc_size The size of buffer to allocate. This is a
soft limit, calls to prepare for buffers exceeding this size
will allocate the larger size. The default allocation size
is 1KB (1024 bytes).
@param alloc The allocator to use. If this parameter is
unspecified, a default constructed allocator will be used.
*/
explicit
basic_streambuf(std::size_t alloc_size = 1024,
Allocator const& alloc = allocator_type{});
/// Returns a copy of the associated allocator.
allocator_type
get_allocator() const
{
return this->member();
}
/// Returns the size of the input sequence.
size_type
size() const
{
return in_size_;
}
/// Returns the permitted maximum sum of the sizes of the input and output sequence.
size_type
max_size() const
{
return std::numeric_limits<std::size_t>::max();
}
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
std::size_t
capacity() const;
/** Get a list of buffers that represents the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type
data() const;
/** Get a list of buffers that represents the output sequence, with the given size.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
mutable_buffers_type
prepare(size_type n);
/** Move bytes from the output sequence to the input sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
void
commit(size_type n);
/// Remove bytes from the input sequence.
void
consume(size_type n);
// Helper for boost::asio::read_until
template<class OtherAllocator>
friend
std::size_t
read_size_helper(basic_streambuf<
OtherAllocator> const& streambuf, std::size_t max_size);
private:
void
clear();
void
move_assign(basic_streambuf& other, std::false_type);
void
move_assign(basic_streambuf& other, std::true_type);
void
copy_assign(basic_streambuf const& other, std::false_type);
void
copy_assign(basic_streambuf const& other, std::true_type);
void
delete_list();
void
debug_check() const;
};
/** Format output to a @ref basic_streambuf.
@param streambuf The @ref basic_streambuf to write to.
@param t The object to write.
@return A reference to the @ref basic_streambuf.
*/
template<class Allocator, class T>
basic_streambuf<Allocator>&
operator<<(basic_streambuf<Allocator>& streambuf, T const& t);
} // beast
#include <beast/core/impl/basic_streambuf.ipp>
#endif

View File

@@ -0,0 +1,68 @@
//
// 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_BIND_HANDLER_HPP
#define BEAST_BIND_HANDLER_HPP
#include <beast/core/handler_concepts.hpp>
#include <beast/core/detail/bind_handler.hpp>
#include <type_traits>
#include <utility>
namespace beast {
/** Bind parameters to a completion handler, creating a wrapped handler.
This function creates a new handler which, when invoked with no
parameters, calls the original handler with the list of bound arguments.
The passed handler and arguments are forwarded into the returned handler,
which provides the same `io_service` execution guarantees as the original
handler.
Unlike `io_service::wrap`, the returned handler can be used in a
subsequent call to `io_service::post` instead of `io_service::dispatch`,
to ensure that the handler will not be invoked immediately by the
calling function.
Example:
@code
template<class AsyncReadStream, class ReadHandler>
void
do_cancel(AsyncReadStream& stream, ReadHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::forward<ReadHandler>(handler),
boost::asio::error::operation_aborted, 0));
}
@endcode
@param handler The handler to wrap.
@param args A list of arguments to bind to the handler. The
arguments are forwarded into the returned object.
*/
template<class CompletionHandler, class... Args>
#if GENERATING_DOCS
implementation_defined
#else
detail::bound_handler<
typename std::decay<CompletionHandler>::type, Args...>
#endif
bind_handler(CompletionHandler&& handler, Args&&... args)
{
static_assert(is_CompletionHandler<
CompletionHandler, void(Args...)>::value,
"CompletionHandler requirements not met");
return detail::bound_handler<typename std::decay<
CompletionHandler>::type, Args...>(std::forward<
CompletionHandler>(handler),
std::forward<Args>(args)...);
}
} // beast
#endif

View File

@@ -0,0 +1,53 @@
//
// 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_BUFFER_CAT_HPP
#define BEAST_BUFFER_CAT_HPP
#include <beast/core/detail/buffer_cat.hpp>
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <iterator>
#include <new>
#include <stdexcept>
#include <tuple>
#include <utility>
namespace beast {
/** Concatenate 2 or more buffer sequences to form a `ConstBufferSequence`.
This function returns a @b `ConstBufferSequence` that when iterated,
efficiently concatenates the input buffer sequences. Copies of the
arguments passed will be made; however, the returned object does
not take ownership of the underlying memory. The application is still
responsible for managing the lifetime of the referenced memory.
@param buffers The list of buffer sequences to concatenate.
@return A new @b `ConstBufferSequence` that represents the
concatenation of the input buffer sequences.
*/
#if GENERATING_DOCS
template<class... BufferSequence>
implementation_defined
buffer_cat(BufferSequence const&... buffers)
#else
template<class B1, class B2, class... Bn>
detail::buffer_cat_helper<
boost::asio::const_buffer, B1, B2, Bn...>
buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
#endif
{
return detail::buffer_cat_helper<
boost::asio::const_buffer,
B1, B2, Bn...>(b1, b2, bn...);
}
} // beast
#endif

View File

@@ -0,0 +1,61 @@
//
// 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_BUFFER_CONCEPTS_HPP
#define BEAST_BUFFER_CONCEPTS_HPP
#include <beast/core/detail/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <type_traits>
namespace beast {
/// Determine if `T` meets the requirements of @b `BufferSequence`.
template<class T, class BufferType>
#if GENERATING_DOCS
struct is_BufferSequence : std::integral_constant<bool, ...>
#else
struct is_BufferSequence : detail::is_BufferSequence<T, BufferType>::type
#endif
{
};
/// Determine if `T` meets the requirements of @b `ConstBufferSequence`.
template<class T>
#if GENERATING_DOCS
struct is_ConstBufferSequence : std::integral_constant<bool, ...>
#else
struct is_ConstBufferSequence :
is_BufferSequence<T, boost::asio::const_buffer>
#endif
{
};
/// Determine if `T` meets the requirements of @b `DynamicBuffer`.
template<class T>
#if GENERATING_DOCS
struct is_DynamicBuffer : std::integral_constant<bool, ...>
#else
struct is_DynamicBuffer : detail::is_DynamicBuffer<T>::type
#endif
{
};
/// Determine if `T` meets the requirements of @b `MutableBufferSequence`.
template<class T>
#if GENERATING_DOCS
struct is_MutableBufferSequence : std::integral_constant<bool, ...>
#else
struct is_MutableBufferSequence :
is_BufferSequence<T, boost::asio::mutable_buffer>
#endif
{
};
} // beast
#endif

View File

@@ -0,0 +1,150 @@
//
// 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_BUFFERS_ADAPTER_HPP
#define BEAST_BUFFERS_ADAPTER_HPP
#include <beast/core/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <type_traits>
namespace beast {
/** Adapts a @b `MutableBufferSequence` into a @b `DynamicBuffer`.
This class wraps a @b `MutableBufferSequence` to meet the requirements
of @b `DynamicBuffer`. Upon construction the input and output sequences are
empty. A copy of the mutable buffer sequence object is stored; however,
ownership of the underlying memory is not transferred. The caller is
responsible for making sure that referenced memory remains valid
for the duration of any operations.
The size of the mutable buffer sequence determines the maximum
number of bytes which may be prepared and committed.
@tparam MutableBufferSequence The type of mutable buffer sequence to wrap.
*/
template<class MutableBufferSequence>
class buffers_adapter
{
static_assert(is_MutableBufferSequence<MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
using iter_type = typename MutableBufferSequence::const_iterator;
MutableBufferSequence bs_;
iter_type begin_;
iter_type out_;
iter_type end_;
std::size_t max_size_;
std::size_t in_pos_ = 0; // offset in *begin_
std::size_t in_size_ = 0; // size of input sequence
std::size_t out_pos_ = 0; // offset in *out_
std::size_t out_end_ = 0; // output end offset
template<class Deduced>
buffers_adapter(Deduced&& other,
std::size_t nbegin, std::size_t nout,
std::size_t nend)
: bs_(std::forward<Deduced>(other).bs_)
, begin_(std::next(bs_.begin(), nbegin))
, out_(std::next(bs_.begin(), nout))
, end_(std::next(bs_.begin(), nend))
, max_size_(other.max_size_)
, in_pos_(other.in_pos_)
, in_size_(other.in_size_)
, out_pos_(other.out_pos_)
, out_end_(other.out_end_)
{
}
public:
#if GENERATING_DOCS
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = implementation_defined;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = implementation_defined;
#else
class const_buffers_type;
class mutable_buffers_type;
#endif
/// Move constructor.
buffers_adapter(buffers_adapter&& other);
/// Copy constructor.
buffers_adapter(buffers_adapter const& other);
/// Move assignment.
buffers_adapter& operator=(buffers_adapter&& other);
/// Copy assignment.
buffers_adapter& operator=(buffers_adapter const&);
/** Construct a buffers adapter.
@param buffers The mutable buffer sequence to wrap. A copy of
the object will be made, but ownership of the memory is not
transferred.
*/
explicit
buffers_adapter(MutableBufferSequence const& buffers);
/// Returns the largest size output sequence possible.
std::size_t
max_size() const
{
return max_size_;
}
/// Get the size of the input sequence.
std::size_t
size() const
{
return in_size_;
}
/** Get a list of buffers that represents the output sequence, with the given size.
@throws std::length_error if the size would exceed the limit
imposed by the underlying mutable buffer sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
mutable_buffers_type
prepare(std::size_t n);
/** Move bytes from the output sequence to the input sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
void
commit(std::size_t n);
/** Get a list of buffers that represents the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type
data() const;
/// Remove bytes from the input sequence.
void
consume(std::size_t n);
};
} // beast
#include <beast/core/impl/buffers_adapter.ipp>
#endif

View File

@@ -0,0 +1,140 @@
//
// 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_CONSUMING_BUFFERS_HPP
#define BEAST_CONSUMING_BUFFERS_HPP
#include <beast/core/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <iterator>
#include <type_traits>
#include <utility>
namespace beast {
/** Adapter to trim the front of a `BufferSequence`.
This adapter wraps a buffer sequence to create a new sequence
which may be incrementally consumed. Bytes consumed are removed
from the front of the buffer. The underlying memory is not changed,
instead the adapter efficiently iterates through a subset of
the buffers wrapped.
The wrapped buffer is not modified, a copy is made instead.
Ownership of the underlying memory is not transferred, the application
is still responsible for managing its lifetime.
@tparam BufferSequence The buffer sequence to wrap.
@tparam ValueType The type of buffer of the final buffer sequence. This
can be different from the buffer type of the wrapped sequence. For
example, a `MutableBufferSequence` can be transformed into a
consumable `ConstBufferSequence`. Violations of buffer const safety
are not permitted, and will result in a compile error.
*/
template<class BufferSequence,
class ValueType = typename BufferSequence::value_type>
class consuming_buffers
{
using iter_type =
typename BufferSequence::const_iterator;
static_assert(is_BufferSequence<BufferSequence, ValueType>::value,
"BufferSequence requirements not met");
static_assert(std::is_constructible<ValueType,
typename std::iterator_traits<iter_type>::value_type>::value,
"ValueType requirements not met");
BufferSequence bs_;
iter_type begin_;
std::size_t skip_ = 0;
template<class Deduced>
consuming_buffers(Deduced&& other, std::size_t nbegin)
: bs_(std::forward<Deduced>(other).bs_)
, begin_(std::next(bs_.begin(), nbegin))
, skip_(other.skip_)
{
}
public:
/// The type for each element in the list of buffers.
using value_type = ValueType;
#if GENERATING_DOCS
/// A bidirectional iterator type that may be used to read elements.
using const_iterator = implementation_defined;
#else
class const_iterator;
#endif
/// Move constructor.
consuming_buffers(consuming_buffers&&);
/// Copy constructor.
consuming_buffers(consuming_buffers const&);
/// Move assignment.
consuming_buffers& operator=(consuming_buffers&&);
/// Copy assignment.
consuming_buffers& operator=(consuming_buffers const&);
/** Construct to represent a buffer sequence.
A copy of the buffer sequence is made. Ownership of the
underlying memory is not transferred or copied.
*/
explicit
consuming_buffers(BufferSequence const& buffers);
/// Get a bidirectional iterator to the first element.
const_iterator
begin() const;
/// Get a bidirectional iterator for one past the last element.
const_iterator
end() const;
/** Remove bytes from the beginning of the sequence.
@param n The number of bytes to remove. If this is
larger than the number of bytes remaining, all the
bytes remaining are removed.
*/
void
consume(std::size_t n);
};
/** Returns a new, consumed buffer sequence.
This function returns a new buffer sequence which when iterated,
efficiently represents the portion of the original buffer sequence
with `n` bytes removed from the beginning.
Copies will be made of the buffer sequence passed, but ownership
of the underlying memory is not transferred.
@param buffers The buffer sequence to consume.
@param n The number of bytes to remove from the front. If this is
larger than the size of the buffer sequence, an empty buffer sequence
is returned.
*/
template<class BufferSequence>
consuming_buffers<BufferSequence, typename BufferSequence::value_type>
consumed_buffers(BufferSequence const& buffers, std::size_t n);
} // beast
#include <beast/core/impl/consuming_buffers.ipp>
#endif

View File

@@ -0,0 +1,178 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_BASE64_HPP
#define BEAST_DETAIL_BASE64_HPP
#include <cctype>
#include <string>
namespace beast {
namespace detail {
/*
Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
Copyright notice:
base64.cpp and base64.h
Copyright (C) 2004-2008 Ren<65> Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
template<class = void>
std::string const&
base64_alphabet()
{
static std::string const alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
return alphabet;
}
inline
bool
is_base64(unsigned char c)
{
return (std::isalnum(c) || (c == '+') || (c == '/'));
}
template<class = void>
std::string
base64_encode (std::uint8_t const* data,
std::size_t in_len)
{
unsigned char c3[3], c4[4];
int i = 0;
int j = 0;
std::string ret;
ret.reserve (3 + in_len * 8 / 6);
char const* alphabet (base64_alphabet().data());
while(in_len--)
{
c3[i++] = *(data++);
if(i == 3)
{
c4[0] = (c3[0] & 0xfc) >> 2;
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
c4[3] = c3[2] & 0x3f;
for(i = 0; (i < 4); i++)
ret += alphabet[c4[i]];
i = 0;
}
}
if(i)
{
for(j = i; j < 3; j++)
c3[j] = '\0';
c4[0] = (c3[0] & 0xfc) >> 2;
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
c4[3] = c3[2] & 0x3f;
for(j = 0; (j < i + 1); j++)
ret += alphabet[c4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
template<class = void>
std::string
base64_encode (std::string const& s)
{
return base64_encode (reinterpret_cast <
std::uint8_t const*> (s.data()), s.size());
}
template<class = void>
std::string
base64_decode(std::string const& data)
{
auto in_len = data.size();
unsigned char c3[3], c4[4];
int i = 0;
int j = 0;
int in_ = 0;
std::string ret;
ret.reserve (in_len * 6 / 8); // ???
while(in_len-- && (data[in_] != '=') &&
is_base64(data[in_]))
{
c4[i++] = data[in_]; in_++;
if(i == 4) {
for(i = 0; i < 4; i++)
c4[i] = static_cast<unsigned char>(
base64_alphabet().find(c4[i]));
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
for(i = 0; (i < 3); i++)
ret += c3[i];
i = 0;
}
}
if(i)
{
for(j = i; j < 4; j++)
c4[j] = 0;
for(j = 0; j < 4; j++)
c4[j] = static_cast<unsigned char>(
base64_alphabet().find(c4[j]));
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
for(j = 0; (j < i - 1); j++)
ret += c3[j];
}
return ret;
}
} // detail
} // beast
#endif

View File

@@ -0,0 +1,113 @@
//
// 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_BIND_DETAIL_HANDLER_HPP
#define BEAST_BIND_DETAIL_HANDLER_HPP
#include <beast/core/detail/integer_sequence.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/detail/handler_cont_helpers.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <utility>
namespace beast {
namespace detail {
/* Nullary handler that calls Handler with bound arguments.
The bound handler provides the same io_service execution
guarantees as the original handler.
*/
template<class Handler, class... Args>
class bound_handler
{
private:
using args_type = std::tuple<
typename std::decay<Args>::type...>;
Handler h_;
args_type args_;
template<class Tuple, std::size_t... S>
static void invoke(Handler& h, Tuple& args,
index_sequence<S...>)
{
h(std::get<S>(args)...);
}
public:
using result_type = void;
template<class DeducedHandler>
explicit
bound_handler(DeducedHandler&& handler, Args&&... args)
: h_(std::forward<DeducedHandler>(handler))
, args_(std::forward<Args>(args)...)
{
}
void
operator()()
{
invoke(h_, args_,
index_sequence_for<Args...>());
}
void
operator()() const
{
invoke(h_, args_,
index_sequence_for<Args...>());
}
friend
void*
asio_handler_allocate(
std::size_t size, bound_handler* h)
{
return boost_asio_handler_alloc_helpers::
allocate(size, h->h_);
}
friend
void
asio_handler_deallocate(
void* p, std::size_t size, bound_handler* h)
{
boost_asio_handler_alloc_helpers::
deallocate(p, size, h->h_);
}
friend
bool
asio_handler_is_continuation(bound_handler* h)
{
return boost_asio_handler_cont_helpers::
is_continuation (h->h_);
}
template<class F>
friend
void
asio_handler_invoke(F&& f, bound_handler* h)
{
boost_asio_handler_invoke_helpers::
invoke(f, h->h_);
}
};
} // detail
} // beast
#include <functional>
namespace std {
template<class Handler, class... Args>
void bind(beast::detail::bound_handler<
Handler, Args...>, ...) = delete;
} // std
#endif

View File

@@ -0,0 +1,471 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_BUFFER_CAT_HPP
#define BEAST_DETAIL_BUFFER_CAT_HPP
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <iterator>
#include <new>
#include <stdexcept>
#include <tuple>
#include <utility>
namespace beast {
namespace detail {
template<class ValueType, class... Bs>
class buffer_cat_helper
{
std::tuple<Bs...> bs_;
public:
using value_type = ValueType;
class const_iterator;
buffer_cat_helper(buffer_cat_helper&&) = default;
buffer_cat_helper(buffer_cat_helper const&) = default;
buffer_cat_helper& operator=(buffer_cat_helper&&) = default;
buffer_cat_helper& operator=(buffer_cat_helper const&) = default;
explicit
buffer_cat_helper(Bs const&... bs)
: bs_(bs...)
{
}
const_iterator
begin() const;
const_iterator
end() const;
};
template<class U>
std::size_t constexpr
max_sizeof()
{
return sizeof(U);
}
template<class U0, class U1, class... Us>
std::size_t constexpr
max_sizeof()
{
return
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
max_sizeof<U0>() : max_sizeof<U1, Us...>();
}
template<class ValueType, class... Bs>
class buffer_cat_helper<
ValueType, Bs...>::const_iterator
{
std::size_t n_;
std::tuple<Bs...> const* bs_;
std::array<std::uint8_t,
max_sizeof<typename Bs::const_iterator...>()> buf_;
friend class buffer_cat_helper<ValueType, Bs...>;
template<std::size_t I>
using C = std::integral_constant<std::size_t, I>;
template<std::size_t I>
using iter_t = typename std::tuple_element<
I, std::tuple<Bs...>>::type::const_iterator;
template<std::size_t I>
iter_t<I>&
iter()
{
return *reinterpret_cast<
iter_t<I>*>(buf_.data());
}
template<std::size_t I>
iter_t<I> const&
iter() const
{
return *reinterpret_cast<
iter_t<I> const*>(buf_.data());
}
public:
using value_type = ValueType;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
~const_iterator();
const_iterator();
const_iterator(const_iterator&& other);
const_iterator(const_iterator const& other);
const_iterator& operator=(const_iterator&& other);
const_iterator& operator=(const_iterator const& other);
bool
operator==(const_iterator const& other) const;
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const;
pointer
operator->() const = delete;
const_iterator&
operator++();
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--();
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
const_iterator(
std::tuple<Bs...> const& bs, bool at_end);
void
construct(C<sizeof...(Bs)>)
{
auto constexpr I = sizeof...(Bs);
n_ = I;
}
template<std::size_t I>
void
construct(C<I>)
{
if(std::get<I>(*bs_).begin() !=
std::get<I>(*bs_).end())
{
n_ = I;
new(buf_.data()) iter_t<I>{
std::get<I>(*bs_).begin()};
return;
}
construct(C<I+1>{});
}
void
destroy(C<sizeof...(Bs)>)
{
return;
}
template<std::size_t I>
void
destroy(C<I>)
{
if(n_ == I)
{
using Iter = iter_t<I>;
iter<I>().~Iter();
return;
}
destroy(C<I+1>{});
}
void
move(C<sizeof...(Bs)>, const_iterator&&)
{
}
template<std::size_t I>
void
move(C<I>, const_iterator&& other)
{
if(n_ == I)
{
new(buf_.data()) iter_t<I>{
std::move(other.iter<I>())};
return;
}
move(C<I+1>{}, std::move(other));
}
void
copy(C<sizeof...(Bs)>, const_iterator const&)
{
}
template<std::size_t I>
void
copy(C<I>, const_iterator const& other)
{
if(n_ == I)
{
new(buf_.data()) iter_t<I>{
other.iter<I>()};
return;
}
copy(C<I+1>{}, other);
}
bool
equal(C<sizeof...(Bs)>,
const_iterator const&) const
{
return true;
}
template<std::size_t I>
bool
equal(C<I>, const_iterator const& other) const
{
if(n_ == I)
return iter<I>() == other.iter<I>();
return equal(C<I+1>{}, other);
}
[[noreturn]]
reference
dereference(C<sizeof...(Bs)>) const
{
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
reference
dereference(C<I>) const
{
if(n_ == I)
return *iter<I>();
return dereference(C<I+1>{});
}
[[noreturn]]
void
increment(C<sizeof...(Bs)>)
{
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
void
increment(C<I>)
{
if(n_ == I)
{
if(++iter<I>() !=
std::get<I>(*bs_).end())
return;
using Iter = iter_t<I>;
iter<I>().~Iter();
return construct(C<I+1>{});
}
increment(C<I+1>{});
}
void
decrement(C<sizeof...(Bs)>)
{
auto constexpr I = sizeof...(Bs);
if(n_ == I)
{
--n_;
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()};
}
decrement(C<I-1>{});
}
void
decrement(C<0>)
{
auto constexpr I = 0;
if(iter<I>() != std::get<I>(*bs_).begin())
{
--iter<I>();
return;
}
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
void
decrement(C<I>)
{
if(n_ == I)
{
if(iter<I>() != std::get<I>(*bs_).begin())
{
--iter<I>();
return;
}
--n_;
using Iter = iter_t<I>;
iter<I>().~Iter();
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()};
}
decrement(C<I-1>{});
}
};
//------------------------------------------------------------------------------
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::~const_iterator()
{
destroy(C<0>{});
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator()
: n_(sizeof...(Bs))
, bs_(nullptr)
{
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator(
std::tuple<Bs...> const& bs, bool at_end)
: bs_(&bs)
{
if(at_end)
n_ = sizeof...(Bs);
else
construct(C<0>{});
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator(const_iterator&& other)
: n_(other.n_)
, bs_(other.bs_)
{
move(C<0>{}, std::move(other));
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator(const_iterator const& other)
: n_(other.n_)
, bs_(other.bs_)
{
copy(C<0>{}, other);
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator=(const_iterator&& other) ->
const_iterator&
{
if(&other == this)
return *this;
destroy(C<0>{});
n_ = other.n_;
bs_ = other.bs_;
move(C<0>{}, std::move(other));
return *this;
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator=(const_iterator const& other) ->
const_iterator&
{
if(&other == this)
return *this;
destroy(C<0>{});
n_ = other.n_;
bs_ = other.bs_;
copy(C<0>{}, other);
return *this;
}
template<class ValueType, class... Bs>
bool
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator==(const_iterator const& other) const
{
if(bs_ != other.bs_)
return false;
if(n_ != other.n_)
return false;
return equal(C<0>{}, other);
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator*() const ->
reference
{
return dereference(C<0>{});
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator++() ->
const_iterator&
{
increment(C<0>{});
return *this;
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator--() ->
const_iterator&
{
decrement(C<sizeof...(Bs)>{});
return *this;
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::begin() const ->
const_iterator
{
return const_iterator(bs_, false);
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::end() const ->
const_iterator
{
return const_iterator(bs_, true);
}
} // detail
} // beast
#endif

View File

@@ -0,0 +1,140 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_BUFFER_CONCEPTS_HPP
#define BEAST_DETAIL_BUFFER_CONCEPTS_HPP
#include <boost/asio/buffer.hpp>
#include <iterator>
#include <type_traits>
namespace beast {
namespace detail {
// Types that meet the requirements,
// for use with std::declval only.
template<class BufferType>
struct BufferSequence
{
using value_type = BufferType;
using const_iterator = BufferType const*;
~BufferSequence();
BufferSequence(BufferSequence const&) = default;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
};
using ConstBufferSequence =
BufferSequence<boost::asio::const_buffer>;
using MutableBufferSequence =
BufferSequence<boost::asio::mutable_buffer>;
template<class T, class BufferType>
class is_BufferSequence
{
template<class U, class R = std::is_convertible<
typename U::value_type, BufferType> >
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_base_of<
#if 0
std::bidirectional_iterator_tag,
typename std::iterator_traits<
typename U::const_iterator>::iterator_category>>
#else
// workaround:
// boost::asio::detail::consuming_buffers::const_iterator
// is not bidirectional
std::forward_iterator_tag,
typename std::iterator_traits<
typename U::const_iterator>::iterator_category>>
#endif
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R = typename
std::is_convertible<decltype(
std::declval<U>().begin()),
typename U::const_iterator>::type>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
template<class U, class R = typename std::is_convertible<decltype(
std::declval<U>().end()),
typename U::const_iterator>::type>
static R check4(int);
template<class>
static std::false_type check4(...);
using type4 = decltype(check4<T>(0));
public:
using type = std::integral_constant<bool,
std::is_copy_constructible<T>::value &&
std::is_destructible<T>::value &&
type1::value && type2::value &&
type3::value && type4::value>;
};
template<class T>
class is_DynamicBuffer
{
template<class U, class R = std::integral_constant<
bool, is_BufferSequence<decltype(
std::declval<U>().prepare(1)),
boost::asio::mutable_buffer>::type::value>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::integral_constant<
bool, is_BufferSequence<decltype(
std::declval<U>().data()),
boost::asio::const_buffer>::type::value>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R = decltype(
std::declval<U>().commit(1), std::true_type{})>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
template<class U, class R = decltype(
std::declval<U>().consume(1), std::true_type{})>
static R check4(int);
template<class>
static std::false_type check4(...);
using type4 = decltype(check4<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().size()), std::size_t>>
static R check5(int);
template<class>
static std::false_type check5(...);
using type5 = decltype(check5<T>(0));
public:
using type = std::integral_constant<bool,
type1::value && type2::value &&
type3::value && type4::value &&
type5::value>;
};
} // detail
} // beast
#endif

View File

@@ -0,0 +1,106 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_CI_CHAR_TRAITS_HPP
#define BEAST_DETAIL_CI_CHAR_TRAITS_HPP
#include <boost/range/algorithm/equal.hpp>
#include <boost/utility/string_ref.hpp>
#include <array>
#include <cstdint>
namespace beast {
namespace detail {
inline
char
tolower(char c)
{
static std::array<std::uint8_t, 256> constexpr tab = {{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
}};
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
}
template<std::size_t N>
inline
boost::string_ref
string_helper(const char (&s)[N])
{
return boost::string_ref{s, N-1};
}
template<class T>
inline
T const&
string_helper(T const& t)
{
return t;
}
// Case-insensitive less
struct ci_less
{
static bool const is_transparent = true;
template<class S1, class S2>
bool
operator()(S1 const& lhs, S2 const& rhs) const noexcept
{
using std::begin;
using std::end;
auto const s1 = string_helper(lhs);
auto const s2 = string_helper(rhs);
return std::lexicographical_compare(
begin(s1), end(s1), begin(s2), end(s2),
[](char lhs, char rhs)
{
return tolower(lhs) < tolower(rhs);
}
);
}
};
// Case-insensitive equal
struct ci_equal_pred
{
bool
operator()(char c1, char c2) const noexcept
{
return tolower(c1) == tolower(c2);
}
};
// Case-insensitive equal
template<class S1, class S2>
bool
ci_equal(S1 const& lhs, S2 const& rhs)
{
return boost::range::equal(
string_helper(lhs), string_helper(rhs),
ci_equal_pred{});
}
} // detail
} // beast
#endif

View File

@@ -0,0 +1,94 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
#define BEAST_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
#include <type_traits>
#include <utility>
namespace beast {
namespace detail {
template<class T>
struct empty_base_optimization_decide
: std::integral_constant <bool,
std::is_empty <T>::value
#ifdef __clang__
&& !__is_final(T)
#endif
>
{
};
template<
class T,
int UniqueID = 0,
bool ShouldDeriveFrom =
empty_base_optimization_decide<T>::value
>
class empty_base_optimization : private T
{
public:
empty_base_optimization() = default;
empty_base_optimization(T const& t)
: T (t)
{}
empty_base_optimization(T&& t)
: T (std::move (t))
{}
T& member() noexcept
{
return *this;
}
T const& member() const noexcept
{
return *this;
}
};
//------------------------------------------------------------------------------
template<
class T,
int UniqueID
>
class empty_base_optimization <T, UniqueID, false>
{
public:
empty_base_optimization() = default;
empty_base_optimization(T const& t)
: m_t (t)
{}
empty_base_optimization(T&& t)
: m_t (std::move (t))
{}
T& member() noexcept
{
return m_t;
}
T const& member() const noexcept
{
return m_t;
}
private:
T m_t;
};
} // detail
} // beast
#endif

View File

@@ -0,0 +1,53 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_GET_LOWEST_LAYER_HPP
#define BEAST_DETAIL_GET_LOWEST_LAYER_HPP
#include <type_traits>
namespace beast {
namespace detail {
template<class T>
class has_lowest_layer
{
template<class U, class R =
typename U::lowest_layer_type>
static std::true_type check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
static bool constexpr value = type::value;
};
template<class T, bool B>
struct maybe_get_lowest_layer
{
using type = T;
};
template<class T>
struct maybe_get_lowest_layer<T, true>
{
using type = typename T::lowest_layer_type;
};
// If T has a nested type lowest_layer_type,
// returns that, else returns T.
template<class T>
struct get_lowest_layer
{
using type = typename maybe_get_lowest_layer<T,
has_lowest_layer<T>::value>::type;
};
} // detail
} // beast
#endif

View File

@@ -0,0 +1,145 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
#define BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
#include <cstddef>
#include <type_traits>
#include <utility>
namespace beast {
namespace detail {
template<class T, T... Ints>
struct integer_sequence
{
using value_type = T;
static_assert (std::is_integral<T>::value,
"std::integer_sequence can only be instantiated with an integral type" );
static std::size_t constexpr static_size = sizeof...(Ints);
static std::size_t constexpr size()
{
return sizeof...(Ints);
}
};
template<std::size_t... Ints>
using index_sequence = integer_sequence<std::size_t, Ints...>;
// This workaround is needed for broken sizeof...
template<class... Args>
struct sizeof_workaround
{
static std::size_t constexpr size = sizeof... (Args);
};
#ifdef _MSC_VER
// This implementation compiles on MSVC and clang but not gcc
template<class T, unsigned long long N, class Seq>
struct make_integer_sequence_unchecked;
template<class T, unsigned long long N, unsigned long long ...Indices>
struct make_integer_sequence_unchecked<
T, N, integer_sequence<T, Indices...>>
{
using type = typename make_integer_sequence_unchecked<
T, N-1, integer_sequence<T, N-1, Indices...>>::type;
};
template<class T, unsigned long long ...Indices>
struct make_integer_sequence_unchecked<
T, 0, integer_sequence<T, Indices...>>
{
using type = integer_sequence<T, Indices...>;
};
template<class T, T N>
struct make_integer_sequence_checked
{
static_assert (std::is_integral<T>::value,
"T must be an integral type");
static_assert (N >= 0,
"N must be non-negative");
using type = typename make_integer_sequence_unchecked<
T, N, integer_sequence<T>>::type;
};
template<class T, T N>
using make_integer_sequence =
typename make_integer_sequence_checked<T, N>::type;
template<std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
template<class... Args>
using index_sequence_for =
make_index_sequence<sizeof_workaround<Args...>::size>;
#else
// This implementation compiles on gcc but not MSVC
template<std::size_t... Ints>
struct index_tuple
{
using next = index_tuple<Ints..., sizeof... (Ints)>;
};
template<std::size_t N>
struct build_index_tuple
{
using type = typename build_index_tuple<N-1>::type::next;
};
template<>
struct build_index_tuple<0>
{
using type = index_tuple<>;
};
template<class T, T N,
class Seq = typename build_index_tuple<N>::type
>
struct integer_sequence_helper;
template<class T, T N, std::size_t... Ints>
struct integer_sequence_helper<T, N, index_tuple<Ints...>>
{
static_assert (std::is_integral<T>::value,
"T must be an integral type");
static_assert (N >= 0,
"N must be non-negative");
using type = integer_sequence<T, static_cast<T> (Ints)...>;
};
template<class T, T N>
using make_integer_sequence =
typename integer_sequence_helper<T, N>::type;
template<std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
template<class... Args>
using index_sequence_for =
make_index_sequence<sizeof_workaround<Args...>::size>;
#endif
} // detail
} // beast
#endif

View File

@@ -0,0 +1,90 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
#define BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
#include <type_traits>
namespace beast {
namespace detail {
template<class R, class C, class ...A>
auto
is_call_possible_test(C&& c, int, A&& ...a)
-> decltype(std::is_convertible<
decltype(c(a...)), R>::value ||
std::is_same<R, void>::value,
std::true_type());
template<class R, class C, class ...A>
std::false_type
is_call_possible_test(C&& c, long, A&& ...a);
/** Metafunction returns `true` if F callable as R(A...)
Example:
is_call_possible<T, void(std::string)>
*/
/** @{ */
template<class C, class F>
struct is_call_possible
: std::false_type
{
};
template<class C, class R, class ...A>
struct is_call_possible<C, R(A...)>
: decltype(is_call_possible_test<R>(
std::declval<C>(), 1, std::declval<A>()...))
{
};
/** @} */
namespace test {
struct is_call_possible_udt1
{
void operator()(int) const;
};
struct is_call_possible_udt2
{
int operator()(int) const;
};
struct is_call_possible_udt3
{
int operator()(int);
};
static_assert(is_call_possible<
is_call_possible_udt1, void(int)>::value, "");
static_assert(! is_call_possible<
is_call_possible_udt1, void(void)>::value, "");
static_assert(is_call_possible<
is_call_possible_udt2, int(int)>::value, "");
static_assert(! is_call_possible<
is_call_possible_udt2, int(void)>::value, "");
static_assert(! is_call_possible<
is_call_possible_udt2, void(void)>::value, "");
static_assert(is_call_possible<
is_call_possible_udt3, int(int)>::value, "");
static_assert(! is_call_possible<
is_call_possible_udt3 const, int(int)>::value, "");
} // test
} // detail
} // beast
#endif

View File

@@ -0,0 +1,309 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_SHA1_HPP
#define BEAST_DETAIL_SHA1_HPP
#include <algorithm>
#include <cstdint>
#include <cstring>
// Based on https://github.com/vog/sha1
/*
Original authors:
Steve Reid (Original C Code)
Bruce Guenter (Small changes to fit into bglibs)
Volker Grabsch (Translation to simpler C++ Code)
Eugene Hopkinson (Safety improvements)
Vincent Falco (beast adaptation)
*/
namespace beast {
namespace detail {
namespace sha1 {
static std::size_t constexpr BLOCK_INTS = 16;
static std::size_t constexpr BLOCK_BYTES = 64;
static std::size_t constexpr DIGEST_BYTES = 20;
inline
std::uint32_t
rol(std::uint32_t value, std::size_t bits)
{
return (value << bits) | (value >> (32 - bits));
}
inline
std::uint32_t
blk(std::uint32_t block[BLOCK_INTS], std::size_t i)
{
return rol(
block[(i+13)&15] ^ block[(i+8)&15] ^
block[(i+2)&15] ^ block[i], 1);
}
inline
void
R0(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
std::uint32_t &z, std::size_t i)
{
z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5);
w = rol(w, 30);
}
inline
void
R1(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
std::uint32_t &z, std::size_t i)
{
block[i] = blk(block, i);
z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5);
w = rol(w, 30);
}
inline
void
R2(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
std::uint32_t &z, std::size_t i)
{
block[i] = blk(block, i);
z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5);
w = rol(w, 30);
}
inline
void
R3(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
std::uint32_t &z, std::size_t i)
{
block[i] = blk(block, i);
z += (((w|x)&y)|(w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5);
w = rol(w, 30);
}
inline
void
R4(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
std::uint32_t &z, std::size_t i)
{
block[i] = blk(block, i);
z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5);
w = rol(w, 30);
}
inline
void
make_block(std::uint8_t const* p,
std::uint32_t block[BLOCK_INTS])
{
for(std::size_t i = 0; i < BLOCK_INTS; i++)
block[i] =
(static_cast<std::uint32_t>(p[4*i+3])) |
(static_cast<std::uint32_t>(p[4*i+2]))<< 8 |
(static_cast<std::uint32_t>(p[4*i+1]))<<16 |
(static_cast<std::uint32_t>(p[4*i+0]))<<24;
}
template<class = void>
void
transform(
std::uint32_t digest[], std::uint32_t block[BLOCK_INTS])
{
std::uint32_t a = digest[0];
std::uint32_t b = digest[1];
std::uint32_t c = digest[2];
std::uint32_t d = digest[3];
std::uint32_t e = digest[4];
R0(block, a, b, c, d, e, 0);
R0(block, e, a, b, c, d, 1);
R0(block, d, e, a, b, c, 2);
R0(block, c, d, e, a, b, 3);
R0(block, b, c, d, e, a, 4);
R0(block, a, b, c, d, e, 5);
R0(block, e, a, b, c, d, 6);
R0(block, d, e, a, b, c, 7);
R0(block, c, d, e, a, b, 8);
R0(block, b, c, d, e, a, 9);
R0(block, a, b, c, d, e, 10);
R0(block, e, a, b, c, d, 11);
R0(block, d, e, a, b, c, 12);
R0(block, c, d, e, a, b, 13);
R0(block, b, c, d, e, a, 14);
R0(block, a, b, c, d, e, 15);
R1(block, e, a, b, c, d, 0);
R1(block, d, e, a, b, c, 1);
R1(block, c, d, e, a, b, 2);
R1(block, b, c, d, e, a, 3);
R2(block, a, b, c, d, e, 4);
R2(block, e, a, b, c, d, 5);
R2(block, d, e, a, b, c, 6);
R2(block, c, d, e, a, b, 7);
R2(block, b, c, d, e, a, 8);
R2(block, a, b, c, d, e, 9);
R2(block, e, a, b, c, d, 10);
R2(block, d, e, a, b, c, 11);
R2(block, c, d, e, a, b, 12);
R2(block, b, c, d, e, a, 13);
R2(block, a, b, c, d, e, 14);
R2(block, e, a, b, c, d, 15);
R2(block, d, e, a, b, c, 0);
R2(block, c, d, e, a, b, 1);
R2(block, b, c, d, e, a, 2);
R2(block, a, b, c, d, e, 3);
R2(block, e, a, b, c, d, 4);
R2(block, d, e, a, b, c, 5);
R2(block, c, d, e, a, b, 6);
R2(block, b, c, d, e, a, 7);
R3(block, a, b, c, d, e, 8);
R3(block, e, a, b, c, d, 9);
R3(block, d, e, a, b, c, 10);
R3(block, c, d, e, a, b, 11);
R3(block, b, c, d, e, a, 12);
R3(block, a, b, c, d, e, 13);
R3(block, e, a, b, c, d, 14);
R3(block, d, e, a, b, c, 15);
R3(block, c, d, e, a, b, 0);
R3(block, b, c, d, e, a, 1);
R3(block, a, b, c, d, e, 2);
R3(block, e, a, b, c, d, 3);
R3(block, d, e, a, b, c, 4);
R3(block, c, d, e, a, b, 5);
R3(block, b, c, d, e, a, 6);
R3(block, a, b, c, d, e, 7);
R3(block, e, a, b, c, d, 8);
R3(block, d, e, a, b, c, 9);
R3(block, c, d, e, a, b, 10);
R3(block, b, c, d, e, a, 11);
R4(block, a, b, c, d, e, 12);
R4(block, e, a, b, c, d, 13);
R4(block, d, e, a, b, c, 14);
R4(block, c, d, e, a, b, 15);
R4(block, b, c, d, e, a, 0);
R4(block, a, b, c, d, e, 1);
R4(block, e, a, b, c, d, 2);
R4(block, d, e, a, b, c, 3);
R4(block, c, d, e, a, b, 4);
R4(block, b, c, d, e, a, 5);
R4(block, a, b, c, d, e, 6);
R4(block, e, a, b, c, d, 7);
R4(block, d, e, a, b, c, 8);
R4(block, c, d, e, a, b, 9);
R4(block, b, c, d, e, a, 10);
R4(block, a, b, c, d, e, 11);
R4(block, e, a, b, c, d, 12);
R4(block, d, e, a, b, c, 13);
R4(block, c, d, e, a, b, 14);
R4(block, b, c, d, e, a, 15);
digest[0] += a;
digest[1] += b;
digest[2] += c;
digest[3] += d;
digest[4] += e;
}
} // sha1
struct sha1_context
{
static unsigned int constexpr block_size = sha1::BLOCK_BYTES;
static unsigned int constexpr digest_size = 20;
std::size_t buflen;
std::size_t blocks;
std::uint32_t digest[5];
std::uint8_t buf[block_size];
};
template<class = void>
void
init(sha1_context& ctx) noexcept
{
ctx.buflen = 0;
ctx.blocks = 0;
ctx.digest[0] = 0x67452301;
ctx.digest[1] = 0xefcdab89;
ctx.digest[2] = 0x98badcfe;
ctx.digest[3] = 0x10325476;
ctx.digest[4] = 0xc3d2e1f0;
}
template<class = void>
void
update(sha1_context& ctx,
void const* message, std::size_t size) noexcept
{
auto p = reinterpret_cast<
std::uint8_t const*>(message);
for(;;)
{
auto const n = std::min(
size, sizeof(ctx.buf) - ctx.buflen);
std::memcpy(ctx.buf + ctx.buflen, p, n);
ctx.buflen += n;
if(ctx.buflen != 64)
return;
p += n;
size -= n;
ctx.buflen = 0;
std::uint32_t block[sha1::BLOCK_INTS];
sha1::make_block(ctx.buf, block);
sha1::transform(ctx.digest, block);
++ctx.blocks;
}
}
template<class = void>
void
finish(sha1_context& ctx, void* digest) noexcept
{
using sha1::BLOCK_INTS;
using sha1::BLOCK_BYTES;
std::uint64_t total_bits =
(ctx.blocks*64 + ctx.buflen) * 8;
// pad
ctx.buf[ctx.buflen++] = 0x80;
auto const buflen = ctx.buflen;
while(ctx.buflen < 64)
ctx.buf[ctx.buflen++] = 0x00;
std::uint32_t block[BLOCK_INTS];
sha1::make_block(ctx.buf, block);
if(buflen > BLOCK_BYTES - 8)
{
sha1::transform(ctx.digest, block);
for(size_t i = 0; i < BLOCK_INTS - 2; i++)
block[i] = 0;
}
/* Append total_bits, split this uint64_t into two uint32_t */
block[BLOCK_INTS - 1] = total_bits & 0xffffffff;
block[BLOCK_INTS - 2] = (total_bits >> 32);
sha1::transform(ctx.digest, block);
for(std::size_t i = 0; i < sha1::DIGEST_BYTES/4; i++)
{
std::uint8_t* d =
reinterpret_cast<std::uint8_t*>(digest) + 4 * i;
d[3] = ctx.digest[i] & 0xff;
d[2] = (ctx.digest[i] >> 8) & 0xff;
d[1] = (ctx.digest[i] >> 16) & 0xff;
d[0] = (ctx.digest[i] >> 24) & 0xff;
}
}
} // detail
} // beast
#endif

View File

@@ -0,0 +1,140 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_STREAM_CONCEPTS_HPP
#define BEAST_DETAIL_STREAM_CONCEPTS_HPP
#include <beast/core/buffer_concepts.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/system/error_code.hpp>
#include <type_traits>
#include <utility>
namespace beast {
namespace detail {
// Types that meet the requirements,
// for use with std::declval only.
struct StreamHandler
{
StreamHandler(StreamHandler const&) = default;
void operator()(boost::system::error_code ec, std::size_t);
};
using ReadHandler = StreamHandler;
using WriteHandler = StreamHandler;
template<class T>
class has_get_io_service
{
template<class U, class R = typename std::is_same<
decltype(std::declval<U>().get_io_service()),
boost::asio::io_service&>>
static R check(int);
template<class>
static std::false_type check(...);
public:
using type = decltype(check<T>(0));
};
template<class T>
class is_AsyncReadStream
{
template<class U, class R = decltype(
std::declval<U>().async_read_some(
std::declval<MutableBufferSequence>(),
std::declval<ReadHandler>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type1 = decltype(check<T>(0));
public:
using type = std::integral_constant<bool,
type1::value &&
has_get_io_service<T>::type::value>;
};
template<class T>
class is_AsyncWriteStream
{
template<class U, class R = decltype(
std::declval<U>().async_write_some(
std::declval<ConstBufferSequence>(),
std::declval<WriteHandler>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type1 = decltype(check<T>(0));
public:
using type = std::integral_constant<bool,
type1::value &&
has_get_io_service<T>::type::value>;
};
template<class T>
class is_SyncReadStream
{
using error_code =
boost::system::error_code;
template<class U, class R = std::is_same<decltype(
std::declval<U>().read_some(
std::declval<MutableBufferSequence>())),
std::size_t>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().read_some(
std::declval<MutableBufferSequence>(),
std::declval<error_code&>())), std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
public:
using type = std::integral_constant<bool,
type1::value && type2::value>;
};
template<class T>
class is_SyncWriteStream
{
using error_code =
boost::system::error_code;
template<class U, class R = std::is_same<decltype(
std::declval<U>().write_some(
std::declval<ConstBufferSequence>())),
std::size_t>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().write_some(
std::declval<ConstBufferSequence>(),
std::declval<error_code&>())), std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
public:
using type = std::integral_constant<bool,
type1::value && type2::value>;
};
} // detail
} // beast
#endif

View File

@@ -0,0 +1,140 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_WRITE_DYNABUF_HPP
#define BEAST_DETAIL_WRITE_DYNABUF_HPP
#include <beast/core/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/lexical_cast.hpp>
#include <utility>
namespace beast {
namespace detail {
// detects string literals.
template<class T>
struct is_string_literal : std::integral_constant<bool,
! std::is_same<T, typename std::remove_extent<T>::type>::value &&
std::is_same<char, typename std::remove_extent<T>::type>::value>
{
};
// `true` if a call to boost::asio::buffer(T const&) is possible
// note: we exclude string literals because boost::asio::buffer()
// will include the null terminator, which we don't want.
template<class T>
class is_BufferConvertible
{
template<class U, class R = decltype(
boost::asio::buffer(std::declval<U const&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
static bool const value = type::value &&
! is_string_literal<T>::value;
};
template<class DynamicBuffer>
void
write_dynabuf(DynamicBuffer& dynabuf,
boost::asio::const_buffer const& buffer)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
dynabuf.commit(buffer_copy(
dynabuf.prepare(buffer_size(buffer)),
buffer));
}
template<class DynamicBuffer>
void
write_dynabuf(DynamicBuffer& dynabuf,
boost::asio::mutable_buffer const& buffer)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
dynabuf.commit(buffer_copy(
dynabuf.prepare(buffer_size(buffer)),
buffer));
}
template<class DynamicBuffer, class T>
typename std::enable_if<
is_BufferConvertible<T>::value &&
! std::is_convertible<T, boost::asio::const_buffer>::value &&
! std::is_convertible<T, boost::asio::mutable_buffer>::value
>::type
write_dynabuf(DynamicBuffer& dynabuf, T const& t)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
auto const buffers = boost::asio::buffer(t);
dynabuf.commit(buffer_copy(
dynabuf.prepare(buffer_size(buffers)),
buffers));
}
template<class DynamicBuffer, class Buffers>
typename std::enable_if<
is_ConstBufferSequence<Buffers>::value &&
! is_BufferConvertible<Buffers>::value &&
! std::is_convertible<Buffers, boost::asio::const_buffer>::value &&
! std::is_convertible<Buffers, boost::asio::mutable_buffer>::value
>::type
write_dynabuf(DynamicBuffer& dynabuf, Buffers const& buffers)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
dynabuf.commit(buffer_copy(
dynabuf.prepare(buffer_size(buffers)),
buffers));
}
template<class DynamicBuffer, std::size_t N>
void
write_dynabuf(DynamicBuffer& dynabuf, const char (&s)[N])
{
using boost::asio::buffer_copy;
dynabuf.commit(buffer_copy(
dynabuf.prepare(N - 1),
boost::asio::buffer(s, N - 1)));
}
template<class DynamicBuffer, class T>
typename std::enable_if<
! is_string_literal<T>::value &&
! is_ConstBufferSequence<T>::value &&
! is_BufferConvertible<T>::value &&
! std::is_convertible<T, boost::asio::const_buffer>::value &&
! std::is_convertible<T, boost::asio::mutable_buffer>::value
>::type
write_dynabuf(DynamicBuffer& dynabuf, T const& t)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
auto const s = boost::lexical_cast<std::string>(t);
dynabuf.commit(buffer_copy(
dynabuf.prepare(s.size()), buffer(s)));
}
template<class DynamicBuffer, class T0, class T1, class... TN>
void
write_dynabuf(DynamicBuffer& dynabuf,
T0 const& t0, T1 const& t1, TN const&... tn)
{
write_dynabuf(dynabuf, t0);
write_dynabuf(dynabuf, t1, tn...);
}
} // detail
} // beast
#endif

View File

@@ -0,0 +1,280 @@
//
// 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_DYNABUF_READSTREAM_HPP
#define BEAST_DYNABUF_READSTREAM_HPP
#include <beast/core/async_completion.hpp>
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/error.hpp>
#include <beast/core/stream_concepts.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/core/detail/get_lowest_layer.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/system/error_code.hpp>
#include <cstdint>
#include <utility>
namespace beast {
/** A @b `Stream` with attached @b `DynamicBuffer` to buffer reads.
This wraps a @b `Stream` implementation so that calls to write are
passed through to the underlying stream, while calls to read will
first consume the input sequence stored in a @b `DynamicBuffer` which
is part of the object.
The use-case for this class is different than that of the
`boost::asio::buffered_readstream`. It is designed to facilitate
the use of `boost::asio::read_until`, and to allow buffers
acquired during detection of handshakes to be made transparently
available to callers. A hypothetical implementation of the
buffered version of `boost::asio::ssl::stream::async_handshake`
could make use of this wrapper.
Uses:
@li Transparently leave untouched input acquired in calls
to `boost::asio::read_until` behind for subsequent callers.
@li "Preload" a stream with handshake input data acquired
from other sources.
Example:
@code
// Process the next HTTP headers on the stream,
// leaving excess bytes behind for the next call.
//
template<class DynamicBuffer>
void process_http_message(
dynabuf_readstream<DynamicBuffer>& stream)
{
// Read up to and including the end of the HTTP
// headers, leaving the sequence in the stream's
// buffer. read_until may read past the end of the
// headers; the return value will include only the
// part up to the end of the delimiter.
//
std::size_t bytes_transferred =
boost::asio::read_until(
stream.next_layer(), stream.buffer(), "\r\n\r\n");
// Use prepare_buffers() to limit the input
// sequence to only the data up to and including
// the trailing "\r\n\r\n".
//
auto header_buffers = prepare_buffers(
bytes_transferred, stream.buffer().data());
...
// Discard the portion of the input corresponding
// to the HTTP headers.
//
stream.buffer().consume(bytes_transferred);
// Everything we read from the stream
// is part of the content-body.
}
@endcode
@tparam Stream The type of stream to wrap.
@tparam DynamicBuffer The type of stream buffer to use.
*/
template<class Stream, class DynamicBuffer>
class dynabuf_readstream
{
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
template<class Buffers, class Handler>
class read_some_op;
DynamicBuffer sb_;
std::size_t capacity_ = 0;
Stream next_layer_;
public:
/// The type of the internal buffer
using dynabuf_type = DynamicBuffer;
/// The type of the next layer.
using next_layer_type =
typename std::remove_reference<Stream>::type;
/// The type of the lowest layer.
using lowest_layer_type =
#if GENERATING_DOCS
implementation_defined;
#else
typename detail::get_lowest_layer<
next_layer_type>::type;
#endif
/** Move constructor.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
dynabuf_readstream(dynabuf_readstream&&) = default;
/** Move assignment.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
dynabuf_readstream& operator=(dynabuf_readstream&&) = default;
/** Construct the wrapping stream.
@param args Parameters forwarded to the `Stream` constructor.
*/
template<class... Args>
explicit
dynabuf_readstream(Args&&... args);
/// Get a reference to the next layer.
next_layer_type&
next_layer()
{
return next_layer_;
}
/// Get a reference to the lowest layer.
lowest_layer_type&
lowest_layer()
{
return next_layer_.lowest_layer();
}
/// Get a const reference to the lowest layer.
lowest_layer_type const&
lowest_layer() const
{
return next_layer_.lowest_layer();
}
/// Get the io_service associated with the object.
boost::asio::io_service&
get_io_service()
{
return next_layer_.get_io_service();
}
/** Access the internal buffer.
The internal buffer is returned. It is possible for the
caller to break invariants with this function. For example,
by causing the internal buffer size to increase beyond
the caller defined maximum.
*/
DynamicBuffer&
buffer()
{
return sb_;
}
/** Access the internal buffer.
The internal buffer is returned. It is possible for the
caller to break invariants with this function. For example,
by causing the internal buffer size to increase beyond
the caller defined maximum.
*/
DynamicBuffer const&
buffer() const
{
return sb_;
}
/** Set the maximum buffer size.
This changes the maximum size of the internal buffer used
to hold read data. No bytes are discarded by this call. If
the buffer size is set to zero, no more data will be buffered.
Thread safety:
The caller is responsible for making sure the call is
made from the same implicit or explicit strand.
@param size The number of bytes in the read buffer.
@note This is a soft limit. If the new maximum size is smaller
than the amount of data in the buffer, no bytes are discarded.
*/
void
capacity(std::size_t size)
{
capacity_ = size;
}
/// Write the given data to the stream. Returns the number of bytes written.
/// Throws an exception on failure.
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
static_assert(is_SyncWriteStream<next_layer_type>::value,
"SyncWriteStream requirements not met");
return next_layer_.write_some(buffers);
}
/// Write the given data to the stream. Returns the number of bytes written,
/// or 0 if an error occurred.
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers,
error_code& ec)
{
static_assert(is_SyncWriteStream<next_layer_type>::value,
"SyncWriteStream requirements not met");
return next_layer_.write_some(buffers, ec);
}
/// Start an asynchronous write. The data being written must be valid for the
/// lifetime of the asynchronous operation.
template<class ConstBufferSequence, class WriteHandler>
#if GENERATING_DOCS
void_or_deduced
#else
typename async_completion<WriteHandler, void(error_code)>::result_type
#endif
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler);
/// Read some data from the stream. Returns the number of bytes read.
/// Throws an exception on failure.
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers);
/// Read some data from the stream. Returns the number of bytes read
/// or 0 if an error occurred.
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers,
error_code& ec);
/// Start an asynchronous read. The buffer into which the data will be read
/// must be valid for the lifetime of the asynchronous operation.
template<class MutableBufferSequence, class ReadHandler>
#if GENERATING_DOCS
void_or_deduced
#else
typename async_completion<ReadHandler, void(error_code)>::result_type
#endif
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler);
};
} // beast
#include <beast/core/impl/dynabuf_readstream.ipp>
#endif

View File

@@ -0,0 +1,24 @@
//
// 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_ERROR_HPP
#define BEAST_ERROR_HPP
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
namespace beast {
/// The type of error code used by the library
using error_code = boost::system::error_code;
/// The type of system error thrown by the library
using system_error = boost::system::system_error;
} // beast
#endif

View File

@@ -0,0 +1,148 @@
//
// 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_HANDLER_ALLOC_HPP
#define BEAST_HANDLER_ALLOC_HPP
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <cstdlib>
#include <memory>
#include <type_traits>
#include <utility>
namespace beast {
// Guidance from
// http://howardhinnant.github.io/allocator_boilerplate.html
/** An allocator that uses handler customizations.
This allocator uses the handler customizations `asio_handler_allocate`
and `asio_handler_deallocate` to manage memory. It meets the requirements
of `Allocator` and can be used anywhere a `std::allocator` is
accepted.
@tparam T The type of objects allocated by the allocator.
@tparam CompletionHandler The type of handler.
@note Allocated memory is only valid until the handler is called. The
caller is still responsible for freeing memory.
*/
#if GENERATING_DOCS
template<class T, class CompletionHandler>
class handler_alloc;
#else
template<class T, class CompletionHandler>
class handler_alloc
{
private:
// We want a partial template specialization as a friend
// but that isn't allowed so we friend all versions. This
// should produce a compile error if CompletionHandler is not
// constructible from H.
//
template<class U, class H>
friend class handler_alloc;
CompletionHandler h_;
public:
using value_type = T;
using is_always_equal = std::true_type;
handler_alloc() = delete;
handler_alloc(handler_alloc&&) = default;
handler_alloc(handler_alloc const&) = default;
handler_alloc& operator=(handler_alloc&&) = default;
handler_alloc& operator=(handler_alloc const&) = default;
/** Construct the allocator.
The handler is moved or copied into the allocator.
*/
explicit
handler_alloc(CompletionHandler&& h)
: h_(std::move(h))
{
}
/** Construct the allocator.
A copy of the handler is made.
*/
explicit
handler_alloc(CompletionHandler const& h)
: h_(h)
{
}
template<class U>
handler_alloc(
handler_alloc<U, CompletionHandler>&& other)
: h_(std::move(other.h_))
{
}
template<class U>
handler_alloc(
handler_alloc<U, CompletionHandler> const& other)
: h_(other.h_)
{
}
value_type*
allocate(std::ptrdiff_t n)
{
auto const size = n * sizeof(T);
return static_cast<value_type*>(
boost_asio_handler_alloc_helpers::allocate(
size, h_));
}
void
deallocate(value_type* p, std::ptrdiff_t n)
{
auto const size = n * sizeof(T);
boost_asio_handler_alloc_helpers::deallocate(
p, size, h_);
}
#ifdef _MSC_VER
// Work-around for MSVC not using allocator_traits
// in the implementation of shared_ptr
//
void
destroy(T* t)
{
t->~T();
}
#endif
template<class U>
friend
bool
operator==(handler_alloc const& lhs,
handler_alloc<U, CompletionHandler> const& rhs)
{
return true;
}
template<class U>
friend
bool
operator!=(handler_alloc const& lhs,
handler_alloc<U, CompletionHandler> const& rhs)
{
return !(lhs == rhs);
}
};
#endif
} // beast
#endif

View File

@@ -0,0 +1,27 @@
//
// 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_HANDLER_CONCEPTS_HPP
#define BEAST_HANDLER_CONCEPTS_HPP
#include <beast/core/detail/is_call_possible.hpp>
#include <type_traits>
namespace beast {
/// Determine if `T` meets the requirements of @b `CompletionHandler`.
template<class T, class Signature>
#if GENERATING_DOCS
using is_CompletionHandler = std::integral_constant<bool, ...>;
#else
using is_CompletionHandler = std::integral_constant<bool,
std::is_copy_constructible<typename std::decay<T>::type>::value &&
detail::is_call_possible<T, Signature>::value>;
#endif
} // beast
#endif

View File

@@ -0,0 +1,875 @@
//
// 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_IMPL_BASIC_STREAMBUF_IPP
#define BEAST_IMPL_BASIC_STREAMBUF_IPP
#include <beast/core/detail/write_dynabuf.hpp>
#include <algorithm>
#include <cassert>
#include <exception>
#include <sstream>
#include <string>
#include <utility>
namespace beast {
/* These diagrams illustrate the layout and state variables.
1 Input and output contained entirely in one element:
0 out_
|<-------------+------------------------------------------->|
in_pos_ out_pos_ out_end_
2 Output contained in first and second elements:
out_
|<------+----------+------->| |<----------+-------------->|
in_pos_ out_pos_ out_end_
3 Output contained in the second element:
out_
|<------------+------------>| |<----+-------------------->|
in_pos_ out_pos_ out_end_
4 Output contained in second and third elements:
out_
|<-----+-------->| |<-------+------>| |<--------------->|
in_pos_ out_pos_ out_end_
5 Input sequence is empty:
out_
|<------+------------------>| |<-----------+------------->|
out_pos_ out_end_
in_pos_
6 Output sequence is empty:
out_
|<------+------------------>| |<------+------------------>|
in_pos_ out_pos_
out_end_
7 The end of output can point to the end of an element.
But out_pos_ should never point to the end:
out_
|<------+------------------>| |<------+------------------>|
in_pos_ out_pos_ out_end_
8 When the input sequence entirely fills the last element and
the output sequence is empty, out_ will point to the end of
the list of buffers, and out_pos_ and out_end_ will be 0:
|<------+------------------>| out_ == list_.end()
in_pos_ out_pos_ == 0
out_end_ == 0
*/
template<class Allocator>
class basic_streambuf<Allocator>::element
: public boost::intrusive::list_base_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>
{
using size_type = typename std::allocator_traits<Allocator>::size_type;
size_type const size_;
public:
element(element const&) = delete;
element& operator=(element const&) = delete;
explicit
element(size_type n)
: size_(n)
{
}
size_type
size() const
{
return size_;
}
char*
data() const
{
return const_cast<char*>(
reinterpret_cast<char const*>(this+1));
}
};
template<class Allocator>
class basic_streambuf<Allocator>::const_buffers_type
{
basic_streambuf const* sb_;
friend class basic_streambuf;
explicit
const_buffers_type(basic_streambuf const& sb);
public:
// Why?
using value_type = boost::asio::const_buffer;
class const_iterator;
const_buffers_type() = delete;
const_buffers_type(const_buffers_type const&) = default;
const_buffers_type& operator=(const_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
};
template<class Allocator>
class basic_streambuf<Allocator>::mutable_buffers_type
{
basic_streambuf const* sb_;
friend class basic_streambuf;
explicit
mutable_buffers_type(basic_streambuf const& sb);
public:
using value_type = mutable_buffer;
class const_iterator;
mutable_buffers_type() = delete;
mutable_buffers_type(mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(mutable_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
};
//------------------------------------------------------------------------------
template<class Allocator>
class basic_streambuf<Allocator>::const_buffers_type::const_iterator
{
basic_streambuf const* sb_ = nullptr;
typename list_type::const_iterator it_;
public:
using value_type =
typename const_buffers_type::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
const_iterator(basic_streambuf const& sb,
typename list_type::const_iterator const& it)
: sb_(&sb)
, it_(it)
{
}
bool
operator==(const_iterator const& other) const
{
return sb_ == other.sb_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
auto const& e = *it_;
return value_type{e.data(),
(sb_->out_ == sb_->list_.end() ||
&e != &*sb_->out_) ? e.size() : sb_->out_pos_} +
(&e == &*sb_->list_.begin() ? sb_->in_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
};
template<class Allocator>
basic_streambuf<Allocator>::const_buffers_type::const_buffers_type(
basic_streambuf const& sb)
: sb_(&sb)
{
}
template<class Allocator>
auto
basic_streambuf<Allocator>::const_buffers_type::begin() const ->
const_iterator
{
return const_iterator{*sb_, sb_->list_.begin()};
}
template<class Allocator>
auto
basic_streambuf<Allocator>::const_buffers_type::end() const ->
const_iterator
{
return const_iterator{*sb_, sb_->out_ ==
sb_->list_.end() ? sb_->list_.end() :
std::next(sb_->out_)};
}
//------------------------------------------------------------------------------
template<class Allocator>
class basic_streambuf<Allocator>::mutable_buffers_type::const_iterator
{
basic_streambuf const* sb_ = nullptr;
typename list_type::const_iterator it_;
public:
using value_type =
typename mutable_buffers_type::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
const_iterator(basic_streambuf const& sb,
typename list_type::const_iterator const& it)
: sb_(&sb)
, it_(it)
{
}
bool
operator==(const_iterator const& other) const
{
return sb_ == other.sb_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
auto const& e = *it_;
return value_type{e.data(),
&e == &*std::prev(sb_->list_.end()) ?
sb_->out_end_ : e.size()} +
(&e == &*sb_->out_ ? sb_->out_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
};
template<class Allocator>
basic_streambuf<Allocator>::mutable_buffers_type::mutable_buffers_type(
basic_streambuf const& sb)
: sb_(&sb)
{
}
template<class Allocator>
auto
basic_streambuf<Allocator>::mutable_buffers_type::begin() const ->
const_iterator
{
return const_iterator{*sb_, sb_->out_};
}
template<class Allocator>
auto
basic_streambuf<Allocator>::mutable_buffers_type::end() const ->
const_iterator
{
return const_iterator{*sb_, sb_->list_.end()};
}
//------------------------------------------------------------------------------
template<class Allocator>
basic_streambuf<Allocator>::~basic_streambuf()
{
delete_list();
}
template<class Allocator>
basic_streambuf<Allocator>::
basic_streambuf(basic_streambuf&& other)
: detail::empty_base_optimization<allocator_type>(
std::move(other.member()))
, alloc_size_(other.alloc_size_)
, in_size_(other.in_size_)
, in_pos_(other.in_pos_)
, out_pos_(other.out_pos_)
, out_end_(other.out_end_)
{
auto const at_end =
other.out_ == other.list_.end();
list_ = std::move(other.list_);
out_ = at_end ? list_.end() : other.out_;
other.in_size_ = 0;
other.out_ = other.list_.end();
other.in_pos_ = 0;
other.out_pos_ = 0;
other.out_end_ = 0;
}
template<class Allocator>
basic_streambuf<Allocator>::
basic_streambuf(basic_streambuf&& other,
allocator_type const& alloc)
: basic_streambuf(other.alloc_size_, alloc)
{
using boost::asio::buffer_copy;
if(this->member() != other.member())
commit(buffer_copy(prepare(other.size()), other.data()));
else
move_assign(other, std::true_type{});
}
template<class Allocator>
auto
basic_streambuf<Allocator>::operator=(
basic_streambuf&& other) -> basic_streambuf&
{
if(this == &other)
return *this;
// VFALCO If any memory allocated we could use it first?
clear();
alloc_size_ = other.alloc_size_;
move_assign(other, std::integral_constant<bool,
alloc_traits::propagate_on_container_move_assignment::value>{});
return *this;
}
template<class Allocator>
basic_streambuf<Allocator>::
basic_streambuf(basic_streambuf const& other)
: basic_streambuf(other.alloc_size_,
alloc_traits::select_on_container_copy_construction(other.member()))
{
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
basic_streambuf<Allocator>::
basic_streambuf(basic_streambuf const& other,
allocator_type const& alloc)
: basic_streambuf(other.alloc_size_, alloc)
{
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
auto
basic_streambuf<Allocator>::operator=(
basic_streambuf const& other) ->
basic_streambuf&
{
if(this == &other)
return *this;
using boost::asio::buffer_copy;
clear();
copy_assign(other, std::integral_constant<bool,
alloc_traits::propagate_on_container_copy_assignment::value>{});
commit(buffer_copy(prepare(other.size()), other.data()));
return *this;
}
template<class Allocator>
template<class OtherAlloc>
basic_streambuf<Allocator>::basic_streambuf(
basic_streambuf<OtherAlloc> const& other)
: basic_streambuf(other.alloc_size_)
{
using boost::asio::buffer_copy;
commit(buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
template<class OtherAlloc>
basic_streambuf<Allocator>::basic_streambuf(
basic_streambuf<OtherAlloc> const& other,
allocator_type const& alloc)
: basic_streambuf(other.alloc_size_, alloc)
{
using boost::asio::buffer_copy;
commit(buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
template<class OtherAlloc>
auto
basic_streambuf<Allocator>::operator=(
basic_streambuf<OtherAlloc> const& other) ->
basic_streambuf&
{
using boost::asio::buffer_copy;
clear();
commit(buffer_copy(prepare(other.size()), other.data()));
return *this;
}
template<class Allocator>
basic_streambuf<Allocator>::basic_streambuf(
std::size_t alloc_size, Allocator const& alloc)
: detail::empty_base_optimization<allocator_type>(alloc)
, out_(list_.end())
, alloc_size_(alloc_size)
{
if(alloc_size <= 0)
throw std::invalid_argument(
"basic_streambuf: invalid alloc_size");
}
template<class Allocator>
std::size_t
basic_streambuf<Allocator>::capacity() const
{
auto pos = out_;
if(pos == list_.end())
return 0;
auto n = pos->size() - out_pos_;
while(++pos != list_.end())
n += pos->size();
return in_size_ + n;
}
template<class Allocator>
auto
basic_streambuf<Allocator>::
data() const ->
const_buffers_type
{
return const_buffers_type(*this);
}
template<class Allocator>
auto
basic_streambuf<Allocator>::prepare(size_type n) ->
mutable_buffers_type
{
list_type reuse;
if(out_ != list_.end())
{
if(out_ != list_.iterator_to(list_.back()))
{
out_end_ = out_->size();
reuse.splice(reuse.end(), list_,
std::next(out_), list_.end());
debug_check();
}
auto const avail = out_->size() - out_pos_;
if(n > avail)
{
out_end_ = out_->size();
n -= avail;
}
else
{
out_end_ = out_pos_ + n;
n = 0;
}
debug_check();
}
while(n > 0 && ! reuse.empty())
{
auto& e = reuse.front();
reuse.erase(reuse.iterator_to(e));
list_.push_back(e);
if(n > e.size())
{
out_end_ = e.size();
n -= e.size();
}
else
{
out_end_ = n;
n = 0;
}
debug_check();
}
while(n > 0)
{
auto const size = std::max(alloc_size_, n);
auto& e = *reinterpret_cast<element*>(
alloc_traits::allocate(this->member(),
sizeof(element) + size));
alloc_traits::construct(this->member(), &e, size);
list_.push_back(e);
if(out_ == list_.end())
out_ = list_.iterator_to(e);
if(n >= e.size())
{
out_end_ = e.size();
n -= e.size();
}
else
{
out_end_ = n;
n = 0;
}
debug_check();
}
for(auto it = reuse.begin(); it != reuse.end();)
{
auto& e = *it++;
reuse.erase(list_.iterator_to(e));
auto const len = e.size() + sizeof(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<std::uint8_t*>(&e), len);
}
return mutable_buffers_type(*this);
}
template<class Allocator>
void
basic_streambuf<Allocator>::commit(size_type n)
{
if(list_.empty())
return;
if(out_ == list_.end())
return;
auto const back =
list_.iterator_to(list_.back());
while(out_ != back)
{
auto const avail =
out_->size() - out_pos_;
if(n < avail)
{
out_pos_ += n;
in_size_ += n;
debug_check();
return;
}
++out_;
n -= avail;
out_pos_ = 0;
in_size_ += avail;
debug_check();
}
n = std::min(n, out_end_ - out_pos_);
out_pos_ += n;
in_size_ += n;
if(out_pos_ == out_->size())
{
++out_;
out_pos_ = 0;
out_end_ = 0;
}
debug_check();
}
template<class Allocator>
void
basic_streambuf<Allocator>::consume(size_type n)
{
if(list_.empty())
return;
for(;;)
{
if(list_.begin() != out_)
{
auto const avail = list_.front().size() - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
debug_check();
break;
}
n -= avail;
in_size_ -= avail;
in_pos_ = 0;
auto& e = list_.front();
list_.erase(list_.iterator_to(e));
auto const len = e.size() + sizeof(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<std::uint8_t*>(&e), len);
debug_check();
}
else
{
auto const avail = out_pos_ - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
}
else
{
in_size_ = 0;
if(out_ != list_.iterator_to(list_.back()) ||
out_pos_ != out_end_)
{
in_pos_ = out_pos_;
}
else
{
// Input and output sequences are empty, reuse buffer.
// Alternatively we could deallocate it.
in_pos_ = 0;
out_pos_ = 0;
out_end_ = 0;
}
}
debug_check();
break;
}
}
}
template<class Allocator>
void
basic_streambuf<Allocator>::
clear()
{
delete_list();
list_.clear();
out_ = list_.begin();
in_size_ = 0;
in_pos_ = 0;
out_pos_ = 0;
out_end_ = 0;
}
template<class Allocator>
void
basic_streambuf<Allocator>::
move_assign(basic_streambuf& other, std::false_type)
{
using boost::asio::buffer_copy;
if(this->member() != other.member())
{
commit(buffer_copy(prepare(other.size()), other.data()));
other.clear();
}
else
move_assign(other, std::true_type{});
}
template<class Allocator>
void
basic_streambuf<Allocator>::
move_assign(basic_streambuf& other, std::true_type)
{
this->member() = std::move(other.member());
auto const at_end =
other.out_ == other.list_.end();
list_ = std::move(other.list_);
out_ = at_end ? list_.end() : other.out_;
in_size_ = other.in_size_;
in_pos_ = other.in_pos_;
out_pos_ = other.out_pos_;
out_end_ = other.out_end_;
other.in_size_ = 0;
other.out_ = other.list_.end();
other.in_pos_ = 0;
other.out_pos_ = 0;
other.out_end_ = 0;
}
template<class Allocator>
void
basic_streambuf<Allocator>::
copy_assign(basic_streambuf const& other, std::false_type)
{
}
template<class Allocator>
void
basic_streambuf<Allocator>::
copy_assign(basic_streambuf const& other, std::true_type)
{
this->member() = other.member();
}
template<class Allocator>
void
basic_streambuf<Allocator>::delete_list()
{
for(auto iter = list_.begin(); iter != list_.end();)
{
auto& e = *iter++;
auto const n = e.size() + sizeof(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<std::uint8_t*>(&e), n);
}
}
template<class Allocator>
void
basic_streambuf<Allocator>::debug_check() const
{
#ifndef NDEBUG
using boost::asio::buffer_size;
assert(buffer_size(data()) == in_size_);
if(list_.empty())
{
assert(in_pos_ == 0);
assert(in_size_ == 0);
assert(out_pos_ == 0);
assert(out_end_ == 0);
assert(out_ == list_.end());
return;
}
auto const& front = list_.front();
assert(in_pos_ < front.size());
if(out_ == list_.end())
{
assert(out_pos_ == 0);
assert(out_end_ == 0);
}
else
{
auto const& out = *out_;
auto const& back = list_.back();
assert(out_end_ <= back.size());
assert(out_pos_ < out.size());
assert(&out != &front || out_pos_ >= in_pos_);
assert(&out != &front || out_pos_ - in_pos_ == in_size_);
assert(&out != &back || out_pos_ <= out_end_);
}
#endif
}
template<class Allocator>
std::size_t
read_size_helper(basic_streambuf<
Allocator> const& streambuf, std::size_t max_size)
{
auto const avail = streambuf.capacity() - streambuf.size();
if(avail == 0)
return std::min(max_size,
std::max<std::size_t>(512, streambuf.alloc_size_));
return std::min(max_size, avail);
}
template<class Alloc, class T>
basic_streambuf<Alloc>&
operator<<(basic_streambuf<Alloc>& streambuf, T const& t)
{
detail::write_dynabuf(streambuf, t);
return streambuf;
}
} // beast
#endif

View File

@@ -0,0 +1,503 @@
//
// 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_IMPL_BUFFERS_ADAPTER_IPP
#define BEAST_IMPL_BUFFERS_ADAPTER_IPP
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <stdexcept>
#include <type_traits>
namespace beast {
template<class MutableBufferSequence>
class buffers_adapter<MutableBufferSequence>::
const_buffers_type
{
buffers_adapter const* ba_;
public:
using value_type = boost::asio::const_buffer;
class const_iterator;
const_buffers_type() = delete;
const_buffers_type(
const_buffers_type const&) = default;
const_buffers_type& operator=(
const_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class buffers_adapter;
const_buffers_type(buffers_adapter const& ba)
: ba_(&ba)
{
}
};
template<class MutableBufferSequence>
class buffers_adapter<MutableBufferSequence>::
const_buffers_type::const_iterator
{
iter_type it_;
buffers_adapter const* ba_ = nullptr;
public:
using value_type = boost::asio::const_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return ba_ == other.ba_ &&
it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return value_type{buffer_cast<void const*>(*it_),
(ba_->out_ == ba_->bs_.end() ||
it_ != ba_->out_) ? buffer_size(*it_) : ba_->out_pos_} +
(it_ == ba_->begin_ ? ba_->in_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class const_buffers_type;
const_iterator(buffers_adapter const& ba,
iter_type iter)
: it_(iter)
, ba_(&ba)
{
}
};
template<class MutableBufferSequence>
inline
auto
buffers_adapter<MutableBufferSequence>::const_buffers_type::begin() const ->
const_iterator
{
return const_iterator{*ba_, ba_->begin_};
}
template<class MutableBufferSequence>
inline
auto
buffers_adapter<MutableBufferSequence>::const_buffers_type::end() const ->
const_iterator
{
return const_iterator{*ba_, ba_->out_ ==
ba_->end_ ? ba_->end_ : std::next(ba_->out_)};
}
//------------------------------------------------------------------------------
template<class MutableBufferSequence>
class buffers_adapter<MutableBufferSequence>::
mutable_buffers_type
{
buffers_adapter const* ba_;
public:
using value_type = boost::asio::mutable_buffer;
class const_iterator;
mutable_buffers_type() = delete;
mutable_buffers_type(
mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(
mutable_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class buffers_adapter;
mutable_buffers_type(
buffers_adapter const& ba)
: ba_(&ba)
{
}
};
template<class MutableBufferSequence>
class buffers_adapter<MutableBufferSequence>::
mutable_buffers_type::const_iterator
{
iter_type it_;
buffers_adapter const* ba_ = nullptr;
public:
using value_type = boost::asio::mutable_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return ba_ == other.ba_ &&
it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return value_type{buffer_cast<void*>(*it_),
it_ == std::prev(ba_->end_) ?
ba_->out_end_ : buffer_size(*it_)} +
(it_ == ba_->out_ ? ba_->out_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class mutable_buffers_type;
const_iterator(buffers_adapter const& ba,
iter_type iter)
: it_(iter)
, ba_(&ba)
{
}
};
template<class MutableBufferSequence>
inline
auto
buffers_adapter<MutableBufferSequence>::mutable_buffers_type::begin() const ->
const_iterator
{
return const_iterator{*ba_, ba_->out_};
}
template<class MutableBufferSequence>
inline
auto
buffers_adapter<MutableBufferSequence>::mutable_buffers_type::end() const ->
const_iterator
{
return const_iterator{*ba_, ba_->end_};
}
//------------------------------------------------------------------------------
template<class MutableBufferSequence>
buffers_adapter<MutableBufferSequence>::buffers_adapter(
buffers_adapter&& other)
: buffers_adapter(std::move(other),
std::distance<iter_type>(other.bs_.begin(), other.begin_),
std::distance<iter_type>(other.bs_.begin(), other.out_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class MutableBufferSequence>
buffers_adapter<MutableBufferSequence>::buffers_adapter(
buffers_adapter const& other)
: buffers_adapter(other,
std::distance<iter_type>(other.bs_.begin(), other.begin_),
std::distance<iter_type>(other.bs_.begin(), other.out_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class MutableBufferSequence>
auto
buffers_adapter<MutableBufferSequence>::operator=(
buffers_adapter&& other) -> buffers_adapter&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
auto const nout = std::distance<iter_type>(
other.bs_.begin(), other.out_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = std::move(other.bs_);
begin_ = std::next(bs_.begin(), nbegin);
out_ = std::next(bs_.begin(), nout);
end_ = std::next(bs_.begin(), nend);
max_size_ = other.max_size_;
in_pos_ = other.in_pos_;
in_size_ = other.in_size_;
out_pos_ = other.out_pos_;
out_end_ = other.out_end_;
return *this;
}
template<class MutableBufferSequence>
auto
buffers_adapter<MutableBufferSequence>::operator=(
buffers_adapter const& other) -> buffers_adapter&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
auto const nout = std::distance<iter_type>(
other.bs_.begin(), other.out_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = other.bs_;
begin_ = std::next(bs_.begin(), nbegin);
out_ = std::next(bs_.begin(), nout);
end_ = std::next(bs_.begin(), nend);
max_size_ = other.max_size_;
in_pos_ = other.in_pos_;
in_size_ = other.in_size_;
out_pos_ = other.out_pos_;
out_end_ = other.out_end_;
return *this;
}
template<class MutableBufferSequence>
buffers_adapter<MutableBufferSequence>::buffers_adapter(
MutableBufferSequence const& bs)
: bs_(bs)
, begin_(bs_.begin())
, out_(bs_.begin())
, end_(bs_.begin())
, max_size_(boost::asio::buffer_size(bs_))
{
}
template<class MutableBufferSequence>
auto
buffers_adapter<MutableBufferSequence>::prepare(std::size_t n) ->
mutable_buffers_type
{
using boost::asio::buffer_size;
end_ = out_;
if(end_ != bs_.end())
{
auto size = buffer_size(*end_) - out_pos_;
if(n > size)
{
n -= size;
while(++end_ != bs_.end())
{
size = buffer_size(*end_);
if(n < size)
{
out_end_ = n;
n = 0;
++end_;
break;
}
n -= size;
out_end_ = size;
}
}
else
{
++end_;
out_end_ = out_pos_ + n;
n = 0;
}
}
if(n > 0)
throw std::length_error(
"no space in buffers_adapter");
return mutable_buffers_type{*this};
}
template<class MutableBufferSequence>
void
buffers_adapter<MutableBufferSequence>::commit(std::size_t n)
{
using boost::asio::buffer_size;
if(out_ == end_)
return;
auto const last = std::prev(end_);
while(out_ != last)
{
auto const avail =
buffer_size(*out_) - out_pos_;
if(n < avail)
{
out_pos_ += n;
in_size_ += n;
max_size_ -= n;
return;
}
++out_;
n -= avail;
out_pos_ = 0;
in_size_ += avail;
max_size_ -= avail;
}
n = std::min(n, out_end_ - out_pos_);
out_pos_ += n;
in_size_ += n;
max_size_ -= n;
if(out_pos_ == buffer_size(*out_))
{
++out_;
out_pos_ = 0;
out_end_ = 0;
}
}
template<class MutableBufferSequence>
inline
auto
buffers_adapter<MutableBufferSequence>::data() const ->
const_buffers_type
{
return const_buffers_type{*this};
}
template<class MutableBufferSequence>
void
buffers_adapter<MutableBufferSequence>::consume(std::size_t n)
{
using boost::asio::buffer_size;
while(begin_ != out_)
{
auto const avail =
buffer_size(*begin_) - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
return;
}
n -= avail;
in_size_ -= avail;
in_pos_ = 0;
++begin_;
}
auto const avail = out_pos_ - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
}
else
{
in_size_ -= avail;
in_pos_ = out_pos_;
}
}
} // beast
#endif

View File

@@ -0,0 +1,210 @@
//
// 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_IMPL_CONSUMING_BUFFERS_IPP
#define BEAST_IMPL_CONSUMING_BUFFERS_IPP
#include <beast/core/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <type_traits>
#include <utility>
namespace beast {
template<class BufferSequence, class ValueType>
class consuming_buffers<BufferSequence, ValueType>::const_iterator
{
friend class consuming_buffers<BufferSequence, ValueType>;
using iter_type =
typename BufferSequence::const_iterator;
iter_type it_;
consuming_buffers const* b_ = nullptr;
public:
using value_type =
typename std::iterator_traits<iter_type>::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return b_ == other.b_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return it_ == b_->begin_ ?
*it_ + b_->skip_ : *it_;
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
const_iterator(consuming_buffers const& b,
iter_type it)
: it_(it)
, b_(&b)
{
}
};
template<class BufferSequence, class ValueType>
consuming_buffers<BufferSequence, ValueType>::
consuming_buffers(consuming_buffers&& other)
: consuming_buffers(std::move(other),
std::distance<iter_type>(
other.bs_.begin(), other.begin_))
{
}
template<class BufferSequence, class ValueType>
consuming_buffers<BufferSequence, ValueType>::
consuming_buffers(consuming_buffers const& other)
: consuming_buffers(other,
std::distance<iter_type>(
other.bs_.begin(), other.begin_))
{
}
template<class BufferSequence, class ValueType>
auto
consuming_buffers<BufferSequence, ValueType>::
operator=(consuming_buffers&& other) ->
consuming_buffers&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
bs_ = std::move(other.bs_);
begin_ = std::next(bs_.begin(), nbegin);
skip_ = other.skip_;
return *this;
}
template<class BufferSequence, class ValueType>
auto
consuming_buffers<BufferSequence, ValueType>::
operator=(consuming_buffers const& other) ->
consuming_buffers&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
bs_ = other.bs_;
begin_ = std::next(bs_.begin(), nbegin);
skip_ = other.skip_;
return *this;
}
template<class BufferSequence, class ValueType>
consuming_buffers<BufferSequence, ValueType>::
consuming_buffers(BufferSequence const& bs)
: bs_(bs)
, begin_(bs_.begin())
{
static_assert(is_BufferSequence<BufferSequence, ValueType>::value,
"BufferSequence requirements not met");
}
template<class BufferSequence, class ValueType>
auto
consuming_buffers<BufferSequence, ValueType>::begin() const ->
const_iterator
{
return const_iterator{*this, begin_};
}
template<class BufferSequence, class ValueType>
auto
consuming_buffers<BufferSequence, ValueType>::end() const ->
const_iterator
{
return const_iterator{*this, bs_.end()};
}
template<class BufferSequence, class ValueType>
void
consuming_buffers<BufferSequence, ValueType>::consume(std::size_t n)
{
using boost::asio::buffer_size;
for(;n > 0 && begin_ != bs_.end(); ++begin_)
{
auto const len =
buffer_size(*begin_) - skip_;
if(n < len)
{
skip_ += n;
break;
}
n -= len;
skip_ = 0;
}
}
template<class BufferSequence>
consuming_buffers<BufferSequence, typename BufferSequence::value_type>
consumed_buffers(BufferSequence const& bs, std::size_t n)
{
consuming_buffers<BufferSequence> cb(bs);
cb.consume(n);
return cb;
}
} // beast
#endif

View File

@@ -0,0 +1,261 @@
//
// 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_IMPL_DYNABUF_READSTREAM_HPP
#define BEAST_IMPL_DYNABUF_READSTREAM_HPP
#include <beast/core/bind_handler.hpp>
#include <beast/core/handler_concepts.hpp>
#include <beast/core/handler_alloc.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
namespace beast {
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class Handler>
class dynabuf_readstream<
Stream, DynamicBuffer>::read_some_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
dynabuf_readstream& srs;
MutableBufferSequence bs;
Handler h;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_,
dynabuf_readstream& srs_,
MutableBufferSequence const& bs_)
: srs(srs_)
, bs(bs_)
, h(std::forward<DeducedHandler>(h_))
{
}
};
std::shared_ptr<data> d_;
public:
read_some_op(read_some_op&&) = default;
read_some_op(read_some_op const&) = default;
template<class DeducedHandler, class... Args>
read_some_op(DeducedHandler&& h,
dynabuf_readstream& srs, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), srs,
std::forward<Args>(args)...))
{
(*this)(error_code{}, 0);
}
void
operator()(error_code const& ec,
std::size_t bytes_transferred);
friend
void* asio_handler_allocate(
std::size_t size, read_some_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_some_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
bool asio_handler_is_continuation(read_some_op* op)
{
return boost_asio_handler_cont_helpers::
is_continuation(op->d_->h);
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_some_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class Handler>
void
dynabuf_readstream<Stream, DynamicBuffer>::
read_some_op<MutableBufferSequence, Handler>::operator()(
error_code const& ec, std::size_t bytes_transferred)
{
auto& d = *d_;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
if(d.srs.sb_.size() == 0)
{
d.state =
d.srs.capacity_ > 0 ? 2 : 1;
break;
}
d.state = 4;
d.srs.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
return;
case 1:
// read (unbuffered)
d.state = 99;
d.srs.next_layer_.async_read_some(
d.bs, std::move(*this));
return;
case 2:
// read
d.state = 3;
d.srs.next_layer_.async_read_some(
d.srs.sb_.prepare(d.srs.capacity_),
std::move(*this));
return;
// got data
case 3:
d.state = 4;
d.srs.sb_.commit(bytes_transferred);
break;
// copy
case 4:
bytes_transferred =
boost::asio::buffer_copy(
d.bs, d.srs.sb_.data());
d.srs.sb_.consume(bytes_transferred);
// call handler
d.state = 99;
break;
}
}
d.h(ec, bytes_transferred);
}
//------------------------------------------------------------------------------
template<class Stream, class DynamicBuffer>
template<class... Args>
dynabuf_readstream<Stream, DynamicBuffer>::
dynabuf_readstream(Args&&... args)
: next_layer_(std::forward<Args>(args)...)
{
}
template<class Stream, class DynamicBuffer>
template<class ConstBufferSequence, class WriteHandler>
auto
dynabuf_readstream<Stream, DynamicBuffer>::
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler) ->
typename async_completion<
WriteHandler, void(error_code)>::result_type
{
static_assert(is_AsyncWriteStream<next_layer_type>::value,
"AsyncWriteStream requirements not met");
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
static_assert(is_CompletionHandler<WriteHandler,
void(error_code, std::size_t)>::value,
"WriteHandler requirements not met");
return next_layer_.async_write_some(buffers,
std::forward<WriteHandler>(handler));
}
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence>
std::size_t
dynabuf_readstream<Stream, DynamicBuffer>::
read_some(
MutableBufferSequence const& buffers)
{
static_assert(is_SyncReadStream<next_layer_type>::value,
"SyncReadStream requirements not met");
static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
error_code ec;
auto n = read_some(buffers, ec);
if(ec)
throw system_error{ec};
return n;
}
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence>
std::size_t
dynabuf_readstream<Stream, DynamicBuffer>::
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
static_assert(is_SyncReadStream<next_layer_type>::value,
"SyncReadStream requirements not met");
static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
using boost::asio::buffer_size;
using boost::asio::buffer_copy;
if(sb_.size() == 0)
{
if(capacity_ == 0)
return next_layer_.read_some(buffers, ec);
sb_.commit(next_layer_.read_some(
sb_.prepare(capacity_), ec));
if(ec)
return 0;
}
auto bytes_transferred =
buffer_copy(buffers, sb_.data());
sb_.consume(bytes_transferred);
return bytes_transferred;
}
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class ReadHandler>
auto
dynabuf_readstream<Stream, DynamicBuffer>::
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler) ->
typename async_completion<
ReadHandler, void(error_code)>::result_type
{
static_assert(is_AsyncReadStream<next_layer_type>::value,
"Stream requirements not met");
static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
beast::async_completion<
ReadHandler, void(error_code, std::size_t)
> completion(handler);
read_some_op<MutableBufferSequence,
decltype(completion.handler)>{
completion.handler, *this, buffers};
return completion.result.get();
}
} // beast
#endif

View File

@@ -0,0 +1,215 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_PREPARE_BUFFERS_IPP
#define BEAST_IMPL_PREPARE_BUFFERS_IPP
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <stdexcept>
#include <utility>
namespace beast {
template<class BufferSequence>
void
prepared_buffers<BufferSequence>::
setup(std::size_t n)
{
for(end_ = bs_.begin(); end_ != bs_.end(); ++end_)
{
auto const len =
boost::asio::buffer_size(*end_);
if(n <= len)
{
size_ = n;
back_ = end_++;
return;
}
n -= len;
}
size_ = 0;
back_ = end_;
}
template<class BufferSequence>
class prepared_buffers<BufferSequence>::const_iterator
{
friend class prepared_buffers<BufferSequence>;
using iter_type =
typename BufferSequence::const_iterator;
prepared_buffers const* b_ = nullptr;
typename BufferSequence::const_iterator it_;
public:
using value_type =
typename std::iterator_traits<iter_type>::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return b_ == other.b_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
if(it_ == b_->back_)
return prepare_buffer(b_->size_, *it_);
return *it_;
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
const_iterator(prepared_buffers const& b,
bool at_end)
: b_(&b)
, it_(at_end ? b.end_ : b.bs_.begin())
{
}
};
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(prepared_buffers&& other)
: prepared_buffers(std::move(other),
std::distance<iter_type>(other.bs_.begin(), other.back_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(prepared_buffers const& other)
: prepared_buffers(other,
std::distance<iter_type>(other.bs_.begin(), other.back_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::
operator=(prepared_buffers&& other) ->
prepared_buffers&
{
auto const nback = std::distance<iter_type>(
other.bs_.begin(), other.back_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = std::move(other.bs_);
back_ = std::next(bs_.begin(), nback);
end_ = std::next(bs_.begin(), nend);
size_ = other.size_;
return *this;
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::
operator=(prepared_buffers const& other) ->
prepared_buffers&
{
auto const nback = std::distance<iter_type>(
other.bs_.begin(), other.back_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = other.bs_;
back_ = std::next(bs_.begin(), nback);
end_ = std::next(bs_.begin(), nend);
size_ = other.size_;
return *this;
}
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(std::size_t n, BufferSequence const& bs)
: bs_(bs)
{
setup(n);
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::begin() const ->
const_iterator
{
return const_iterator{*this, false};
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::end() const ->
const_iterator
{
return const_iterator{*this, true};
}
template<class BufferSequence>
inline
prepared_buffers<BufferSequence>
prepare_buffers(std::size_t n, BufferSequence const& buffers)
{
return prepared_buffers<BufferSequence>(n, buffers);
}
} // beast
#endif

View File

@@ -0,0 +1,304 @@
//
// 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_IMPL_STATIC_STREAMBUF_IPP
#define BEAST_IMPL_STATIC_STREAMBUF_IPP
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <stdexcept>
namespace beast {
class static_streambuf::const_buffers_type
{
std::size_t n_;
std::uint8_t const* p_;
public:
using value_type = boost::asio::const_buffer;
class const_iterator;
const_buffers_type() = delete;
const_buffers_type(
const_buffers_type const&) = default;
const_buffers_type& operator=(
const_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class static_streambuf;
const_buffers_type(
std::uint8_t const* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
class static_streambuf::const_buffers_type::const_iterator
{
std::size_t n_ = 0;
std::uint8_t const* p_ = nullptr;
public:
using value_type = boost::asio::const_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return p_ == other.p_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return value_type{p_, n_};
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
p_ += n_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
p_ -= n_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class const_buffers_type;
const_iterator(
std::uint8_t const* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
inline
auto
static_streambuf::const_buffers_type::begin() const ->
const_iterator
{
return const_iterator{p_, n_};
}
inline
auto
static_streambuf::const_buffers_type::end() const ->
const_iterator
{
return const_iterator{p_ + n_, n_};
}
//------------------------------------------------------------------------------
class static_streambuf::mutable_buffers_type
{
std::size_t n_;
std::uint8_t* p_;
public:
using value_type = boost::asio::mutable_buffer;
class const_iterator;
mutable_buffers_type() = delete;
mutable_buffers_type(
mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(
mutable_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class static_streambuf;
mutable_buffers_type(
std::uint8_t* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
class static_streambuf::mutable_buffers_type::const_iterator
{
std::size_t n_ = 0;
std::uint8_t* p_ = nullptr;
public:
using value_type = boost::asio::mutable_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return p_ == other.p_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return value_type{p_, n_};
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
p_ += n_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
p_ -= n_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class mutable_buffers_type;
const_iterator(std::uint8_t* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
inline
auto
static_streambuf::mutable_buffers_type::begin() const ->
const_iterator
{
return const_iterator{p_, n_};
}
inline
auto
static_streambuf::mutable_buffers_type::end() const ->
const_iterator
{
return const_iterator{p_ + n_, n_};
}
//------------------------------------------------------------------------------
inline
auto
static_streambuf::prepare(std::size_t n) ->
mutable_buffers_type
{
if(n > static_cast<std::size_t>(end_ - out_))
throw std::length_error("no space in streambuf");
last_ = out_ + n;
return mutable_buffers_type{out_, n};
}
inline
auto
static_streambuf::data() const ->
const_buffers_type
{
return const_buffers_type{in_,
static_cast<std::size_t>(out_ - in_)};
}
} // beast
#endif

View File

@@ -0,0 +1,29 @@
//
// 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_PLACEHOLDERS_HPP
#define BEAST_PLACEHOLDERS_HPP
#include <functional>
namespace beast {
namespace asio {
namespace placeholders {
// asio placeholders that work with std::bind
namespace {
static auto const error (std::placeholders::_1);
static auto const bytes_transferred (std::placeholders::_2);
static auto const iterator (std::placeholders::_2);
static auto const signal_number (std::placeholders::_2);
}
}
}
}
#endif

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