mirror of
https://github.com/boostorg/beast.git
synced 2025-06-25 03:51:36 +02:00
Truncate history, version 1.0.0-b2
This commit is contained in:
12
.gitattributes
vendored
Normal file
12
.gitattributes
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Set default behaviour, in case users don't have core.autocrlf set.
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
# Github
|
||||||
|
.md text eol=lf
|
||||||
|
|
||||||
|
# Visual Studio
|
||||||
|
*.sln text eol=crlf
|
||||||
|
*.vcproj text eol=crlf
|
||||||
|
*.vcxproj text eol=crlf
|
||||||
|
*.props text eol=crlf
|
||||||
|
*.filters text eol=crlf
|
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
docs/
|
||||||
|
._*
|
||||||
|
*.mode1v3
|
||||||
|
*.pbxuser
|
||||||
|
*.perspectivev3
|
||||||
|
*.user
|
||||||
|
*.ncb
|
||||||
|
*.suo
|
||||||
|
*.obj
|
||||||
|
*.ilk
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.dep
|
||||||
|
*.idb
|
||||||
|
*.manifest
|
||||||
|
*.manifest.res
|
||||||
|
*.o
|
||||||
|
*.opensdf
|
||||||
|
*.d
|
||||||
|
*.sdf
|
||||||
|
xcuserdata
|
||||||
|
contents.xcworkspacedata
|
||||||
|
.DS_Store
|
||||||
|
.svn
|
||||||
|
profile
|
||||||
|
bin/
|
69
.travis.yml
Normal file
69
.travis.yml
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
sudo: false
|
||||||
|
language: cpp
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
# 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.
|
||||||
|
- 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
|
||||||
|
- protobuf-compiler
|
||||||
|
- libstdc++6
|
||||||
|
- binutils-gold
|
||||||
|
# Provides a backtrace if the unittests crash
|
||||||
|
- gdb
|
||||||
|
|
||||||
|
packages: &clang36_pkgs
|
||||||
|
- clang-3.6
|
||||||
|
- g++-5
|
||||||
|
- python-software-properties
|
||||||
|
- libssl-dev
|
||||||
|
- libstdc++6
|
||||||
|
- binutils-gold
|
||||||
|
# Provides a backtrace if the unittests crash
|
||||||
|
- gdb
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- compiler: gcc
|
||||||
|
env: GCC_VER=5 VARIANT=debug
|
||||||
|
addons: &ao_gcc5
|
||||||
|
apt:
|
||||||
|
sources: ['ubuntu-toolchain-r-test']
|
||||||
|
packages: *gcc5_pkgs
|
||||||
|
|
||||||
|
- compiler: gcc
|
||||||
|
env: GCC_VER=5 VARIANT=release
|
||||||
|
addons: *ao_gcc5
|
||||||
|
|
||||||
|
- compiler: clang
|
||||||
|
env: GCC_VER=5 VARIANT=debug CLANG_VER=3.6
|
||||||
|
addons: &ao_clang36
|
||||||
|
apt:
|
||||||
|
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
|
||||||
|
packages: *clang36_pkgs
|
||||||
|
|
||||||
|
- compiler: clang
|
||||||
|
env: GCC_VER=5 VARIANT=release CLANG_VER=3.6
|
||||||
|
addons: *ao_clang36
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $BOOST_ROOT
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- scripts/install-dependencies.sh
|
||||||
|
|
||||||
|
script:
|
||||||
|
- scripts/build-and-test.sh
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
false
|
41
CMakeLists.txt
Normal file
41
CMakeLists.txt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Part of Beast
|
||||||
|
|
||||||
|
cmake_minimum_required (VERSION 3.5)
|
||||||
|
|
||||||
|
project (Beast)
|
||||||
|
|
||||||
|
set_property (GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /wd4100 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1")
|
||||||
|
else()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message ("cxx Flags: " ${CMAKE_CXX_FLAGS})
|
||||||
|
|
||||||
|
macro(GroupSources curdir)
|
||||||
|
file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*)
|
||||||
|
foreach(child ${children})
|
||||||
|
if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${curdir}/${child})
|
||||||
|
GroupSources(${curdir}/${child})
|
||||||
|
elseif(${child} STREQUAL "CMakeLists.txt")
|
||||||
|
source_group("" FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
|
||||||
|
else()
|
||||||
|
string(REPLACE "/" "\\" groupname ${curdir})
|
||||||
|
string(REPLACE "src" "Common" groupname ${groupname})
|
||||||
|
source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
include_directories (include)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE BEAST_INCLUDES
|
||||||
|
${PROJECT_SOURCE_DIR}/include/beast/*.hpp
|
||||||
|
${PROJECT_SOURCE_DIR}/include/beast/*.ipp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory (test)
|
||||||
|
add_subdirectory (examples)
|
||||||
|
|
||||||
|
#enable_testing()
|
92
Jamroot
Normal file
92
Jamroot
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
#
|
||||||
|
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
#
|
||||||
|
|
||||||
|
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 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
project beast
|
||||||
|
: requirements
|
||||||
|
<include>.
|
||||||
|
<include>./include
|
||||||
|
#<use>/boost//headers
|
||||||
|
<library>/boost/system//boost_system
|
||||||
|
<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
|
||||||
|
<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=0x0501
|
||||||
|
<os>NT,<toolset>cw:<library>ws2_32
|
||||||
|
<os>NT,<toolset>cw:<library>mswsock
|
||||||
|
<os>NT,<toolset>gcc:<library>ws2_32
|
||||||
|
<os>NT,<toolset>gcc:<library>mswsock
|
||||||
|
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
|
||||||
|
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
|
||||||
|
<os>HPUX:<library>ipv6
|
||||||
|
<os>QNXNTO:<library>socket
|
||||||
|
<os>HAIKU:<library>network
|
||||||
|
: usage-requirements
|
||||||
|
<include>.
|
||||||
|
:
|
||||||
|
build-dir bin
|
||||||
|
;
|
||||||
|
|
||||||
|
build-project test ;
|
||||||
|
build-project examples ;
|
23
LICENSE_1_0.txt
Normal file
23
LICENSE_1_0.txt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Boost Software License - Version 1.0 - August 17th, 2003
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
do so, all subject to the following:
|
||||||
|
|
||||||
|
The copyright notices in the Software and this entire statement, including
|
||||||
|
the above license grant, this restriction and the following disclaimer,
|
||||||
|
must be included in all copies of the Software, in whole or in part, and
|
||||||
|
all derivative works of the Software, unless such copies or derivative
|
||||||
|
works are solely in the form of machine-executable object code generated by
|
||||||
|
a source language processor.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Beast [](https://travis-ci.org/vinniefalco/Beast)
|
||||||
|
|
||||||
|
Beast provides implementations of the HTTP and WebSocket protocols
|
||||||
|
built on top of Boost.Asio and other parts of boost.
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
* Boost
|
||||||
|
* C++11 or greater
|
||||||
|
* OpenSSL (optional)
|
||||||
|
|
||||||
|
Linking applications with beast:
|
||||||
|
|
||||||
|
You need to include the .cpp file `src/beast_http_nodejs_parser.cpp`
|
||||||
|
in the build script or Makefile for your program in order to link.
|
||||||
|
|
||||||
|
Links:
|
||||||
|
|
||||||
|
* [Home](http://vinniefalco.github.io/)
|
||||||
|
* [Repository](https://github.com/vinniefalco/Beast)
|
||||||
|
* [Documentation](http://vinniefalco.github.io/beast/)
|
||||||
|
* [Autobahn.testsuite results](http://vinniefalco.github.io/autobahn/index.html)
|
||||||
|
|
||||||
|
Please report issues or questions here:
|
||||||
|
https://github.com/vinniefalco/Beast/issues
|
32
TODO.txt
Normal file
32
TODO.txt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
* Change build options to C++11 only
|
||||||
|
* Replace Jamroot with Jamfile
|
||||||
|
* Fix failing test/message.cpp
|
||||||
|
* Complete allocator testing in basic_streambuf, basic_headers
|
||||||
|
* Tidy up type_checks
|
||||||
|
- Derive from std::integral_constant
|
||||||
|
* Check DOXYGEN, GENERATIC_DOCS directives in source
|
||||||
|
- See if we can include them now that xsl is fixed
|
||||||
|
* Go over each header and split header material into detail and impl files
|
||||||
|
* Make buffers_debug a detail
|
||||||
|
* Roll header-only http parser
|
||||||
|
* Define Parser concept in HTTP
|
||||||
|
* melpon sandbox?
|
||||||
|
* invokable unit test
|
||||||
|
* trim public interface of rfc2616.h to essentials only
|
||||||
|
* Use new http routines in JSONRPCClient
|
||||||
|
* Remove or change http::headers alias
|
||||||
|
* Do something about the methods.hpp and fields.hpp type headers
|
||||||
|
* Fix index in docs
|
||||||
|
* Fix integer warning in file_body.hpp
|
||||||
|
* Use make_error_code in websocket to set the category right
|
||||||
|
* Figure out why namespace rfc2616 is included in docs
|
||||||
|
(currently disabled via GENERATING_DOCS macro)
|
||||||
|
* Include Example program listings in the docs
|
||||||
|
* Update for rfc7230
|
||||||
|
* HTTP parser size limit with test (configurable?)
|
||||||
|
* HTTP parser trailers with test
|
||||||
|
* URL parser, strong URL checking in HTTP parser
|
||||||
|
* Fix method, use string instead of enum
|
||||||
|
* More fine grained parser errors
|
||||||
|
* Fix all the warnings in all projects/build configs
|
||||||
|
* Fix bidirectional buffers iterators operator->()
|
4
doc/.gitignore
vendored
Normal file
4
doc/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
html
|
||||||
|
temp
|
||||||
|
reference.qbk
|
||||||
|
out.txt
|
83
doc/Jamfile
Normal file
83
doc/Jamfile
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
#
|
||||||
|
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
#
|
||||||
|
|
||||||
|
import os ;
|
||||||
|
|
||||||
|
local broot = [ os.environ BOOST_ROOT ] ;
|
||||||
|
|
||||||
|
project beast/doc ;
|
||||||
|
|
||||||
|
using boostbook ;
|
||||||
|
using quickbook ;
|
||||||
|
using doxygen ;
|
||||||
|
|
||||||
|
xml beast_boostbook : beast.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=1007
|
||||||
|
<xsl:param>boost.image.h=107
|
||||||
|
<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]
|
373
doc/beast.dox
Normal file
373
doc/beast.dox
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# 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/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/buffers_debug.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/type_check.hpp \
|
||||||
|
../include/beast/websocket.hpp \
|
||||||
|
../include/beast/write_streambuf.hpp \
|
||||||
|
../include/beast/http/basic_headers.hpp \
|
||||||
|
../include/beast/http/basic_parser.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/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
|
197
doc/beast.qbk
Normal file
197
doc/beast.qbk
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
[/
|
||||||
|
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>''']
|
||||||
|
[template ticket[number]'''<ulink url="https://svn.boost.org/trac/boost/ticket/'''[number]'''">'''#[number]'''</ulink>''']
|
||||||
|
[def __POSIX__ /POSIX/]
|
||||||
|
[def __Windows__ /Windows/]
|
||||||
|
[def __accept__ [@http://www.opengroup.org/onlinepubs/000095399/functions/accept.html `accept()`]]
|
||||||
|
[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 cross-platform C++ library built on 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 a high performance network server.
|
||||||
|
|
||||||
|
* [*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"}));
|
||||||
|
|
||||||
|
using namespace beast::http;
|
||||||
|
|
||||||
|
// Send HTTP request using beast
|
||||||
|
request<empty_body> req({method_t::http_get, "/", 11});
|
||||||
|
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
|
||||||
|
req.headers.replace("User-Agent", "Beast");
|
||||||
|
write(sock, req);
|
||||||
|
|
||||||
|
// Receive and print HTTP response using beast
|
||||||
|
beast::streambuf sb;
|
||||||
|
response<streambuf_body> resp;
|
||||||
|
read(sock, sb, resp);
|
||||||
|
std::cout << resp;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Establish a WebSocket connection, send a message and receive the reply:
|
||||||
|
```
|
||||||
|
#include <beast/websocket.hpp>
|
||||||
|
#include <beast/buffers_debug.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"}));
|
||||||
|
|
||||||
|
using namespace beast::websocket;
|
||||||
|
|
||||||
|
// WebSocket connect and send message using beast
|
||||||
|
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;
|
||||||
|
opcode op;
|
||||||
|
ws.read(op, sb);
|
||||||
|
ws.close(close_code::normal);
|
||||||
|
std::cout <<
|
||||||
|
beast::debug::buffers_to_string(sb.data()) << "\n";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:credits Credits]
|
||||||
|
|
||||||
|
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. Thanks also to Christopher Kohloff, whose Asio
|
||||||
|
C++ library is the inspiration behind which much of the design and
|
||||||
|
documentation is based.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[include http.qbk]
|
||||||
|
[include websocket.qbk]
|
||||||
|
[include types.qbk]
|
||||||
|
[include design.qbk]
|
||||||
|
[section:quickref Quick Reference]
|
||||||
|
[xinclude quickref.xml]
|
||||||
|
[endsect]
|
||||||
|
[include reference.qbk]
|
||||||
|
[section:idx Index]
|
||||||
|
[xinclude index.xml]
|
||||||
|
[endsect]
|
439
doc/boostbook.dtd
Normal file
439
doc/boostbook.dtd
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
<!--
|
||||||
|
BoostBook DTD - development version
|
||||||
|
|
||||||
|
For further information, see: http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost_Documentation_Format
|
||||||
|
|
||||||
|
Copyright (c) 2002 by Peter Simons <simons@cryp.to>
|
||||||
|
Copyright (c) 2003-2004 by Douglas Gregor <doug.gregor -at- gmail.com>
|
||||||
|
Copyright (c) 2007 by Frank Mori Hess <fmhess@users.sourceforge.net>
|
||||||
|
|
||||||
|
Distributed under the Boost Software License, Version 1.0.
|
||||||
|
(See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
The latest stable DTD module is identified by the PUBLIC and SYSTEM identifiers:
|
||||||
|
|
||||||
|
PUBLIC "-//Boost//DTD BoostBook XML V1.1//EN"
|
||||||
|
SYSTEM "http://www.boost.org/tools/boostbook/dtd/1.1/boostbook.dtd"
|
||||||
|
|
||||||
|
$Revision$
|
||||||
|
$Date$
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--========== Define XInclude features. ==========-->
|
||||||
|
<!-- This is not really integrated into the DTD yet. Needs more
|
||||||
|
research. -->
|
||||||
|
<!--
|
||||||
|
<!ELEMENT xi:include (xi:fallback)?>
|
||||||
|
<!ATTLIST xi:include
|
||||||
|
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude"
|
||||||
|
href CDATA #REQUIRED
|
||||||
|
parse (xml|text) "xml"
|
||||||
|
encoding CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT xi:fallback ANY>
|
||||||
|
<!ATTLIST xi:fallback
|
||||||
|
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude">
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!ENTITY % local.common.attrib "last-revision CDATA #IMPLIED">
|
||||||
|
|
||||||
|
<!--========== Define the BoostBook extensions ==========-->
|
||||||
|
<!ENTITY % boost.common.attrib "%local.common.attrib;
|
||||||
|
id CDATA #IMPLIED">
|
||||||
|
|
||||||
|
<!ENTITY % boost.namespace.mix
|
||||||
|
"class|class-specialization|struct|struct-specialization|
|
||||||
|
union|union-specialization|typedef|enum|
|
||||||
|
free-function-group|function|overloaded-function|
|
||||||
|
namespace">
|
||||||
|
|
||||||
|
<!ENTITY % boost.template.mix
|
||||||
|
"template-type-parameter|template-nontype-parameter|template-varargs">
|
||||||
|
|
||||||
|
<!ENTITY % boost.class.members
|
||||||
|
"static-constant|typedef|enum|
|
||||||
|
copy-assignment|constructor|destructor|method-group|
|
||||||
|
method|overloaded-method|data-member|class|class-specialization|struct|
|
||||||
|
struct-specialization|union|union-specialization">
|
||||||
|
|
||||||
|
<!ENTITY % boost.class.mix
|
||||||
|
"%boost.class.members;|free-function-group|function|overloaded-function">
|
||||||
|
|
||||||
|
<!ENTITY % boost.class.content
|
||||||
|
"template?, inherit*, purpose?, description?,
|
||||||
|
(%boost.class.mix;|access)*">
|
||||||
|
|
||||||
|
<!ENTITY % boost.class-specialization.content
|
||||||
|
"template?, specialization?, inherit?, purpose?, description?,
|
||||||
|
(%boost.class.mix;|access)*">
|
||||||
|
|
||||||
|
<!ENTITY % boost.function.semantics
|
||||||
|
"purpose?, description?, requires?, effects?, postconditions?,
|
||||||
|
returns?, throws?, complexity?, notes?, rationale?">
|
||||||
|
|
||||||
|
<!ENTITY % library.content
|
||||||
|
"libraryinfo, (title, ((section|library-reference|testsuite))+)?">
|
||||||
|
|
||||||
|
<!ELEMENT library (%library.content;)>
|
||||||
|
<!ATTLIST library
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
dirname CDATA #REQUIRED
|
||||||
|
html-only CDATA #IMPLIED
|
||||||
|
url CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT boostbook (title, (chapter|library)*)>
|
||||||
|
<!ATTLIST boostbook %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT libraryinfo (author+, copyright*, legalnotice*, librarypurpose, librarycategory*)>
|
||||||
|
<!ATTLIST libraryinfo %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT librarypurpose (#PCDATA|code|ulink|functionname|methodname|classname|macroname|headername|enumname|globalname)*>
|
||||||
|
<!ATTLIST librarypurpose %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT librarycategory (#PCDATA)>
|
||||||
|
<!ATTLIST librarycategory
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT libraryname (#PCDATA)>
|
||||||
|
<!ATTLIST libraryname %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT library-reference ANY>
|
||||||
|
<!ATTLIST library-reference
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT librarylist EMPTY>
|
||||||
|
<!ATTLIST librarylist %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT librarycategorylist (librarycategorydef)*>
|
||||||
|
<!ATTLIST librarycategorylist %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT librarycategorydef (#PCDATA)>
|
||||||
|
<!ATTLIST librarycategorydef
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT header ANY>
|
||||||
|
<!ATTLIST header
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT namespace (%boost.namespace.mix;)*>
|
||||||
|
<!ATTLIST namespace
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT class (%boost.class.content;)>
|
||||||
|
<!ATTLIST class
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT struct (%boost.class.content;)>
|
||||||
|
<!ATTLIST struct
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT union (%boost.class.content;)>
|
||||||
|
<!ATTLIST union
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT class-specialization (%boost.class-specialization.content;)>
|
||||||
|
<!ATTLIST class-specialization
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT struct-specialization (%boost.class-specialization.content;)>
|
||||||
|
<!ATTLIST struct-specialization
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT union-specialization (%boost.class-specialization.content;)>
|
||||||
|
<!ATTLIST union-specialization
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT access (%boost.class.members;)+>
|
||||||
|
<!ATTLIST access
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!--========= C++ Templates =========-->
|
||||||
|
<!ELEMENT template (%boost.template.mix;)*>
|
||||||
|
<!ATTLIST template %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT template-type-parameter (default?, purpose?)>
|
||||||
|
<!ATTLIST template-type-parameter
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
pack CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT template-nontype-parameter (type, default?, purpose?)>
|
||||||
|
<!ATTLIST template-nontype-parameter
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
pack CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT template-varargs EMPTY>
|
||||||
|
<!ATTLIST template-varargs %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT specialization (template-arg)*>
|
||||||
|
<!ATTLIST specialization %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT template-arg ANY>
|
||||||
|
<!ATTLIST template-arg
|
||||||
|
pack CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT default ANY>
|
||||||
|
<!ATTLIST default %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT inherit (type, purpose?)>
|
||||||
|
<!ATTLIST inherit
|
||||||
|
access CDATA #IMPLIED
|
||||||
|
pack CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT purpose ANY>
|
||||||
|
<!ATTLIST purpose %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT description ANY>
|
||||||
|
<!ATTLIST description %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT type ANY>
|
||||||
|
<!ATTLIST type %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT typedef (type, purpose?, description?)>
|
||||||
|
<!ATTLIST typedef
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT enum (enumvalue*, purpose?, description?)>
|
||||||
|
<!ATTLIST enum
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT enumvalue (default?, purpose?, description?)>
|
||||||
|
<!ATTLIST enumvalue
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT static-constant (type, default, purpose?, description?)>
|
||||||
|
<!ATTLIST static-constant
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT data-member (type, purpose?, description?)>
|
||||||
|
<!ATTLIST data-member
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT paramtype ANY>
|
||||||
|
<!ATTLIST paramtype %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT effects ANY>
|
||||||
|
<!ATTLIST effects %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT postconditions ANY>
|
||||||
|
<!ATTLIST postconditions %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT method-group (method|overloaded-method)*>
|
||||||
|
<!ATTLIST method-group
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT constructor (template?, parameter*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST constructor
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT destructor (%boost.function.semantics;)>
|
||||||
|
<!ATTLIST destructor
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT method (template?, type, parameter*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST method
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
cv CDATA #IMPLIED
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT function (template?, type, parameter*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST function
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT overloaded-method (signature*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST overloaded-method
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT overloaded-function (signature*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST overloaded-function
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT signature (template?, type, parameter*)>
|
||||||
|
<!ATTLIST signature
|
||||||
|
cv CDATA #IMPLIED
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT requires ANY>
|
||||||
|
<!ATTLIST requires %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT returns ANY>
|
||||||
|
<!ATTLIST returns %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT throws ANY>
|
||||||
|
<!ATTLIST throws %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT complexity ANY>
|
||||||
|
<!ATTLIST complexity %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT notes ANY>
|
||||||
|
<!ATTLIST notes %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT rationale ANY>
|
||||||
|
<!ATTLIST rationale %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT functionname (#PCDATA)>
|
||||||
|
<!ATTLIST functionname
|
||||||
|
alt CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT enumname (#PCDATA)>
|
||||||
|
<!ATTLIST enumname
|
||||||
|
alt CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT macroname (#PCDATA)>
|
||||||
|
<!ATTLIST macroname
|
||||||
|
alt CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT headername (#PCDATA)>
|
||||||
|
<!ATTLIST headername
|
||||||
|
alt CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT globalname (#PCDATA)>
|
||||||
|
<!ATTLIST globalname
|
||||||
|
alt CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT copy-assignment
|
||||||
|
(template?, type?, parameter*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST copy-assignment
|
||||||
|
cv CDATA #IMPLIED
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT free-function-group (function|overloaded-function)*>
|
||||||
|
<!ATTLIST free-function-group
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT precondition ANY>
|
||||||
|
<!ATTLIST precondition %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT code ANY>
|
||||||
|
<!ATTLIST code %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT using-namespace EMPTY>
|
||||||
|
<!ATTLIST using-namespace
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT using-class EMPTY>
|
||||||
|
<!ATTLIST using-class
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!--========== Boost Testsuite Extensions ==========-->
|
||||||
|
<!ENTITY % boost.testsuite.tests
|
||||||
|
"compile-test|link-test|run-test|
|
||||||
|
compile-fail-test|link-fail-test|run-fail-test">
|
||||||
|
<!ENTITY % boost.testsuite.test.content
|
||||||
|
"source*, lib*, requirement*, purpose, if-fails?">
|
||||||
|
|
||||||
|
<!ELEMENT testsuite ((%boost.testsuite.tests;)+)>
|
||||||
|
<!ATTLIST testsuite %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT compile-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST compile-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT link-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST link-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT run-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST run-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT compile-fail-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST compile-fail-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT link-fail-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST link-fail-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT run-fail-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST run-fail-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT source (#PCDATA|snippet)*>
|
||||||
|
|
||||||
|
<!ELEMENT snippet EMPTY>
|
||||||
|
<!ATTLIST snippet
|
||||||
|
name CDATA #REQUIRED>
|
||||||
|
|
||||||
|
<!ELEMENT lib (#PCDATA)>
|
||||||
|
|
||||||
|
<!ELEMENT requirement (#PCDATA)>
|
||||||
|
<!ATTLIST requirement
|
||||||
|
name CDATA #REQUIRED>
|
||||||
|
|
||||||
|
<!ELEMENT if-fails ANY>
|
||||||
|
|
||||||
|
<!ELEMENT parameter (paramtype, default?, description?)>
|
||||||
|
<!ATTLIST parameter
|
||||||
|
name CDATA #IMPLIED
|
||||||
|
pack CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT programlisting ANY>
|
||||||
|
<!ATTLIST programlisting
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!--========== Customize the DocBook DTD ==========-->
|
||||||
|
<!ENTITY % local.tech.char.class "|functionname|libraryname|enumname|headername|macroname|code">
|
||||||
|
<!ENTITY % local.para.class
|
||||||
|
"|using-namespace|using-class|librarylist|librarycategorylist">
|
||||||
|
<!ENTITY % local.descobj.class "|libraryinfo">
|
||||||
|
<!ENTITY % local.classname.attrib "alt CDATA #IMPLIED">
|
||||||
|
<!ENTITY % local.methodname.attrib "alt CDATA #IMPLIED">
|
||||||
|
<!ENTITY % local.refentry.class "|library-reference|testsuite">
|
||||||
|
<!ENTITY % local.title.char.mix "">
|
||||||
|
<!ENTITY % programlisting.module "IGNORE">
|
||||||
|
<!ENTITY % parameter.module "IGNORE">
|
||||||
|
<!ENTITY % function.module "IGNORE">
|
||||||
|
<!ENTITY % type.module "IGNORE">
|
||||||
|
|
||||||
|
<!--========== Import DocBook DTD ==========-->
|
||||||
|
<!ENTITY % DocBook PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||||
|
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||||
|
|
||||||
|
%DocBook;
|
213
doc/design.qbk
Normal file
213
doc/design.qbk
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
[/
|
||||||
|
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 ([link https://ripple.com Ripple] written in C++. These
|
||||||
|
needs were not met by existing solutions so new code was written. The new
|
||||||
|
code tries to avoid design flaws encountered in the already-existing software
|
||||||
|
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 formalizes the [link beast.types.Streambuf [*`Streambuf`]] concept,
|
||||||
|
and relies heavily on the Boost.Asio [*`ConstBufferSequence`] and
|
||||||
|
[*`MutableBufferSequence`] concepts for passing buffers to functions.
|
||||||
|
The authors have found the `Streambuf` 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 will be used in [*rippled], an
|
||||||
|
asynchronous peer to peer server that implements the
|
||||||
|
[*Ripple Consensus Protocol]. These servers are deployed in multiple
|
||||||
|
production environments, with banks in many countries running client
|
||||||
|
applications that connect to [*rippled].
|
||||||
|
]]
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
[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
|
||||||
|
[[
|
||||||
|
What about message compression?
|
||||||
|
][
|
||||||
|
The feature is not currently present in the library, but the choice
|
||||||
|
of type requirements for buffers passed to the read functions have been
|
||||||
|
made with compression in mind. There is the plan to add this feature;
|
||||||
|
however, we feel that even without compression users can begin taking
|
||||||
|
advantage of the WebSocket protocol immediately with this library.
|
||||||
|
]]
|
||||||
|
|
||||||
|
[[
|
||||||
|
Where is the TLS/SSL interface?
|
||||||
|
][
|
||||||
|
The `websocket::stream` just wraps the socket or stream that you
|
||||||
|
provide (for example, a `boost::asio::ip::tcp::socket` or a
|
||||||
|
`boost::asio::ssl::stream`). You establish your TLS connection
|
||||||
|
using the interface on `ssl::stream` like shown in all of the Asio
|
||||||
|
examples, they construct your `websocket::stream` around it.
|
||||||
|
It works perfectly fine - Beast.WebSocket doesn't try to reinvent the
|
||||||
|
wheel or put a fresh coat of interface paint on the `ssl::stream`.
|
||||||
|
|
||||||
|
The WebSocket implementation [*does] provides support for shutting down
|
||||||
|
the TLS connection through the use of the ADL compile-time virtual functions
|
||||||
|
[link beast.ref.wsproto__teardown `teardown`] and
|
||||||
|
[link beast.ref.wsproto__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]
|
367
doc/http.qbk
Normal file
367
doc/http.qbk
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
[/
|
||||||
|
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 using Boost.Asio.
|
||||||
|
|
||||||
|
The HTTP protocol is described fully in
|
||||||
|
[@https://tools.ietf.org/html/rfc2616 rfc2616]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[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 HTTP parser from NodeJS, which is
|
||||||
|
extensively field tested and exceptionally robust. 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.
|
||||||
|
|
||||||
|
[heading Scope]
|
||||||
|
|
||||||
|
The scope of this library is meant to include only the functionality of
|
||||||
|
modeling the HTTP message, serializing and deserializing the message, and
|
||||||
|
sending and receiving messages on sockets or streams. It is designed to
|
||||||
|
be a building block for creating higher level abstractions.
|
||||||
|
|
||||||
|
[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;
|
||||||
|
```
|
||||||
|
]
|
||||||
|
|
||||||
|
A HTTP message (referred to hereafter as "message") contains a request or
|
||||||
|
response line, 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. The library
|
||||||
|
provides interfaces to perform these operations on messages:
|
||||||
|
|
||||||
|
* [*Parse] a new message from a series of octets.
|
||||||
|
|
||||||
|
* [*Assemble] a new message from scratch or from an existing message.
|
||||||
|
|
||||||
|
* [*Serialize] a message into a series of octets.
|
||||||
|
|
||||||
|
* [*Read] a message from a stream. This can be thought of as a compound
|
||||||
|
operation; a network read, followed by a [*parse].
|
||||||
|
|
||||||
|
* [*Write] a message to a stream. This can be thought of as a compound
|
||||||
|
operation: a [*serialize] followed by a network write.
|
||||||
|
|
||||||
|
In the paragraphs that follow we describe simple interfaces that will serve
|
||||||
|
the majority of users looking merely to interact with a HTTP server, or
|
||||||
|
handle simple HTTP requests from clients. Subsequent sections cover the
|
||||||
|
message model in more depth, for advanced applications.
|
||||||
|
|
||||||
|
[heading Declarations]
|
||||||
|
|
||||||
|
To do anything, a message must be declared. The message class template
|
||||||
|
requires at mininum, a bool indicating whether the message is a request
|
||||||
|
(versus a response), and a `Body` type. The choice of `Body` determines the
|
||||||
|
kind of container used to represent the message body. Here we will
|
||||||
|
declare a request that has a `std::string` for the body container:
|
||||||
|
```
|
||||||
|
http::message<true, http::string_body> req;
|
||||||
|
```
|
||||||
|
|
||||||
|
Two type aliases are provided for notational convenience when declaring
|
||||||
|
messages. These two statements declare a request and a response respectively:
|
||||||
|
```
|
||||||
|
http::request<http::string_body> req;
|
||||||
|
http::response<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<http::string_body> req;
|
||||||
|
req.method = http::method_t::http_get;
|
||||||
|
req.url = "/index.html";
|
||||||
|
req.version = 11; // HTTP/1.1
|
||||||
|
req.headers.insert("User-Agent", "hello_world");
|
||||||
|
req.body = "";
|
||||||
|
|
||||||
|
http::response<http::string_body> resp;
|
||||||
|
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.";
|
||||||
|
```
|
||||||
|
|
||||||
|
The following statements achieve the same effects as the statements above:
|
||||||
|
```
|
||||||
|
http::request<http::string_body> req({http::method_t::http_get, "/index.html", 11});
|
||||||
|
req.headers.insert("User-Agent", "hello_world");
|
||||||
|
req.body = "";
|
||||||
|
|
||||||
|
http::response<http::string_body> resp({404, "Not Found", 10});
|
||||||
|
resp.headers.insert("Server", "Beast.HTTP");
|
||||||
|
resp.body = "The requested resource was not found.";
|
||||||
|
```
|
||||||
|
|
||||||
|
[heading Headers]
|
||||||
|
|
||||||
|
The `message::headers` member is a container for setting the field/value
|
||||||
|
pairs in the message. These statements change the values of the headers
|
||||||
|
in the message passed:
|
||||||
|
```
|
||||||
|
template<class Body>
|
||||||
|
void set_fields(http::request<Body>& req)
|
||||||
|
{
|
||||||
|
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<http::empty_body> req({http::method_t::http_get, "/index.html", 11});
|
||||||
|
```
|
||||||
|
|
||||||
|
* [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<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__basic_streambuf_body [*`basic_streambuf_body`:]] A body with a
|
||||||
|
`value_type` of `streambuf`. A streambuf is an efficient storage object which
|
||||||
|
uses multiple octet arrays of varying lengths to represent data. Here is
|
||||||
|
a body that uses a `boost::asio::streambuf` as its container:
|
||||||
|
```
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
http::response<http::basic_streambuf_body<boost::asio::streambuf>>
|
||||||
|
make_response(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
http::response<http::streambuf_body<boost::asio::streambuf>> resp;
|
||||||
|
resp.body.commit(boost::asio::buffer_copy(resp.body.prepare(
|
||||||
|
boost::asio::buffer_size(buffers)), buffers));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[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({http::method_t::http_get, "/index.html", 11});
|
||||||
|
...
|
||||||
|
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<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 paramter: a [link beast.types.Streambuf [*`Streambuf`]]
|
||||||
|
object. This example reads a message from the socket, with the extra bytes
|
||||||
|
stored in the streambuf parameter for use in a subsequent call to read:
|
||||||
|
```
|
||||||
|
boost::asio::streambuf sb;
|
||||||
|
...
|
||||||
|
http::response<http::string_body> resp;
|
||||||
|
http::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<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
|
||||||
|
[*`Streambuf`] and is optimized for performance:
|
||||||
|
```
|
||||||
|
void handle_read(boost::system::error_code);
|
||||||
|
...
|
||||||
|
beast::streambuf sb;
|
||||||
|
http::response<http::string_body> resp;
|
||||||
|
http::read(sock, sb, resp);
|
||||||
|
```
|
||||||
|
|
||||||
|
The `read` implementation can use any object meeting the requirements of
|
||||||
|
[link beast.types.Streambuf [*`Streambuf`]], 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]
|
||||||
|
|
BIN
doc/images/beast.png
Normal file
BIN
doc/images/beast.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
BIN
doc/images/body.png
Normal file
BIN
doc/images/body.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
BIN
doc/images/body.psd
Normal file
BIN
doc/images/body.psd
Normal file
Binary file not shown.
BIN
doc/images/message.png
Normal file
BIN
doc/images/message.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.3 KiB |
13
doc/index.xml
Normal file
13
doc/index.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" "boostbook.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
|
||||||
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
-->
|
||||||
|
|
||||||
|
<section id="index">
|
||||||
|
<index/>
|
||||||
|
</section>
|
13
doc/makeqbk.sh
Normal file
13
doc/makeqbk.sh
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
#
|
||||||
|
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
mkdir -p ../bin/doc/xml
|
||||||
|
doxygen beast.dox
|
||||||
|
cd ../bin/doc/xml
|
||||||
|
xsltproc combine.xslt index.xml > all.xml
|
||||||
|
cd ../../../doc
|
||||||
|
xsltproc reference.xsl ../bin/doc/xml/all.xml > reference.qbk
|
163
doc/quickref.xml
Normal file
163
doc/quickref.xml
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<?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_headers">basic_headers</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__basic_parser">basic_parser</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__basic_streambuf_body">basic_streambuf_body</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__empty_body">empty_body</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__error_code">error_code</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">Type Traits</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<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__chunk_encode">chunk_encode</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__chunk_encode_final">chunk_encode_final</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__read">read</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__write">write</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.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__static_string">static_string</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__stream">stream</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__read_buffer_size">read_buffer_size</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__read_message_max">read_message_max</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__write_buffer_size">write_buffer_size</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
<entry valign="top">
|
||||||
|
<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__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.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.streambuf">streambuf</link></member>
|
||||||
|
<member><link linkend="beast.ref.streambuf_readstream">streambuf_readstream</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.prepare_buffer">prepare_buffer</link></member>
|
||||||
|
<member><link linkend="beast.ref.prepare_buffers">prepare_buffers</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_BufferSequence">is_BufferSequence</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_ConstBufferSequence">is_ConstBufferSequence</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_Handler">is_Handler</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_MutableBufferSequence">is_MutableBufferSequence</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_Stream">is_Stream</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_Streambuf">is_Streambuf</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_SyncReadStream">is_SyncReadStream</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.BufferSequence">BufferSequence</link></member>
|
||||||
|
<member><link linkend="beast.types.Stream">Stream</link></member>
|
||||||
|
<member><link linkend="beast.types.Streambuf">Streambuf</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</informaltable>
|
1639
doc/reference.xsl
Normal file
1639
doc/reference.xsl
Normal file
File diff suppressed because it is too large
Load Diff
446
doc/types.qbk
Normal file
446
doc/types.qbk
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
[/
|
||||||
|
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:types Type Requirements]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Body Body]
|
||||||
|
|
||||||
|
In this table:
|
||||||
|
|
||||||
|
* `X` is a type meeting the requirements of [*`Body`].
|
||||||
|
|
||||||
|
[table Body requirements
|
||||||
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`X::value_type`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
The type of the `message::body` member.
|
||||||
|
If this is not movable or not copyable, the containing message
|
||||||
|
will be not movable or not copyable.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`X:value_type{}`]
|
||||||
|
[]
|
||||||
|
[`DefaultConstructible`]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`Body::reader`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
If present, a type meeting the requirements of
|
||||||
|
[link beast.types.Reader [*`Reader`]].
|
||||||
|
Provides an implementation to parse the body.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`Body::writer`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
If present, a type meeting the requirements of
|
||||||
|
[link beast.types.Writer [*`Writer`]].
|
||||||
|
Provides an implementation to serialize the body.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:BufferSequence BufferSequence]
|
||||||
|
|
||||||
|
A `BufferSequence` meets [*one of] the following requirements:
|
||||||
|
|
||||||
|
* `ConstBufferSequence`
|
||||||
|
* `MutableBufferSequence`
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Field Field]
|
||||||
|
|
||||||
|
A [*`Field`] represents a single HTTP header field/value pair.
|
||||||
|
|
||||||
|
In this table:
|
||||||
|
|
||||||
|
* `X` denotes a type meeting the requirements of [*`Field`].
|
||||||
|
* `a` denotes a value of type `X`.
|
||||||
|
|
||||||
|
[table Field requirements
|
||||||
|
|
||||||
|
[[operation][type][semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`a.name()`]
|
||||||
|
[`boost::string_ref`]
|
||||||
|
[
|
||||||
|
This function returns a value implicitly convertible to
|
||||||
|
`boost::string_ref` containing the case-insensitve field
|
||||||
|
name, without leading or trailing white space.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.value()`]
|
||||||
|
[`boost::string_ref`]
|
||||||
|
[
|
||||||
|
This function returns a value implicitly convertible to
|
||||||
|
`boost::string_ref` containing the value for the field. The
|
||||||
|
value is considered canonical if there is no leading or
|
||||||
|
trailing whitespace.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:FieldSequence FieldSequence]
|
||||||
|
|
||||||
|
A [*`FieldSequence`] is an iterable container whose value type meets
|
||||||
|
the requirements of [link beast.types.Field [*`Field`]].
|
||||||
|
|
||||||
|
In this table:
|
||||||
|
|
||||||
|
* `X` denotes a type that meets the requirements of [*`FieldSequence`].
|
||||||
|
|
||||||
|
* `a` is a value of type `X`.
|
||||||
|
|
||||||
|
[table FieldSequence requirements
|
||||||
|
[[operation][type][semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`X::value_type`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
A type that meets the requirements of `Field`.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`X::const_iterator`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
A type that meets the requirements of `ForwardIterator`.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.begin()`]
|
||||||
|
[`X::const_iterator`]
|
||||||
|
[
|
||||||
|
Returns an iterator to the beginning of the field sequence.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.end()`]
|
||||||
|
[`X::const_iterator`]
|
||||||
|
[
|
||||||
|
Returns an iterator to the end of the field sequence.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Reader Reader]
|
||||||
|
|
||||||
|
Parser implementations will construct the corresponding `reader` object
|
||||||
|
during the parse. This customization point allows the Body to determine
|
||||||
|
the strategy for storing incoming message body data.
|
||||||
|
|
||||||
|
In this table:
|
||||||
|
|
||||||
|
* `X` denotes a type meeting the requirements of [*`Reader`].
|
||||||
|
|
||||||
|
* `a` denotes a value of type `X`.
|
||||||
|
|
||||||
|
* `p` is any pointer.
|
||||||
|
|
||||||
|
* `n` is a value convertible to `std::size_t`.
|
||||||
|
|
||||||
|
* `ec` is a value of type `error_code&`.
|
||||||
|
|
||||||
|
* `m` denotes a value of type `message const&` where
|
||||||
|
`std::is_same<decltype(m.body), Body::value_type>:value == true`
|
||||||
|
|
||||||
|
|
||||||
|
[table Reader requirements
|
||||||
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`X a(m);`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
`a` is constructible from `m`. The lifetime of `m` is
|
||||||
|
guaranteed to end no earlier than after `a` is destroyed.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.write(p, n, ec)`]
|
||||||
|
[`void`]
|
||||||
|
[
|
||||||
|
Deserializes the input sequence into the body.
|
||||||
|
If `ec` is set, the deserialization is aborted and the error
|
||||||
|
is returned to the caller.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[note Definitions for required `Reader` member functions should be declared
|
||||||
|
inline so the generated code becomes part of the implementation. ]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Writer Writer]
|
||||||
|
|
||||||
|
A `Writer` serializes the message body. The implementation creates an instance
|
||||||
|
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.reference.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::size_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]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Stream Stream]
|
||||||
|
|
||||||
|
A `Stream` meets the following requirements:
|
||||||
|
|
||||||
|
* `SyncReadStream`
|
||||||
|
* `SyncWriteStream`
|
||||||
|
* `AsyncReadStream`
|
||||||
|
* `AsyncWriteStream`
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Streambuf Streambuf]
|
||||||
|
|
||||||
|
In the table below, `X` denotes a class, `a` denotes a value
|
||||||
|
of type `X`, `n` denotes a value convertible to `std::size_t`,
|
||||||
|
and `U` and `T` denote unspecified types.
|
||||||
|
|
||||||
|
[table Streambuf requirements
|
||||||
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`X::const_buffers_type`]
|
||||||
|
[`T`]
|
||||||
|
[`T` meets the requirements for `ConstBufferSequence`.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`X::mutable_buffers_type`]
|
||||||
|
[`U`]
|
||||||
|
[`U` meets the requirements for `MutableBufferSequence`.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.commit(n)`]
|
||||||
|
[`void`]
|
||||||
|
[Moves bytes from the output sequence to the input sequence.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.consume(n)`]
|
||||||
|
[`void`]
|
||||||
|
[Removes bytes from the input sequence.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.data()`]
|
||||||
|
[`T`]
|
||||||
|
[Returns a list of buffers that represents the input sequence.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.prepare(n)`]
|
||||||
|
[`U`]
|
||||||
|
[Returns a list of buffers that represents the output sequence, with
|
||||||
|
the given size.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.size()`]
|
||||||
|
[`std::size_t`]
|
||||||
|
[Returns the size of the input sequence.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.max_size()`]
|
||||||
|
[`std::size_t`]
|
||||||
|
[Returns the maximum size of the `Streambuf`.]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[endsect]
|
395
doc/websocket.qbk
Normal file
395
doc/websocket.qbk
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
[/
|
||||||
|
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 (`asio_handler_invoke`),
|
||||||
|
providing execution guarantees on composed operations in a manner
|
||||||
|
identical to Boost.Asio. The implementation also uses handler allocation
|
||||||
|
hooks (`asio_handler_allocate`) when allocating memory internally for
|
||||||
|
composed operations.
|
||||||
|
|
||||||
|
There is no need for inheritance or virtual members in `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:creating Creating the socket]
|
||||||
|
|
||||||
|
The interface to Beast's WebSocket implementation is a single template
|
||||||
|
class [link beast.ref.websocket__stream `websocket::stream`] which wraps a
|
||||||
|
"next layer" object. The next layer object must meet the requirements of
|
||||||
|
`SyncReadStream` and `SyncWriteStream` if synchronous operations are performed,
|
||||||
|
`AsyncReadStream` and `AsyncWriteStream` is asynchronous operations are
|
||||||
|
performed, or both. Arguments supplied during construction are passed to
|
||||||
|
next layer's constructor. Here we declare two websockets which have ownership
|
||||||
|
of the next layer:
|
||||||
|
```
|
||||||
|
io_service ios;
|
||||||
|
websocket::stream<ip::tcp::socket> ws(ios);
|
||||||
|
|
||||||
|
ssl::context ctx(ssl::context::sslv23);
|
||||||
|
websocket::stream<ssl::stream<ip::tcp::socket>> wss(ios, ctx);
|
||||||
|
```
|
||||||
|
|
||||||
|
For servers that can handshake in multiple protocols, it may be desired
|
||||||
|
to wrap an object that already exists. This socket can be moved in:
|
||||||
|
```
|
||||||
|
tcp::socket&& sock;
|
||||||
|
...
|
||||||
|
websocket::stream<ip::tcp::socket> ws(std::move(sock));
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, the wrapper can be constructed with a non-owning reference. In
|
||||||
|
this case, the caller is responsible for managing the lifetime of the
|
||||||
|
underlying socket being wrapped:
|
||||||
|
```
|
||||||
|
tcp::socket sock;
|
||||||
|
...
|
||||||
|
websocket::stream<ip::tcp::socket&> ws(sock);
|
||||||
|
```
|
||||||
|
|
||||||
|
The layer being wrapped can be accessed through the websocket's "next layer",
|
||||||
|
permitting callers to interact directly with its interface.
|
||||||
|
```
|
||||||
|
ssl::context ctx(ssl::context::sslv23);
|
||||||
|
websocket::stream<ssl::stream<ip::tcp::socket>> ws(ios, ctx);
|
||||||
|
...
|
||||||
|
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";
|
||||||
|
io_service ios;
|
||||||
|
tcp::resolver r(ios);
|
||||||
|
websocket::stream<ip::tcp::socket> ws(ios);
|
||||||
|
connect(ws.next_layer(), r.resolve(tcp::resolver::query{host, "ws"}));
|
||||||
|
```
|
||||||
|
|
||||||
|
Accepting an incoming connection:
|
||||||
|
```
|
||||||
|
void do_accept(tcp::acceptor& acceptor)
|
||||||
|
{
|
||||||
|
websocket::stream<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. `hanshake` is used to send the
|
||||||
|
request with the required host and resource strings.
|
||||||
|
```
|
||||||
|
websocket::stream<ip::tcp::socket> ws(ios);
|
||||||
|
...
|
||||||
|
ws.set_option(websocket::keep_alive(true));
|
||||||
|
ws.handshake("ws.mywebapp.com:80", "/cgi-bin/bitcoin-prices");
|
||||||
|
```
|
||||||
|
|
||||||
|
The `websocket::stream` automatically handles receiving and processing
|
||||||
|
the HTTP response to the handshake request. The call to handshake is
|
||||||
|
successful if a HTTP response is received with the 101 "Switching Protocols"
|
||||||
|
status code. On failure, an error is returned or an exception is thrown.
|
||||||
|
Depending on the keep alive setting, the socket may remain open for a
|
||||||
|
subsequent handshake attempt
|
||||||
|
|
||||||
|
Performing a handshake for an incoming websocket upgrade request operates
|
||||||
|
similarly. If the handshake fails, an error is returned or exception thrown:
|
||||||
|
```
|
||||||
|
websocket::stream<ip::tcp::socket> ws(ios);
|
||||||
|
...
|
||||||
|
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(tcp::socket& sock)
|
||||||
|
{
|
||||||
|
boost::asio::streambuf sb;
|
||||||
|
read_until(sock, sb, "\r\n\r\n");
|
||||||
|
...
|
||||||
|
websocket::stream<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(tcp::socket& sock)
|
||||||
|
{
|
||||||
|
boost::asio::streambuf sb;
|
||||||
|
http::request<http::empty_body> request;
|
||||||
|
http::read(sock, request);
|
||||||
|
if(http::is_upgrade(request))
|
||||||
|
{
|
||||||
|
websocket::stream<ip::tcp::socket&> ws(sock);
|
||||||
|
ws.accept(request);
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[note Identifiers in the `http` namespace are part of Beast.HTTP. ]
|
||||||
|
|
||||||
|
[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(websocket::stream<ip::tcp::socket>& ws)
|
||||||
|
{
|
||||||
|
streambuf sb;
|
||||||
|
websocket::opcode::value op;
|
||||||
|
ws.read(sb);
|
||||||
|
|
||||||
|
ws.set_option(websocket::message_type(op));
|
||||||
|
websocket::write(ws, sb.data());
|
||||||
|
sb.consume(sb.size());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[important Calls to `set_option` must be made from the same implicit
|
||||||
|
or explicit strand as that used to perform other operations. ]
|
||||||
|
|
||||||
|
[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(websocket::stream<ip::tcp::socket>& ws)
|
||||||
|
{
|
||||||
|
streambuf sb;
|
||||||
|
websocket::frame_info fi;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
ws.read_frame(fi, sb);
|
||||||
|
if(fi.fin)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ws.set_option(websocket::message_type(fi.op));
|
||||||
|
consuming_buffers<streambuf::const_buffers_type> cb(sb.data());
|
||||||
|
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 noted. The receipt of a close frame
|
||||||
|
initiates the WebSocket close procedure, eventually resulting in the
|
||||||
|
error code `websocket::error::closed` being delivered to the caller in
|
||||||
|
a subsequent read operation, assuming no other error takes place.
|
||||||
|
|
||||||
|
To 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(websocket::auto_fragment_size(8192));
|
||||||
|
```
|
||||||
|
|
||||||
|
The WebSocket protocol defines a procedure and control message for initiating
|
||||||
|
a close of the session. Handling of close initiated by the remote end of the
|
||||||
|
connection is performed automatically. To manually initiate a close, use
|
||||||
|
`websocket::stream::close`:
|
||||||
|
```
|
||||||
|
ws.close();
|
||||||
|
```
|
||||||
|
|
||||||
|
[note To receive the `websocket::error::closed` error, a read operation
|
||||||
|
is required. ]
|
||||||
|
|
||||||
|
[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 `Streambuf`. This concept is modeled on `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 io_service]
|
||||||
|
|
||||||
|
The creation and operation of the `boost::asio::io_service` associated with
|
||||||
|
the underlying stream is left to the callers, permitting any implementation
|
||||||
|
strategy including one that does not require threads for environments where
|
||||||
|
threads are unavailable. Beast.WSProto itself does not use or require threads.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:safety Thread Safety]
|
||||||
|
|
||||||
|
Like a regular asio socket, a `websocket::stream` is not thread safe. Callers
|
||||||
|
are responsible for synchronizing operations on the socket using an implicit
|
||||||
|
or explicit strand, as per the Asio documentation. The asynchronous interface
|
||||||
|
supports one active read and one active write simultaneously. Undefined
|
||||||
|
behavior results if two or more reads or two or more writes are attempted
|
||||||
|
concurrently. Caller initiated WebSocket ping, pong, and close operations
|
||||||
|
each count as an active write.
|
||||||
|
|
||||||
|
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]
|
30
examples/CMakeLists.txt
Normal file
30
examples/CMakeLists.txt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Part of Beast
|
||||||
|
|
||||||
|
GroupSources(include/beast)
|
||||||
|
GroupSources(examples)
|
||||||
|
|
||||||
|
add_executable (http-crawl
|
||||||
|
${BEAST_INCLUDES}
|
||||||
|
http_crawl.cpp
|
||||||
|
urls_large_data.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable (http-server
|
||||||
|
${BEAST_INCLUDES}
|
||||||
|
http_server.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable (http-example
|
||||||
|
${BEAST_INCLUDES}
|
||||||
|
http_example.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable (websocket-echo
|
||||||
|
${BEAST_INCLUDES}
|
||||||
|
websocket_echo.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable (websocket-example
|
||||||
|
${BEAST_INCLUDES}
|
||||||
|
websocket_example.cpp
|
||||||
|
)
|
30
examples/Jamfile
Normal file
30
examples/Jamfile
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#
|
||||||
|
# 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_echo :
|
||||||
|
websocket_echo.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
exe websocket_example :
|
||||||
|
websocket_example.cpp
|
||||||
|
;
|
||||||
|
|
95
examples/file_body.h
Normal file
95
examples/file_body.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
|
||||||
|
#define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/message.hpp>
|
||||||
|
#include <beast/http/resume_context.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::size_t size_;
|
||||||
|
std::size_t offset_ = 0;
|
||||||
|
std::string const& path_;
|
||||||
|
FILE* file_ = nullptr;
|
||||||
|
char buf_[4096];
|
||||||
|
std::size_t buf_len_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static bool constexpr is_single_pass = false;
|
||||||
|
|
||||||
|
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::size_t
|
||||||
|
content_length() const
|
||||||
|
{
|
||||||
|
return size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Write>
|
||||||
|
boost::tribool
|
||||||
|
operator()(resume_context&&, error_code&, Write&& write)
|
||||||
|
{
|
||||||
|
buf_len_ = std::min(size_ - offset_, 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
|
202
examples/http_async_server.h
Normal file
202
examples/http_async_server.h
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
|
||||||
|
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
|
||||||
|
|
||||||
|
#include "file_body.h"
|
||||||
|
#include "http_stream.h"
|
||||||
|
|
||||||
|
#include <beast/placeholders.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#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<string_body>;
|
||||||
|
using resp_type = response<file_body>;
|
||||||
|
|
||||||
|
boost::asio::io_service ios_;
|
||||||
|
socket_type sock_;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
std::string root_;
|
||||||
|
std::vector<std::thread> thread_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
http_async_server(endpoint_type const& ep,
|
||||||
|
int threads, std::string const& root)
|
||||||
|
: sock_(ios_)
|
||||||
|
, acceptor_(ios_)
|
||||||
|
, 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(int 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class peer : public std::enable_shared_from_this<peer>
|
||||||
|
{
|
||||||
|
int id_;
|
||||||
|
stream<socket_type> stream_;
|
||||||
|
boost::asio::io_service::strand strand_;
|
||||||
|
std::string root_;
|
||||||
|
req_type req_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
peer(peer&&) = default;
|
||||||
|
peer(peer const&) = default;
|
||||||
|
peer& operator=(peer&&) = delete;
|
||||||
|
peer& operator=(peer const&) = delete;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
peer(socket_type&& sock, std::string const& root)
|
||||||
|
: stream_(std::move(sock))
|
||||||
|
, strand_(stream_.get_io_service())
|
||||||
|
, root_(root)
|
||||||
|
{
|
||||||
|
static int n = 0;
|
||||||
|
id_ = ++n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
do_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_read()
|
||||||
|
{
|
||||||
|
stream_.async_read(req_, strand_.wrap(
|
||||||
|
std::bind(&peer::on_read, shared_from_this(),
|
||||||
|
asio::placeholders::error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_read(error_code ec)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "read");
|
||||||
|
do_read();
|
||||||
|
auto path = req_.url;
|
||||||
|
if(path == "/")
|
||||||
|
path = "/index.html";
|
||||||
|
path = root_ + path;
|
||||||
|
if(! boost::filesystem::exists(path))
|
||||||
|
{
|
||||||
|
response<string_body> resp(
|
||||||
|
{404, "Not Found", req_.version});
|
||||||
|
resp.headers.replace("Server", "http_async_server");
|
||||||
|
resp.body = "The file '" + path + "' was not found";
|
||||||
|
stream_.async_write(std::move(resp),
|
||||||
|
std::bind(&peer::on_write, shared_from_this(),
|
||||||
|
asio::placeholders::error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
response<file_body> resp(
|
||||||
|
{200, "OK", req_.version});
|
||||||
|
resp.headers.replace("Server", "http_async_server");
|
||||||
|
resp.headers.replace("Content-Type", "text/html");
|
||||||
|
resp.body = path;
|
||||||
|
stream_.async_write(std::move(resp),
|
||||||
|
std::bind(&peer::on_write, shared_from_this(),
|
||||||
|
asio::placeholders::error));
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_write(error_code ec)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
fail(ec, "write");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec != boost::asio::error::operation_aborted)
|
||||||
|
{
|
||||||
|
std::cerr <<
|
||||||
|
"#" << std::to_string(id_) << " " <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
std::cerr <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
maybe_throw(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
fail(ec, what);
|
||||||
|
throw ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_accept(error_code ec)
|
||||||
|
{
|
||||||
|
if(! acceptor_.is_open())
|
||||||
|
return;
|
||||||
|
maybe_throw(ec, "accept");
|
||||||
|
socket_type sock(std::move(sock_));
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&http_async_server::on_accept, this,
|
||||||
|
asio::placeholders::error));
|
||||||
|
std::make_shared<peer>(std::move(sock), root_)->run();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
67
examples/http_crawl.cpp
Normal file
67
examples/http_crawl.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include "http_stream.h"
|
||||||
|
#include "urls_large_data.h"
|
||||||
|
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace beast::http;
|
||||||
|
using namespace boost::asio;
|
||||||
|
|
||||||
|
template<class String>
|
||||||
|
void
|
||||||
|
err(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"});
|
||||||
|
stream<ip::tcp::socket> hs(ios);
|
||||||
|
connect(hs.lowest_layer(), it);
|
||||||
|
auto ep = hs.lowest_layer().remote_endpoint();
|
||||||
|
request<empty_body> req({method_t::http_get, "/", 11});
|
||||||
|
req.headers.insert("Host", host +
|
||||||
|
std::string(":") + std::to_string(ep.port()));
|
||||||
|
req.headers.insert("User-Agent", "beast/http");
|
||||||
|
hs.write(req);
|
||||||
|
response<string_body> resp;
|
||||||
|
hs.read(resp);
|
||||||
|
std::cout << resp;
|
||||||
|
}
|
||||||
|
catch(boost::system::system_error const& ec)
|
||||||
|
{
|
||||||
|
std::cerr << host << ": " << ec.what();
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
std::cerr << host << ": unknown exception" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
examples/http_example.cpp
Normal file
36
examples/http_example.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#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"}));
|
||||||
|
|
||||||
|
using namespace beast::http;
|
||||||
|
|
||||||
|
// Send HTTP request using beast
|
||||||
|
request<empty_body> req({method_t::http_get, "/", 11});
|
||||||
|
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
|
||||||
|
req.headers.replace("User-Agent", "Beast");
|
||||||
|
write(sock, req);
|
||||||
|
|
||||||
|
// Receive and print HTTP response using beast
|
||||||
|
beast::streambuf sb;
|
||||||
|
response<streambuf_body> resp;
|
||||||
|
read(sock, sb, resp);
|
||||||
|
std::cout << resp;
|
||||||
|
}
|
81
examples/http_server.cpp
Normal file
81
examples/http_server.cpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include "http_async_server.h"
|
||||||
|
#include "http_sync_server.h"
|
||||||
|
#include "sig_wait.h"
|
||||||
|
|
||||||
|
#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);
|
||||||
|
sig_wait();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
http_async_server server(ep, threads, root);
|
||||||
|
sig_wait();
|
||||||
|
}
|
||||||
|
}
|
492
examples/http_stream.h
Normal file
492
examples/http_stream.h
Normal file
@ -0,0 +1,492 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_STREAM_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_STREAM_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http.hpp>
|
||||||
|
#include <beast/async_completion.hpp>
|
||||||
|
#include <beast/basic_streambuf.hpp>
|
||||||
|
#include <boost/asio/io_service.hpp>
|
||||||
|
#include <boost/intrusive/list.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class stream_base
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
struct op
|
||||||
|
: boost::intrusive::list_base_hook<
|
||||||
|
boost::intrusive::link_mode<
|
||||||
|
boost::intrusive::normal_link>>
|
||||||
|
{
|
||||||
|
virtual ~op() = default;
|
||||||
|
virtual void operator()() = 0;
|
||||||
|
virtual void cancel() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using op_list = typename boost::intrusive::make_list<
|
||||||
|
op, boost::intrusive::constant_time_size<false>>::type;
|
||||||
|
|
||||||
|
op_list wr_q_;
|
||||||
|
bool wr_active_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
/** Provides message-oriented functionality using HTTP.
|
||||||
|
|
||||||
|
The stream class template provides asynchronous and blocking
|
||||||
|
message-oriented functionality necessary for clients and servers
|
||||||
|
to utilize the HTTP protocol.
|
||||||
|
|
||||||
|
@par Thread Safety
|
||||||
|
@e Distinct @e objects: Safe.@n
|
||||||
|
@e Shared @e objects: Unsafe. The application must ensure that
|
||||||
|
all asynchronous operations are performed within the same
|
||||||
|
implicit or explicit strand.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
|
||||||
|
To use the class template with an `ip::tcp::socket`, you would write:
|
||||||
|
|
||||||
|
@code
|
||||||
|
http::stream<ip::tcp::socket> hs(io_service);
|
||||||
|
@endcode
|
||||||
|
Alternatively, you can write:
|
||||||
|
@code
|
||||||
|
ip::tcp::socket sock(io_service);
|
||||||
|
http::stream<ip::tcp::socket&> hs(sock);
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@note A stream object must not be destroyed while there are
|
||||||
|
pending asynchronous operations associated with it.
|
||||||
|
|
||||||
|
@par Concepts
|
||||||
|
AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
|
||||||
|
*/
|
||||||
|
template<class NextLayer,
|
||||||
|
class Allocator = std::allocator<char>>
|
||||||
|
class stream : public detail::stream_base
|
||||||
|
{
|
||||||
|
NextLayer next_layer_;
|
||||||
|
basic_streambuf<Allocator> rd_buf_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// The type of the next layer.
|
||||||
|
using next_layer_type =
|
||||||
|
typename std::remove_reference<NextLayer>::type;
|
||||||
|
|
||||||
|
/// The type of the lowest layer.
|
||||||
|
using lowest_layer_type =
|
||||||
|
typename next_layer_type::lowest_layer_type;
|
||||||
|
|
||||||
|
/// The type of endpoint of the lowest layer.
|
||||||
|
using endpoint_type =
|
||||||
|
typename lowest_layer_type::endpoint_type;
|
||||||
|
|
||||||
|
/// The protocol of the next layer.
|
||||||
|
using protocol_type =
|
||||||
|
typename lowest_layer_type::protocol_type;
|
||||||
|
|
||||||
|
/// The type of resolver of the next layer.
|
||||||
|
using resolver_type =
|
||||||
|
typename protocol_type::resolver;
|
||||||
|
|
||||||
|
/** Destructor.
|
||||||
|
|
||||||
|
@note A stream object must not be destroyed while there
|
||||||
|
are pending asynchronous operations associated with it.
|
||||||
|
*/
|
||||||
|
~stream();
|
||||||
|
|
||||||
|
/** Move constructor.
|
||||||
|
|
||||||
|
Undefined behavior if operations are active or pending.
|
||||||
|
*/
|
||||||
|
stream(stream&&) = default;
|
||||||
|
|
||||||
|
/** Move assignment.
|
||||||
|
|
||||||
|
Undefined behavior if operations are active or pending.
|
||||||
|
*/
|
||||||
|
stream& operator=(stream&&) = default;
|
||||||
|
|
||||||
|
/** Construct a HTTP stream.
|
||||||
|
|
||||||
|
This constructor creates a HTTP stream and initialises
|
||||||
|
the next layer.
|
||||||
|
|
||||||
|
@throws Any exceptions thrown by the Stream constructor.
|
||||||
|
|
||||||
|
@param args The arguments to be passed to initialise the
|
||||||
|
next layer. The arguments are forwarded to the next layer's
|
||||||
|
constructor.
|
||||||
|
*/
|
||||||
|
template<class... Args>
|
||||||
|
explicit
|
||||||
|
stream(Args&&... args);
|
||||||
|
|
||||||
|
/** Get the io_service associated with the stream.
|
||||||
|
|
||||||
|
This function may be used to obtain the io_service object
|
||||||
|
that the stream uses to dispatch handlers for asynchronous
|
||||||
|
operations.
|
||||||
|
|
||||||
|
@return A reference to the io_service object that the stream
|
||||||
|
will use to dispatch handlers. Ownership is not transferred
|
||||||
|
to the caller.
|
||||||
|
*/
|
||||||
|
boost::asio::io_service&
|
||||||
|
get_io_service()
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer().get_io_service();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the next layer.
|
||||||
|
|
||||||
|
This function returns a reference to the next layer
|
||||||
|
in a stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the next layer in the stack of
|
||||||
|
stream layers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
next_layer_type&
|
||||||
|
next_layer()
|
||||||
|
{
|
||||||
|
return next_layer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the next layer.
|
||||||
|
|
||||||
|
This function returns a reference to the next layer in a
|
||||||
|
stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the next layer in the stack of
|
||||||
|
stream layers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
next_layer_type const&
|
||||||
|
next_layer() const
|
||||||
|
{
|
||||||
|
return next_layer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the lowest layer.
|
||||||
|
|
||||||
|
This function returns a reference to the lowest layer
|
||||||
|
in a stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the lowest layer in the stack of
|
||||||
|
stream layers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
lowest_layer_type&
|
||||||
|
lowest_layer()
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the lowest layer.
|
||||||
|
|
||||||
|
This function returns a reference to the lowest layer
|
||||||
|
in a stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the lowest layer in the stack of
|
||||||
|
stream layers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
lowest_layer_type const&
|
||||||
|
lowest_layer() const
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cancel pending operations.
|
||||||
|
|
||||||
|
This will cancel all of the asynchronous operations pending,
|
||||||
|
including pipelined writes that have not been started. Handlers for
|
||||||
|
canceled writes will be called with
|
||||||
|
`boost::asio::error::operation_aborted`.
|
||||||
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
cancel()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
cancel(ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cancel pending operations.
|
||||||
|
|
||||||
|
This will cancel all of the asynchronous operations pending,
|
||||||
|
including pipelined writes that have not been started. Handlers for
|
||||||
|
canceled writes will be called with
|
||||||
|
`boost::asio::error::operation_aborted`.
|
||||||
|
|
||||||
|
@param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
cancel(error_code& ec);
|
||||||
|
|
||||||
|
/** Read a HTTP message from the stream.
|
||||||
|
|
||||||
|
This function is used to read a single HTTP message from the stream.
|
||||||
|
The call will block until one of the followign conditions is true:
|
||||||
|
|
||||||
|
@li A message has been read.
|
||||||
|
|
||||||
|
@li An error occurred.
|
||||||
|
|
||||||
|
The operation is implemented in terms of zero or more calls to the
|
||||||
|
next layer's `read_some` function.
|
||||||
|
|
||||||
|
@param msg An object used to store the message. The previous
|
||||||
|
contents of the object will be overwritten.
|
||||||
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
read(message<isRequest, Body, Headers>& msg)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
read(msg, ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read a HTTP message from the stream.
|
||||||
|
|
||||||
|
This function is used to read a single HTTP message from the stream.
|
||||||
|
The call will block until one of the followign conditions is true:
|
||||||
|
|
||||||
|
@li A message has been read.
|
||||||
|
|
||||||
|
@li An error occurred.
|
||||||
|
|
||||||
|
The operation is implemented in terms of zero or more calls to the
|
||||||
|
next layer's `read_some` function.
|
||||||
|
|
||||||
|
@param msg An object used to store the message. The previous
|
||||||
|
contents of the object will be overwritten.
|
||||||
|
|
||||||
|
@param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
read(message<isRequest, Body, Headers>& msg,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Start reading a HTTP message from the stream asynchronously.
|
||||||
|
|
||||||
|
This function is used to asynchronously read a single HTTP message
|
||||||
|
from the stream. The function call always returns immediately. The
|
||||||
|
asynchronous operation will continue until one of the following
|
||||||
|
conditions is true:
|
||||||
|
|
||||||
|
@li The message has been written.
|
||||||
|
|
||||||
|
@li An error occurred.
|
||||||
|
|
||||||
|
This operation is implemented in terms of zero or more calls to the
|
||||||
|
next layer's async_read_some function, and is known as a composed
|
||||||
|
operation. The program must ensure that the stream performs no other
|
||||||
|
read operations or any other composed operations that perform reads
|
||||||
|
until this operation completes.
|
||||||
|
|
||||||
|
@param msg An object used to store the message. The previous
|
||||||
|
contents of the object will be overwritten. Ownership of the message
|
||||||
|
is not transferred; the caller must guarantee that the object remains
|
||||||
|
valid until the handler is called.
|
||||||
|
|
||||||
|
@param handler The handler to be called when the request completes.
|
||||||
|
Copies will be made of the handler as required. The equivalent
|
||||||
|
function signature of the handler must be:
|
||||||
|
@code void handler(
|
||||||
|
error_code const& error // result of operation
|
||||||
|
); @endcode
|
||||||
|
Regardless of whether the asynchronous operation completes
|
||||||
|
immediately or not, the handler will not be invoked from within
|
||||||
|
this function. Invocation of the handler will be performed in a
|
||||||
|
manner equivalent to using boost::asio::io_service::post().
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class ReadHandler>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
void_or_deduced
|
||||||
|
#else
|
||||||
|
typename async_completion<
|
||||||
|
ReadHandler, void(error_code)>::result_type
|
||||||
|
#endif
|
||||||
|
async_read(message<isRequest, Body, Headers>& msg,
|
||||||
|
ReadHandler&& handler);
|
||||||
|
|
||||||
|
/** Write a HTTP message to the stream.
|
||||||
|
|
||||||
|
This function is used to write a single HTTP message to the
|
||||||
|
stream. The call will block until one of the following conditions
|
||||||
|
is true:
|
||||||
|
|
||||||
|
@li The entire message is sent.
|
||||||
|
|
||||||
|
@li An error occurred.
|
||||||
|
|
||||||
|
If the semantics of the message require that the connection is
|
||||||
|
closed to indicate the end of the content body,
|
||||||
|
`boost::asio::error::eof` is thrown after the message is sent.
|
||||||
|
successfuly. The caller is responsible for actually closing the
|
||||||
|
connection. For regular TCP/IP streams this means shutting down the
|
||||||
|
send side, while SSL streams may call the SSL shutdown function.
|
||||||
|
|
||||||
|
@param msg The message to send.
|
||||||
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
write(message<isRequest, Body, Headers> const& msg)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
write(msg, ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write a HTTP message to the stream.
|
||||||
|
|
||||||
|
This function is used to write a single HTTP message to the
|
||||||
|
stream. The call will block until one of the following conditions
|
||||||
|
is true:
|
||||||
|
|
||||||
|
@li The entire message is sent.
|
||||||
|
|
||||||
|
@li An error occurred.
|
||||||
|
|
||||||
|
If the semantics of the message require that the connection is
|
||||||
|
closed to indicate the end of the content body,
|
||||||
|
`boost::asio::error::eof` is returned after the message is sent.
|
||||||
|
successfuly. The caller is responsible for actually closing the
|
||||||
|
connection. For regular TCP/IP streams this means shutting down the
|
||||||
|
send side, while SSL streams may call the SSL shutdown function.
|
||||||
|
|
||||||
|
@param msg The message to send.
|
||||||
|
|
||||||
|
@param ec Set to the error, if any occurred.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
write(message<isRequest, Body, Headers> const& msg,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Start pipelining a HTTP message to the stream asynchronously.
|
||||||
|
|
||||||
|
This function is used to queue a message to be sent on the stream.
|
||||||
|
Unlike the free function, this version will place the message on an
|
||||||
|
outgoing message queue if there is already a write pending.
|
||||||
|
|
||||||
|
If the semantics of the message require that the connection is
|
||||||
|
closed to indicate the end of the content body, the handler
|
||||||
|
is called with the error `boost::asio::error::eof` after the message
|
||||||
|
has been sent successfully. The caller is responsible for actually
|
||||||
|
closing the connection. For regular TCP/IP streams this means
|
||||||
|
shutting down the send side, while SSL streams may call the SSL
|
||||||
|
`async_shutdown` function.
|
||||||
|
|
||||||
|
@param msg The message to send. A copy of the message will be made.
|
||||||
|
|
||||||
|
@param handler The handler to be called when the request completes.
|
||||||
|
Copies will be made of the handler as required. The equivalent
|
||||||
|
function signature of the handler must be:
|
||||||
|
@code void handler(
|
||||||
|
error_code const& error // result of operation
|
||||||
|
); @endcode
|
||||||
|
Regardless of whether the asynchronous operation completes
|
||||||
|
immediately or not, the handler will not be invoked from within
|
||||||
|
this function. Invocation of the handler will be performed in a
|
||||||
|
manner equivalent to using boost::asio::io_service::post().
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class WriteHandler>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
void_or_deduced
|
||||||
|
#else
|
||||||
|
typename async_completion<
|
||||||
|
WriteHandler, void(error_code)>::result_type
|
||||||
|
#endif
|
||||||
|
async_write(message<isRequest, Body, Headers> const& msg,
|
||||||
|
WriteHandler&& handler);
|
||||||
|
|
||||||
|
/** Start pipelining a HTTP message to the stream asynchronously.
|
||||||
|
|
||||||
|
This function is used to queue a message to be sent on the stream.
|
||||||
|
Unlike the free function, this version will place the message on an
|
||||||
|
outgoing message queue if there is already a write pending.
|
||||||
|
|
||||||
|
If the semantics of the message require that the connection is
|
||||||
|
closed to indicate the end of the content body, the handler
|
||||||
|
is called with the error boost::asio::error::eof. The caller is
|
||||||
|
responsible for actually closing the connection. For regular
|
||||||
|
TCP/IP streams this means shutting down the send side, while SSL
|
||||||
|
streams may call the SSL async_shutdown function.
|
||||||
|
|
||||||
|
@param msg The message to send. Ownership of the message, which
|
||||||
|
must be movable, is transferred to the implementation. The message
|
||||||
|
will not be destroyed until the asynchronous operation completes.
|
||||||
|
|
||||||
|
@param handler The handler to be called when the request completes.
|
||||||
|
Copies will be made of the handler as required. The equivalent
|
||||||
|
function signature of the handler must be:
|
||||||
|
@code void handler(
|
||||||
|
error_code const& error // result of operation
|
||||||
|
); @endcode
|
||||||
|
Regardless of whether the asynchronous operation completes
|
||||||
|
immediately or not, the handler will not be invoked from within
|
||||||
|
this function. Invocation of the handler will be performed in a
|
||||||
|
manner equivalent to using boost::asio::io_service::post().
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class WriteHandler>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
void_or_deduced
|
||||||
|
#else
|
||||||
|
typename async_completion<
|
||||||
|
WriteHandler, void(error_code)>::result_type
|
||||||
|
#endif
|
||||||
|
async_write(message<isRequest, Body, Headers>&& msg,
|
||||||
|
WriteHandler&& handler);
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<bool, class, class, class> class read_op;
|
||||||
|
template<bool, class, class, class> class write_op;
|
||||||
|
|
||||||
|
void
|
||||||
|
cancel_all();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include "http_stream.ipp"
|
||||||
|
|
||||||
|
#endif
|
423
examples/http_stream.ipp
Normal file
423
examples/http_stream.ipp
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_STREAM_IPP_INCLUDED
|
||||||
|
#define BEAST_HTTP_STREAM_IPP_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/bind_handler.hpp>
|
||||||
|
#include <beast/handler_alloc.hpp>
|
||||||
|
#include <beast/http/read.hpp>
|
||||||
|
#include <beast/http/write.hpp>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class Handler>
|
||||||
|
class stream<NextLayer, Allocator>::read_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
stream<NextLayer>& s;
|
||||||
|
message<isRequest, Body, Headers>& m;
|
||||||
|
Handler h;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, stream<NextLayer>& s_,
|
||||||
|
message<isRequest, Body, Headers>& m_)
|
||||||
|
: s(s_)
|
||||||
|
, m(m_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
read_op(read_op&&) = default;
|
||||||
|
read_op(read_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
read_op(DeducedHandler&& h,
|
||||||
|
stream<NextLayer>& s, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), s,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code const& ec, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
void* asio_handler_allocate(
|
||||||
|
std::size_t size, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
bool asio_handler_is_continuation(read_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
void asio_handler_invoke(Function&& f, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
read_op<isRequest, Body, Headers, Handler>::
|
||||||
|
operator()(error_code const& ec, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
d.state = 99;
|
||||||
|
beast::http::async_read(d.s.next_layer_,
|
||||||
|
d.s.rd_buf_, d.m, std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class Handler>
|
||||||
|
class stream<NextLayer, Allocator>::write_op : public op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
stream<NextLayer>& s;
|
||||||
|
message<isRequest, Body, Headers> m;
|
||||||
|
Handler h;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, stream<NextLayer>& s_,
|
||||||
|
message<isRequest, Body, Headers> const& m_,
|
||||||
|
bool cont_)
|
||||||
|
: s(s_)
|
||||||
|
, m(m_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(cont_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, stream<NextLayer>& s_,
|
||||||
|
message<isRequest, Body, Headers>&& m_,
|
||||||
|
bool cont_)
|
||||||
|
: s(s_)
|
||||||
|
, m(std::move(m_))
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(cont_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
write_op(write_op&&) = default;
|
||||||
|
write_op(write_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
write_op(DeducedHandler&& h,
|
||||||
|
stream<NextLayer>& s, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), s,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()() override
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancel() override;
|
||||||
|
|
||||||
|
void operator()(error_code const& ec, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
void* asio_handler_allocate(
|
||||||
|
std::size_t size, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
bool asio_handler_is_continuation(write_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
void asio_handler_invoke(Function&& f, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
write_op<isRequest, Body, Headers, Handler>::
|
||||||
|
cancel()
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.s.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this),
|
||||||
|
boost::asio::error::operation_aborted));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
write_op<isRequest, Body, Headers, Handler>::
|
||||||
|
operator()(error_code const& ec, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
d.state = 99;
|
||||||
|
beast::http::async_write(d.s.next_layer_,
|
||||||
|
d.m, std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
if(! d.s.wr_q_.empty())
|
||||||
|
{
|
||||||
|
auto& op = d.s.wr_q_.front();
|
||||||
|
op();
|
||||||
|
// VFALCO Use allocator
|
||||||
|
delete &op;
|
||||||
|
d.s.wr_q_.pop_front();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d.s.wr_active_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
~stream()
|
||||||
|
{
|
||||||
|
// Can't destroy with pending operations!
|
||||||
|
assert(wr_q_.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<class... Args>
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
stream(Args&&... args)
|
||||||
|
: next_layer_(std::forward<Args>(args)...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
cancel(error_code& ec)
|
||||||
|
{
|
||||||
|
cancel_all();
|
||||||
|
lowest_layer().cancel(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
read(message<isRequest, Body, Headers>& msg,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
beast::http::read(next_layer_, rd_buf_, msg, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class ReadHandler>
|
||||||
|
auto
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
async_read(message<isRequest, Body, Headers>& msg,
|
||||||
|
ReadHandler&& handler) ->
|
||||||
|
typename async_completion<
|
||||||
|
ReadHandler, void(error_code)>::result_type
|
||||||
|
{
|
||||||
|
async_completion<
|
||||||
|
ReadHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
read_op<isRequest, Body, Headers,
|
||||||
|
decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, msg};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
write(message<isRequest, Body, Headers> const& msg,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
beast::http::write(next_layer_, msg, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class WriteHandler>
|
||||||
|
auto
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
async_write(message<isRequest, Body, Headers> const& msg,
|
||||||
|
WriteHandler&& handler) ->
|
||||||
|
typename async_completion<
|
||||||
|
WriteHandler, void(error_code)>::result_type
|
||||||
|
{
|
||||||
|
async_completion<
|
||||||
|
WriteHandler, void(error_code)> completion(handler);
|
||||||
|
auto const cont = wr_active_ ||
|
||||||
|
boost_asio_handler_cont_helpers::is_continuation(handler);
|
||||||
|
if(! wr_active_)
|
||||||
|
{
|
||||||
|
wr_active_ = true;
|
||||||
|
write_op<isRequest, Body, Headers,
|
||||||
|
decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, msg, cont }();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// VFALCO Use allocator
|
||||||
|
wr_q_.push_back(*new write_op<isRequest, Body, Headers,
|
||||||
|
decltype(completion.handler)>(
|
||||||
|
completion.handler, *this, msg, cont));
|
||||||
|
}
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class WriteHandler>
|
||||||
|
auto
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
async_write(message<isRequest, Body, Headers>&& msg,
|
||||||
|
WriteHandler&& handler) ->
|
||||||
|
typename async_completion<
|
||||||
|
WriteHandler, void(error_code)>::result_type
|
||||||
|
{
|
||||||
|
async_completion<
|
||||||
|
WriteHandler, void(error_code)> completion(handler);
|
||||||
|
auto const cont = wr_active_ ||
|
||||||
|
boost_asio_handler_cont_helpers::is_continuation(handler);
|
||||||
|
if(! wr_active_)
|
||||||
|
{
|
||||||
|
wr_active_ = true;
|
||||||
|
write_op<isRequest, Body, Headers,
|
||||||
|
decltype(completion.handler)>{completion.handler,
|
||||||
|
*this, std::move(msg), cont}();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// VFALCO Use allocator
|
||||||
|
wr_q_.push_back(*new write_op<isRequest, Body, Headers,
|
||||||
|
decltype(completion.handler)>(completion.handler,
|
||||||
|
*this, std::move(msg), cont));
|
||||||
|
}
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
cancel_all()
|
||||||
|
{
|
||||||
|
for(auto it = wr_q_.begin(); it != wr_q_.end();)
|
||||||
|
{
|
||||||
|
auto& op = *it++;
|
||||||
|
op.cancel();
|
||||||
|
// VFALCO Use allocator
|
||||||
|
delete &op;
|
||||||
|
}
|
||||||
|
wr_q_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
182
examples/http_sync_server.h
Normal file
182
examples/http_sync_server.h
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
|
||||||
|
#define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
|
||||||
|
|
||||||
|
#include "file_body.h"
|
||||||
|
#include "http_stream.h"
|
||||||
|
|
||||||
|
#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<string_body>;
|
||||||
|
using resp_type = response<file_body>;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
std::cerr <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
maybe_throw(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
fail(ec, what);
|
||||||
|
throw ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
maybe_throw(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
|
||||||
|
fail(int id, error_code const& ec)
|
||||||
|
{
|
||||||
|
if(ec != boost::asio::error::operation_aborted &&
|
||||||
|
ec != boost::asio::error::eof)
|
||||||
|
std::cerr <<
|
||||||
|
"#" << std::to_string(id) << " " << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
do_peer(int id, socket_type&& sock)
|
||||||
|
{
|
||||||
|
http::stream<socket_type> hs(std::move(sock));
|
||||||
|
error_code ec;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
req_type req;
|
||||||
|
hs.read(req, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
auto path = req.url;
|
||||||
|
if(path == "/")
|
||||||
|
path = "/index.html";
|
||||||
|
path = root_ + path;
|
||||||
|
if(! boost::filesystem::exists(path))
|
||||||
|
{
|
||||||
|
response<string_body> resp(
|
||||||
|
{404, "Not Found", req.version});
|
||||||
|
resp.headers.replace("Server", "http_sync_server");
|
||||||
|
resp.body = "The file '" + path + "' was not found";
|
||||||
|
hs.write(resp, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
response<file_body> resp(
|
||||||
|
{200, "OK", req.version});
|
||||||
|
resp.headers.replace("Server", "http_sync_server");
|
||||||
|
resp.headers.replace("Content-Type", "text/html");
|
||||||
|
resp.body = path;
|
||||||
|
hs.write(resp, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fail(id, ec);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
42
examples/sig_wait.h
Normal file
42
examples/sig_wait.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_EXAMPLE_SIG_WAIT_H_INCLUDED
|
||||||
|
#define BEAST_EXAMPLE_SIG_WAIT_H_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
// Block until SIGINT or SIGTERM
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
10031
examples/urls_large_data.cpp
Normal file
10031
examples/urls_large_data.cpp
Normal file
File diff suppressed because it is too large
Load Diff
28
examples/urls_large_data.h
Normal file
28
examples/urls_large_data.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef URLS_LARGE_DATA_H_INCLUDED
|
||||||
|
#define URLS_LARGE_DATA_H_INCLUDED
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
std::vector<char const*> const&
|
||||||
|
urls_large_data();
|
||||||
|
|
||||||
|
#endif
|
267
examples/websocket_async_echo_peer.h
Normal file
267
examples/websocket_async_echo_peer.h
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_WEBSOCKET_ASYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
#define BEAST_WEBSOCKET_ASYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/placeholders.hpp>
|
||||||
|
#include <beast/streambuf.hpp>
|
||||||
|
#include <beast/websocket.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace websocket {
|
||||||
|
|
||||||
|
// Asynchronous WebSocket echo client/server
|
||||||
|
//
|
||||||
|
class async_echo_peer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using error_code = boost::system::error_code;
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
using socket_type = boost::asio::ip::tcp::socket;
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::asio::io_service ios_;
|
||||||
|
socket_type sock_;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
std::vector<std::thread> thread_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
async_echo_peer(bool server,
|
||||||
|
endpoint_type const& ep, std::size_t threads)
|
||||||
|
: sock_(ios_)
|
||||||
|
, acceptor_(ios_)
|
||||||
|
{
|
||||||
|
if(server)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
acceptor_.open(ep.protocol(), ec);
|
||||||
|
maybe_throw(ec, "open");
|
||||||
|
acceptor_.bind(ep, ec);
|
||||||
|
maybe_throw(ec, "bind");
|
||||||
|
acceptor_.listen(
|
||||||
|
boost::asio::socket_base::max_connections, ec);
|
||||||
|
maybe_throw(ec, "listen");
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&async_echo_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Peer{std::move(sock_), ep};
|
||||||
|
}
|
||||||
|
thread_.reserve(threads);
|
||||||
|
for(std::size_t i = 0; i < threads; ++i)
|
||||||
|
thread_.emplace_back(
|
||||||
|
[&]{ ios_.run(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
~async_echo_peer()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
ios_.dispatch(
|
||||||
|
[&]{ acceptor_.close(ec); });
|
||||||
|
for(auto& t : thread_)
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Peer
|
||||||
|
{
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
int state = 0;
|
||||||
|
boost::optional<endpoint_type> ep;
|
||||||
|
websocket::stream<socket_type> ws;
|
||||||
|
websocket::opcode op;
|
||||||
|
beast::streambuf sb;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
data(socket_type&& sock_)
|
||||||
|
: ws(std::move(sock_))
|
||||||
|
, id([]
|
||||||
|
{
|
||||||
|
static int n = 0;
|
||||||
|
return ++n;
|
||||||
|
}())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
data(socket_type&& sock_,
|
||||||
|
endpoint_type const& ep_)
|
||||||
|
: ep(ep_)
|
||||||
|
, ws(std::move(sock_))
|
||||||
|
, id([]
|
||||||
|
{
|
||||||
|
static int n = 0;
|
||||||
|
return ++n;
|
||||||
|
}())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Peer(Peer&&) = default;
|
||||||
|
Peer(Peer const&) = default;
|
||||||
|
Peer& operator=(Peer&&) = delete;
|
||||||
|
Peer& operator=(Peer const&) = delete;
|
||||||
|
|
||||||
|
struct identity
|
||||||
|
{
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<true, Body, Headers>& req)
|
||||||
|
{
|
||||||
|
req.headers.replace("User-Agent", "async_echo_client");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<false, Body, Headers>& resp)
|
||||||
|
{
|
||||||
|
resp.headers.replace("Server", "async_echo_server");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
explicit
|
||||||
|
Peer(socket_type&& sock, Args&&... args)
|
||||||
|
: d_(std::make_shared<data>(
|
||||||
|
std::forward<socket_type>(sock),
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.ws.set_option(decorate(identity{}));
|
||||||
|
d.ws.set_option(read_message_max(64 * 1024 * 1024));
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
if(! d.ep)
|
||||||
|
{
|
||||||
|
d.ws.async_accept(std::move(*this));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d.state = 4;
|
||||||
|
d.ws.next_layer().async_connect(
|
||||||
|
*d.ep, std::move(*this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code ec)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
switch(d_->state)
|
||||||
|
{
|
||||||
|
// did accept
|
||||||
|
case 0:
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_accept");
|
||||||
|
|
||||||
|
// start
|
||||||
|
case 1:
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_handshake");
|
||||||
|
d.sb.consume(d.sb.size());
|
||||||
|
// read message
|
||||||
|
d.state = 2;
|
||||||
|
d.ws.async_read(d.op, d.sb, std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// got message
|
||||||
|
case 2:
|
||||||
|
if(ec == websocket::error::closed)
|
||||||
|
return;
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_read");
|
||||||
|
// write message
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.set_option(websocket::message_type(d.op));
|
||||||
|
d.ws.async_write(d.sb.data(), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// connected
|
||||||
|
case 4:
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_connect");
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.async_handshake(
|
||||||
|
d.ep->address().to_string() + ":" +
|
||||||
|
std::to_string(d.ep->port()),
|
||||||
|
"/", std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec != websocket::error::closed)
|
||||||
|
std::cerr << "#" << d_->id << " " <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
std::cerr <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
maybe_throw(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
fail(ec, what);
|
||||||
|
throw ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_accept(error_code ec)
|
||||||
|
{
|
||||||
|
if(! acceptor_.is_open())
|
||||||
|
return;
|
||||||
|
maybe_throw(ec, "accept");
|
||||||
|
socket_type sock(std::move(sock_));
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&async_echo_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
Peer{std::move(sock)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // websocket
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
36
examples/websocket_echo.cpp
Normal file
36
examples/websocket_echo.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include "websocket_async_echo_peer.h"
|
||||||
|
#include "websocket_sync_echo_peer.h"
|
||||||
|
#include "sig_wait.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
|
||||||
|
beast::websocket::async_echo_peer s1(true, endpoint_type{
|
||||||
|
address_type::from_string("127.0.0.1"), 6000 }, 4);
|
||||||
|
|
||||||
|
beast::websocket::sync_echo_peer s2(true, endpoint_type{
|
||||||
|
address_type::from_string("127.0.0.1"), 6001 });
|
||||||
|
|
||||||
|
sig_wait();
|
||||||
|
}
|
38
examples/websocket_example.cpp
Normal file
38
examples/websocket_example.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <beast/websocket.hpp>
|
||||||
|
#include <beast/buffers_debug.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"}));
|
||||||
|
|
||||||
|
using namespace beast::websocket;
|
||||||
|
|
||||||
|
// WebSocket connect and send message using beast
|
||||||
|
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;
|
||||||
|
opcode op;
|
||||||
|
ws.read(op, sb);
|
||||||
|
ws.close(close_code::normal);
|
||||||
|
std::cout <<
|
||||||
|
beast::debug::buffers_to_string(sb.data()) << "\n";
|
||||||
|
}
|
192
examples/websocket_sync_echo_peer.h
Normal file
192
examples/websocket_sync_echo_peer.h
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/streambuf.hpp>
|
||||||
|
#include <beast/websocket.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace websocket {
|
||||||
|
|
||||||
|
// Synchronous WebSocket echo client/server
|
||||||
|
//
|
||||||
|
class sync_echo_peer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using error_code = boost::system::error_code;
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
using socket_type = boost::asio::ip::tcp::socket;
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::asio::io_service ios_;
|
||||||
|
socket_type sock_;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
std::thread thread_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
sync_echo_peer(bool server, endpoint_type ep)
|
||||||
|
: sock_(ios_)
|
||||||
|
, acceptor_(ios_)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
acceptor_.open(ep.protocol(), ec);
|
||||||
|
maybe_throw(ec, "open");
|
||||||
|
acceptor_.bind(ep, ec);
|
||||||
|
maybe_throw(ec, "bind");
|
||||||
|
acceptor_.listen(
|
||||||
|
boost::asio::socket_base::max_connections, ec);
|
||||||
|
maybe_throw(ec, "listen");
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&sync_echo_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
thread_ = std::thread{[&]{ ios_.run(); }};
|
||||||
|
}
|
||||||
|
|
||||||
|
~sync_echo_peer()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
ios_.dispatch(
|
||||||
|
[&]{ acceptor_.close(ec); });
|
||||||
|
thread_.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
std::cerr <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
fail(int id, error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
std::cerr << "#" << std::to_string(id) << " " <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
maybe_throw(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
fail(ec, what);
|
||||||
|
throw ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct lambda
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
sync_echo_peer& self;
|
||||||
|
socket_type sock;
|
||||||
|
boost::asio::io_service::work work;
|
||||||
|
|
||||||
|
lambda(int id_, sync_echo_peer& 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(ec == boost::asio::error::operation_aborted)
|
||||||
|
return;
|
||||||
|
maybe_throw(ec, "accept");
|
||||||
|
static int id_ = 0;
|
||||||
|
std::thread{lambda{++id_, *this, std::move(sock_)}}.detach();
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&sync_echo_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct identity
|
||||||
|
{
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<true, Body, Headers>& req)
|
||||||
|
{
|
||||||
|
req.headers.replace("User-Agent", "sync_echo_client");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<false, Body, Headers>& resp)
|
||||||
|
{
|
||||||
|
resp.headers.replace("Server", "sync_echo_server");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
do_peer(int id, socket_type&& sock)
|
||||||
|
{
|
||||||
|
websocket::stream<socket_type> ws(std::move(sock));
|
||||||
|
ws.set_option(decorate(identity{}));
|
||||||
|
ws.set_option(read_message_max(64 * 1024 * 1024));
|
||||||
|
error_code ec;
|
||||||
|
ws.accept(ec);
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
fail(id, ec, "accept");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
websocket::opcode op;
|
||||||
|
beast::streambuf sb;
|
||||||
|
ws.read(op, sb, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
ws.set_option(websocket::message_type(op));
|
||||||
|
ws.write(sb.data(), ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(ec && ec != websocket::error::closed)
|
||||||
|
{
|
||||||
|
fail(id, ec, "read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // websocket
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
87
include/beast/async_completion.hpp
Normal file
87
include/beast/async_completion.hpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_ASYNC_COMPLETION_HPP
|
||||||
|
#define BEAST_ASYNC_COMPLETION_HPP
|
||||||
|
|
||||||
|
#include <beast/type_check.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
|
||||||
|
tokens 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 CompletionToken A CompletionHandler, 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 CompletionToken>
|
||||||
|
typename async_completion<CompletionToken,
|
||||||
|
void(boost::system::error_code)>::result_type
|
||||||
|
async_initfn(..., CompletionToken&& token)
|
||||||
|
{
|
||||||
|
async_completion<CompletionToken,
|
||||||
|
void(boost::system::error_code)> completion(token);
|
||||||
|
...
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
See <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf">
|
||||||
|
Library Foundations For Asynchronous Operations</a>
|
||||||
|
*/
|
||||||
|
template <class CompletionToken, 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<
|
||||||
|
CompletionToken, 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 token. Copies will be made as
|
||||||
|
required. If `CompletionToken` is movable, it may also be moved.
|
||||||
|
*/
|
||||||
|
async_completion(typename std::remove_reference<CompletionToken>::type& token)
|
||||||
|
: handler(std::forward<CompletionToken>(token))
|
||||||
|
, result(handler)
|
||||||
|
{
|
||||||
|
static_assert(is_Handler<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
|
298
include/beast/basic_streambuf.hpp
Normal file
298
include/beast/basic_streambuf.hpp
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
//
|
||||||
|
// 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/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 `Streambuf` 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.
|
||||||
|
|
||||||
|
@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:
|
||||||
|
/// The type of allocator used.
|
||||||
|
using allocator_type = typename
|
||||||
|
std::allocator_traits<Allocator>::
|
||||||
|
template rebind_alloc<std::uint8_t>;
|
||||||
|
|
||||||
|
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 output sequence of this object will be empty.
|
||||||
|
|
||||||
|
After the move, the moved-from object will have an
|
||||||
|
empty input and output sequence, with no internal
|
||||||
|
buffers allocated.
|
||||||
|
|
||||||
|
@param other The stream buffer to move from.
|
||||||
|
*/
|
||||||
|
basic_streambuf(basic_streambuf&& other);
|
||||||
|
|
||||||
|
/** Move constructor.
|
||||||
|
|
||||||
|
The output sequence of this object will be empty.
|
||||||
|
|
||||||
|
After the move, the moved-from object will have an
|
||||||
|
empty input and output sequence, with no internal
|
||||||
|
buffers allocated.
|
||||||
|
|
||||||
|
@param other The stream buffer to move from.
|
||||||
|
|
||||||
|
@param alloc The allocator to associate with the
|
||||||
|
stream buffer.
|
||||||
|
*/
|
||||||
|
basic_streambuf(basic_streambuf&& other,
|
||||||
|
allocator_type const& alloc);
|
||||||
|
|
||||||
|
/** Move assignment.
|
||||||
|
|
||||||
|
The output sequence of this object will be empty.
|
||||||
|
|
||||||
|
After the move, the moved-from object will have an
|
||||||
|
empty input and output sequence, with no internal
|
||||||
|
buffers allocated.
|
||||||
|
|
||||||
|
@param other The stream buffer to move from.
|
||||||
|
*/
|
||||||
|
basic_streambuf&
|
||||||
|
operator=(basic_streambuf&& other);
|
||||||
|
|
||||||
|
/// Copy constructor.
|
||||||
|
basic_streambuf(basic_streambuf const& other);
|
||||||
|
|
||||||
|
/** Copy constructor.
|
||||||
|
|
||||||
|
The output sequence of this object will be empty.
|
||||||
|
|
||||||
|
@param other The stream buffer to copy.
|
||||||
|
|
||||||
|
@param alloc The allocator to associate with the
|
||||||
|
stream buffer.
|
||||||
|
*/
|
||||||
|
basic_streambuf(basic_streambuf const& other,
|
||||||
|
allocator_type const& alloc);
|
||||||
|
|
||||||
|
/** Copy assignment.
|
||||||
|
|
||||||
|
The output sequence of this object will be empty.
|
||||||
|
|
||||||
|
@param other The stream buffer to copy.
|
||||||
|
*/
|
||||||
|
basic_streambuf& operator=(basic_streambuf const& other);
|
||||||
|
|
||||||
|
/** Copy constructor.
|
||||||
|
|
||||||
|
The output sequence of this object will be empty.
|
||||||
|
|
||||||
|
@param other The stream buffer to copy.
|
||||||
|
*/
|
||||||
|
template<class OtherAlloc>
|
||||||
|
basic_streambuf(basic_streambuf<OtherAlloc> const& other);
|
||||||
|
|
||||||
|
/** Copy constructor.
|
||||||
|
|
||||||
|
The output sequence of this object will be empty.
|
||||||
|
|
||||||
|
@param other The stream buffer to copy.
|
||||||
|
|
||||||
|
@param alloc The allocator to associate with the
|
||||||
|
stream buffer.
|
||||||
|
*/
|
||||||
|
template<class OtherAlloc>
|
||||||
|
basic_streambuf(basic_streambuf<OtherAlloc> const& other,
|
||||||
|
allocator_type const& alloc);
|
||||||
|
|
||||||
|
/** Copy assignment.
|
||||||
|
|
||||||
|
The output sequence of this object will be empty.
|
||||||
|
|
||||||
|
@param other The stream buffer to copy.
|
||||||
|
*/
|
||||||
|
template<class OtherAlloc>
|
||||||
|
basic_streambuf& operator=(basic_streambuf<OtherAlloc> const& other);
|
||||||
|
|
||||||
|
/** Default constructor.
|
||||||
|
|
||||||
|
@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.
|
||||||
|
|
||||||
|
@param alloc The allocator to use.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
basic_streambuf(std::size_t alloc_size = 1024,
|
||||||
|
Allocator const& alloc = allocator_type{});
|
||||||
|
|
||||||
|
/// Get the associated allocator
|
||||||
|
allocator_type
|
||||||
|
get_allocator() const
|
||||||
|
{
|
||||||
|
return this->member();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the maximum size of the basic_streambuf.
|
||||||
|
size_type
|
||||||
|
max_size() const
|
||||||
|
{
|
||||||
|
return std::numeric_limits<std::size_t>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the size of the input sequence.
|
||||||
|
size_type
|
||||||
|
size() const
|
||||||
|
{
|
||||||
|
return in_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a list of buffers that represents the output sequence, with the given size.
|
||||||
|
mutable_buffers_type
|
||||||
|
prepare(size_type n);
|
||||||
|
|
||||||
|
/// Move bytes from the output sequence to the input sequence.
|
||||||
|
void
|
||||||
|
commit(size_type n);
|
||||||
|
|
||||||
|
/// Get a list of buffers that represents the input sequence.
|
||||||
|
const_buffers_type
|
||||||
|
data() const;
|
||||||
|
|
||||||
|
/// Remove bytes from the input sequence.
|
||||||
|
void
|
||||||
|
consume(size_type n);
|
||||||
|
|
||||||
|
/// Clear everything.
|
||||||
|
void
|
||||||
|
clear();
|
||||||
|
|
||||||
|
// Helper for read_until
|
||||||
|
template<class OtherAllocator>
|
||||||
|
friend
|
||||||
|
std::size_t
|
||||||
|
read_size_helper(basic_streambuf<
|
||||||
|
OtherAllocator> const& streambuf, std::size_t max_size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
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();
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
prepare_size() const;
|
||||||
|
|
||||||
|
void
|
||||||
|
debug_check() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Format output to a stream buffer.
|
||||||
|
|
||||||
|
@param streambuf The streambuf to write to.
|
||||||
|
|
||||||
|
@param t The object to write.
|
||||||
|
|
||||||
|
@return The stream buffer.
|
||||||
|
*/
|
||||||
|
template<class Alloc, class T>
|
||||||
|
basic_streambuf<Alloc>&
|
||||||
|
operator<<(basic_streambuf<Alloc>& streambuf, T const& t);
|
||||||
|
|
||||||
|
/** Convert the entire basic_streambuf to a string.
|
||||||
|
|
||||||
|
@param streambuf The streambuf to convert.
|
||||||
|
|
||||||
|
@return A string representing the contents of the input sequence.
|
||||||
|
*/
|
||||||
|
template<class Allocator>
|
||||||
|
std::string
|
||||||
|
to_string(basic_streambuf<Allocator> const& streambuf);
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/impl/basic_streambuf.ipp>
|
||||||
|
|
||||||
|
#endif
|
162
include/beast/bind_handler.hpp
Normal file
162
include/beast/bind_handler.hpp
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
//
|
||||||
|
// 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/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 <functional>
|
||||||
|
#include <type_traits>
|
||||||
|
#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
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Bind parameters to a completion handler, creating a wrapped handler.
|
||||||
|
|
||||||
|
This function creates a new handler which 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)
|
||||||
|
{
|
||||||
|
return detail::bound_handler<typename std::decay<
|
||||||
|
CompletionHandler>::type, Args...>(std::forward<
|
||||||
|
CompletionHandler>(handler),
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<class Handler, class... Args>
|
||||||
|
void bind(beast::detail::bound_handler<
|
||||||
|
Handler, Args...>, ...) = delete;
|
||||||
|
} // std
|
||||||
|
|
||||||
|
#endif
|
506
include/beast/buffer_cat.hpp
Normal file
506
include/beast/buffer_cat.hpp
Normal file
@ -0,0 +1,506 @@
|
|||||||
|
//
|
||||||
|
// 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 <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&&)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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&)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Concatenate 2 or more buffer sequences to form a `ConstBufferSequence`.
|
||||||
|
|
||||||
|
This function returns a `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 `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
|
142
include/beast/buffers_adapter.hpp
Normal file
142
include/beast/buffers_adapter.hpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
//
|
||||||
|
// 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 <boost/asio/buffer.hpp>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
/** Adapts a `MutableBufferSequence` into a `Streambuf`.
|
||||||
|
|
||||||
|
This class wraps a `MutableBufferSequence` to meet the requirements
|
||||||
|
of `Streambuf`. 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 Buffers The type of mutable buffer sequence to wrap.
|
||||||
|
*/
|
||||||
|
template<class Buffers>
|
||||||
|
class buffers_adapter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using buffers_type = typename std::decay<Buffers>::type;
|
||||||
|
using iter_type = typename buffers_type::const_iterator;
|
||||||
|
|
||||||
|
static auto constexpr is_mutable =
|
||||||
|
std::is_constructible<boost::asio::mutable_buffer,
|
||||||
|
typename std::iterator_traits<iter_type>::value_type>::value;
|
||||||
|
|
||||||
|
Buffers 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(Buffers 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.
|
||||||
|
*/
|
||||||
|
mutable_buffers_type
|
||||||
|
prepare(std::size_t n);
|
||||||
|
|
||||||
|
/// Move bytes from the output sequence to the input sequence.
|
||||||
|
void
|
||||||
|
commit(std::size_t n);
|
||||||
|
|
||||||
|
/// Get a list of buffers that represents the input sequence.
|
||||||
|
const_buffers_type
|
||||||
|
data() const;
|
||||||
|
|
||||||
|
/// Remove bytes from the input sequence.
|
||||||
|
void
|
||||||
|
consume(std::size_t n);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/impl/buffers_adapter.ipp>
|
||||||
|
|
||||||
|
#endif
|
44
include/beast/buffers_debug.hpp
Normal file
44
include/beast/buffers_debug.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// 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_DEBUG_HPP
|
||||||
|
#define BEAST_BUFFERS_DEBUG_HPP
|
||||||
|
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace debug {
|
||||||
|
|
||||||
|
/** Diagnostic utility to convert a `ConstBufferSequence` to a string.
|
||||||
|
|
||||||
|
@note Carriage returns and linefeeds will have additional escape
|
||||||
|
representations printed for visibility.
|
||||||
|
*/
|
||||||
|
template<class Buffers>
|
||||||
|
std::string
|
||||||
|
buffers_to_string(Buffers const& bs)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
std::string s;
|
||||||
|
s.reserve(buffer_size(bs));
|
||||||
|
for(auto const& b : bs)
|
||||||
|
s.append(buffer_cast<char const*>(b),
|
||||||
|
buffer_size(b));
|
||||||
|
for(auto i = s.size(); i-- > 0;)
|
||||||
|
if(s[i] == '\r')
|
||||||
|
s.replace(i, 1, "\\r");
|
||||||
|
else if(s[i] == '\n')
|
||||||
|
s.replace(i, 1, "\\n\n");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // debug
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
122
include/beast/consuming_buffers.hpp
Normal file
122
include/beast/consuming_buffers.hpp
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_CONSUMING_BUFFERS_HPP
|
||||||
|
#define BEAST_CONSUMING_BUFFERS_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 Buffers The buffer sequence to wrap.
|
||||||
|
|
||||||
|
@ptaram 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 Buffers,
|
||||||
|
class ValueType = typename Buffers::value_type>
|
||||||
|
class consuming_buffers
|
||||||
|
{
|
||||||
|
using iter_type =
|
||||||
|
typename Buffers::const_iterator;
|
||||||
|
|
||||||
|
static_assert(std::is_constructible<ValueType,
|
||||||
|
typename std::iterator_traits<iter_type>::value_type>::value,
|
||||||
|
"ValueType requirements not met");
|
||||||
|
|
||||||
|
Buffers 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(Buffers 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 consumed buffer
|
||||||
|
template<class Buffers>
|
||||||
|
consuming_buffers<Buffers, typename Buffers::value_type>
|
||||||
|
consumed_buffers(Buffers const& bs, std::size_t n);
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/impl/consuming_buffers.ipp>
|
||||||
|
|
||||||
|
#endif
|
178
include/beast/detail/base64.hpp
Normal file
178
include/beast/detail/base64.hpp
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_BASE64_HPP
|
||||||
|
#define BEAST_DETAIL_BASE64_HPP
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
|
||||||
|
Copyright notice:
|
||||||
|
|
||||||
|
base64.cpp and base64.h
|
||||||
|
|
||||||
|
Copyright (C) 2004-2008 Ren<65> Nyffenegger
|
||||||
|
|
||||||
|
This source code is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the author be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this source code must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original source code. If you use this source code
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original source code.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
|
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
std::string const&
|
||||||
|
base64_alphabet()
|
||||||
|
{
|
||||||
|
static std::string const alphabet =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
|
"0123456789+/";
|
||||||
|
return alphabet;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_base64(unsigned char c)
|
||||||
|
{
|
||||||
|
return (std::isalnum(c) || (c == '+') || (c == '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
std::string
|
||||||
|
base64_encode (std::uint8_t const* data,
|
||||||
|
std::size_t in_len)
|
||||||
|
{
|
||||||
|
unsigned char c3[3], c4[4];
|
||||||
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
|
std::string ret;
|
||||||
|
ret.reserve (3 + in_len * 8 / 6);
|
||||||
|
|
||||||
|
char const* alphabet (base64_alphabet().data());
|
||||||
|
|
||||||
|
while(in_len--)
|
||||||
|
{
|
||||||
|
c3[i++] = *(data++);
|
||||||
|
if(i == 3)
|
||||||
|
{
|
||||||
|
c4[0] = (c3[0] & 0xfc) >> 2;
|
||||||
|
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
|
||||||
|
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
|
||||||
|
c4[3] = c3[2] & 0x3f;
|
||||||
|
for(i = 0; (i < 4); i++)
|
||||||
|
ret += alphabet[c4[i]];
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i)
|
||||||
|
{
|
||||||
|
for(j = i; j < 3; j++)
|
||||||
|
c3[j] = '\0';
|
||||||
|
|
||||||
|
c4[0] = (c3[0] & 0xfc) >> 2;
|
||||||
|
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
|
||||||
|
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
|
||||||
|
c4[3] = c3[2] & 0x3f;
|
||||||
|
|
||||||
|
for(j = 0; (j < i + 1); j++)
|
||||||
|
ret += alphabet[c4[j]];
|
||||||
|
|
||||||
|
while((i++ < 3))
|
||||||
|
ret += '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
std::string
|
||||||
|
base64_encode (std::string const& s)
|
||||||
|
{
|
||||||
|
return base64_encode (reinterpret_cast <
|
||||||
|
std::uint8_t const*> (s.data()), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
std::string
|
||||||
|
base64_decode(std::string const& data)
|
||||||
|
{
|
||||||
|
int 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
|
86
include/beast/detail/ci_char_traits.hpp
Normal file
86
include/beast/detail/ci_char_traits.hpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
//
|
||||||
|
// 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/utility/string_ref.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <cctype>
|
||||||
|
#include <iterator>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/** Case-insensitive function object for performing less than comparisons. */
|
||||||
|
struct ci_less
|
||||||
|
{
|
||||||
|
static bool const is_transparent = true;
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator()(boost::string_ref const& lhs,
|
||||||
|
boost::string_ref const& rhs) const noexcept
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::lexicographical_compare(
|
||||||
|
begin(lhs), end(lhs), begin(rhs), end(rhs),
|
||||||
|
[](char lhs, char rhs)
|
||||||
|
{
|
||||||
|
return std::tolower(lhs) < std::tolower(rhs);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
ci_equal(std::pair<const char*, std::size_t> lhs,
|
||||||
|
std::pair<const char*, std::size_t> rhs)
|
||||||
|
{
|
||||||
|
if(lhs.second != rhs.second)
|
||||||
|
return false;
|
||||||
|
return std::equal (lhs.first, lhs.first + lhs.second,
|
||||||
|
rhs.first,
|
||||||
|
[] (char lhs, char rhs)
|
||||||
|
{
|
||||||
|
return std::tolower(lhs) == std::tolower(rhs);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
inline
|
||||||
|
std::pair<const char*, std::size_t>
|
||||||
|
view(const char (&s)[N])
|
||||||
|
{
|
||||||
|
return {s, N-1};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::pair<const char*, std::size_t>
|
||||||
|
view(std::string const& s)
|
||||||
|
{
|
||||||
|
return {s.data(), s.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if strings are case-insensitive equal. */
|
||||||
|
template <class String1, class String2>
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
ci_equal(String1 const& lhs, String2 const& rhs)
|
||||||
|
{
|
||||||
|
return ci_equal(view(lhs), view(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
89
include/beast/detail/const_container.hpp
Normal file
89
include/beast/detail/const_container.hpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// 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_CONST_CONTAINER_HPP
|
||||||
|
#define BEAST_DETAIL_CONST_CONTAINER_HPP
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
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
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
94
include/beast/detail/empty_base_optimization.hpp
Normal file
94
include/beast/detail/empty_base_optimization.hpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
|
||||||
|
#define BEAST_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct empty_base_optimization_decide
|
||||||
|
: std::integral_constant <bool,
|
||||||
|
std::is_empty <T>::value
|
||||||
|
#ifdef __clang__
|
||||||
|
&& !__is_final(T)
|
||||||
|
#endif
|
||||||
|
>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <
|
||||||
|
class T,
|
||||||
|
int UniqueID = 0,
|
||||||
|
bool ShouldDeriveFrom =
|
||||||
|
empty_base_optimization_decide<T>::value
|
||||||
|
>
|
||||||
|
class empty_base_optimization : private T
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
empty_base_optimization() = default;
|
||||||
|
|
||||||
|
empty_base_optimization(T const& t)
|
||||||
|
: T (t)
|
||||||
|
{}
|
||||||
|
|
||||||
|
empty_base_optimization(T&& t)
|
||||||
|
: T (std::move (t))
|
||||||
|
{}
|
||||||
|
|
||||||
|
T& member() noexcept
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
T const& member() const noexcept
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <
|
||||||
|
class T,
|
||||||
|
int UniqueID
|
||||||
|
>
|
||||||
|
class empty_base_optimization <T, UniqueID, false>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
empty_base_optimization() = default;
|
||||||
|
|
||||||
|
empty_base_optimization(T const& t)
|
||||||
|
: m_t (t)
|
||||||
|
{}
|
||||||
|
|
||||||
|
empty_base_optimization(T&& t)
|
||||||
|
: m_t (std::move (t))
|
||||||
|
{}
|
||||||
|
|
||||||
|
T& member() noexcept
|
||||||
|
{
|
||||||
|
return m_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
T const& member() const noexcept
|
||||||
|
{
|
||||||
|
return m_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T m_t;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
145
include/beast/detail/integer_sequence.hpp
Normal file
145
include/beast/detail/integer_sequence.hpp
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
|
||||||
|
#define BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class T, T... Ints>
|
||||||
|
struct integer_sequence
|
||||||
|
{
|
||||||
|
using value_type = T;
|
||||||
|
static_assert (std::is_integral<T>::value,
|
||||||
|
"std::integer_sequence can only be instantiated with an integral type" );
|
||||||
|
|
||||||
|
static std::size_t constexpr static_size = sizeof...(Ints);
|
||||||
|
|
||||||
|
static std::size_t constexpr size()
|
||||||
|
{
|
||||||
|
return sizeof...(Ints);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<std::size_t... Ints>
|
||||||
|
using index_sequence = integer_sequence<std::size_t, Ints...>;
|
||||||
|
|
||||||
|
// This workaround is needed for broken sizeof...
|
||||||
|
template<class... Args>
|
||||||
|
struct sizeof_workaround
|
||||||
|
{
|
||||||
|
static std::size_t constexpr size = sizeof... (Args);
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
// This implementation compiles on MSVC and clang but not gcc
|
||||||
|
|
||||||
|
template<class T, unsigned long long N, class Seq>
|
||||||
|
struct make_integer_sequence_unchecked;
|
||||||
|
|
||||||
|
template<class T, unsigned long long N, unsigned long long ...Indices>
|
||||||
|
struct make_integer_sequence_unchecked<
|
||||||
|
T, N, integer_sequence<T, Indices...>>
|
||||||
|
{
|
||||||
|
using type = typename make_integer_sequence_unchecked<
|
||||||
|
T, N-1, integer_sequence<T, N-1, Indices...>>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, unsigned long long ...Indices>
|
||||||
|
struct make_integer_sequence_unchecked<
|
||||||
|
T, 0, integer_sequence<T, Indices...>>
|
||||||
|
{
|
||||||
|
using type = integer_sequence<T, Indices...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, T N>
|
||||||
|
struct make_integer_sequence_checked
|
||||||
|
{
|
||||||
|
static_assert (std::is_integral<T>::value,
|
||||||
|
"T must be an integral type");
|
||||||
|
|
||||||
|
static_assert (N >= 0,
|
||||||
|
"N must be non-negative");
|
||||||
|
|
||||||
|
using type = typename make_integer_sequence_unchecked<
|
||||||
|
T, N, integer_sequence<T>>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, T N>
|
||||||
|
using make_integer_sequence =
|
||||||
|
typename make_integer_sequence_checked<T, N>::type;
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
using index_sequence_for =
|
||||||
|
make_index_sequence<sizeof_workaround<Args...>::size>;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// This implementation compiles on gcc but not MSVC
|
||||||
|
|
||||||
|
template<std::size_t... Ints>
|
||||||
|
struct index_tuple
|
||||||
|
{
|
||||||
|
using next = index_tuple<Ints..., sizeof... (Ints)>;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
struct build_index_tuple
|
||||||
|
{
|
||||||
|
using type = typename build_index_tuple<N-1>::type::next;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct build_index_tuple<0>
|
||||||
|
{
|
||||||
|
using type = index_tuple<>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, T N,
|
||||||
|
class Seq = typename build_index_tuple<N>::type
|
||||||
|
>
|
||||||
|
struct integer_sequence_helper;
|
||||||
|
|
||||||
|
template<class T, T N, std::size_t... Ints>
|
||||||
|
struct integer_sequence_helper<T, N, index_tuple<Ints...>>
|
||||||
|
{
|
||||||
|
static_assert (std::is_integral<T>::value,
|
||||||
|
"T must be an integral type");
|
||||||
|
|
||||||
|
static_assert (N >= 0,
|
||||||
|
"N must be non-negative");
|
||||||
|
|
||||||
|
using type = integer_sequence<T, static_cast<T> (Ints)...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, T N>
|
||||||
|
using make_integer_sequence =
|
||||||
|
typename integer_sequence_helper<T, N>::type;
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
using index_sequence_for =
|
||||||
|
make_index_sequence<sizeof_workaround<Args...>::size>;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
90
include/beast/detail/is_call_possible.hpp
Normal file
90
include/beast/detail/is_call_possible.hpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
|
||||||
|
#define BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <class R, class C, class ...A>
|
||||||
|
auto
|
||||||
|
is_call_possible_test(C&& c, int, A&& ...a)
|
||||||
|
-> decltype(std::is_convertible<
|
||||||
|
decltype(c(a...)), R>::value ||
|
||||||
|
std::is_same<R, void>::value,
|
||||||
|
std::true_type());
|
||||||
|
|
||||||
|
template <class R, class C, class ...A>
|
||||||
|
std::false_type
|
||||||
|
is_call_possible_test(C&& c, long, A&& ...a);
|
||||||
|
|
||||||
|
/** Metafunction returns `true` if F callable as R(A...)
|
||||||
|
Example:
|
||||||
|
is_call_possible<T, void(std::string)>
|
||||||
|
*/
|
||||||
|
/** @{ */
|
||||||
|
template <class C, class F>
|
||||||
|
struct is_call_possible
|
||||||
|
: std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class C, class R, class ...A>
|
||||||
|
struct is_call_possible<C, R(A...)>
|
||||||
|
: decltype(is_call_possible_test<R>(
|
||||||
|
std::declval<C>(), 1, std::declval<A>()...))
|
||||||
|
{
|
||||||
|
};
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
struct is_call_possible_udt1
|
||||||
|
{
|
||||||
|
void operator()(int) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct is_call_possible_udt2
|
||||||
|
{
|
||||||
|
int operator()(int) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct is_call_possible_udt3
|
||||||
|
{
|
||||||
|
int operator()(int);
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(is_call_possible<
|
||||||
|
is_call_possible_udt1, void(int)>::value, "");
|
||||||
|
|
||||||
|
static_assert(! is_call_possible<
|
||||||
|
is_call_possible_udt1, void(void)>::value, "");
|
||||||
|
|
||||||
|
static_assert(is_call_possible<
|
||||||
|
is_call_possible_udt2, int(int)>::value, "");
|
||||||
|
|
||||||
|
static_assert(! is_call_possible<
|
||||||
|
is_call_possible_udt2, int(void)>::value, "");
|
||||||
|
|
||||||
|
static_assert(! is_call_possible<
|
||||||
|
is_call_possible_udt2, void(void)>::value, "");
|
||||||
|
|
||||||
|
static_assert(is_call_possible<
|
||||||
|
is_call_possible_udt3, int(int)>::value, "");
|
||||||
|
|
||||||
|
static_assert(! is_call_possible<
|
||||||
|
is_call_possible_udt3 const, int(int)>::value, "");
|
||||||
|
|
||||||
|
} // test
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
310
include/beast/detail/sha1.hpp
Normal file
310
include/beast/detail/sha1.hpp
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
//
|
||||||
|
// 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 const block_size = sha1::BLOCK_BYTES;
|
||||||
|
static unsigned int const digest_size = 20;
|
||||||
|
|
||||||
|
std::uint32_t digest[5];
|
||||||
|
std::uint8_t buf[block_size];
|
||||||
|
std::size_t buflen;
|
||||||
|
std::size_t blocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
init(sha1_context& ctx) noexcept
|
||||||
|
{
|
||||||
|
ctx.buflen = 0;
|
||||||
|
ctx.digest[0] = 0x67452301;
|
||||||
|
ctx.digest[1] = 0xefcdab89;
|
||||||
|
ctx.digest[2] = 0x98badcfe;
|
||||||
|
ctx.digest[3] = 0x10325476;
|
||||||
|
ctx.digest[4] = 0xc3d2e1f0;
|
||||||
|
ctx.blocks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
auto const buflen = ctx.buflen;
|
||||||
|
ctx.buf[ctx.buflen++] = 0x80;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
++ctx.blocks;
|
||||||
|
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
|
22
include/beast/detail/stream/abstract_ostream.hpp
Normal file
22
include/beast/detail/stream/abstract_ostream.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// 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_ABSTRACT_OSTREAM_HPP
|
||||||
|
#define BEAST_DETAIL_STREAM_ABSTRACT_OSTREAM_HPP
|
||||||
|
|
||||||
|
#include <beast/detail/stream/basic_abstract_ostream.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/** An abstract ostream for `char`. */
|
||||||
|
using abstract_ostream = basic_abstract_ostream <char>;
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
87
include/beast/detail/stream/basic_abstract_ostream.hpp
Normal file
87
include/beast/detail/stream/basic_abstract_ostream.hpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_STREAM_BASIC_ABSTRACT_OSTREAM_HPP
|
||||||
|
#define BEAST_DETAIL_STREAM_BASIC_ABSTRACT_OSTREAM_HPP
|
||||||
|
|
||||||
|
#include <beast/detail/stream/basic_scoped_ostream.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/** Abstraction for an output stream similar to std::basic_ostream. */
|
||||||
|
template <
|
||||||
|
class CharT,
|
||||||
|
class Traits = std::char_traits <CharT>
|
||||||
|
>
|
||||||
|
class basic_abstract_ostream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using string_type = std::basic_string <CharT, Traits>;
|
||||||
|
using scoped_stream_type = basic_scoped_ostream <CharT, Traits>;
|
||||||
|
|
||||||
|
basic_abstract_ostream() = default;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~basic_abstract_ostream() = default;
|
||||||
|
|
||||||
|
basic_abstract_ostream (basic_abstract_ostream const&) = default;
|
||||||
|
basic_abstract_ostream& operator= (
|
||||||
|
basic_abstract_ostream const&) = default;
|
||||||
|
|
||||||
|
/** Returns `true` if the stream is active.
|
||||||
|
Inactive streams do not produce output.
|
||||||
|
*/
|
||||||
|
/** @{ */
|
||||||
|
virtual
|
||||||
|
bool
|
||||||
|
active() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return active();
|
||||||
|
}
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** Called to output each string. */
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
write (string_type const& s) = 0;
|
||||||
|
|
||||||
|
scoped_stream_type
|
||||||
|
operator<< (std::ostream& manip (std::ostream&))
|
||||||
|
{
|
||||||
|
return scoped_stream_type (manip,
|
||||||
|
[this](string_type const& s)
|
||||||
|
{
|
||||||
|
this->write (s);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
scoped_stream_type
|
||||||
|
operator<< (T const& t)
|
||||||
|
{
|
||||||
|
return scoped_stream_type (t,
|
||||||
|
[this](string_type const& s)
|
||||||
|
{
|
||||||
|
this->write (s);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
138
include/beast/detail/stream/basic_scoped_ostream.hpp
Normal file
138
include/beast/detail/stream/basic_scoped_ostream.hpp
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
//
|
||||||
|
// 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_BASIC_SCOPED_OSTREAM_HPP
|
||||||
|
#define BEAST_DETAIL_STREAM_BASIC_SCOPED_OSTREAM_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
// gcc libstd++ doesn't have move constructors for basic_ostringstream
|
||||||
|
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316
|
||||||
|
//
|
||||||
|
#ifndef BEAST_NO_STDLIB_STREAM_MOVE
|
||||||
|
# ifdef __GLIBCXX__
|
||||||
|
# define BEAST_NO_STDLIB_STREAM_MOVE 1
|
||||||
|
# else
|
||||||
|
# define BEAST_NO_STDLIB_STREAM_MOVE 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <
|
||||||
|
class CharT,
|
||||||
|
class Traits
|
||||||
|
>
|
||||||
|
class basic_abstract_ostream;
|
||||||
|
|
||||||
|
/** Scoped output stream that forwards to a functor upon destruction. */
|
||||||
|
template <
|
||||||
|
class CharT,
|
||||||
|
class Traits = std::char_traits <CharT>,
|
||||||
|
class Allocator = std::allocator <CharT>
|
||||||
|
>
|
||||||
|
class basic_scoped_ostream
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using handler_t = std::function <void (
|
||||||
|
std::basic_string <CharT, Traits, Allocator> const&)>;
|
||||||
|
|
||||||
|
using stream_type = std::basic_ostringstream <
|
||||||
|
CharT, Traits, Allocator>;
|
||||||
|
|
||||||
|
handler_t m_handler;
|
||||||
|
|
||||||
|
#if BEAST_NO_STDLIB_STREAM_MOVE
|
||||||
|
std::unique_ptr <stream_type> m_ss;
|
||||||
|
|
||||||
|
stream_type& stream()
|
||||||
|
{
|
||||||
|
return *m_ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
stream_type m_ss;
|
||||||
|
|
||||||
|
stream_type& stream()
|
||||||
|
{
|
||||||
|
return m_ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
using string_type = std::basic_string <CharT, Traits>;
|
||||||
|
|
||||||
|
// Disallow copy since that would duplicate the output
|
||||||
|
basic_scoped_ostream (basic_scoped_ostream const&) = delete;
|
||||||
|
basic_scoped_ostream& operator= (basic_scoped_ostream const) = delete;
|
||||||
|
|
||||||
|
template <class Handler>
|
||||||
|
explicit basic_scoped_ostream (Handler&& handler)
|
||||||
|
: m_handler (std::forward <Handler> (handler))
|
||||||
|
#if BEAST_NO_STDLIB_STREAM_MOVE
|
||||||
|
, m_ss (new stream_type())
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class Handler>
|
||||||
|
basic_scoped_ostream (T const& t, Handler&& handler)
|
||||||
|
: m_handler (std::forward <Handler> (handler))
|
||||||
|
#if BEAST_NO_STDLIB_STREAM_MOVE
|
||||||
|
, m_ss (new stream_type())
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
stream() << t;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_scoped_ostream (basic_abstract_ostream <
|
||||||
|
CharT, Traits>& ostream)
|
||||||
|
: m_handler (
|
||||||
|
[&](string_type const& s)
|
||||||
|
{
|
||||||
|
ostream.write (s);
|
||||||
|
})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_scoped_ostream (basic_scoped_ostream&& other)
|
||||||
|
: m_handler (std::move (other.m_handler))
|
||||||
|
, m_ss (std::move (other.m_ss))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~basic_scoped_ostream()
|
||||||
|
{
|
||||||
|
auto const& s (stream().str());
|
||||||
|
if (! s.empty())
|
||||||
|
m_handler (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_scoped_ostream&
|
||||||
|
operator<< (std::ostream& manip (std::ostream&))
|
||||||
|
{
|
||||||
|
stream() << manip;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
basic_scoped_ostream&
|
||||||
|
operator<< (T const& t)
|
||||||
|
{
|
||||||
|
stream() << t;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
62
include/beast/detail/stream/basic_std_ostream.hpp
Normal file
62
include/beast/detail/stream/basic_std_ostream.hpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
//
|
||||||
|
// 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_BASIC_STD_OSTREAM_H_INCLUDED
|
||||||
|
#define BEAST_DETAIL_STREAM_BASIC_STD_OSTREAM_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/detail/stream/basic_abstract_ostream.hpp>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/** Wraps an existing std::basic_ostream as an abstract_ostream. */
|
||||||
|
template <
|
||||||
|
class CharT,
|
||||||
|
class Traits = std::char_traits <CharT>
|
||||||
|
>
|
||||||
|
class basic_std_ostream
|
||||||
|
: public basic_abstract_ostream <CharT, Traits>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using typename basic_abstract_ostream <CharT, Traits>::string_type;
|
||||||
|
|
||||||
|
std::reference_wrapper <std::ostream> m_stream;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit basic_std_ostream (
|
||||||
|
std::basic_ostream <CharT, Traits>& stream)
|
||||||
|
: m_stream (stream)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
write (string_type const& s) override
|
||||||
|
{
|
||||||
|
m_stream.get() << s << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using std_ostream = basic_std_ostream <char>;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Returns a basic_std_ostream using template argument deduction. */
|
||||||
|
template <
|
||||||
|
class CharT,
|
||||||
|
class Traits = std::char_traits <CharT>
|
||||||
|
>
|
||||||
|
basic_std_ostream <CharT, Traits>
|
||||||
|
make_std_ostream (std::basic_ostream <CharT, Traits>& stream)
|
||||||
|
{
|
||||||
|
return basic_std_ostream <CharT, Traits> (stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
80
include/beast/detail/stream/debug_ostream.hpp
Normal file
80
include/beast/detail/stream/debug_ostream.hpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
//
|
||||||
|
// 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_DEBUG_OSTREAM_HPP
|
||||||
|
#define BEAST_DETAIL_STREAM_DEBUG_OSTREAM_HPP
|
||||||
|
|
||||||
|
#include <beast/detail/stream/abstract_ostream.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# ifndef WIN32_LEAN_AND_MEAN // VC_EXTRALEAN
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
# undef WIN32_LEAN_AND_MEAN
|
||||||
|
# else
|
||||||
|
#include <windows.h>
|
||||||
|
# endif
|
||||||
|
# ifdef min
|
||||||
|
# undef min
|
||||||
|
# endif
|
||||||
|
# ifdef max
|
||||||
|
# undef max
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
/** A basic_abstract_ostream that redirects output to an attached debugger. */
|
||||||
|
class debug_ostream
|
||||||
|
: public abstract_ostream
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool m_debugger;
|
||||||
|
|
||||||
|
public:
|
||||||
|
debug_ostream()
|
||||||
|
: m_debugger (IsDebuggerPresent() != FALSE)
|
||||||
|
{
|
||||||
|
// Note that the check for an attached debugger is made only
|
||||||
|
// during construction time, for efficiency. A stream created before
|
||||||
|
// the debugger is attached will not have output redirected.
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
write (string_type const& s) override
|
||||||
|
{
|
||||||
|
if (m_debugger)
|
||||||
|
{
|
||||||
|
OutputDebugStringA ((s + "\n").c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << s << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
class debug_ostream
|
||||||
|
: public abstract_ostream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
write (string_type const& s) override
|
||||||
|
{
|
||||||
|
std::cout << s << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
73
include/beast/detail/temp_dir.hpp
Normal file
73
include/beast/detail/temp_dir.hpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_TEMP_DIR_H_INCLUDED
|
||||||
|
#define BEAST_DETAIL_TEMP_DIR_H_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/** RAII temporary directory.
|
||||||
|
|
||||||
|
The directory and all its contents are deleted when
|
||||||
|
the instance of `temp_dir` is destroyed.
|
||||||
|
*/
|
||||||
|
class temp_dir
|
||||||
|
{
|
||||||
|
boost::filesystem::path path_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
#if ! GENERATING_DOCS
|
||||||
|
temp_dir(const temp_dir&) = delete;
|
||||||
|
temp_dir& operator=(const temp_dir&) = delete;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Construct a temporary directory.
|
||||||
|
temp_dir()
|
||||||
|
{
|
||||||
|
auto const dir =
|
||||||
|
boost::filesystem::temp_directory_path();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
path_ =
|
||||||
|
dir / boost::filesystem::unique_path();
|
||||||
|
}
|
||||||
|
while(boost::filesystem::exists(path_));
|
||||||
|
boost::filesystem::create_directory (path_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy a temporary directory.
|
||||||
|
~temp_dir()
|
||||||
|
{
|
||||||
|
boost::filesystem::remove_all (path_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the native path for the temporary directory
|
||||||
|
std::string
|
||||||
|
path() const
|
||||||
|
{
|
||||||
|
return path_.string();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the native path for the a file.
|
||||||
|
|
||||||
|
The file does not need to exist.
|
||||||
|
*/
|
||||||
|
std::string
|
||||||
|
file(std::string const& name) const
|
||||||
|
{
|
||||||
|
return (path_ / name).string();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
23
include/beast/detail/unit_test.h
Normal file
23
include/beast/detail/unit_test.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_H_INCLUDED
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/detail/unit_test/amount.h>
|
||||||
|
#include <beast/detail/unit_test/print.h>
|
||||||
|
#include <beast/detail/unit_test/global_suites.h>
|
||||||
|
#include <beast/detail/unit_test/match.h>
|
||||||
|
#include <beast/detail/unit_test/recorder.h>
|
||||||
|
#include <beast/detail/unit_test/reporter.h>
|
||||||
|
#include <beast/detail/unit_test/results.h>
|
||||||
|
#include <beast/detail/unit_test/runner.h>
|
||||||
|
#include <beast/detail/unit_test/suite.h>
|
||||||
|
#include <beast/detail/unit_test/suite_info.h>
|
||||||
|
#include <beast/detail/unit_test/suite_list.h>
|
||||||
|
|
||||||
|
#endif
|
59
include/beast/detail/unit_test/amount.hpp
Normal file
59
include/beast/detail/unit_test/amount.hpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_AMOUNT_HPP
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_AMOUNT_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
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
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
90
include/beast/detail/unit_test/define_print.cpp
Normal file
90
include/beast/detail/unit_test/define_print.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <beast/detail/unit_test/amount.hpp>
|
||||||
|
#include <beast/detail/unit_test/global_suites.hpp>
|
||||||
|
#include <beast/detail/unit_test/suite.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Include this .cpp in your project to gain access to the printing suite
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
namespace unit_test {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/** A suite that prints the list of globally defined suites. */
|
||||||
|
class print_test : public suite
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
template <class = void>
|
||||||
|
void
|
||||||
|
do_run();
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <class = void>
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
prefix (suite_info const& s);
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
void
|
||||||
|
print (suite_list &c);
|
||||||
|
|
||||||
|
void
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
do_run();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
void
|
||||||
|
print_test::do_run()
|
||||||
|
{
|
||||||
|
log << "------------------------------------------";
|
||||||
|
print (global_suites());
|
||||||
|
log << "------------------------------------------";
|
||||||
|
pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
std::string
|
||||||
|
print_test::prefix (suite_info const& s)
|
||||||
|
{
|
||||||
|
if (s.manual())
|
||||||
|
return "|M| ";
|
||||||
|
return " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
void
|
||||||
|
print_test::print (suite_list &c)
|
||||||
|
{
|
||||||
|
std::size_t manual (0);
|
||||||
|
for (auto const& s : c)
|
||||||
|
{
|
||||||
|
log <<
|
||||||
|
prefix (s) <<
|
||||||
|
s.full_name();
|
||||||
|
if (s.manual())
|
||||||
|
++manual;
|
||||||
|
}
|
||||||
|
log <<
|
||||||
|
amount (c.size(), "suite") << " total, " <<
|
||||||
|
amount (manual, "manual suite")
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE_MANUAL(print,unit_test,beast);
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // unit_test
|
||||||
|
} // detail
|
||||||
|
} // beast
|
60
include/beast/detail/unit_test/global_suites.hpp
Normal file
60
include/beast/detail/unit_test/global_suites.hpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_GLOBAL_SUITES_HPP
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_GLOBAL_SUITES_HPP
|
||||||
|
|
||||||
|
#include <beast/detail/unit_test/suite_list.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
namespace unit_test {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
suite_list&
|
||||||
|
global_suites()
|
||||||
|
{
|
||||||
|
static suite_list s;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Suite>
|
||||||
|
struct insert_suite
|
||||||
|
{
|
||||||
|
template <class = void>
|
||||||
|
insert_suite (char const* name, char const* module,
|
||||||
|
char const* library, bool manual);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Suite>
|
||||||
|
template <class>
|
||||||
|
insert_suite<Suite>::insert_suite (char const* name,
|
||||||
|
char const* module, char const* library, bool manual)
|
||||||
|
{
|
||||||
|
global_suites().insert <Suite> (
|
||||||
|
name, module, library, manual);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
/** Holds suites registered during static initialization. */
|
||||||
|
inline
|
||||||
|
suite_list const&
|
||||||
|
global_suites()
|
||||||
|
{
|
||||||
|
return detail::global_suites();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unit_test
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
177
include/beast/detail/unit_test/match.hpp
Normal file
177
include/beast/detail/unit_test/match.hpp
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_MATCH_HPP
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_MATCH_HPP
|
||||||
|
|
||||||
|
#include <beast/detail/unit_test/suite_info.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
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
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
71
include/beast/detail/unit_test/print.hpp
Normal file
71
include/beast/detail/unit_test/print.hpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_PRINT_H_INCLUDED
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_PRINT_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/detail/unit_test/amount.hpp>
|
||||||
|
#include <beast/detail/unit_test/results.hpp>
|
||||||
|
#include <beast/detail/stream/abstract_ostream.hpp>
|
||||||
|
#include <beast/detail/stream/basic_std_ostream.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
namespace unit_test {
|
||||||
|
|
||||||
|
/** Write test results to the specified output stream. */
|
||||||
|
/** @{ */
|
||||||
|
template <class = void>
|
||||||
|
void
|
||||||
|
print (results const& r, beast::detail::abstract_ostream& stream)
|
||||||
|
{
|
||||||
|
for (auto const& s : r)
|
||||||
|
{
|
||||||
|
for (auto const& c : s)
|
||||||
|
{
|
||||||
|
stream <<
|
||||||
|
s.name() <<
|
||||||
|
(c.name().empty() ? "" : ("." + c.name()));
|
||||||
|
|
||||||
|
std::size_t i (1);
|
||||||
|
for (auto const& t : c.tests)
|
||||||
|
{
|
||||||
|
if (! t.pass)
|
||||||
|
stream <<
|
||||||
|
"#" << i <<
|
||||||
|
" failed: " << t.reason;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream <<
|
||||||
|
amount (r.size(), "suite") << ", " <<
|
||||||
|
amount (r.cases(), "case") << ", " <<
|
||||||
|
amount (r.total(), "test") << " total, " <<
|
||||||
|
amount (r.failed(), "failure")
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
void
|
||||||
|
print (results const& r, std::ostream& stream = std::cout)
|
||||||
|
{
|
||||||
|
auto s (make_std_ostream (stream));
|
||||||
|
print (r, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unit_test
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
96
include/beast/detail/unit_test/recorder.hpp
Normal file
96
include/beast/detail/unit_test/recorder.hpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_RECORDER_HPP
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_RECORDER_HPP
|
||||||
|
|
||||||
|
#include <beast/detail/unit_test/results.hpp>
|
||||||
|
#include <beast/detail/unit_test/runner.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
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
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
324
include/beast/detail/unit_test/reporter.hpp
Normal file
324
include/beast/detail/unit_test/reporter.hpp
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_REPORTER_HPP
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_REPORTER_HPP
|
||||||
|
|
||||||
|
#include <beast/detail/unit_test/amount.hpp>
|
||||||
|
#include <beast/detail/unit_test/recorder.hpp>
|
||||||
|
#include <beast/detail/stream/abstract_ostream.hpp>
|
||||||
|
#include <beast/detail/stream/basic_std_ostream.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
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;
|
||||||
|
|
||||||
|
case_results (std::string const& 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 const& 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
boost::optional <std_ostream> std_ostream_;
|
||||||
|
std::reference_wrapper <beast::detail::abstract_ostream> stream_;
|
||||||
|
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& stream = std::cout);
|
||||||
|
|
||||||
|
explicit
|
||||||
|
reporter (beast::detail::abstract_ostream& stream);
|
||||||
|
|
||||||
|
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 _>
|
||||||
|
reporter<_>::case_results::case_results (
|
||||||
|
std::string const& name_)
|
||||||
|
: name (name_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _>
|
||||||
|
reporter<_>::suite_results::suite_results (
|
||||||
|
std::string const& name_)
|
||||||
|
: name (name_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _>
|
||||||
|
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& stream)
|
||||||
|
: std_ostream_ (std::ref (stream))
|
||||||
|
, stream_ (*std_ostream_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _>
|
||||||
|
reporter<_>::~reporter()
|
||||||
|
{
|
||||||
|
if (results_.top.size() > 0)
|
||||||
|
{
|
||||||
|
stream_.get() << "Longest suite times:";
|
||||||
|
for (auto const& i : results_.top)
|
||||||
|
stream_.get() << std::setw(8) <<
|
||||||
|
fmtdur(i.second) << " " << i.first;
|
||||||
|
}
|
||||||
|
auto const elapsed =
|
||||||
|
clock_type::now() - results_.start;
|
||||||
|
stream_.get() <<
|
||||||
|
fmtdur(elapsed) << ", " <<
|
||||||
|
amount (results_.suites, "suite") << ", " <<
|
||||||
|
amount (results_.cases, "case") << ", " <<
|
||||||
|
amount (results_.total, "test") << " total, " <<
|
||||||
|
amount (results_.failed, "failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _>
|
||||||
|
reporter<_>::reporter (
|
||||||
|
beast::detail::abstract_ostream& stream)
|
||||||
|
: stream_ (stream)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
stream_.get() <<
|
||||||
|
suite_results_.name <<
|
||||||
|
(case_results_.name.empty() ?
|
||||||
|
"" : (" " + case_results_.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
stream_.get() <<
|
||||||
|
"#" << case_results_.total <<
|
||||||
|
" failed" <<
|
||||||
|
(reason.empty() ? "" : ": ") << reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _>
|
||||||
|
void
|
||||||
|
reporter<_>::on_log (
|
||||||
|
std::string const& s)
|
||||||
|
{
|
||||||
|
stream_.get() << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
using reporter = detail::reporter<>;
|
||||||
|
|
||||||
|
} // unit_test
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
246
include/beast/detail/unit_test/results.hpp
Normal file
246
include/beast/detail/unit_test/results.hpp
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_RESULTS_HPP
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_RESULTS_HPP
|
||||||
|
|
||||||
|
#include <beast/detail/const_container.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
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 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 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 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 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
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
338
include/beast/detail/unit_test/runner.hpp
Normal file
338
include/beast/detail/unit_test/runner.hpp
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_RUNNER_H_INCLUDED
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_RUNNER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/detail/unit_test/suite_info.hpp>
|
||||||
|
#include <beast/detail/stream/abstract_ostream.hpp>
|
||||||
|
#include <cassert>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
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
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Reroutes log output to the runner
|
||||||
|
class stream_t : public beast::detail::abstract_ostream
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
runner& owner_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
stream_t() = delete;
|
||||||
|
stream_t& operator= (stream_t const&) = delete;
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
stream_t (runner& owner);
|
||||||
|
|
||||||
|
void
|
||||||
|
write (string_type const& s) override
|
||||||
|
{
|
||||||
|
owner_.log (s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stream_t stream_;
|
||||||
|
std::string arg_;
|
||||||
|
bool default_ = false;
|
||||||
|
bool failed_ = false;
|
||||||
|
bool cond_ = false;
|
||||||
|
std::recursive_mutex mutex_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~runner() = default;
|
||||||
|
runner (runner const&) = default;
|
||||||
|
runner& operator= (runner const&) = default;
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
runner();
|
||||||
|
|
||||||
|
/** 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{});
|
||||||
|
|
||||||
|
private:
|
||||||
|
//
|
||||||
|
// Overrides
|
||||||
|
//
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
|
||||||
|
abstract_ostream&
|
||||||
|
stream()
|
||||||
|
{
|
||||||
|
return stream_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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>
|
||||||
|
runner::stream_t::stream_t (runner& owner)
|
||||||
|
: owner_ (owner)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
runner::runner()
|
||||||
|
: stream_ (*this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
608
include/beast/detail/unit_test/suite.hpp
Normal file
608
include/beast/detail/unit_test/suite.hpp
Normal file
@ -0,0 +1,608 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_SUITE_HPP
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_SUITE_HPP
|
||||||
|
|
||||||
|
#include <beast/detail/unit_test/runner.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
namespace unit_test {
|
||||||
|
|
||||||
|
class thread;
|
||||||
|
|
||||||
|
/** 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
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum abort_t
|
||||||
|
{
|
||||||
|
no_abort_on_fail,
|
||||||
|
abort_on_fail
|
||||||
|
};
|
||||||
|
|
||||||
|
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 "suite aborted";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Memberspace
|
||||||
|
class log_t
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
friend class suite;
|
||||||
|
suite* suite_ = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
log_t () = default;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
abstract_ostream::scoped_stream_type
|
||||||
|
operator<< (T const& t);
|
||||||
|
|
||||||
|
/** Returns the raw stream used for output. */
|
||||||
|
abstract_ostream&
|
||||||
|
stream();
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
|
||||||
|
class scoped_testcase;
|
||||||
|
|
||||||
|
// Memberspace
|
||||||
|
class testcase_t
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
friend class suite;
|
||||||
|
suite* suite_ = nullptr;
|
||||||
|
std::stringstream ss_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
testcase_t() = default;
|
||||||
|
|
||||||
|
/** Open a new testcase.
|
||||||
|
A testcase is a series of evaluated test conditions. A test suite
|
||||||
|
may have multiple test cases. A test is associated with the last
|
||||||
|
opened testcase. When the test first runs, a default unnamed
|
||||||
|
case is opened. Tests with only one case may omit the call
|
||||||
|
to testcase.
|
||||||
|
@param abort If `true`, the suite will be stopped on first failure.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
operator() (std::string const& name,
|
||||||
|
abort_t abort = no_abort_on_fail);
|
||||||
|
|
||||||
|
/** Stream style composition of testcase names. */
|
||||||
|
/** @{ */
|
||||||
|
scoped_testcase
|
||||||
|
operator() (abort_t abort);
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
scoped_testcase
|
||||||
|
operator<< (T const& t);
|
||||||
|
/** @} */
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Type for scoped stream logging.
|
||||||
|
To use this type, declare a local variable of the type
|
||||||
|
on the stack in derived class member function and construct
|
||||||
|
it from log.stream();
|
||||||
|
|
||||||
|
@code
|
||||||
|
|
||||||
|
scoped_stream ss (log.stream();
|
||||||
|
|
||||||
|
ss << "Hello" << std::endl;
|
||||||
|
ss << "world" << std::endl;
|
||||||
|
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
Streams constructed in this fashion will not have the line
|
||||||
|
ending automatically appended.
|
||||||
|
|
||||||
|
Thread safety:
|
||||||
|
|
||||||
|
The scoped_stream may only be used by one thread.
|
||||||
|
Multiline output sent to the stream will be atomically
|
||||||
|
written to the underlying abstract_Ostream
|
||||||
|
*/
|
||||||
|
using scoped_stream = abstract_ostream::scoped_stream_type;
|
||||||
|
|
||||||
|
/** Memberspace for logging. */
|
||||||
|
log_t 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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);
|
||||||
|
|
||||||
|
/** Evaluate a test condition.
|
||||||
|
The condition is passed as a template argument instead of `bool` so
|
||||||
|
that implicit conversion is not required. The `reason` argument is
|
||||||
|
logged if the condition is false.
|
||||||
|
@return `true` if the test condition indicates success.
|
||||||
|
*/
|
||||||
|
template<class Condition, class String>
|
||||||
|
bool
|
||||||
|
expect(Condition const& shouldBeTrue,
|
||||||
|
String const& reason);
|
||||||
|
|
||||||
|
template<class Condition>
|
||||||
|
bool
|
||||||
|
expect(Condition const& shouldBeTrue)
|
||||||
|
{
|
||||||
|
return expect(shouldBeTrue, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Expect an exception from f() */
|
||||||
|
/** @{ */
|
||||||
|
template <class F, class String>
|
||||||
|
bool
|
||||||
|
except (F&& f, String const& reason);
|
||||||
|
|
||||||
|
template <class F>
|
||||||
|
bool
|
||||||
|
except (F&& f)
|
||||||
|
{
|
||||||
|
return except(f, "");
|
||||||
|
}
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** Expect an exception of the given type from f() */
|
||||||
|
/** @{ */
|
||||||
|
template <class E, class F, class String>
|
||||||
|
bool
|
||||||
|
except (F&& f, String const& reason);
|
||||||
|
|
||||||
|
template <class E, class F>
|
||||||
|
bool
|
||||||
|
except (F&& f)
|
||||||
|
{
|
||||||
|
return except<E>(f, "");
|
||||||
|
}
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** Fail if f() throws */
|
||||||
|
/** @{ */
|
||||||
|
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, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Record a successful test condition. */
|
||||||
|
template <class = void>
|
||||||
|
void
|
||||||
|
pass();
|
||||||
|
|
||||||
|
/** Record a failure. */
|
||||||
|
template <class = void>
|
||||||
|
void
|
||||||
|
fail (std::string const& reason = "");
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class thread;
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
abstract_ostream::scoped_stream_type
|
||||||
|
suite::log_t::operator<< (T const& t)
|
||||||
|
{
|
||||||
|
return suite_->runner_->stream() << t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the raw stream used for output. */
|
||||||
|
inline
|
||||||
|
abstract_ostream&
|
||||||
|
suite::log_t::stream()
|
||||||
|
{
|
||||||
|
return suite_->runner_->stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Helper for streaming testcase names
|
||||||
|
class suite::scoped_testcase
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
suite* suite_;
|
||||||
|
std::stringstream* ss_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~scoped_testcase();
|
||||||
|
|
||||||
|
scoped_testcase (suite* s, std::stringstream* ss);
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
scoped_testcase (suite* s, std::stringstream* ss, T const& t);
|
||||||
|
|
||||||
|
scoped_testcase& operator= (scoped_testcase const&) = delete;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
scoped_testcase&
|
||||||
|
operator<< (T const& t);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
suite::scoped_testcase::~scoped_testcase()
|
||||||
|
{
|
||||||
|
auto const& name (ss_->str());
|
||||||
|
if (! name.empty())
|
||||||
|
suite_->runner_->testcase (name);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
suite::scoped_testcase::scoped_testcase (suite* s, std::stringstream* ss)
|
||||||
|
: suite_ (s)
|
||||||
|
, ss_ (ss)
|
||||||
|
{
|
||||||
|
ss_->clear();
|
||||||
|
ss_->str({});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
suite::scoped_testcase::scoped_testcase (suite* s, std::stringstream* ss, T const& t)
|
||||||
|
: suite_ (s)
|
||||||
|
, ss_ (ss)
|
||||||
|
{
|
||||||
|
ss_->clear();
|
||||||
|
ss_->str({});
|
||||||
|
*ss_ << t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
suite::scoped_testcase&
|
||||||
|
suite::scoped_testcase::operator<< (T const& t)
|
||||||
|
{
|
||||||
|
*ss_ << t;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
suite::testcase_t::operator() (std::string const& name,
|
||||||
|
abort_t abort)
|
||||||
|
{
|
||||||
|
suite_->abort_ = abort == abort_on_fail;
|
||||||
|
suite_->runner_->testcase (name);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
suite::scoped_testcase
|
||||||
|
suite::testcase_t::operator() (abort_t abort)
|
||||||
|
{
|
||||||
|
suite_->abort_ = abort == abort_on_fail;
|
||||||
|
return { suite_, &ss_ };
|
||||||
|
}
|
||||||
|
|
||||||
|
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, class String>
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
suite::expect(Condition const& shouldBeTrue,
|
||||||
|
String const& reason)
|
||||||
|
{
|
||||||
|
if(shouldBeTrue)
|
||||||
|
{
|
||||||
|
pass();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
fail(reason);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
inline
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
log.suite_ = this;
|
||||||
|
testcase.suite_ = this;
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unit_test
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// detail:
|
||||||
|
// This inserts the suite with the given manual flag
|
||||||
|
#define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual) \
|
||||||
|
static beast::detail::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/detail/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
|
119
include/beast/detail/unit_test/suite_info.hpp
Normal file
119
include/beast/detail/unit_test/suite_info.hpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_SUITE_INFO_HPP
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_SUITE_INFO_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
namespace unit_test {
|
||||||
|
|
||||||
|
class runner;
|
||||||
|
|
||||||
|
/** Associates a unit test type with metadata. */
|
||||||
|
class suite_info
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using run_type = std::function <void (runner&)>;
|
||||||
|
|
||||||
|
std::string name_;
|
||||||
|
std::string module_;
|
||||||
|
std::string library_;
|
||||||
|
bool m_manual;
|
||||||
|
run_type m_run;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <class = void>
|
||||||
|
suite_info (std::string const& name, std::string const& module,
|
||||||
|
std::string const& library, bool manual, run_type 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 m_manual;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return the canonical suite name as a string. */
|
||||||
|
template <class = void>
|
||||||
|
std::string
|
||||||
|
full_name() const;
|
||||||
|
|
||||||
|
/** Run a new instance of the associated test suite. */
|
||||||
|
void
|
||||||
|
run (runner& r) const
|
||||||
|
{
|
||||||
|
m_run (r);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
suite_info::suite_info (std::string const& name, std::string const& module,
|
||||||
|
std::string const& library, bool manual, run_type run)
|
||||||
|
: name_ (name)
|
||||||
|
, module_ (module)
|
||||||
|
, library_ (library)
|
||||||
|
, m_manual (manual)
|
||||||
|
, m_run (std::move (run))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
std::string
|
||||||
|
suite_info::full_name() const
|
||||||
|
{
|
||||||
|
return library_ + "." + module_ + "." + name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
operator< (suite_info const& lhs, suite_info const& rhs)
|
||||||
|
{
|
||||||
|
return lhs.full_name() < rhs.full_name();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience for producing suite_info for a given test type. */
|
||||||
|
template <class Suite>
|
||||||
|
suite_info
|
||||||
|
make_suite_info (std::string const& name, std::string const& module,
|
||||||
|
std::string const& library, bool manual)
|
||||||
|
{
|
||||||
|
return suite_info(name, module, library, manual,
|
||||||
|
[](runner& r) { return Suite{}(r); });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unit_test
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
75
include/beast/detail/unit_test/suite_list.hpp
Normal file
75
include/beast/detail/unit_test/suite_list.hpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_SUITE_LIST_HPP
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_SUITE_LIST_HPP
|
||||||
|
|
||||||
|
#include <beast/detail/unit_test/suite_info.hpp>
|
||||||
|
#include <beast/detail/const_container.hpp>
|
||||||
|
#include <cassert>
|
||||||
|
#include <typeindex>
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
namespace unit_test {
|
||||||
|
|
||||||
|
/** A container of test suites. */
|
||||||
|
class suite_list
|
||||||
|
: public 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 (std::move (make_suite_info <Suite> (
|
||||||
|
name, module, library, manual)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unit_test
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
128
include/beast/detail/unit_test/thread.hpp
Normal file
128
include/beast/detail/unit_test/thread.hpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_DETAIL_UNIT_TEST_THREAD_HPP
|
||||||
|
#define BEAST_DETAIL_UNIT_TEST_THREAD_HPP
|
||||||
|
|
||||||
|
#include <beast/detail/unit_test/suite.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
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
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
146
include/beast/detail/write_streambuf.hpp
Normal file
146
include/beast/detail/write_streambuf.hpp
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
//
|
||||||
|
// 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_STREAMBUF_HPP
|
||||||
|
#define BEAST_DETAIL_WRITE_STREAMBUF_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 Streambuf>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
write_streambuf(Streambuf&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
write_streambuf(Streambuf& streambuf,
|
||||||
|
boost::asio::const_buffer const& buffer)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
streambuf.commit(buffer_copy(
|
||||||
|
streambuf.prepare(buffer_size(buffer)),
|
||||||
|
buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
write_streambuf(Streambuf& streambuf,
|
||||||
|
boost::asio::mutable_buffer const& buffer)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
streambuf.commit(buffer_copy(
|
||||||
|
streambuf.prepare(buffer_size(buffer)),
|
||||||
|
buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf, 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_streambuf(Streambuf& streambuf, T const& t)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
auto const buffers = boost::asio::buffer(t);
|
||||||
|
streambuf.commit(buffer_copy(
|
||||||
|
streambuf.prepare(buffer_size(buffers)),
|
||||||
|
buffers));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf, 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_streambuf(Streambuf& streambuf, Buffers const& buffers)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
streambuf.commit(buffer_copy(
|
||||||
|
streambuf.prepare(buffer_size(buffers)),
|
||||||
|
buffers));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf, std::size_t N>
|
||||||
|
void
|
||||||
|
write_streambuf(Streambuf& streambuf, const char (&s)[N])
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
streambuf.commit(buffer_copy(
|
||||||
|
streambuf.prepare(N - 1),
|
||||||
|
boost::asio::buffer(s, N - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf, 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_streambuf(Streambuf& streambuf, T const& t)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
auto const s = boost::lexical_cast<std::string>(t);
|
||||||
|
streambuf.commit(buffer_copy(
|
||||||
|
streambuf.prepare(s.size()), buffer(s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf, class T0, class T1, class... TN>
|
||||||
|
void
|
||||||
|
write_streambuf(Streambuf& streambuf, T0&& t0, T1&& t1, TN... tn)
|
||||||
|
{
|
||||||
|
write_streambuf(streambuf, std::forward<T0>(t0));
|
||||||
|
write_streambuf(streambuf, std::forward<T1>(t1));
|
||||||
|
write_streambuf(streambuf, std::forward<TN>(tn)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
148
include/beast/handler_alloc.hpp
Normal file
148
include/beast/handler_alloc.hpp
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HANDLER_ALLOC_HPP
|
||||||
|
#define BEAST_HANDLER_ALLOC_HPP
|
||||||
|
|
||||||
|
#include <boost/asio/detail/handler_alloc_helpers.hpp>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
// Guidance from
|
||||||
|
// http://howardhinnant.github.io/allocator_boilerplate.html
|
||||||
|
|
||||||
|
/** An allocator that uses handler customizations.
|
||||||
|
|
||||||
|
This allocator uses the handler customizations `asio_handler_allocate`
|
||||||
|
and `asio_handler_deallocate` to manage memory. It meets the requirements
|
||||||
|
of `Allocator` and can be used anywhere a `std::allocator` is
|
||||||
|
accepted.
|
||||||
|
|
||||||
|
@tparam T The type of objects allocated by the allocator.
|
||||||
|
|
||||||
|
@tparam Handler 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 Handler>
|
||||||
|
class handler_alloc;
|
||||||
|
#else
|
||||||
|
template <class T, class Handler>
|
||||||
|
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 Handler is not
|
||||||
|
// constructible from H.
|
||||||
|
//
|
||||||
|
template <class U, class H>
|
||||||
|
friend class handler_alloc;
|
||||||
|
|
||||||
|
Handler 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(Handler&& h)
|
||||||
|
: h_(std::move(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct the allocator.
|
||||||
|
|
||||||
|
A copy of the handler is made.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
handler_alloc(Handler const& h)
|
||||||
|
: h_(h)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class U>
|
||||||
|
handler_alloc(
|
||||||
|
handler_alloc<U, Handler>&& other)
|
||||||
|
: h_(std::move(other.h_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class U>
|
||||||
|
handler_alloc(
|
||||||
|
handler_alloc<U, Handler> 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, Handler> const& rhs)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class U>
|
||||||
|
friend
|
||||||
|
bool
|
||||||
|
operator!=(handler_alloc const& lhs,
|
||||||
|
handler_alloc<U, Handler> const& rhs)
|
||||||
|
{
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
28
include/beast/http.hpp
Normal file
28
include/beast/http.hpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_HPP_INCLUDED
|
||||||
|
#define BEAST_HTTP_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/basic_headers.hpp>
|
||||||
|
#include <beast/http/basic_parser.hpp>
|
||||||
|
#include <beast/http/chunk_encode.hpp>
|
||||||
|
#include <beast/http/empty_body.hpp>
|
||||||
|
#include <beast/http/error.hpp>
|
||||||
|
#include <beast/http/headers.hpp>
|
||||||
|
#include <beast/http/message.hpp>
|
||||||
|
#include <beast/http/parse_error.hpp>
|
||||||
|
#include <beast/http/parser.hpp>
|
||||||
|
#include <beast/http/read.hpp>
|
||||||
|
#include <beast/http/reason.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/write.hpp>
|
||||||
|
|
||||||
|
#endif
|
434
include/beast/http/basic_headers.hpp
Normal file
434
include/beast/http/basic_headers.hpp
Normal file
@ -0,0 +1,434 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_BASIC_HEADERS_HPP
|
||||||
|
#define BEAST_HTTP_BASIC_HEADERS_HPP
|
||||||
|
|
||||||
|
#include <beast/type_check.hpp>
|
||||||
|
#include <beast/detail/ci_char_traits.hpp>
|
||||||
|
#include <beast/detail/empty_base_optimization.hpp>
|
||||||
|
#include <boost/intrusive/list.hpp>
|
||||||
|
#include <boost/intrusive/set.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/utility/string_ref.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
class basic_headers;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class basic_headers_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct value_type
|
||||||
|
{
|
||||||
|
std::string first;
|
||||||
|
std::string second;
|
||||||
|
|
||||||
|
value_type(boost::string_ref const& name_,
|
||||||
|
boost::string_ref const& value_)
|
||||||
|
: first(name_)
|
||||||
|
, second(value_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::string_ref
|
||||||
|
name() const
|
||||||
|
{
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::string_ref
|
||||||
|
value() const
|
||||||
|
{
|
||||||
|
return second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template<class Allocator>
|
||||||
|
friend class beast::http::basic_headers;
|
||||||
|
|
||||||
|
struct element
|
||||||
|
: boost::intrusive::set_base_hook <
|
||||||
|
boost::intrusive::link_mode <
|
||||||
|
boost::intrusive::normal_link>>
|
||||||
|
, boost::intrusive::list_base_hook <
|
||||||
|
boost::intrusive::link_mode <
|
||||||
|
boost::intrusive::normal_link>>
|
||||||
|
{
|
||||||
|
value_type data;
|
||||||
|
|
||||||
|
element(boost::string_ref const& name,
|
||||||
|
boost::string_ref const& value)
|
||||||
|
: data(name, value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct less : private beast::detail::ci_less
|
||||||
|
{
|
||||||
|
template<class String>
|
||||||
|
bool
|
||||||
|
operator()(String const& lhs, element const& rhs) const
|
||||||
|
{
|
||||||
|
return ci_less::operator()(lhs, rhs.data.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class String>
|
||||||
|
bool
|
||||||
|
operator()(element const& lhs, String const& rhs) const
|
||||||
|
{
|
||||||
|
return ci_less::operator()(lhs.data.first, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator()(element const& lhs, element const& rhs) const
|
||||||
|
{
|
||||||
|
return ci_less::operator()(
|
||||||
|
lhs.data.first, rhs.data.first);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using list_t = typename boost::intrusive::make_list<
|
||||||
|
element, boost::intrusive::constant_time_size<false>>::type;
|
||||||
|
|
||||||
|
using set_t = typename boost::intrusive::make_set<
|
||||||
|
element, boost::intrusive::constant_time_size<true>,
|
||||||
|
boost::intrusive::compare<less>>::type;
|
||||||
|
|
||||||
|
// data
|
||||||
|
set_t set_;
|
||||||
|
list_t list_;
|
||||||
|
|
||||||
|
basic_headers_base(set_t&& set, list_t&& list)
|
||||||
|
: set_(std::move(set))
|
||||||
|
, list_(std::move(list))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
class const_iterator;
|
||||||
|
|
||||||
|
using iterator = const_iterator;
|
||||||
|
|
||||||
|
basic_headers_base() = default;
|
||||||
|
|
||||||
|
/// Returns an iterator to the beginning of the field sequence.
|
||||||
|
iterator
|
||||||
|
begin() const;
|
||||||
|
|
||||||
|
/// Returns an iterator to the end of the field sequence.
|
||||||
|
iterator
|
||||||
|
end() const;
|
||||||
|
|
||||||
|
/// Returns an iterator to the beginning of the field sequence.
|
||||||
|
iterator
|
||||||
|
cbegin() const;
|
||||||
|
|
||||||
|
/// Returns an iterator to the end of the field sequence.
|
||||||
|
iterator
|
||||||
|
cend() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class basic_headers_base::const_iterator
|
||||||
|
{
|
||||||
|
using iter_type = list_t::const_iterator;
|
||||||
|
|
||||||
|
iter_type it_;
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
friend class beast::http::basic_headers;
|
||||||
|
|
||||||
|
friend class basic_headers_base;
|
||||||
|
|
||||||
|
const_iterator(iter_type it)
|
||||||
|
: it_(it)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type =
|
||||||
|
typename basic_headers_base::value_type;
|
||||||
|
using pointer = value_type const*;
|
||||||
|
using reference = value_type const&;
|
||||||
|
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 it_ == other.it_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator!=(const_iterator const& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
reference
|
||||||
|
operator*() const
|
||||||
|
{
|
||||||
|
return it_->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer
|
||||||
|
operator->() const
|
||||||
|
{
|
||||||
|
return &**this;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Container to store HTTP headers.
|
||||||
|
|
||||||
|
Meets the requirements of `FieldSequence`.
|
||||||
|
*/
|
||||||
|
template<class Allocator>
|
||||||
|
class basic_headers
|
||||||
|
#if ! GENERATING_DOCS
|
||||||
|
: private beast::detail::empty_base_optimization<
|
||||||
|
typename std::allocator_traits<Allocator>::
|
||||||
|
template rebind_alloc<
|
||||||
|
detail::basic_headers_base::element>>
|
||||||
|
, public detail::basic_headers_base
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
using alloc_type = typename
|
||||||
|
std::allocator_traits<Allocator>::
|
||||||
|
template rebind_alloc<
|
||||||
|
detail::basic_headers_base::element>;
|
||||||
|
|
||||||
|
using alloc_traits =
|
||||||
|
std::allocator_traits<alloc_type>;
|
||||||
|
|
||||||
|
using size_type =
|
||||||
|
typename std::allocator_traits<Allocator>::size_type;
|
||||||
|
|
||||||
|
void
|
||||||
|
delete_all();
|
||||||
|
|
||||||
|
void
|
||||||
|
move_assign(basic_headers&, std::false_type);
|
||||||
|
|
||||||
|
void
|
||||||
|
move_assign(basic_headers&, std::true_type);
|
||||||
|
|
||||||
|
void
|
||||||
|
copy_assign(basic_headers const&, std::false_type);
|
||||||
|
|
||||||
|
void
|
||||||
|
copy_assign(basic_headers const&, std::true_type);
|
||||||
|
|
||||||
|
template<class FieldSequence>
|
||||||
|
void
|
||||||
|
copy_from(FieldSequence const& fs)
|
||||||
|
{
|
||||||
|
for(auto const& e : fs)
|
||||||
|
insert(e.first, e.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// The type of allocator used.
|
||||||
|
using allocator_type = Allocator;
|
||||||
|
|
||||||
|
/// Default constructor.
|
||||||
|
basic_headers() = default;
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
~basic_headers();
|
||||||
|
|
||||||
|
/** Construct the headers.
|
||||||
|
|
||||||
|
@param alloc The allocator to use.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
basic_headers(Allocator const& alloc);
|
||||||
|
|
||||||
|
/** Move constructor.
|
||||||
|
|
||||||
|
The moved-from object becomes an empty field sequence.
|
||||||
|
|
||||||
|
@param other The object to move from.
|
||||||
|
*/
|
||||||
|
basic_headers(basic_headers&& other);
|
||||||
|
|
||||||
|
/** Move assignment.
|
||||||
|
|
||||||
|
The moved-from object becomes an empty field sequence.
|
||||||
|
|
||||||
|
@param other The object to move from.
|
||||||
|
*/
|
||||||
|
basic_headers& operator=(basic_headers&& other);
|
||||||
|
|
||||||
|
/// Copy constructor.
|
||||||
|
basic_headers(basic_headers const&);
|
||||||
|
|
||||||
|
/// Copy assignment.
|
||||||
|
basic_headers& operator=(basic_headers const&);
|
||||||
|
|
||||||
|
/// Copy constructor.
|
||||||
|
template<class OtherAlloc>
|
||||||
|
basic_headers(basic_headers<OtherAlloc> const&);
|
||||||
|
|
||||||
|
/// Copy assignment.
|
||||||
|
template<class OtherAlloc>
|
||||||
|
basic_headers& operator=(basic_headers<OtherAlloc> const&);
|
||||||
|
|
||||||
|
/// Construct from a field sequence.
|
||||||
|
template<class FwdIt>
|
||||||
|
basic_headers(FwdIt first, FwdIt last);
|
||||||
|
|
||||||
|
/// Returns `true` if the field sequence contains no elements.
|
||||||
|
bool
|
||||||
|
empty() const
|
||||||
|
{
|
||||||
|
return set_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of elements in the field sequence.
|
||||||
|
std::size_t
|
||||||
|
size() const
|
||||||
|
{
|
||||||
|
return set_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if the specified field exists. */
|
||||||
|
bool
|
||||||
|
exists(boost::string_ref const& name) const
|
||||||
|
{
|
||||||
|
return set_.find(name, less{}) != set_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns an iterator to the case-insensitive matching header. */
|
||||||
|
iterator
|
||||||
|
find(boost::string_ref const& name) const;
|
||||||
|
|
||||||
|
/** Returns the value for a case-insensitive matching header, or "" */
|
||||||
|
boost::string_ref
|
||||||
|
operator[](boost::string_ref const& name) const;
|
||||||
|
|
||||||
|
/** Clear the contents of the basic_headers. */
|
||||||
|
void
|
||||||
|
clear() noexcept;
|
||||||
|
|
||||||
|
/** Remove a field.
|
||||||
|
|
||||||
|
@return The number of fields removed.
|
||||||
|
*/
|
||||||
|
std::size_t
|
||||||
|
erase(boost::string_ref const& name);
|
||||||
|
|
||||||
|
/** Insert a field value.
|
||||||
|
|
||||||
|
If a field value already exists the new value will be
|
||||||
|
extended as per RFC2616 Section 4.2.
|
||||||
|
*/
|
||||||
|
// VFALCO TODO Consider allowing rvalue references for std::move?
|
||||||
|
void
|
||||||
|
insert(boost::string_ref const& name,
|
||||||
|
boost::string_ref const& value);
|
||||||
|
|
||||||
|
/** Insert a field value.
|
||||||
|
|
||||||
|
If a field value already exists the new value will be
|
||||||
|
extended as per RFC2616 Section 4.2.
|
||||||
|
*/
|
||||||
|
template<class T,
|
||||||
|
class = typename std::enable_if<
|
||||||
|
! std::is_constructible<boost::string_ref, T>::value>::type>
|
||||||
|
void
|
||||||
|
insert(boost::string_ref name, T const& value)
|
||||||
|
{
|
||||||
|
insert(name,
|
||||||
|
boost::lexical_cast<std::string>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Replace a field value.
|
||||||
|
|
||||||
|
The current field value, if any, is removed. Then the
|
||||||
|
specified value is inserted as if by insert(field, value).
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
replace(boost::string_ref const& name,
|
||||||
|
boost::string_ref const& value);
|
||||||
|
|
||||||
|
/** Replace a field value.
|
||||||
|
|
||||||
|
The current field value, if any, is removed. Then the
|
||||||
|
specified value is inserted as if by insert(field, value).
|
||||||
|
*/
|
||||||
|
template<class T,
|
||||||
|
class = typename std::enable_if<
|
||||||
|
! std::is_constructible<boost::string_ref, T>::value>::type>
|
||||||
|
void
|
||||||
|
replace(boost::string_ref const& name, T const& value)
|
||||||
|
{
|
||||||
|
replace(name,
|
||||||
|
boost::lexical_cast<std::string>(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/basic_headers.ipp>
|
||||||
|
|
||||||
|
#endif
|
761
include/beast/http/basic_parser.hpp
Normal file
761
include/beast/http/basic_parser.hpp
Normal file
@ -0,0 +1,761 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_BASIC_PARSER_HPP
|
||||||
|
#define BEAST_HTTP_BASIC_PARSER_HPP
|
||||||
|
|
||||||
|
#include <beast/http/message.hpp>
|
||||||
|
#include <beast/http/parse_error.hpp>
|
||||||
|
#include <beast/http/rfc7230.hpp>
|
||||||
|
#include <beast/http/detail/basic_parser.hpp>
|
||||||
|
#include <beast/type_check.hpp>
|
||||||
|
#include <beast/detail/ci_char_traits.hpp>
|
||||||
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace parse_flag {
|
||||||
|
enum values
|
||||||
|
{
|
||||||
|
chunked = 1 << 0,
|
||||||
|
connection_keep_alive = 1 << 1,
|
||||||
|
connection_close = 1 << 2,
|
||||||
|
connection_upgrade = 1 << 3,
|
||||||
|
trailing = 1 << 4,
|
||||||
|
upgrade = 1 << 5,
|
||||||
|
skipbody = 1 << 6,
|
||||||
|
contentlength = 1 << 7
|
||||||
|
};
|
||||||
|
} // parse_flag
|
||||||
|
|
||||||
|
/** Parser for producing HTTP requests and responses.
|
||||||
|
|
||||||
|
During parsing, callbacks will be made to the derived class
|
||||||
|
if those members are present (detected through SFINAE). The
|
||||||
|
signatures which can be present in the derived class are:<br>
|
||||||
|
|
||||||
|
@li `void on_method(boost::string_ref const&, error_code& ec)`
|
||||||
|
|
||||||
|
Called for each piece of the Request-Method
|
||||||
|
|
||||||
|
@li `void on_uri(boost::string_ref const&, error_code& ec)`
|
||||||
|
|
||||||
|
Called for each piece of the Request-URI
|
||||||
|
|
||||||
|
@li `void on_reason(boost::string_ref const&, error_code& ec)`
|
||||||
|
|
||||||
|
Called for each piece of the reason-phrase
|
||||||
|
|
||||||
|
@li `void on_request(error_code& ec)`
|
||||||
|
|
||||||
|
Called after the entire Request-Line has been parsed successfully.
|
||||||
|
|
||||||
|
@li `void on_response(error_code& ec)`
|
||||||
|
|
||||||
|
Called after the entire Response-Line has been parsed successfully.
|
||||||
|
|
||||||
|
@li `void on_field(boost::string_ref const&, error_code& ec)`
|
||||||
|
|
||||||
|
Called for each piece of the current header field.
|
||||||
|
|
||||||
|
@li `void on_value(boost::string_ref const&, error_code& ec)`
|
||||||
|
|
||||||
|
Called for each piece of the current header value.
|
||||||
|
|
||||||
|
@li `int on_headers(error_code& ec)`
|
||||||
|
|
||||||
|
Called when all the headers have been parsed successfully.
|
||||||
|
|
||||||
|
@li `void on_body(boost::string_ref const&, error_code& ec)`
|
||||||
|
|
||||||
|
Called for each piece of the body. If the headers indicated
|
||||||
|
chunked encoding, the chunk encoding is removed from the
|
||||||
|
buffer before being passed to the callback.
|
||||||
|
|
||||||
|
@li `void on_complete(error_code& ec)`
|
||||||
|
|
||||||
|
Called when the entire message has been parsed successfully.
|
||||||
|
At this point, basic_parser::complete() returns `true`, and
|
||||||
|
the parser is ready to parse another message if keep_alive()
|
||||||
|
would return `true`.
|
||||||
|
|
||||||
|
The return value of `on_headers` is special, it controls whether
|
||||||
|
or not the parser should expect a body. These are the return values:
|
||||||
|
|
||||||
|
@li *0* The parser should expect a body
|
||||||
|
|
||||||
|
@li *1* The parser should skip the body. For example, this is
|
||||||
|
used when sending a response to a HEAD request.
|
||||||
|
|
||||||
|
@li *2* The parser should skip ths body, this is an
|
||||||
|
upgrade to a different protocol.
|
||||||
|
|
||||||
|
The parser uses traits to determine if the callback is possible.
|
||||||
|
If the Derived type omits one or more callbacks, they are simply
|
||||||
|
skipped with no compilation error. The default behavior of on_body
|
||||||
|
when the derived class does not provide the member, is to specify that
|
||||||
|
the body should not be skipped.
|
||||||
|
|
||||||
|
If a callback sets an error, parsing stops at the current octet
|
||||||
|
and the error is returned to the caller.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Derived>
|
||||||
|
class basic_parser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using self = basic_parser;
|
||||||
|
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
||||||
|
|
||||||
|
static std::uint64_t constexpr no_content_length =
|
||||||
|
std::numeric_limits<std::uint64_t>::max();
|
||||||
|
|
||||||
|
enum state : std::uint8_t
|
||||||
|
{
|
||||||
|
s_closed = 1,
|
||||||
|
|
||||||
|
s_req_start,
|
||||||
|
s_req_method_start,
|
||||||
|
s_req_method,
|
||||||
|
s_req_space_before_url,
|
||||||
|
s_req_url_start,
|
||||||
|
s_req_url,
|
||||||
|
s_req_http_start,
|
||||||
|
s_req_http_H,
|
||||||
|
s_req_http_HT,
|
||||||
|
s_req_http_HTT,
|
||||||
|
s_req_http_HTTP,
|
||||||
|
s_req_major_start,
|
||||||
|
s_req_major,
|
||||||
|
s_req_minor_start,
|
||||||
|
s_req_minor,
|
||||||
|
s_req_line_end,
|
||||||
|
|
||||||
|
s_res_start,
|
||||||
|
s_res_H,
|
||||||
|
s_res_HT,
|
||||||
|
s_res_HTT,
|
||||||
|
s_res_HTTP,
|
||||||
|
s_res_major_start,
|
||||||
|
s_res_major,
|
||||||
|
s_res_minor_start,
|
||||||
|
s_res_minor,
|
||||||
|
s_res_status_code_start,
|
||||||
|
s_res_status_code,
|
||||||
|
s_res_status_start,
|
||||||
|
s_res_status,
|
||||||
|
s_res_line_almost_done,
|
||||||
|
s_res_line_done,
|
||||||
|
|
||||||
|
s_header_field_start,
|
||||||
|
s_header_field,
|
||||||
|
s_header_value_start,
|
||||||
|
s_header_value_discard_lWs0,
|
||||||
|
s_header_value_discard_ws0,
|
||||||
|
s_header_value_almost_done0,
|
||||||
|
s_header_value_text_start,
|
||||||
|
s_header_value_discard_lWs,
|
||||||
|
s_header_value_discard_ws,
|
||||||
|
s_header_value_text,
|
||||||
|
s_header_value_almost_done,
|
||||||
|
|
||||||
|
s_headers_almost_done,
|
||||||
|
s_headers_done,
|
||||||
|
|
||||||
|
s_chunk_size_start,
|
||||||
|
s_chunk_size,
|
||||||
|
s_chunk_parameters,
|
||||||
|
s_chunk_size_almost_done,
|
||||||
|
|
||||||
|
// states below do not count towards
|
||||||
|
// the limit on the size of the message
|
||||||
|
|
||||||
|
s_body_identity0,
|
||||||
|
s_body_identity,
|
||||||
|
s_body_identity_eof0,
|
||||||
|
s_body_identity_eof,
|
||||||
|
|
||||||
|
s_chunk_data_start,
|
||||||
|
s_chunk_data,
|
||||||
|
s_chunk_data_almost_done,
|
||||||
|
s_chunk_data_done,
|
||||||
|
|
||||||
|
s_complete,
|
||||||
|
s_restart
|
||||||
|
};
|
||||||
|
|
||||||
|
enum field_state : std::uint8_t
|
||||||
|
{
|
||||||
|
h_general = 0,
|
||||||
|
h_C,
|
||||||
|
h_CO,
|
||||||
|
h_CON,
|
||||||
|
|
||||||
|
h_matching_connection,
|
||||||
|
h_matching_proxy_connection,
|
||||||
|
h_matching_content_length,
|
||||||
|
h_matching_transfer_encoding,
|
||||||
|
h_matching_upgrade,
|
||||||
|
|
||||||
|
h_connection,
|
||||||
|
h_content_length,
|
||||||
|
h_transfer_encoding,
|
||||||
|
h_upgrade,
|
||||||
|
|
||||||
|
h_matching_transfer_encoding_chunked,
|
||||||
|
h_matching_connection_token_start,
|
||||||
|
h_matching_connection_keep_alive,
|
||||||
|
h_matching_connection_close,
|
||||||
|
h_matching_connection_upgrade,
|
||||||
|
h_matching_connection_token,
|
||||||
|
|
||||||
|
h_transfer_encoding_chunked,
|
||||||
|
h_connection_keep_alive,
|
||||||
|
h_connection_close,
|
||||||
|
h_connection_upgrade,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::uint64_t content_length_;
|
||||||
|
std::uint64_t nread_;
|
||||||
|
pmf_t cb_;
|
||||||
|
state s_ : 8;
|
||||||
|
unsigned flags_ : 8;
|
||||||
|
unsigned fs_ : 8;
|
||||||
|
unsigned pos_ : 8; // position in field state
|
||||||
|
unsigned http_major_ : 16;
|
||||||
|
unsigned http_minor_ : 16;
|
||||||
|
unsigned status_code_ : 16;
|
||||||
|
bool upgrade_ : 1; // true if parser exited for upgrade
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Copy constructor.
|
||||||
|
basic_parser(basic_parser const&) = default;
|
||||||
|
|
||||||
|
/// Copy assignment.
|
||||||
|
basic_parser& operator=(basic_parser const&) = default;
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
basic_parser()
|
||||||
|
{
|
||||||
|
init(std::integral_constant<bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns internal flags associated with the parser.
|
||||||
|
unsigned
|
||||||
|
flags() const
|
||||||
|
{
|
||||||
|
return flags_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if the message end is indicated by eof.
|
||||||
|
|
||||||
|
This function returns true if the semantics of the message require
|
||||||
|
that the end of the message is signaled by an end of file. For
|
||||||
|
example, if the message is a HTTP/1.0 message and the Content-Length
|
||||||
|
is unspecified, the end of the message is indicated by an end of file.
|
||||||
|
|
||||||
|
@return `true` if write_eof must be used to indicate the message end.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
needs_eof() const
|
||||||
|
{
|
||||||
|
return needs_eof(
|
||||||
|
std::integral_constant<bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the major HTTP version number.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
* Returns 1 for HTTP/1.1
|
||||||
|
* Returns 1 for HTTP/1.0
|
||||||
|
|
||||||
|
@return The HTTP major version number.
|
||||||
|
*/
|
||||||
|
unsigned
|
||||||
|
http_major() const
|
||||||
|
{
|
||||||
|
return http_major_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the minor HTTP version number.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
* Returns 1 for HTTP/1.1
|
||||||
|
* Returns 0 for HTTP/1.0
|
||||||
|
|
||||||
|
@return The HTTP minor version number.
|
||||||
|
*/
|
||||||
|
unsigned
|
||||||
|
http_minor() const
|
||||||
|
{
|
||||||
|
return http_minor_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if the message is an upgrade message.
|
||||||
|
|
||||||
|
A value of `true` indicates that the parser has successfully
|
||||||
|
completed parsing a HTTP upgrade message.
|
||||||
|
|
||||||
|
@return `true` if the message is an upgrade message.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
upgrade() const
|
||||||
|
{
|
||||||
|
return upgrade_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the numeric HTTP Status-Code of a response.
|
||||||
|
|
||||||
|
@return The Status-Code.
|
||||||
|
*/
|
||||||
|
unsigned
|
||||||
|
status_code() const
|
||||||
|
{
|
||||||
|
return status_code_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if the connection should be kept open.
|
||||||
|
|
||||||
|
@note This function is only valid to call when the parser
|
||||||
|
is complete.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
keep_alive() const;
|
||||||
|
|
||||||
|
/** Returns `true` if the parse has completed succesfully.
|
||||||
|
|
||||||
|
When the parse has completed successfully, and the semantics
|
||||||
|
of the parsed message indicate that the connection is still
|
||||||
|
active, a subsequent call to `write` will begin parsing a
|
||||||
|
new message.
|
||||||
|
|
||||||
|
@return `true` If the parsing has completed successfully.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
complete() const
|
||||||
|
{
|
||||||
|
return s_ == s_restart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write data to the parser.
|
||||||
|
|
||||||
|
@param buffers An object meeting the requirements of
|
||||||
|
ConstBufferSequence that represents the input sequence.
|
||||||
|
|
||||||
|
@param ec Set to the error, if any error occurred.
|
||||||
|
|
||||||
|
@return The number of bytes consumed in the input sequence.
|
||||||
|
*/
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
write(ConstBufferSequence const& buffers, error_code& ec);
|
||||||
|
|
||||||
|
/** Write data to the parser.
|
||||||
|
|
||||||
|
@param data A pointer to a buffer representing the input sequence.
|
||||||
|
@param size The number of bytes in the buffer pointed to by data.
|
||||||
|
@param ec Set to the error, if any error occurred.
|
||||||
|
|
||||||
|
@return The number of bytes consumed in the input sequence.
|
||||||
|
*/
|
||||||
|
std::size_t
|
||||||
|
write(void const* data, std::size_t size, error_code& ec);
|
||||||
|
|
||||||
|
/** Called to indicate the end of file.
|
||||||
|
|
||||||
|
HTTP needs to know where the end of the stream is. For example,
|
||||||
|
sometimes servers send responses without Content-Length and
|
||||||
|
expect the client to consume input (for the body) until EOF.
|
||||||
|
Callbacks and errors will still be processed as usual.
|
||||||
|
|
||||||
|
@note This is typically called when a socket read returns eof.
|
||||||
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
write_eof(error_code& ec);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Derived&
|
||||||
|
impl()
|
||||||
|
{
|
||||||
|
return *static_cast<Derived*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(std::true_type)
|
||||||
|
{
|
||||||
|
s_ = s_req_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(std::false_type)
|
||||||
|
{
|
||||||
|
s_ = s_res_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
needs_eof(std::true_type) const;
|
||||||
|
|
||||||
|
bool
|
||||||
|
needs_eof(std::false_type) const;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_method_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_method(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
std::declval<error_code&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_method =
|
||||||
|
std::integral_constant<bool, has_on_method_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_uri_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_uri(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
std::declval<error_code&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_uri =
|
||||||
|
std::integral_constant<bool, has_on_uri_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_reason_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_reason(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
std::declval<error_code&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_reason =
|
||||||
|
std::integral_constant<bool, has_on_reason_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_request_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_request(
|
||||||
|
std::declval<error_code&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_request =
|
||||||
|
std::integral_constant<bool, has_on_request_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_response_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_response(
|
||||||
|
std::declval<error_code&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_response =
|
||||||
|
std::integral_constant<bool, has_on_response_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_field_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_uri(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
std::declval<error_code&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_field =
|
||||||
|
std::integral_constant<bool, has_on_field_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_value_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_uri(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
std::declval<error_code&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_value =
|
||||||
|
std::integral_constant<bool, has_on_value_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_headers_t
|
||||||
|
{
|
||||||
|
template<class T, class R = std::is_same<int,
|
||||||
|
decltype(std::declval<T>().on_headers(
|
||||||
|
std::declval<error_code&>()))>>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_headers =
|
||||||
|
std::integral_constant<bool, has_on_headers_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_body_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_body(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
std::declval<error_code&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_body =
|
||||||
|
std::integral_constant<bool, has_on_body_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_complete_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_complete(
|
||||||
|
std::declval<error_code&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_complete =
|
||||||
|
std::integral_constant<bool, has_on_complete_t<C>::value>;
|
||||||
|
|
||||||
|
void call_on_method(error_code& ec,
|
||||||
|
boost::string_ref const& s, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_method(s, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_method(error_code&,
|
||||||
|
boost::string_ref const&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_method(error_code& ec,
|
||||||
|
boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
call_on_method(ec, s, std::integral_constant<bool,
|
||||||
|
isRequest && has_on_method<Derived>::value>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_uri(error_code& ec,
|
||||||
|
boost::string_ref const& s, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_uri(s, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_uri(error_code&,
|
||||||
|
boost::string_ref const&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_uri(error_code& ec, boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
call_on_uri(ec, s, std::integral_constant<bool,
|
||||||
|
isRequest && has_on_uri<Derived>::value>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_reason(error_code& ec,
|
||||||
|
boost::string_ref const& s, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_reason(s, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_reason(error_code&,
|
||||||
|
boost::string_ref const&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_reason(error_code& ec, boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
call_on_reason(ec, s, std::integral_constant<bool,
|
||||||
|
! isRequest && has_on_reason<Derived>::value>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_request(error_code& ec, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_request(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_request(error_code&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_request(error_code& ec)
|
||||||
|
{
|
||||||
|
call_on_request(ec, std::integral_constant<bool,
|
||||||
|
isRequest && has_on_request<Derived>::value>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_response(error_code& ec, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_response(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_response(error_code&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_response(error_code& ec)
|
||||||
|
{
|
||||||
|
call_on_response(ec, std::integral_constant<bool,
|
||||||
|
! isRequest && has_on_response<Derived>::value>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_field(error_code& ec,
|
||||||
|
boost::string_ref const& s, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_field(s, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_field(error_code&,
|
||||||
|
boost::string_ref const&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_field(error_code& ec, boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
call_on_field(ec, s, has_on_field<Derived>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_value(error_code& ec,
|
||||||
|
boost::string_ref const& s, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_value(s, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_value(error_code&,
|
||||||
|
boost::string_ref const&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_value(error_code& ec, boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
call_on_value(ec, s, has_on_value<Derived>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
int call_on_headers(error_code& ec, std::true_type)
|
||||||
|
{
|
||||||
|
return impl().on_headers(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
int call_on_headers(error_code& ec, std::false_type)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int call_on_headers(error_code& ec)
|
||||||
|
{
|
||||||
|
return call_on_headers(ec, has_on_headers<Derived>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_body(error_code& ec,
|
||||||
|
boost::string_ref const& s, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_body(s, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_body(error_code&,
|
||||||
|
boost::string_ref const&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_body(error_code& ec, boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
call_on_body(ec, s, has_on_body<Derived>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_complete(error_code& ec, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_complete(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_complete(error_code&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_complete(error_code& ec)
|
||||||
|
{
|
||||||
|
call_on_complete(ec, has_on_complete<Derived>{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/basic_parser.ipp>
|
||||||
|
|
||||||
|
#endif
|
282
include/beast/http/chunk_encode.hpp
Normal file
282
include/beast/http/chunk_encode.hpp
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||||
|
#define BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||||
|
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iterator>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
class chunk_encoded_buffers
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using const_buffer = boost::asio::const_buffer;
|
||||||
|
|
||||||
|
Buffers buffers_;
|
||||||
|
const_buffer head_;
|
||||||
|
const_buffer tail_;
|
||||||
|
|
||||||
|
// Storage for the longest hex string we might need, plus delimiters.
|
||||||
|
std::array<char, 2 * sizeof(std::size_t) + 2> data_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = boost::asio::const_buffer;
|
||||||
|
|
||||||
|
class const_iterator;
|
||||||
|
|
||||||
|
chunk_encoded_buffers() = delete;
|
||||||
|
chunk_encoded_buffers (chunk_encoded_buffers const&) = default;
|
||||||
|
chunk_encoded_buffers& operator= (chunk_encoded_buffers const&) = default;
|
||||||
|
|
||||||
|
chunk_encoded_buffers (Buffers const& buffers, bool final_chunk);
|
||||||
|
|
||||||
|
const_iterator
|
||||||
|
begin() const
|
||||||
|
{
|
||||||
|
return const_iterator(*this, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator
|
||||||
|
end() const
|
||||||
|
{
|
||||||
|
return const_iterator(*this, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Unchecked conversion of unsigned to hex string
|
||||||
|
template<class OutIter, class Unsigned>
|
||||||
|
static
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_unsigned<Unsigned>::value, OutIter>::type
|
||||||
|
to_hex(OutIter const first, OutIter const last, Unsigned n);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
class chunk_encoded_buffers<Buffers>::const_iterator
|
||||||
|
: public std::iterator<std::bidirectional_iterator_tag, const_buffer>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using iterator = typename Buffers::const_iterator;
|
||||||
|
enum class Where { head, input, end };
|
||||||
|
chunk_encoded_buffers const* buffers_;
|
||||||
|
Where where_;
|
||||||
|
iterator iter_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const_iterator();
|
||||||
|
const_iterator (const_iterator const&) = default;
|
||||||
|
const_iterator& operator= (const_iterator const&) = default;
|
||||||
|
bool operator== (const_iterator const& other) const;
|
||||||
|
bool operator!= (const_iterator const& other) const;
|
||||||
|
const_iterator& operator++();
|
||||||
|
const_iterator& operator--();
|
||||||
|
const_iterator operator++(int) const;
|
||||||
|
const_iterator operator--(int) const;
|
||||||
|
const_buffer operator*() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class chunk_encoded_buffers;
|
||||||
|
const_iterator(chunk_encoded_buffers const& buffers, bool past_the_end);
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
chunk_encoded_buffers<Buffers>::chunk_encoded_buffers (
|
||||||
|
Buffers const& buffers, bool final_chunk)
|
||||||
|
: buffers_(buffers)
|
||||||
|
{
|
||||||
|
auto const size = boost::asio::buffer_size(buffers);
|
||||||
|
data_[data_.size() - 2] = '\r';
|
||||||
|
data_[data_.size() - 1] = '\n';
|
||||||
|
auto pos = to_hex(data_.begin(), data_.end() - 2, size);
|
||||||
|
head_ = const_buffer(&*pos,
|
||||||
|
std::distance(pos, data_.end()));
|
||||||
|
if (size > 0 && final_chunk)
|
||||||
|
tail_ = const_buffer("\r\n0\r\n\r\n", 7);
|
||||||
|
else
|
||||||
|
tail_ = const_buffer("\r\n", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
template <class OutIter, class Unsigned>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_unsigned<Unsigned>::value, OutIter>::type
|
||||||
|
chunk_encoded_buffers<Buffers>::to_hex(
|
||||||
|
OutIter const first, OutIter const last, Unsigned n)
|
||||||
|
{
|
||||||
|
assert(first != last);
|
||||||
|
OutIter iter = last;
|
||||||
|
if(n == 0)
|
||||||
|
{
|
||||||
|
*--iter = '0';
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
while(n)
|
||||||
|
{
|
||||||
|
assert(iter != first);
|
||||||
|
*--iter = "0123456789abcdef"[n&0xf];
|
||||||
|
n>>=4;
|
||||||
|
}
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
chunk_encoded_buffers<Buffers>::const_iterator::const_iterator()
|
||||||
|
: buffers_(nullptr)
|
||||||
|
, where_(Where::end)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
bool
|
||||||
|
chunk_encoded_buffers<Buffers>::const_iterator::operator==(
|
||||||
|
const_iterator const& other) const
|
||||||
|
{
|
||||||
|
return buffers_ == other.buffers_ &&
|
||||||
|
where_ == other.where_ && iter_ == other.iter_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
bool
|
||||||
|
chunk_encoded_buffers<Buffers>::const_iterator::operator!=(
|
||||||
|
const_iterator const& other) const
|
||||||
|
{
|
||||||
|
return buffers_ != other.buffers_ ||
|
||||||
|
where_ != other.where_ || iter_ != other.iter_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
auto
|
||||||
|
chunk_encoded_buffers<Buffers>::const_iterator::operator++() ->
|
||||||
|
const_iterator&
|
||||||
|
{
|
||||||
|
assert(buffers_);
|
||||||
|
assert(where_ != Where::end);
|
||||||
|
if (where_ == Where::head)
|
||||||
|
where_ = Where::input;
|
||||||
|
else if (iter_ != buffers_->buffers_.end())
|
||||||
|
++iter_;
|
||||||
|
else
|
||||||
|
where_ = Where::end;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
auto
|
||||||
|
chunk_encoded_buffers<Buffers>::const_iterator::operator--() ->
|
||||||
|
const_iterator&
|
||||||
|
{
|
||||||
|
assert(buffers_);
|
||||||
|
assert(where_ != Where::head);
|
||||||
|
if (where_ == Where::end)
|
||||||
|
where_ = Where::input;
|
||||||
|
else if (iter_ != buffers_->buffers_.begin())
|
||||||
|
--iter_;
|
||||||
|
else
|
||||||
|
where_ = Where::head;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
auto
|
||||||
|
chunk_encoded_buffers<Buffers>::const_iterator::operator++(int) const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
auto iter = *this;
|
||||||
|
++iter;
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
auto
|
||||||
|
chunk_encoded_buffers<Buffers>::const_iterator::operator--(int) const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
auto iter = *this;
|
||||||
|
--iter;
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
auto
|
||||||
|
chunk_encoded_buffers<Buffers>::const_iterator::operator*() const ->
|
||||||
|
const_buffer
|
||||||
|
{
|
||||||
|
assert(buffers_);
|
||||||
|
assert(where_ != Where::end);
|
||||||
|
if (where_ == Where::head)
|
||||||
|
return buffers_->head_;
|
||||||
|
if (iter_ != buffers_->buffers_.end())
|
||||||
|
return *iter_;
|
||||||
|
return buffers_->tail_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Buffers>
|
||||||
|
chunk_encoded_buffers<Buffers>::const_iterator::const_iterator(
|
||||||
|
chunk_encoded_buffers const& buffers, bool past_the_end)
|
||||||
|
: buffers_(&buffers)
|
||||||
|
, where_(past_the_end ? Where::end : Where::head)
|
||||||
|
, iter_(past_the_end ? buffers_->buffers_.end() :
|
||||||
|
buffers_->buffers_.begin())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
/** Returns a chunk-encoded BufferSequence.
|
||||||
|
|
||||||
|
See:
|
||||||
|
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
|
||||||
|
|
||||||
|
@param buffers The input buffer sequence.
|
||||||
|
|
||||||
|
@param final_chunk `true` If this should include a final-chunk.
|
||||||
|
|
||||||
|
@return A chunk-encoded ConstBufferSequence representing the input.
|
||||||
|
*/
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
implementation_defined
|
||||||
|
#else
|
||||||
|
detail::chunk_encoded_buffers<ConstBufferSequence>
|
||||||
|
#endif
|
||||||
|
chunk_encode(ConstBufferSequence const& buffers,
|
||||||
|
bool final_chunk = false)
|
||||||
|
{
|
||||||
|
return detail::chunk_encoded_buffers<
|
||||||
|
ConstBufferSequence>{buffers, final_chunk};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a chunked encoding final chunk.
|
||||||
|
inline
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
implementation_defined
|
||||||
|
#else
|
||||||
|
boost::asio::const_buffers_1
|
||||||
|
#endif
|
||||||
|
chunk_encode_final()
|
||||||
|
{
|
||||||
|
return boost::asio::const_buffers_1(
|
||||||
|
"0\r\n\r\n", 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
203
include/beast/http/detail/basic_parser.hpp
Normal file
203
include/beast/http/detail/basic_parser.hpp
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
|
||||||
|
#define BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
|
||||||
|
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
#include <boost/utility/string_ref.hpp>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// '0'...'9'
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_digit(char c)
|
||||||
|
{
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_token(char c)
|
||||||
|
{
|
||||||
|
/* token = 1*<any CHAR except CTLs or separators>
|
||||||
|
CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||||
|
sep = "(" | ")" | "<" | ">" | "@"
|
||||||
|
| "," | ";" | ":" | "\" | <">
|
||||||
|
| "/" | "[" | "]" | "?" | "="
|
||||||
|
| "{" | "}" | SP | HT
|
||||||
|
*/
|
||||||
|
static std::array<char, 256> constexpr tab = {{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||||
|
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48
|
||||||
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
|
||||||
|
}};
|
||||||
|
return tab[static_cast<std::uint8_t>(c)] != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_text(char c)
|
||||||
|
{
|
||||||
|
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||||
|
static std::array<char, 256> constexpr tab = {{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||||
|
}};
|
||||||
|
return tab[static_cast<std::uint8_t>(c)] != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// converts to lower case,
|
||||||
|
// returns 0 if not a valid token char
|
||||||
|
//
|
||||||
|
inline
|
||||||
|
char
|
||||||
|
to_field_char(char c)
|
||||||
|
{
|
||||||
|
/* token = 1*<any CHAR except CTLs or separators>
|
||||||
|
CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||||
|
sep = "(" | ")" | "<" | ">" | "@"
|
||||||
|
| "," | ";" | ":" | "\" | <">
|
||||||
|
| "/" | "[" | "]" | "?" | "="
|
||||||
|
| "{" | "}" | SP | HT
|
||||||
|
*/
|
||||||
|
static std::array<char, 256> constexpr tab = {{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, '!', 0, '#', '$', '%', '&', '\'', 0, 0, '*', '+', 0, '-', '.', 0,
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||||
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '^', '_',
|
||||||
|
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||||
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0
|
||||||
|
}};
|
||||||
|
return tab[static_cast<std::uint8_t>(c)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// converts to lower case,
|
||||||
|
// returns 0 if not a valid text char
|
||||||
|
//
|
||||||
|
inline
|
||||||
|
char
|
||||||
|
to_value_char(char c)
|
||||||
|
{
|
||||||
|
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||||
|
static std::array<std::uint8_t, 256> constexpr tab = {{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32
|
||||||
|
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48
|
||||||
|
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64
|
||||||
|
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80
|
||||||
|
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96
|
||||||
|
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112
|
||||||
|
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128
|
||||||
|
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144
|
||||||
|
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160
|
||||||
|
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176
|
||||||
|
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192
|
||||||
|
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208
|
||||||
|
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
|
||||||
|
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
|
||||||
|
}};
|
||||||
|
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::uint8_t
|
||||||
|
unhex(char c)
|
||||||
|
{
|
||||||
|
static std::array<std::int8_t, 256> constexpr tab = {{
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48
|
||||||
|
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
|
||||||
|
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 112
|
||||||
|
}};
|
||||||
|
return tab[static_cast<std::uint8_t>(c)];
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
struct parser_str_t
|
||||||
|
{
|
||||||
|
static char constexpr close[6] = "close";
|
||||||
|
static char constexpr chunked[8] = "chunked";
|
||||||
|
static char constexpr keep_alive[11] = "keep-alive";
|
||||||
|
|
||||||
|
static char constexpr upgrade[8] = "upgrade";
|
||||||
|
static char constexpr connection[11] = "connection";
|
||||||
|
static char constexpr content_length[15] = "content-length";
|
||||||
|
static char constexpr proxy_connection[17] = "proxy-connection";
|
||||||
|
static char constexpr transfer_encoding[18] = "transfer-encoding";
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
char constexpr
|
||||||
|
parser_str_t<_>::close[6];
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
char constexpr
|
||||||
|
parser_str_t<_>::chunked[8];
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
char constexpr
|
||||||
|
parser_str_t<_>::keep_alive[11];
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
char constexpr
|
||||||
|
parser_str_t<_>::upgrade[8];
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
char constexpr
|
||||||
|
parser_str_t<_>::connection[11];
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
char constexpr
|
||||||
|
parser_str_t<_>::content_length[15];
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
char constexpr
|
||||||
|
parser_str_t<_>::proxy_connection[17];
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
char constexpr
|
||||||
|
parser_str_t<_>::transfer_encoding[18];
|
||||||
|
|
||||||
|
using parser_str = parser_str_t<>;
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
123
include/beast/http/detail/write_preparation.hpp
Normal file
123
include/beast/http/detail/write_preparation.hpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_DETAIL_WRITE_PREPARATION_HPP
|
||||||
|
#define BEAST_HTTP_DETAIL_WRITE_PREPARATION_HPP
|
||||||
|
|
||||||
|
#include <beast/http/error.hpp>
|
||||||
|
#include <beast/streambuf.hpp>
|
||||||
|
#include <beast/write_streambuf.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class has_content_length_value
|
||||||
|
{
|
||||||
|
template<class U, class R = typename std::is_convertible<
|
||||||
|
decltype(std::declval<U>().content_length()),
|
||||||
|
std::size_t>>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<T>(0));
|
||||||
|
public:
|
||||||
|
// `true` if `T` meets the requirements.
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determines if the writer can provide the content length
|
||||||
|
template<class T>
|
||||||
|
using has_content_length =
|
||||||
|
std::integral_constant<bool,
|
||||||
|
has_content_length_value<T>::value>;
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
struct write_preparation
|
||||||
|
{
|
||||||
|
using headers_type =
|
||||||
|
basic_headers<std::allocator<char>>;
|
||||||
|
|
||||||
|
message<isRequest, Body, Headers> const& msg;
|
||||||
|
typename Body::writer w;
|
||||||
|
streambuf sb;
|
||||||
|
bool chunked;
|
||||||
|
bool close;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
write_preparation(
|
||||||
|
message<isRequest, Body, Headers> const& msg_)
|
||||||
|
: msg(msg_)
|
||||||
|
, w(msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(error_code& ec)
|
||||||
|
{
|
||||||
|
w.init(ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
// VFALCO TODO This implementation requires making a
|
||||||
|
// copy of the headers, we can do better.
|
||||||
|
// VFALCO Should we be using handler_alloc?
|
||||||
|
headers_type h(msg.headers.begin(), msg.headers.end());
|
||||||
|
set_content_length(h, has_content_length<
|
||||||
|
typename Body::writer>{});
|
||||||
|
|
||||||
|
// VFALCO TODO Keep-Alive
|
||||||
|
|
||||||
|
if(close)
|
||||||
|
{
|
||||||
|
if(msg.version >= 11)
|
||||||
|
h.insert("Connection", "close");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(msg.version < 11)
|
||||||
|
h.insert("Connection", "keep-alive");
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.write_firstline(sb);
|
||||||
|
write_fields(sb, h);
|
||||||
|
beast::write(sb, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
set_content_length(headers_type& h,
|
||||||
|
std::true_type)
|
||||||
|
{
|
||||||
|
close = false;
|
||||||
|
chunked = false;
|
||||||
|
h.insert("Content-Length", w.content_length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_content_length(headers_type& h,
|
||||||
|
std::false_type)
|
||||||
|
{
|
||||||
|
if(msg.version >= 11)
|
||||||
|
{
|
||||||
|
close = false;
|
||||||
|
chunked = true;
|
||||||
|
h.insert("Transfer-Encoding", "chunked");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close = true;
|
||||||
|
chunked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
82
include/beast/http/empty_body.hpp
Normal file
82
include/beast/http/empty_body.hpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_EMPTY_BODY_HPP
|
||||||
|
#define BEAST_HTTP_EMPTY_BODY_HPP
|
||||||
|
|
||||||
|
#include <beast/http/error.hpp>
|
||||||
|
#include <beast/http/message.hpp>
|
||||||
|
#include <beast/streambuf.hpp>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** An empty content-body.
|
||||||
|
*/
|
||||||
|
struct empty_body
|
||||||
|
{
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
/// The type of the `message::body` member
|
||||||
|
using value_type = void;
|
||||||
|
#else
|
||||||
|
struct value_type {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
private:
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct reader
|
||||||
|
{
|
||||||
|
template<bool isRequest, class Allocator>
|
||||||
|
explicit
|
||||||
|
reader(message<isRequest, empty_body, Allocator>&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
write(void const*, std::size_t, error_code&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct writer
|
||||||
|
{
|
||||||
|
template<bool isRequest, class Allocator>
|
||||||
|
explicit
|
||||||
|
writer(message<isRequest, empty_body, Allocator> const& m)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(error_code& ec)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
content_length() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Write>
|
||||||
|
boost::tribool
|
||||||
|
operator()(resume_context&&, error_code&, Write&& write)
|
||||||
|
{
|
||||||
|
write(boost::asio::null_buffers{});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
21
include/beast/http/error.hpp
Normal file
21
include/beast/http/error.hpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_ERROR_HPP
|
||||||
|
#define BEAST_HTTP_ERROR_HPP
|
||||||
|
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
using error_code = boost::system::error_code;
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
26
include/beast/http/headers.hpp
Normal file
26
include/beast/http/headers.hpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_HEADERS_HPP
|
||||||
|
#define BEAST_HTTP_HEADERS_HPP
|
||||||
|
|
||||||
|
#include <beast/http/basic_headers.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
using headers = basic_headers<Allocator>;
|
||||||
|
|
||||||
|
using http_headers =
|
||||||
|
basic_headers<std::allocator<char>>;
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
299
include/beast/http/impl/basic_headers.ipp
Normal file
299
include/beast/http/impl/basic_headers.ipp
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
|
||||||
|
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
|
||||||
|
|
||||||
|
#include <beast/type_check.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
basic_headers_base::begin() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return list_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
basic_headers_base::end() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return list_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
basic_headers_base::cbegin() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return list_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
basic_headers_base::cend() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return list_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
delete_all()
|
||||||
|
{
|
||||||
|
for(auto it = list_.begin(); it != list_.end();)
|
||||||
|
{
|
||||||
|
auto& e = *it++;
|
||||||
|
e.~element();
|
||||||
|
alloc_traits::deallocate(
|
||||||
|
this->member(), &e, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
move_assign(basic_headers& other, std::false_type)
|
||||||
|
{
|
||||||
|
if(this->member() != other.member())
|
||||||
|
{
|
||||||
|
copy_from(other);
|
||||||
|
other.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set_ = std::move(other.set_);
|
||||||
|
list_ = std::move(other.list_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
move_assign(basic_headers& other, std::true_type)
|
||||||
|
{
|
||||||
|
this->member() = std::move(other.member());
|
||||||
|
set_ = std::move(other.set_);
|
||||||
|
list_ = std::move(other.list_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
copy_assign(basic_headers const& other, std::false_type)
|
||||||
|
{
|
||||||
|
copy_from(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
copy_assign(basic_headers const& other, std::true_type)
|
||||||
|
{
|
||||||
|
this->member() = other.member();
|
||||||
|
copy_from(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
~basic_headers()
|
||||||
|
{
|
||||||
|
delete_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
basic_headers(Allocator const& alloc)
|
||||||
|
: beast::detail::empty_base_optimization<
|
||||||
|
alloc_type>(alloc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
basic_headers(basic_headers&& other)
|
||||||
|
: beast::detail::empty_base_optimization<alloc_type>(
|
||||||
|
std::move(other.member()))
|
||||||
|
, detail::basic_headers_base(
|
||||||
|
std::move(other.set_), std::move(other.list_))
|
||||||
|
{
|
||||||
|
other.list_.clear();
|
||||||
|
other.set_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
auto
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
operator=(basic_headers&& other) ->
|
||||||
|
basic_headers&
|
||||||
|
{
|
||||||
|
if(this == &other)
|
||||||
|
return *this;
|
||||||
|
clear();
|
||||||
|
move_assign(other, std::integral_constant<bool,
|
||||||
|
alloc_traits::propagate_on_container_move_assignment::value>{});
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
basic_headers(basic_headers const& other)
|
||||||
|
: basic_headers(alloc_traits::
|
||||||
|
select_on_container_copy_construction(other.member()))
|
||||||
|
{
|
||||||
|
copy_from(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
auto
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
operator=(basic_headers const& other) ->
|
||||||
|
basic_headers&
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
copy_assign(other, std::integral_constant<bool,
|
||||||
|
alloc_traits::propagate_on_container_copy_assignment::value>{});
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
template<class OtherAlloc>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
basic_headers(basic_headers<OtherAlloc> const& other)
|
||||||
|
{
|
||||||
|
copy_from(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
template<class OtherAlloc>
|
||||||
|
auto
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
operator=(basic_headers<OtherAlloc> const& other) ->
|
||||||
|
basic_headers&
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
copy_from(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
template<class FwdIt>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
basic_headers(FwdIt first, FwdIt last)
|
||||||
|
{
|
||||||
|
for(;first != last; ++first)
|
||||||
|
insert(first->name(), first->value());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
auto
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
find(boost::string_ref const& name) const ->
|
||||||
|
iterator
|
||||||
|
{
|
||||||
|
auto const it = set_.find(name, less{});
|
||||||
|
if(it == set_.end())
|
||||||
|
return list_.end();
|
||||||
|
return list_.iterator_to(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
boost::string_ref
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
operator[](boost::string_ref const& name) const
|
||||||
|
{
|
||||||
|
// VFALCO This none object looks sketchy
|
||||||
|
static boost::string_ref const none;
|
||||||
|
auto const it = find(name);
|
||||||
|
if(it == end())
|
||||||
|
return none;
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
clear() noexcept
|
||||||
|
{
|
||||||
|
delete_all();
|
||||||
|
list_.clear();
|
||||||
|
set_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
std::size_t
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
erase(boost::string_ref const& name)
|
||||||
|
{
|
||||||
|
auto const it = set_.find(name, less{});
|
||||||
|
if(it == set_.end())
|
||||||
|
return 0;
|
||||||
|
auto& e = *it;
|
||||||
|
set_.erase(set_.iterator_to(e));
|
||||||
|
list_.erase(list_.iterator_to(e));
|
||||||
|
alloc_traits::deallocate(this->member(), &e, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
insert(boost::string_ref const& name,
|
||||||
|
boost::string_ref const& value)
|
||||||
|
{
|
||||||
|
typename set_t::insert_commit_data d;
|
||||||
|
auto const result =
|
||||||
|
set_.insert_check(name, less{}, d);
|
||||||
|
if (result.second)
|
||||||
|
{
|
||||||
|
auto const p = alloc_traits::allocate(
|
||||||
|
this->member(), 1);
|
||||||
|
alloc_traits::construct(
|
||||||
|
this->member(), p, name, value);
|
||||||
|
list_.push_back(*p);
|
||||||
|
set_.insert_commit(*p, d);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If field already exists, insert comma
|
||||||
|
// separated value as per RFC2616 section 4.2
|
||||||
|
auto& cur = result.first->data.second;
|
||||||
|
cur.reserve(cur.size() + 1 + value.size());
|
||||||
|
cur.append(1, ',');
|
||||||
|
cur.append(value.data(), value.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
replace(boost::string_ref const& name,
|
||||||
|
boost::string_ref const& value)
|
||||||
|
{
|
||||||
|
erase(name);
|
||||||
|
insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
1074
include/beast/http/impl/basic_parser.ipp
Normal file
1074
include/beast/http/impl/basic_parser.ipp
Normal file
File diff suppressed because it is too large
Load Diff
318
include/beast/http/impl/message.ipp
Normal file
318
include/beast/http/impl/message.ipp
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||||
|
#define BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||||
|
|
||||||
|
#include <beast/http/chunk_encode.hpp>
|
||||||
|
#include <beast/http/resume_context.hpp>
|
||||||
|
#include <beast/http/rfc2616.hpp>
|
||||||
|
#include <beast/write_streambuf.hpp>
|
||||||
|
#include <beast/type_check.hpp>
|
||||||
|
#include <beast/http/detail/write_preparation.hpp>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/logic/tribool.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
message<isRequest, Body, Headers>::
|
||||||
|
message()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
message<isRequest, Body, Headers>::
|
||||||
|
message(request_params params)
|
||||||
|
{
|
||||||
|
static_assert(isRequest, "message is not a request");
|
||||||
|
this->method = params.method;
|
||||||
|
this->url = std::move(params.url);
|
||||||
|
version = params.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
message<isRequest, Body, Headers>::
|
||||||
|
message(response_params params)
|
||||||
|
{
|
||||||
|
static_assert(! isRequest, "message is not a response");
|
||||||
|
this->status = params.status;
|
||||||
|
this->reason = std::move(params.reason);
|
||||||
|
version = params.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
message<isRequest, Body, Headers>::
|
||||||
|
write_firstline(Streambuf& streambuf,
|
||||||
|
std::true_type) const
|
||||||
|
{
|
||||||
|
write(streambuf, to_string(this->method));
|
||||||
|
write(streambuf, " ");
|
||||||
|
write(streambuf, this->url);
|
||||||
|
switch(version)
|
||||||
|
{
|
||||||
|
case 10:
|
||||||
|
write(streambuf, " HTTP/1.0\r\n");
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
write(streambuf, " HTTP/1.1\r\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
write(streambuf, " HTTP/");
|
||||||
|
write(streambuf, version / 10);
|
||||||
|
write(streambuf, ".");
|
||||||
|
write(streambuf, version % 10);
|
||||||
|
write(streambuf, "\r\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
message<isRequest, Body, Headers>::
|
||||||
|
write_firstline(Streambuf& streambuf,
|
||||||
|
std::false_type) const
|
||||||
|
{
|
||||||
|
switch(version)
|
||||||
|
{
|
||||||
|
case 10:
|
||||||
|
write(streambuf, "HTTP/1.0 ");
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
write(streambuf, "HTTP/1.1 ");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
write(streambuf, " HTTP/");
|
||||||
|
write(streambuf, version / 10);
|
||||||
|
write(streambuf, ".");
|
||||||
|
write(streambuf, version % 10);
|
||||||
|
write(streambuf, " ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write(streambuf, this->status);
|
||||||
|
write(streambuf, " ");
|
||||||
|
write(streambuf, this->reason);
|
||||||
|
write(streambuf, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::string
|
||||||
|
buffers_to_string(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
std::string s;
|
||||||
|
s.reserve(buffer_size(buffers));
|
||||||
|
for(auto const& b : buffers)
|
||||||
|
s.append(buffer_cast<char const*>(b),
|
||||||
|
buffer_size(b));
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
class writef_ostream
|
||||||
|
{
|
||||||
|
std::ostream& os_;
|
||||||
|
bool chunked_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
writef_ostream(std::ostream& os, bool chunked)
|
||||||
|
: os_(os)
|
||||||
|
, chunked_(chunked)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
operator()(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
if(chunked_)
|
||||||
|
os_ << buffers_to_string(
|
||||||
|
chunk_encode(buffers));
|
||||||
|
else
|
||||||
|
os_ << buffers_to_string(
|
||||||
|
buffers);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
// Diagnostic output only
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os,
|
||||||
|
message<isRequest, Body, Headers> const& msg)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
detail::write_preparation<isRequest, Body, Headers> wp(msg);
|
||||||
|
wp.init(ec);
|
||||||
|
if(ec)
|
||||||
|
return os;
|
||||||
|
std::mutex m;
|
||||||
|
std::condition_variable cv;
|
||||||
|
bool ready = false;
|
||||||
|
resume_context resume{
|
||||||
|
[&]
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
ready = true;
|
||||||
|
cv.notify_one();
|
||||||
|
}};
|
||||||
|
auto copy = resume;
|
||||||
|
os << detail::buffers_to_string(wp.sb.data());
|
||||||
|
wp.sb.consume(wp.sb.size());
|
||||||
|
detail::writef_ostream writef(os, wp.chunked);
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto result = wp.w(std::move(copy), ec, writef);
|
||||||
|
if(ec)
|
||||||
|
return os;
|
||||||
|
if(result)
|
||||||
|
break;
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
copy = resume;
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.wait(lock, [&]{ return ready; });
|
||||||
|
ready = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wp.sb.consume(wp.sb.size());
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
auto result = wp.w(std::move(copy), ec, writef);
|
||||||
|
if(ec)
|
||||||
|
return os;
|
||||||
|
if(result)
|
||||||
|
break;
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
copy = resume;
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.wait(lock, [&]{ return ready; });
|
||||||
|
ready = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(wp.chunked)
|
||||||
|
{
|
||||||
|
// VFALCO Unfortunately the current interface to the
|
||||||
|
// Writer concept prevents us from using coalescing the
|
||||||
|
// final body chunk with the final chunk delimiter.
|
||||||
|
//
|
||||||
|
// write final chunk
|
||||||
|
os << detail::buffers_to_string(chunk_encode_final());
|
||||||
|
if(ec)
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
os << std::endl;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
set_connection(bool keep_alive,
|
||||||
|
message<isRequest, Body, Headers>& req)
|
||||||
|
{
|
||||||
|
if(req.version >= 11)
|
||||||
|
{
|
||||||
|
if(! keep_alive)
|
||||||
|
req.headers.replace("Connection", "close");
|
||||||
|
else
|
||||||
|
req.headers.erase("Connection");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(keep_alive)
|
||||||
|
req.headers.replace("Connection", "keep-alive");
|
||||||
|
else
|
||||||
|
req.headers.erase("Connection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Body, class Headers,
|
||||||
|
class OtherBody, class OtherAllocator>
|
||||||
|
void
|
||||||
|
set_connection(bool keep_alive,
|
||||||
|
message<false, Body, Headers>& resp,
|
||||||
|
message<true, OtherBody, OtherAllocator> const& req)
|
||||||
|
{
|
||||||
|
if(req.version >= 11)
|
||||||
|
{
|
||||||
|
if(rfc2616::token_in_list(req["Connection"], "close"))
|
||||||
|
keep_alive = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(! rfc2616::token_in_list(req["Connection"], "keep-alive"))
|
||||||
|
keep_alive = false;
|
||||||
|
}
|
||||||
|
set_connection(keep_alive, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf, class FieldSequence>
|
||||||
|
void
|
||||||
|
write_fields(Streambuf& streambuf, FieldSequence const& fields)
|
||||||
|
{
|
||||||
|
static_assert(is_Streambuf<Streambuf>::value,
|
||||||
|
"Streambuf requirements not met");
|
||||||
|
//static_assert(is_FieldSequence<FieldSequence>::value,
|
||||||
|
// "FieldSequence requirements not met");
|
||||||
|
for(auto const& field : fields)
|
||||||
|
{
|
||||||
|
write(streambuf, field.name());
|
||||||
|
write(streambuf, ": ");
|
||||||
|
write(streambuf, field.value());
|
||||||
|
write(streambuf, "\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
bool
|
||||||
|
is_keep_alive(message<isRequest, Body, Headers> const& msg)
|
||||||
|
{
|
||||||
|
if(msg.version >= 11)
|
||||||
|
{
|
||||||
|
if(rfc2616::token_in_list(
|
||||||
|
msg.headers["Connection"], "close"))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(rfc2616::token_in_list(
|
||||||
|
msg.headers["Connection"], "keep-alive"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
bool
|
||||||
|
is_upgrade(message<isRequest, Body, Headers> const& msg)
|
||||||
|
{
|
||||||
|
if(msg.version < 11)
|
||||||
|
return false;
|
||||||
|
if(rfc2616::token_in_list(
|
||||||
|
msg.headers["Connection"], "upgrade"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/message.ipp>
|
||||||
|
|
||||||
|
#endif
|
286
include/beast/http/impl/read.ipp
Normal file
286
include/beast/http/impl/read.ipp
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||||
|
#define BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||||
|
|
||||||
|
#include <beast/bind_handler.hpp>
|
||||||
|
#include <beast/handler_alloc.hpp>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class Stream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers,
|
||||||
|
class Handler>
|
||||||
|
class read_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
using parser_type =
|
||||||
|
parser<isRequest, Body, Headers>;
|
||||||
|
|
||||||
|
using message_type =
|
||||||
|
message<isRequest, Body, Headers>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
Stream& s;
|
||||||
|
Streambuf& sb;
|
||||||
|
message_type& m;
|
||||||
|
parser_type p;
|
||||||
|
Handler h;
|
||||||
|
bool started = false;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, Stream& s_,
|
||||||
|
Streambuf& sb_, message_type& m_)
|
||||||
|
: s(s_)
|
||||||
|
, sb(sb_)
|
||||||
|
, m(m_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
read_op(read_op&&) = default;
|
||||||
|
read_op(read_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
read_op(DeducedHandler&& h, Stream&s, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), s,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(error_code ec,
|
||||||
|
std::size_t bytes_transferred, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
void* asio_handler_allocate(
|
||||||
|
std::size_t size, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
bool asio_handler_is_continuation(read_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
void asio_handler_invoke(Function&& f, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Stream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers,
|
||||||
|
class Handler>
|
||||||
|
void
|
||||||
|
read_op<Stream, Streambuf, isRequest, Body, Headers, Handler>::
|
||||||
|
operator()(error_code ec, std::size_t bytes_transferred, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
auto const used =
|
||||||
|
d.p.write(d.sb.data(), ec);
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.s.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this), ec, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(used > 0)
|
||||||
|
d.started = true;
|
||||||
|
d.sb.consume(used);
|
||||||
|
if(d.p.complete())
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.m = d.p.release();
|
||||||
|
d.s.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this), ec, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// read
|
||||||
|
d.state = 2;
|
||||||
|
d.s.async_read_some(d.sb.prepare(
|
||||||
|
read_size_helper(d.sb, 65536)),
|
||||||
|
std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// got data
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
if(ec == boost::asio::error::eof)
|
||||||
|
{
|
||||||
|
if(! d.started)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Caller will see eof on next read.
|
||||||
|
ec = {};
|
||||||
|
d.p.write_eof(ec);
|
||||||
|
if(! ec)
|
||||||
|
{
|
||||||
|
assert(d.p.complete());
|
||||||
|
d.m = d.p.release();
|
||||||
|
}
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.sb.commit(bytes_transferred);
|
||||||
|
d.sb.consume(d.p.write(d.sb.data(), ec));
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(d.p.complete())
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.m = d.p.release();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class SyncReadStream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
read(SyncReadStream& stream, Streambuf& streambuf,
|
||||||
|
message<isRequest, Body, Headers>& m,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||||
|
"SyncReadStream requirements not met");
|
||||||
|
static_assert(is_Streambuf<Streambuf>::value,
|
||||||
|
"Streambuf requirements not met");
|
||||||
|
parser<isRequest, Body, Headers> p;
|
||||||
|
bool started = false;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
auto used =
|
||||||
|
p.write(streambuf.data(), ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
streambuf.consume(used);
|
||||||
|
if(used > 0)
|
||||||
|
started = true;
|
||||||
|
if(p.complete())
|
||||||
|
{
|
||||||
|
m = p.release();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
streambuf.commit(stream.read_some(
|
||||||
|
streambuf.prepare(read_size_helper(
|
||||||
|
streambuf, 65536)), ec));
|
||||||
|
if(ec && ec != boost::asio::error::eof)
|
||||||
|
return;
|
||||||
|
if(ec == boost::asio::error::eof)
|
||||||
|
{
|
||||||
|
if(! started)
|
||||||
|
return;
|
||||||
|
// Caller will see eof on next read.
|
||||||
|
ec = {};
|
||||||
|
p.write_eof(ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
assert(p.complete());
|
||||||
|
m = p.release();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class AsyncReadStream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers,
|
||||||
|
class ReadHandler>
|
||||||
|
typename async_completion<
|
||||||
|
ReadHandler, void(error_code)>::result_type
|
||||||
|
async_read(AsyncReadStream& stream, Streambuf& streambuf,
|
||||||
|
message<isRequest, Body, Headers>& m,
|
||||||
|
ReadHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||||
|
"AsyncReadStream requirements not met");
|
||||||
|
static_assert(is_Streambuf<Streambuf>::value,
|
||||||
|
"Streambuf requirements not met");
|
||||||
|
beast::async_completion<ReadHandler,
|
||||||
|
void(error_code)> completion(handler);
|
||||||
|
detail::read_op<AsyncReadStream, Streambuf,
|
||||||
|
isRequest, Body, Headers, decltype(
|
||||||
|
completion.handler)>{completion.handler,
|
||||||
|
stream, streambuf, m};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
456
include/beast/http/impl/write.ipp
Normal file
456
include/beast/http/impl/write.ipp
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_IMPL_WRITE_IPP
|
||||||
|
#define BEAST_HTTP_IMPL_WRITE_IPP
|
||||||
|
|
||||||
|
#include <beast/http/chunk_encode.hpp>
|
||||||
|
#include <beast/http/resume_context.hpp>
|
||||||
|
#include <beast/http/detail/write_preparation.hpp>
|
||||||
|
#include <beast/buffer_cat.hpp>
|
||||||
|
#include <beast/bind_handler.hpp>
|
||||||
|
#include <beast/handler_alloc.hpp>
|
||||||
|
#include <beast/streambuf.hpp>
|
||||||
|
#include <beast/type_check.hpp>
|
||||||
|
#include <boost/asio/write.hpp>
|
||||||
|
#include <boost/logic/tribool.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
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;
|
||||||
|
// VFALCO How do we use handler_alloc in write_preparation?
|
||||||
|
write_preparation<
|
||||||
|
isRequest, Body, Headers> wp;
|
||||||
|
Handler h;
|
||||||
|
resume_context resume;
|
||||||
|
resume_context copy;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, Stream& s_,
|
||||||
|
message<isRequest, Body, Headers> const& m_)
|
||||||
|
: s(s_)
|
||||||
|
, wp(m_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class writef0
|
||||||
|
{
|
||||||
|
write_op& self_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
writef0(write_op& self)
|
||||||
|
: self_(self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void operator()(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
auto& d = *self_.d_;
|
||||||
|
// write headers and body
|
||||||
|
if(d.wp.chunked)
|
||||||
|
boost::asio::async_write(d.s,
|
||||||
|
buffer_cat(d.wp.sb.data(),
|
||||||
|
chunk_encode(buffers)),
|
||||||
|
std::move(self_));
|
||||||
|
else
|
||||||
|
boost::asio::async_write(d.s,
|
||||||
|
buffer_cat(d.wp.sb.data(),
|
||||||
|
buffers), std::move(self_));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class writef
|
||||||
|
{
|
||||||
|
write_op& self_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
writef(write_op& self)
|
||||||
|
: self_(self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void operator()(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
auto& d = *self_.d_;
|
||||||
|
// write body
|
||||||
|
if(d.wp.chunked)
|
||||||
|
boost::asio::async_write(d.s,
|
||||||
|
chunk_encode(buffers),
|
||||||
|
std::move(self_));
|
||||||
|
else
|
||||||
|
boost::asio::async_write(d.s,
|
||||||
|
buffers, std::move(self_));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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)...))
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
auto sp = d_;
|
||||||
|
d.resume = {
|
||||||
|
[sp]() mutable
|
||||||
|
{
|
||||||
|
write_op self(std::move(sp));
|
||||||
|
self.d_->cont = false;
|
||||||
|
auto& ios = self.d_->s.get_io_service();
|
||||||
|
ios.dispatch(bind_handler(std::move(self),
|
||||||
|
error_code{}, 0, false));
|
||||||
|
}};
|
||||||
|
d.copy = d.resume;
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit
|
||||||
|
write_op(std::shared_ptr<data> d)
|
||||||
|
: d_(std::move(d))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(error_code ec,
|
||||||
|
std::size_t bytes_transferred, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
void* asio_handler_allocate(
|
||||||
|
std::size_t size, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
bool asio_handler_is_continuation(write_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
void asio_handler_invoke(Function&& f, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Stream, class Handler,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
write_op<Stream, Handler, isRequest, Body, Headers>::
|
||||||
|
operator()(error_code ec, std::size_t, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
d.wp.init(ec);
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.s.get_io_service().post(bind_handler(
|
||||||
|
std::move(*this), ec, 0, false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
auto const result = d.wp.w(
|
||||||
|
std::move(d.copy), ec, writef0{*this});
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.s.get_io_service().post(bind_handler(
|
||||||
|
std::move(*this), ec, false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
// suspend
|
||||||
|
d.copy = d.resume;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(result)
|
||||||
|
d.state = d.wp.chunked ? 4 : 5;
|
||||||
|
else
|
||||||
|
d.state = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sent headers and body
|
||||||
|
case 2:
|
||||||
|
d.wp.sb.consume(d.wp.sb.size());
|
||||||
|
d.state = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
auto const result = d.wp.w(
|
||||||
|
std::move(d.copy), ec, writef{*this});
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
// suspend
|
||||||
|
d.copy = d.resume;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(result)
|
||||||
|
d.state = d.wp.chunked ? 4 : 5;
|
||||||
|
else
|
||||||
|
d.state = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
// VFALCO Unfortunately the current interface to the
|
||||||
|
// Writer concept prevents us from coalescing the
|
||||||
|
// final body chunk with the final chunk delimiter.
|
||||||
|
//
|
||||||
|
// write final chunk
|
||||||
|
d.state = 5;
|
||||||
|
boost::asio::async_write(d.s,
|
||||||
|
chunk_encode_final(), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
if(d.wp.close)
|
||||||
|
{
|
||||||
|
// VFALCO TODO Decide on an error code
|
||||||
|
ec = boost::asio::error::eof;
|
||||||
|
}
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
d.resume = {};
|
||||||
|
d.copy = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class SyncWriteStream, class Streambuf>
|
||||||
|
class writef0_write
|
||||||
|
{
|
||||||
|
Streambuf const& sb_;
|
||||||
|
SyncWriteStream& stream_;
|
||||||
|
bool chunked_;
|
||||||
|
error_code& ec_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
writef0_write(SyncWriteStream& stream,
|
||||||
|
Streambuf const& sb, bool chunked, error_code& ec)
|
||||||
|
: sb_(sb)
|
||||||
|
, stream_(stream)
|
||||||
|
, chunked_(chunked)
|
||||||
|
, ec_(ec)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void operator()(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
// write headers and body
|
||||||
|
if(chunked_)
|
||||||
|
boost::asio::write(stream_, buffer_cat(
|
||||||
|
sb_.data(), chunk_encode(buffers)), ec_);
|
||||||
|
else
|
||||||
|
boost::asio::write(stream_, buffer_cat(
|
||||||
|
sb_.data(), buffers), ec_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class SyncWriteStream>
|
||||||
|
class writef_write
|
||||||
|
{
|
||||||
|
SyncWriteStream& stream_;
|
||||||
|
bool chunked_;
|
||||||
|
error_code& ec_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
writef_write(SyncWriteStream& stream,
|
||||||
|
bool chunked, error_code& ec)
|
||||||
|
: stream_(stream)
|
||||||
|
, chunked_(chunked)
|
||||||
|
, ec_(ec)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void operator()(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
// write body
|
||||||
|
if(chunked_)
|
||||||
|
boost::asio::write(stream_,
|
||||||
|
chunk_encode(buffers), ec_);
|
||||||
|
else
|
||||||
|
boost::asio::write(stream_, buffers, ec_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class SyncWriteStream,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
write(SyncWriteStream& stream,
|
||||||
|
message<isRequest, Body, Headers> const& msg,
|
||||||
|
boost::system::error_code& ec)
|
||||||
|
{
|
||||||
|
detail::write_preparation<isRequest, Body, Headers> wp(msg);
|
||||||
|
wp.init(ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
std::mutex m;
|
||||||
|
std::condition_variable cv;
|
||||||
|
bool ready = false;
|
||||||
|
resume_context resume{
|
||||||
|
[&]
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
ready = true;
|
||||||
|
cv.notify_one();
|
||||||
|
}};
|
||||||
|
auto copy = resume;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto result = wp.w(std::move(copy), ec,
|
||||||
|
detail::writef0_write<SyncWriteStream, decltype(wp.sb)>{
|
||||||
|
stream, wp.sb, wp.chunked, ec});
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
if(result)
|
||||||
|
break;
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
boost::asio::write(stream, wp.sb.data(), ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
wp.sb.consume(wp.sb.size());
|
||||||
|
copy = resume;
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.wait(lock, [&]{ return ready; });
|
||||||
|
ready = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wp.sb.consume(wp.sb.size());
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
auto result = wp.w(std::move(copy), ec,
|
||||||
|
detail::writef_write<SyncWriteStream>{
|
||||||
|
stream, wp.chunked, ec});
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
if(result)
|
||||||
|
break;
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
copy = resume;
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.wait(lock, [&]{ return ready; });
|
||||||
|
ready = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(wp.chunked)
|
||||||
|
{
|
||||||
|
// VFALCO Unfortunately the current interface to the
|
||||||
|
// Writer concept prevents us from using coalescing the
|
||||||
|
// final body chunk with the final chunk delimiter.
|
||||||
|
//
|
||||||
|
// write final chunk
|
||||||
|
boost::asio::write(stream, chunk_encode_final(), ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(wp.close)
|
||||||
|
{
|
||||||
|
// VFALCO TODO Decide on an error code
|
||||||
|
ec = boost::asio::error::eof;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class AsyncWriteStream,
|
||||||
|
bool isRequest, class Body, class Headers,
|
||||||
|
class WriteHandler>
|
||||||
|
typename async_completion<
|
||||||
|
WriteHandler, void(error_code)>::result_type
|
||||||
|
async_write(AsyncWriteStream& stream,
|
||||||
|
message<isRequest, Body, Headers> const& msg,
|
||||||
|
WriteHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(
|
||||||
|
is_AsyncWriteStream<AsyncWriteStream>::value,
|
||||||
|
"AsyncWriteStream requirements not met");
|
||||||
|
beast::async_completion<WriteHandler,
|
||||||
|
void(error_code)> completion(handler);
|
||||||
|
detail::write_op<AsyncWriteStream, decltype(completion.handler),
|
||||||
|
isRequest, Body, Headers>{completion.handler, stream, msg};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
174
include/beast/http/message.hpp
Normal file
174
include/beast/http/message.hpp
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_MESSAGE_HPP
|
||||||
|
#define BEAST_HTTP_MESSAGE_HPP
|
||||||
|
|
||||||
|
#include <beast/http/basic_headers.hpp>
|
||||||
|
#include <beast/http/method.hpp>
|
||||||
|
#include <beast/buffers_debug.hpp>
|
||||||
|
#include <beast/type_check.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
struct request_fields
|
||||||
|
{
|
||||||
|
http::method_t method;
|
||||||
|
std::string url;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct response_fields
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
std::string reason;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
#if ! GENERATING_DOCS
|
||||||
|
|
||||||
|
struct request_params
|
||||||
|
{
|
||||||
|
http::method_t method;
|
||||||
|
std::string url;
|
||||||
|
int version;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct response_params
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
std::string reason;
|
||||||
|
int version;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** A HTTP message.
|
||||||
|
|
||||||
|
A message can be a request or response, depending on the `isRequest`
|
||||||
|
template argument value. Requests and responses have different types,
|
||||||
|
so functions may be overloaded on them if desired.
|
||||||
|
|
||||||
|
The `Body` template argument type determines the model used
|
||||||
|
to read or write the content body of the message.
|
||||||
|
|
||||||
|
@tparam isRequest `true` if this is a request.
|
||||||
|
|
||||||
|
@tparam Body A type meeting the requirements of Body.
|
||||||
|
|
||||||
|
@tparam Headers A type meeting the requirements of Headers.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
struct message
|
||||||
|
: std::conditional<isRequest,
|
||||||
|
detail::request_fields, detail::response_fields>::type
|
||||||
|
{
|
||||||
|
/** The trait type characterizing the body.
|
||||||
|
|
||||||
|
The body member will be of type body_type::value_type.
|
||||||
|
*/
|
||||||
|
using body_type = Body;
|
||||||
|
using headers_type = Headers;
|
||||||
|
|
||||||
|
using is_request =
|
||||||
|
std::integral_constant<bool, isRequest>;
|
||||||
|
|
||||||
|
int version; // 10 or 11
|
||||||
|
headers_type headers;
|
||||||
|
typename Body::value_type body;
|
||||||
|
|
||||||
|
message();
|
||||||
|
message(message&&) = default;
|
||||||
|
message(message const&) = default;
|
||||||
|
message& operator=(message&&) = default;
|
||||||
|
message& operator=(message const&) = default;
|
||||||
|
|
||||||
|
/** Construct a HTTP request.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
message(request_params params);
|
||||||
|
|
||||||
|
/** Construct a HTTP response.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
message(response_params params);
|
||||||
|
|
||||||
|
/// Serialize the request or response line to a Streambuf.
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
write_firstline(Streambuf& streambuf) const
|
||||||
|
{
|
||||||
|
write_firstline(streambuf,
|
||||||
|
std::integral_constant<bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Diagnostics only
|
||||||
|
template<bool, class, class>
|
||||||
|
friend
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os,
|
||||||
|
message const& m);
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
write_firstline(Streambuf& streambuf,
|
||||||
|
std::true_type) const;
|
||||||
|
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
write_firstline(Streambuf& streambuf,
|
||||||
|
std::false_type) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if ! GENERATING_DOCS
|
||||||
|
|
||||||
|
/// A typical HTTP request
|
||||||
|
template<class Body,
|
||||||
|
class Headers = basic_headers<std::allocator<char>>>
|
||||||
|
using request = message<true, Body, Headers>;
|
||||||
|
|
||||||
|
/// A typical HTTP response
|
||||||
|
template<class Body,
|
||||||
|
class Headers = basic_headers<std::allocator<char>>>
|
||||||
|
using response = message<false, Body, Headers>;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// For diagnostic output only
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os,
|
||||||
|
message<isRequest, Body, Headers> const& m);
|
||||||
|
|
||||||
|
/// Write a FieldSequence to a Streambuf.
|
||||||
|
template<class Streambuf, class FieldSequence>
|
||||||
|
void
|
||||||
|
write_fields(Streambuf& streambuf, FieldSequence const& fields);
|
||||||
|
|
||||||
|
/// Returns `true` if a message indicates a keep alive
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
bool
|
||||||
|
is_keep_alive(message<isRequest, Body, Headers> const& msg);
|
||||||
|
|
||||||
|
/// Returns `true` if a message indicates a HTTP Upgrade request or response
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
bool
|
||||||
|
is_upgrade(message<isRequest, Body, Headers> const& msg);
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/message.ipp>
|
||||||
|
|
||||||
|
#endif
|
179
include/beast/http/method.hpp
Normal file
179
include/beast/http/method.hpp
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_METHOD_HPP
|
||||||
|
#define BEAST_HTTP_METHOD_HPP
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
enum class method_t
|
||||||
|
{
|
||||||
|
http_delete,
|
||||||
|
http_get,
|
||||||
|
http_head,
|
||||||
|
http_post,
|
||||||
|
http_put,
|
||||||
|
|
||||||
|
// pathological
|
||||||
|
http_connect,
|
||||||
|
http_options,
|
||||||
|
http_trace,
|
||||||
|
|
||||||
|
// webdav
|
||||||
|
http_copy,
|
||||||
|
http_lock,
|
||||||
|
http_mkcol,
|
||||||
|
http_move,
|
||||||
|
http_propfind,
|
||||||
|
http_proppatch,
|
||||||
|
http_search,
|
||||||
|
http_unlock,
|
||||||
|
http_bind,
|
||||||
|
http_rebind,
|
||||||
|
http_unbind,
|
||||||
|
http_acl,
|
||||||
|
|
||||||
|
// subversion
|
||||||
|
http_report,
|
||||||
|
http_mkactivity,
|
||||||
|
http_checkout,
|
||||||
|
http_merge,
|
||||||
|
|
||||||
|
// upnp
|
||||||
|
http_msearch,
|
||||||
|
http_notify,
|
||||||
|
http_subscribe,
|
||||||
|
http_unsubscribe,
|
||||||
|
|
||||||
|
// RFC-5789
|
||||||
|
http_patch,
|
||||||
|
http_purge,
|
||||||
|
|
||||||
|
// CalDav
|
||||||
|
http_mkcalendar,
|
||||||
|
|
||||||
|
// RFC-2068, section 19.6.1.2
|
||||||
|
http_link,
|
||||||
|
http_unlink
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
std::string
|
||||||
|
to_string(method_t m)
|
||||||
|
{
|
||||||
|
switch(m)
|
||||||
|
{
|
||||||
|
case method_t::http_delete: return "DELETE";
|
||||||
|
case method_t::http_get: return "GET";
|
||||||
|
case method_t::http_head: return "HEAD";
|
||||||
|
case method_t::http_post: return "POST";
|
||||||
|
case method_t::http_put: return "PUT";
|
||||||
|
|
||||||
|
case method_t::http_connect: return "CONNECT";
|
||||||
|
case method_t::http_options: return "OPTIONS";
|
||||||
|
case method_t::http_trace: return "TRACE";
|
||||||
|
|
||||||
|
case method_t::http_copy: return "COPY";
|
||||||
|
case method_t::http_lock: return "LOCK";
|
||||||
|
case method_t::http_mkcol: return "MKCOL";
|
||||||
|
case method_t::http_move: return "MOVE";
|
||||||
|
case method_t::http_propfind: return "PROPFIND";
|
||||||
|
case method_t::http_proppatch: return "PROPPATCH";
|
||||||
|
case method_t::http_search: return "SEARCH";
|
||||||
|
case method_t::http_unlock: return "UNLOCK";
|
||||||
|
|
||||||
|
case method_t::http_report: return "REPORT";
|
||||||
|
case method_t::http_mkactivity: return "MKACTIVITY";
|
||||||
|
case method_t::http_checkout: return "CHECKOUT";
|
||||||
|
case method_t::http_merge: return "MERGE";
|
||||||
|
|
||||||
|
case method_t::http_msearch: return "MSEARCH";
|
||||||
|
case method_t::http_notify: return "NOTIFY";
|
||||||
|
case method_t::http_subscribe: return "SUBSCRIBE";
|
||||||
|
case method_t::http_unsubscribe: return "UNSUBSCRIBE";
|
||||||
|
|
||||||
|
case method_t::http_patch: return "PATCH";
|
||||||
|
case method_t::http_purge: return "PURGE";
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
return "GET";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Stream>
|
||||||
|
Stream&
|
||||||
|
operator<< (Stream& s, method_t m)
|
||||||
|
{
|
||||||
|
return s << to_string(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the string corresponding to the numeric HTTP status code. */
|
||||||
|
template<class = void>
|
||||||
|
std::string
|
||||||
|
status_text (int status)
|
||||||
|
{
|
||||||
|
switch(status)
|
||||||
|
{
|
||||||
|
case 100: return "Continue";
|
||||||
|
case 101: return "Switching Protocols";
|
||||||
|
case 200: return "OK";
|
||||||
|
case 201: return "Created";
|
||||||
|
case 202: return "Accepted";
|
||||||
|
case 203: return "Non-Authoritative Information";
|
||||||
|
case 204: return "No Content";
|
||||||
|
case 205: return "Reset Content";
|
||||||
|
case 206: return "Partial Content";
|
||||||
|
case 300: return "Multiple Choices";
|
||||||
|
case 301: return "Moved Permanently";
|
||||||
|
case 302: return "Found";
|
||||||
|
case 303: return "See Other";
|
||||||
|
case 304: return "Not Modified";
|
||||||
|
case 305: return "Use Proxy";
|
||||||
|
//case 306: return "<reserved>";
|
||||||
|
case 307: return "Temporary Redirect";
|
||||||
|
case 400: return "Bad Request";
|
||||||
|
case 401: return "Unauthorized";
|
||||||
|
case 402: return "Payment Required";
|
||||||
|
case 403: return "Forbidden";
|
||||||
|
case 404: return "Not Found";
|
||||||
|
case 405: return "Method Not Allowed";
|
||||||
|
case 406: return "Not Acceptable";
|
||||||
|
case 407: return "Proxy Authentication Required";
|
||||||
|
case 408: return "Request Timeout";
|
||||||
|
case 409: return "Conflict";
|
||||||
|
case 410: return "Gone";
|
||||||
|
case 411: return "Length Required";
|
||||||
|
case 412: return "Precondition Failed";
|
||||||
|
case 413: return "Request Entity Too Large";
|
||||||
|
case 414: return "Request-URI Too Long";
|
||||||
|
case 415: return "Unsupported Media Type";
|
||||||
|
case 416: return "Requested Range Not Satisfiable";
|
||||||
|
case 417: return "Expectation Failed";
|
||||||
|
case 500: return "Internal Server Error";
|
||||||
|
case 501: return "Not Implemented";
|
||||||
|
case 502: return "Bad Gateway";
|
||||||
|
case 503: return "Service Unavailable";
|
||||||
|
case 504: return "Gateway Timeout";
|
||||||
|
case 505: return "HTTP Version Not Supported";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "Unknown HTTP status";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
157
include/beast/http/parse_error.hpp
Normal file
157
include/beast/http/parse_error.hpp
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_PARSE_ERROR_HPP
|
||||||
|
#define BEAST_HTTP_PARSE_ERROR_HPP
|
||||||
|
|
||||||
|
#include <beast/http/error.hpp>
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
enum class parse_error
|
||||||
|
{
|
||||||
|
connection_closed,
|
||||||
|
|
||||||
|
bad_method,
|
||||||
|
bad_uri,
|
||||||
|
bad_version,
|
||||||
|
bad_crlf,
|
||||||
|
bad_request,
|
||||||
|
|
||||||
|
bad_status_code,
|
||||||
|
bad_status,
|
||||||
|
|
||||||
|
bad_field,
|
||||||
|
bad_value,
|
||||||
|
bad_content_length,
|
||||||
|
illegal_content_length,
|
||||||
|
bad_on_headers_rv,
|
||||||
|
|
||||||
|
invalid_chunk_size,
|
||||||
|
|
||||||
|
short_read
|
||||||
|
};
|
||||||
|
|
||||||
|
class parse_error_category : public boost::system::error_category
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const char*
|
||||||
|
name() const noexcept override
|
||||||
|
{
|
||||||
|
return "http";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
message(int ev) const override
|
||||||
|
{
|
||||||
|
switch(static_cast<parse_error>(ev))
|
||||||
|
{
|
||||||
|
case parse_error::connection_closed:
|
||||||
|
return "data after Connection close";
|
||||||
|
|
||||||
|
case parse_error::bad_method:
|
||||||
|
return "bad method";
|
||||||
|
|
||||||
|
case parse_error::bad_uri:
|
||||||
|
return "bad Request-URI";
|
||||||
|
|
||||||
|
case parse_error::bad_version:
|
||||||
|
return "bad HTTP-Version";
|
||||||
|
|
||||||
|
case parse_error::bad_crlf:
|
||||||
|
return "missing CRLF";
|
||||||
|
|
||||||
|
case parse_error::bad_request:
|
||||||
|
return "bad Request-Line";
|
||||||
|
|
||||||
|
case parse_error::bad_status_code:
|
||||||
|
return "bad Status-Code";
|
||||||
|
|
||||||
|
case parse_error::bad_status:
|
||||||
|
return "bad Status-Line";
|
||||||
|
|
||||||
|
case parse_error::bad_field:
|
||||||
|
return "bad field token";
|
||||||
|
|
||||||
|
case parse_error::bad_value:
|
||||||
|
return "bad field-value";
|
||||||
|
|
||||||
|
case parse_error::bad_content_length:
|
||||||
|
return "bad Content-Length";
|
||||||
|
|
||||||
|
case parse_error::illegal_content_length:
|
||||||
|
return "illegal Content-Length with chunked Transfer-Encoding";
|
||||||
|
|
||||||
|
case parse_error::bad_on_headers_rv:
|
||||||
|
return "on_headers returned an unknown value";
|
||||||
|
|
||||||
|
case parse_error::invalid_chunk_size:
|
||||||
|
return "invalid chunk size";
|
||||||
|
|
||||||
|
case parse_error::short_read:
|
||||||
|
return "unexpected end of data";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "beast::http::parser 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_parse_error_category()
|
||||||
|
{
|
||||||
|
static parse_error_category const cat{};
|
||||||
|
return cat;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
boost::system::error_code
|
||||||
|
make_error_code(parse_error ev)
|
||||||
|
{
|
||||||
|
return error_code(static_cast<int>(ev),
|
||||||
|
get_parse_error_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace system {
|
||||||
|
template<>
|
||||||
|
struct is_error_code_enum<beast::http::parse_error>
|
||||||
|
{
|
||||||
|
static bool const value = true;
|
||||||
|
};
|
||||||
|
} // system
|
||||||
|
} // boost
|
||||||
|
|
||||||
|
#endif
|
234
include/beast/http/parser.hpp
Normal file
234
include/beast/http/parser.hpp
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_PARSER_HPP
|
||||||
|
#define BEAST_HTTP_PARSER_HPP
|
||||||
|
|
||||||
|
#include <beast/http/basic_parser.hpp>
|
||||||
|
#include <beast/http/error.hpp>
|
||||||
|
#include <beast/http/message.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
struct parser_request
|
||||||
|
{
|
||||||
|
std::string method_;
|
||||||
|
std::string uri_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct parser_response
|
||||||
|
{
|
||||||
|
std::string reason_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
class parser
|
||||||
|
: public basic_parser<isRequest,
|
||||||
|
parser<isRequest, Body, Headers>>
|
||||||
|
, private std::conditional<isRequest,
|
||||||
|
detail::parser_request, detail::parser_response>::type
|
||||||
|
{
|
||||||
|
using message_type =
|
||||||
|
message<isRequest, Body, Headers>;
|
||||||
|
|
||||||
|
std::string field_;
|
||||||
|
std::string value_;
|
||||||
|
message_type m_;
|
||||||
|
typename message_type::body_type::reader r_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
parser(parser&&) = default;
|
||||||
|
|
||||||
|
parser()
|
||||||
|
: r_(m_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
message_type
|
||||||
|
release()
|
||||||
|
{
|
||||||
|
return std::move(m_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class basic_parser<isRequest, parser>;
|
||||||
|
|
||||||
|
void flush()
|
||||||
|
{
|
||||||
|
if(! value_.empty())
|
||||||
|
{
|
||||||
|
rfc2616::trim_right_in_place(value_);
|
||||||
|
// VFALCO could std::move
|
||||||
|
m_.headers.insert(field_, value_);
|
||||||
|
field_.clear();
|
||||||
|
value_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_method(boost::string_ref const& s, error_code&)
|
||||||
|
{
|
||||||
|
this->method_.append(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_uri(boost::string_ref const& s, error_code&)
|
||||||
|
{
|
||||||
|
this->uri_.append(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_reason(boost::string_ref const& s, error_code&)
|
||||||
|
{
|
||||||
|
this->reason_.append(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_field(boost::string_ref const& s, error_code&)
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
field_.append(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_value(boost::string_ref const& s, error_code&)
|
||||||
|
{
|
||||||
|
value_.append(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(std::true_type)
|
||||||
|
{
|
||||||
|
// VFALCO This is terrible for setting method
|
||||||
|
auto m =
|
||||||
|
[&](char const* s, method_t m)
|
||||||
|
{
|
||||||
|
if(this->method_ == s)
|
||||||
|
{
|
||||||
|
m_.method = m;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if(m("DELETE", method_t::http_delete))
|
||||||
|
break;
|
||||||
|
if(m("GET", method_t::http_get))
|
||||||
|
break;
|
||||||
|
if(m("HEAD", method_t::http_head))
|
||||||
|
break;
|
||||||
|
if(m("POST", method_t::http_post))
|
||||||
|
break;
|
||||||
|
if(m("PUT", method_t::http_put))
|
||||||
|
break;
|
||||||
|
if(m("CONNECT", method_t::http_connect))
|
||||||
|
break;
|
||||||
|
if(m("OPTIONS", method_t::http_options))
|
||||||
|
break;
|
||||||
|
if(m("TRACE", method_t::http_trace))
|
||||||
|
break;
|
||||||
|
if(m("COPY", method_t::http_copy))
|
||||||
|
break;
|
||||||
|
if(m("LOCK", method_t::http_lock))
|
||||||
|
break;
|
||||||
|
if(m("MKCOL", method_t::http_mkcol))
|
||||||
|
break;
|
||||||
|
if(m("MOVE", method_t::http_move))
|
||||||
|
break;
|
||||||
|
if(m("PROPFIND", method_t::http_propfind))
|
||||||
|
break;
|
||||||
|
if(m("PROPPATCH", method_t::http_proppatch))
|
||||||
|
break;
|
||||||
|
if(m("SEARCH", method_t::http_search))
|
||||||
|
break;
|
||||||
|
if(m("UNLOCK", method_t::http_unlock))
|
||||||
|
break;
|
||||||
|
if(m("BIND", method_t::http_bind))
|
||||||
|
break;
|
||||||
|
if(m("REBID", method_t::http_rebind))
|
||||||
|
break;
|
||||||
|
if(m("UNBIND", method_t::http_unbind))
|
||||||
|
break;
|
||||||
|
if(m("ACL", method_t::http_acl))
|
||||||
|
break;
|
||||||
|
if(m("REPORT", method_t::http_report))
|
||||||
|
break;
|
||||||
|
if(m("MKACTIVITY", method_t::http_mkactivity))
|
||||||
|
break;
|
||||||
|
if(m("CHECKOUT", method_t::http_checkout))
|
||||||
|
break;
|
||||||
|
if(m("MERGE", method_t::http_merge))
|
||||||
|
break;
|
||||||
|
if(m("MSEARCH", method_t::http_msearch))
|
||||||
|
break;
|
||||||
|
if(m("NOTIFY", method_t::http_notify))
|
||||||
|
break;
|
||||||
|
if(m("SUBSCRIBE", method_t::http_subscribe))
|
||||||
|
break;
|
||||||
|
if(m("UNSUBSCRIBE",method_t::http_unsubscribe))
|
||||||
|
break;
|
||||||
|
if(m("PATCH", method_t::http_patch))
|
||||||
|
break;
|
||||||
|
if(m("PURGE", method_t::http_purge))
|
||||||
|
break;
|
||||||
|
if(m("MKCALENDAR", method_t::http_mkcalendar))
|
||||||
|
break;
|
||||||
|
if(m("LINK", method_t::http_link))
|
||||||
|
break;
|
||||||
|
if(m("UNLINK", method_t::http_unlink))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while(false);
|
||||||
|
|
||||||
|
m_.url = std::move(this->uri_);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(std::false_type)
|
||||||
|
{
|
||||||
|
m_.status = this->status_code();
|
||||||
|
m_.reason = this->reason_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int on_headers(error_code&)
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
m_.version = 10 * this->http_major() + this->http_minor();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_request(error_code& ec)
|
||||||
|
{
|
||||||
|
set(std::integral_constant<
|
||||||
|
bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_response(error_code& ec)
|
||||||
|
{
|
||||||
|
set(std::integral_constant<
|
||||||
|
bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_body(boost::string_ref const& s, error_code& ec)
|
||||||
|
{
|
||||||
|
r_.write(s.data(), s.size(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_complete(error_code&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
108
include/beast/http/read.hpp
Normal file
108
include/beast/http/read.hpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_READ_HPP
|
||||||
|
#define BEAST_HTTP_READ_HPP
|
||||||
|
|
||||||
|
#include <beast/async_completion.hpp>
|
||||||
|
#include <beast/http/error.hpp>
|
||||||
|
#include <beast/http/parser.hpp>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** Read a HTTP message from a stream.
|
||||||
|
|
||||||
|
@param stream The stream to read the message from.
|
||||||
|
|
||||||
|
@param streambuf A Streambuf used to hold unread bytes. The
|
||||||
|
implementation may read past the end of the message. The extra
|
||||||
|
bytes are stored here, to be presented in a subsequent call to
|
||||||
|
read.
|
||||||
|
|
||||||
|
@param msg An object used to store the read message. Any
|
||||||
|
contents will be overwritten.
|
||||||
|
|
||||||
|
@throws boost::system::system_error on failure.
|
||||||
|
*/
|
||||||
|
template<class SyncReadStream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
read(SyncReadStream& stream, Streambuf& streambuf,
|
||||||
|
message<isRequest, Body, Headers>& msg)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
read(stream, streambuf, msg, ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read a HTTP message from a stream.
|
||||||
|
|
||||||
|
@param stream The stream to read the message from.
|
||||||
|
|
||||||
|
@param streambuf A Streambuf used to hold unread bytes. The
|
||||||
|
implementation may read past the end of the message. The extra
|
||||||
|
bytes are stored here, to be presented in a subsequent call to
|
||||||
|
read.
|
||||||
|
|
||||||
|
@param msg An object used to store the read message. Any
|
||||||
|
contents will be overwritten.
|
||||||
|
|
||||||
|
@param ec Set to the error, if any occurred.
|
||||||
|
*/
|
||||||
|
template<class SyncReadStream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
read(SyncReadStream& stream, Streambuf& streambuf,
|
||||||
|
message<isRequest, Body, Headers>& msg,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Start reading a HTTP message from a stream asynchronously.
|
||||||
|
|
||||||
|
@param stream The stream to read the message from.
|
||||||
|
|
||||||
|
@param streambuf A Streambuf used to hold unread bytes. The
|
||||||
|
implementation may read past the end of the message. The extra
|
||||||
|
bytes are stored here, to be presented in a subsequent call to
|
||||||
|
async_read.
|
||||||
|
|
||||||
|
@param msg An object used to store the read message. Any
|
||||||
|
contents will be overwritten.
|
||||||
|
|
||||||
|
@param handler The handler to be called when the request completes.
|
||||||
|
Copies will be made of the handler as required. The equivalent
|
||||||
|
function signature of the handler must be:
|
||||||
|
@code void handler(
|
||||||
|
error_code const& error // result of operation
|
||||||
|
); @endcode
|
||||||
|
Regardless of whether the asynchronous operation completes
|
||||||
|
immediately or not, the handler will not be invoked from within
|
||||||
|
this function. Invocation of the handler will be performed in a
|
||||||
|
manner equivalent to using boost::asio::io_service::post().
|
||||||
|
*/
|
||||||
|
template<class AsyncReadStream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers,
|
||||||
|
class ReadHandler>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
void_or_deduced
|
||||||
|
#else
|
||||||
|
typename async_completion<
|
||||||
|
ReadHandler, void(error_code)>::result_type
|
||||||
|
#endif
|
||||||
|
async_read(AsyncReadStream& stream, Streambuf& streambuf,
|
||||||
|
message<isRequest, Body, Headers>& msg,
|
||||||
|
ReadHandler&& handler);
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/read.ipp>
|
||||||
|
|
||||||
|
#endif
|
72
include/beast/http/reason.hpp
Normal file
72
include/beast/http/reason.hpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_REASON_HPP
|
||||||
|
#define BEAST_HTTP_REASON_HPP
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** Returns the text for a known status code integer. */
|
||||||
|
template<class = void>
|
||||||
|
char const*
|
||||||
|
reason_string(int status)
|
||||||
|
{
|
||||||
|
switch(status)
|
||||||
|
{
|
||||||
|
case 100: return "Continue";
|
||||||
|
case 101: return "Switching Protocols";
|
||||||
|
case 200: return "OK";
|
||||||
|
case 201: return "Created";
|
||||||
|
case 202: return "Accepted";
|
||||||
|
case 203: return "Non-Authoritative Information";
|
||||||
|
case 204: return "No Content";
|
||||||
|
case 205: return "Reset Content";
|
||||||
|
case 206: return "Partial Content";
|
||||||
|
case 300: return "Multiple Choices";
|
||||||
|
case 301: return "Moved Permanently";
|
||||||
|
case 302: return "Found";
|
||||||
|
case 303: return "See Other";
|
||||||
|
case 304: return "Not Modified";
|
||||||
|
case 305: return "Use Proxy";
|
||||||
|
case 307: return "Temporary Redirect";
|
||||||
|
case 400: return "Bad Request";
|
||||||
|
case 401: return "Unauthorized";
|
||||||
|
case 402: return "Payment Required";
|
||||||
|
case 403: return "Forbidden";
|
||||||
|
case 404: return "Not Found";
|
||||||
|
case 405: return "Method Not Allowed";
|
||||||
|
case 406: return "Not Acceptable";
|
||||||
|
case 407: return "Proxy Authentication Required";
|
||||||
|
case 408: return "Request Timeout";
|
||||||
|
case 409: return "Conflict";
|
||||||
|
case 410: return "Gone";
|
||||||
|
case 411: return "Length Required";
|
||||||
|
case 412: return "Precondition Failed";
|
||||||
|
case 413: return "Request Entity Too Large";
|
||||||
|
case 414: return "Request-URI Too Long";
|
||||||
|
case 415: return "Unsupported Media Type";
|
||||||
|
case 416: return "Requested Range Not Satisfiable";
|
||||||
|
case 417: return "Expectation Failed";
|
||||||
|
case 500: return "Internal Server Error";
|
||||||
|
case 501: return "Not Implemented";
|
||||||
|
case 502: return "Bad Gateway";
|
||||||
|
case 503: return "Service Unavailable";
|
||||||
|
case 504: return "Gateway Timeout";
|
||||||
|
case 505: return "HTTP Version Not Supported";
|
||||||
|
|
||||||
|
case 306: return "<reserved>";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "<unknown-status>";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
34
include/beast/http/resume_context.hpp
Normal file
34
include/beast/http/resume_context.hpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_RESUME_CONTEXT_HPP
|
||||||
|
#define BEAST_HTTP_RESUME_CONTEXT_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** A functor that resumes a write operation.
|
||||||
|
|
||||||
|
An rvalue reference to an object of this type is provided by the
|
||||||
|
write implementation to the `writer` associated with the body of
|
||||||
|
a message being sent.
|
||||||
|
|
||||||
|
If it is desired that the `writer` suspend the write operation (for
|
||||||
|
example, to wait until data is ready), it can take ownership of
|
||||||
|
the resume context using a move. Then, it returns `boost::indeterminate`
|
||||||
|
to indicate that the write operation should suspend. Later, the calling
|
||||||
|
code invokes the resume function and the write operation continues
|
||||||
|
from where it left off.
|
||||||
|
*/
|
||||||
|
using resume_context = std::function<void(void)>;
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
464
include/beast/http/rfc2616.hpp
Normal file
464
include/beast/http/rfc2616.hpp
Normal file
@ -0,0 +1,464 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_RFC2616_HPP
|
||||||
|
#define BEAST_HTTP_RFC2616_HPP
|
||||||
|
|
||||||
|
#include <boost/range/algorithm/equal.hpp>
|
||||||
|
#include <boost/range/iterator_range.hpp>
|
||||||
|
#include <boost/utility/string_ref.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <string>
|
||||||
|
#include <iterator>
|
||||||
|
#include <tuple> // for std::tie, remove ASAP
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
#if ! GENERATING_DOCS
|
||||||
|
|
||||||
|
/** Routines for performing RFC2616 compliance.
|
||||||
|
RFC2616:
|
||||||
|
Hypertext Transfer Protocol -- HTTP/1.1
|
||||||
|
http://www.w3.org/Protocols/rfc2616/rfc2616
|
||||||
|
*/
|
||||||
|
namespace rfc2616 {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
struct ci_equal_pred
|
||||||
|
{
|
||||||
|
bool operator()(char c1, char c2)
|
||||||
|
{
|
||||||
|
// VFALCO TODO Use a table lookup here
|
||||||
|
return std::tolower(c1) == std::tolower(c2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
/** Returns `true` if `c` is linear white space.
|
||||||
|
|
||||||
|
This excludes the CRLF sequence allowed for line continuations.
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_lws(char c)
|
||||||
|
{
|
||||||
|
return c == ' ' || c == '\t';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if `c` is any whitespace character. */
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_white(char c)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case ' ': case '\f': case '\n':
|
||||||
|
case '\r': case '\t': case '\v':
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if `c` is a control character. */
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_control(char c)
|
||||||
|
{
|
||||||
|
return c <= 31 || c >= 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if `c` is a separator. */
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_separator(char c)
|
||||||
|
{
|
||||||
|
// VFALCO Could use a static table
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '(': case ')': case '<': case '>': case '@':
|
||||||
|
case ',': case ';': case ':': case '\\': case '"':
|
||||||
|
case '{': case '}': case ' ': case '\t':
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if `c` is a character. */
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_char(char c)
|
||||||
|
{
|
||||||
|
return c >= 0 && c <= 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class FwdIter>
|
||||||
|
FwdIter
|
||||||
|
trim_left (FwdIter first, FwdIter last)
|
||||||
|
{
|
||||||
|
return std::find_if_not (first, last,
|
||||||
|
is_white);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class FwdIter>
|
||||||
|
FwdIter
|
||||||
|
trim_right (FwdIter first, FwdIter last)
|
||||||
|
{
|
||||||
|
if (first == last)
|
||||||
|
return last;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
--last;
|
||||||
|
if (! is_white (*last))
|
||||||
|
return ++last;
|
||||||
|
}
|
||||||
|
while (last != first);
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class CharT, class Traits, class Allocator>
|
||||||
|
void
|
||||||
|
trim_right_in_place (std::basic_string <
|
||||||
|
CharT, Traits, Allocator>& s)
|
||||||
|
{
|
||||||
|
s.resize (std::distance (s.begin(),
|
||||||
|
trim_right (s.begin(), s.end())));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class FwdIter>
|
||||||
|
std::pair <FwdIter, FwdIter>
|
||||||
|
trim (FwdIter first, FwdIter last)
|
||||||
|
{
|
||||||
|
first = trim_left (first, last);
|
||||||
|
last = trim_right (first, last);
|
||||||
|
return std::make_pair (first, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class String>
|
||||||
|
String
|
||||||
|
trim (String const& s)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
auto first = begin(s);
|
||||||
|
auto last = end(s);
|
||||||
|
std::tie (first, last) = trim (first, last);
|
||||||
|
return { first, last };
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class String>
|
||||||
|
String
|
||||||
|
trim_right (String const& s)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
auto first (begin(s));
|
||||||
|
auto last (end(s));
|
||||||
|
last = trim_right (first, last);
|
||||||
|
return { first, last };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::string
|
||||||
|
trim (std::string const& s)
|
||||||
|
{
|
||||||
|
return trim <std::string> (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse a character sequence of values separated by commas.
|
||||||
|
Double quotes and escape sequences will be converted. Excess white
|
||||||
|
space, commas, double quotes, and empty elements are not copied.
|
||||||
|
Format:
|
||||||
|
#(token|quoted-string)
|
||||||
|
Reference:
|
||||||
|
http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2
|
||||||
|
*/
|
||||||
|
template <class FwdIt,
|
||||||
|
class Result = std::vector<
|
||||||
|
std::basic_string<typename
|
||||||
|
std::iterator_traits<FwdIt>::value_type>>,
|
||||||
|
class Char>
|
||||||
|
Result
|
||||||
|
split(FwdIt first, FwdIt last, Char delim)
|
||||||
|
{
|
||||||
|
Result result;
|
||||||
|
using string = typename Result::value_type;
|
||||||
|
FwdIt iter = first;
|
||||||
|
string e;
|
||||||
|
while (iter != last)
|
||||||
|
{
|
||||||
|
if (*iter == '"')
|
||||||
|
{
|
||||||
|
// quoted-string
|
||||||
|
++iter;
|
||||||
|
while (iter != last)
|
||||||
|
{
|
||||||
|
if (*iter == '"')
|
||||||
|
{
|
||||||
|
++iter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*iter == '\\')
|
||||||
|
{
|
||||||
|
// quoted-pair
|
||||||
|
++iter;
|
||||||
|
if (iter != last)
|
||||||
|
e.append (1, *iter++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// qdtext
|
||||||
|
e.append (1, *iter++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! e.empty())
|
||||||
|
{
|
||||||
|
result.emplace_back(std::move(e));
|
||||||
|
e.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*iter == delim)
|
||||||
|
{
|
||||||
|
e = trim_right (e);
|
||||||
|
if (! e.empty())
|
||||||
|
{
|
||||||
|
result.emplace_back(std::move(e));
|
||||||
|
e.clear();
|
||||||
|
}
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
else if (is_lws (*iter))
|
||||||
|
{
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e.append (1, *iter++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! e.empty())
|
||||||
|
{
|
||||||
|
e = trim_right (e);
|
||||||
|
if (! e.empty())
|
||||||
|
result.emplace_back(std::move(e));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class FwdIt,
|
||||||
|
class Result = std::vector<
|
||||||
|
std::basic_string<typename std::iterator_traits<
|
||||||
|
FwdIt>::value_type>>>
|
||||||
|
Result
|
||||||
|
split_commas(FwdIt first, FwdIt last)
|
||||||
|
{
|
||||||
|
return split(first, last, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Result = std::vector<std::string>>
|
||||||
|
Result
|
||||||
|
split_commas(boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
return split_commas(s.begin(), s.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Iterates through a comma separated list.
|
||||||
|
|
||||||
|
Meets the requirements of ForwardIterator.
|
||||||
|
|
||||||
|
List defined in rfc2616 2.1.
|
||||||
|
|
||||||
|
@note Values returned may contain backslash escapes.
|
||||||
|
*/
|
||||||
|
class list_iterator
|
||||||
|
{
|
||||||
|
using iter_type = boost::string_ref::const_iterator;
|
||||||
|
|
||||||
|
iter_type it_;
|
||||||
|
iter_type end_;
|
||||||
|
boost::string_ref value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = boost::string_ref;
|
||||||
|
using pointer = value_type const*;
|
||||||
|
using reference = value_type const&;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator_category =
|
||||||
|
std::forward_iterator_tag;
|
||||||
|
|
||||||
|
list_iterator(iter_type begin, iter_type end)
|
||||||
|
: it_(begin)
|
||||||
|
, end_(end)
|
||||||
|
{
|
||||||
|
if(it_ != end_)
|
||||||
|
increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(list_iterator const& other) const
|
||||||
|
{
|
||||||
|
return other.it_ == it_ && other.end_ == end_
|
||||||
|
&& other.value_.size() == value_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator!=(list_iterator const& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
reference
|
||||||
|
operator*() const
|
||||||
|
{
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer
|
||||||
|
operator->() const
|
||||||
|
{
|
||||||
|
return &*(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_iterator&
|
||||||
|
operator++()
|
||||||
|
{
|
||||||
|
increment();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_iterator
|
||||||
|
operator++(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
increment();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class>
|
||||||
|
void
|
||||||
|
list_iterator::increment()
|
||||||
|
{
|
||||||
|
value_.clear();
|
||||||
|
while(it_ != end_)
|
||||||
|
{
|
||||||
|
if(*it_ == '"')
|
||||||
|
{
|
||||||
|
// quoted-string
|
||||||
|
++it_;
|
||||||
|
if(it_ == end_)
|
||||||
|
return;
|
||||||
|
if(*it_ != '"')
|
||||||
|
{
|
||||||
|
auto start = it_;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
++it_;
|
||||||
|
if(it_ == end_)
|
||||||
|
{
|
||||||
|
value_ = boost::string_ref(
|
||||||
|
&*start, std::distance(start, it_));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(*it_ == '"')
|
||||||
|
{
|
||||||
|
value_ = boost::string_ref(
|
||||||
|
&*start, std::distance(start, it_));
|
||||||
|
++it_;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++it_;
|
||||||
|
}
|
||||||
|
else if(*it_ == ',')
|
||||||
|
{
|
||||||
|
it_++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(is_lws(*it_))
|
||||||
|
{
|
||||||
|
++it_;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto start = it_;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
++it_;
|
||||||
|
if(it_ == end_ ||
|
||||||
|
*it_ == ',' ||
|
||||||
|
is_lws(*it_))
|
||||||
|
{
|
||||||
|
value_ = boost::string_ref(
|
||||||
|
&*start, std::distance(start, it_));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if two strings are equal.
|
||||||
|
|
||||||
|
A case-insensitive comparison is used.
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
ci_equal(boost::string_ref s1, boost::string_ref s2)
|
||||||
|
{
|
||||||
|
return boost::range::equal(s1, s2,
|
||||||
|
detail::ci_equal_pred{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a range representing the list. */
|
||||||
|
inline
|
||||||
|
boost::iterator_range<list_iterator>
|
||||||
|
make_list(boost::string_ref const& field)
|
||||||
|
{
|
||||||
|
return boost::iterator_range<list_iterator>{
|
||||||
|
list_iterator{field.begin(), field.end()},
|
||||||
|
list_iterator{field.end(), field.end()}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if the specified token exists in the list.
|
||||||
|
|
||||||
|
A case-insensitive comparison is used.
|
||||||
|
*/
|
||||||
|
template<class = void>
|
||||||
|
bool
|
||||||
|
token_in_list(boost::string_ref const& value,
|
||||||
|
boost::string_ref const& token)
|
||||||
|
{
|
||||||
|
for(auto const& item : make_list(value))
|
||||||
|
if(ci_equal(item, token))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // rfc2616
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user