mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Remove Beast subtree in preparation for git-subtree add
This commit is contained in:
12
src/beast/.gitattributes
vendored
12
src/beast/.gitattributes
vendored
@@ -1,12 +0,0 @@
|
|||||||
# 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
2
src/beast/.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
bin/
|
|
||||||
bin64/
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
# 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)
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
#
|
|
||||||
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
#
|
|
||||||
|
|
||||||
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 ;
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
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.
|
|
||||||
@@ -1,229 +0,0 @@
|
|||||||
<img width="880" height = "80" alt = "Beast"
|
|
||||||
src="https://raw.githubusercontent.com/vinniefalco/Beast/master/doc/images/readme.png">
|
|
||||||
|
|
||||||
[](https://gitter.im/vinniefalco/Beast?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status]
|
|
||||||
(https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) [![codecov]
|
|
||||||
(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
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
* 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
4
src/beast/doc/.gitignore
vendored
@@ -1,4 +0,0 @@
|
|||||||
html
|
|
||||||
temp
|
|
||||||
reference.qbk
|
|
||||||
out.txt
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
#
|
|
||||||
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
#
|
|
||||||
|
|
||||||
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]
|
|
||||||
@@ -1,439 +0,0 @@
|
|||||||
<!--
|
|
||||||
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;
|
|
||||||
@@ -1,639 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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]
|
|
||||||
@@ -1,356 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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.
|
Before Width: | Height: | Size: 241 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 6.4 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 9.3 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 92 KiB |
@@ -1,13 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/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
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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[] '''— ''']
|
|
||||||
[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]
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
<?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>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,381 +0,0 @@
|
|||||||
#---------------------------------------------------------------------------
|
|
||||||
# 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
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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]
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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]
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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]
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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]
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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]
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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]
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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]
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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]
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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]
|
|
||||||
@@ -1,452 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
]
|
|
||||||
|
|
||||||
[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]
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
# 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()
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
#
|
|
||||||
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
#
|
|
||||||
|
|
||||||
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
|
|
||||||
;
|
|
||||||
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,325 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
@@ -1,16 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef URLS_LARGE_DATA_H_INCLUDED
|
|
||||||
#define URLS_LARGE_DATA_H_INCLUDED
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
std::vector<char const*> const&
|
|
||||||
urls_large_data();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#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";
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Extras
|
|
||||||
|
|
||||||
These are not part of the official public Beast interface but they are used by the tests and some third party programs.
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,294 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,657 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_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
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,303 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_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
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,471 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_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
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_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
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_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
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_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
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_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
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_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
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_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
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_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
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_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
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_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
|
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,875 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,503 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,304 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_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
Reference in New Issue
Block a user