mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-21 11:35:53 +00:00
Squashed 'src/beast/' changes from 2f9a844..1ab7a2f
1ab7a2f Set version to 1.0.0-b22
2eb4b0c Fix code sample in websocket.qbk
58802f4 Fix typos in design.qbk
19dc4bb Update documentation examples
10dbc5b Disable Boost.Coroutine deprecation warning
01c76c7 Fix websocket stream read documentation
d152c96 Update README.md example programs
995d86f Avoid copies in handler_alloc
851cb62 Add handler helpers
114175c Implement asio dealloc-before-invoke guarantee:
681db2e Add missing include
7db3c6e Fix broken Intellisense (MSVC)
09c183d Set version to 1.0.0-b21
1cb01fe Remove extraneous includes
62e65ed Set version to 1.0.0-b20
45eaa8c Increase utf8 checker code coverage
9ff1a27 Add zlib module:
a0a3359 Refactor HTTP identifier names (API Change):
79be7f8 Set version to 1.0.0-b19
eda1120 Tidy up internal name
4130ad4 Better buffer_cat:
f94f21d Fix consuming_buffers value_type (API Change):
2c524b4 prepared_buffers is private (API Change)
df2a108 Fix prepare_buffers value_type:
a4af9d6 Use boost::lexical_cast instead of std::to_string
62d670b Fix with_body example:
a63bd84 Increase code coverage
84a6775 Boost library min/max guidance:
02feea5 Add read, async_read for message_headers:
f224585 Add write, async_write, operator<< for message_headers:
ea48bcf Make chunk_encode public:
f6dd744 Refactor message and message_headers declarations:
9fd8aed Move sync_ostream to core/detail
c98b2d3 Optimize mask operations
d4dfc1a Optimize utf8 validation
7b4de4b Set version to 1.0.0-b18
feb5204 Add websocket::stream pong and async_pong
d4ffde5 Close connection during async_read on close frame:
644d518 Move clamp to core
427ba38 Fix write_frame masking and auto-fragment handling
54a51b1 Write buffer option does not change capacity
591dbc0 Meet DynamicBuffer requirements for static_streambuf
46d5e72 Reorganize source files and definitions
efa4b8f Override incremental link flags:
eef6e86 Higher optimization settings for MSVC builds
b6f3a36 Check invariants in parse_op:
47b0fa6 Remove unused field in test
8b8e57e unit_test improvements:
e907252 Clean up message docs
1e3543f Set version to 1.0.0-b17
de97a69 Trim unused code
796b484 Doc fixes
95c37e2 Fix unused parameter warnings and missing includes:
8b0d285 Refactor read_size_helper
97a9dcb Improve websocket example in README.md
236caef Engaged invokable is destructible:
d107ba1 Add headers_parser:
2f90627 Fix handling of body_what::pause in basic_parser_v1
9353d04 Add basic_parser_v1::reset
658e03c Add on_body_what parser callback (API Change):
50bd446 Fix parser traits detection (API Change):
df8d306 Tidy up documentation:
47105f8 Tidy up basic_headers for documentation
ada1f60 Refine message class hierarchy:
cf43f51 Rework HTTP concepts (API Change):
8a261ca HTTP Reader (API Change):
183055a Parser callbacks may not throw (API Change)
ebebe52 Add basic_streambuf::alloc_size
c9cd171 Fix basic_streambuf::capacity
0eb0e48 Tidying:
c5c436d Change implicit_value to default_value
01f939d Set version to 1.0.0-b16
206d0a9 Fix websocket failure tests
6b4fb28 Fix Writer exemplar in docs
4224a3a Relax ForwardIterator requirements in FieldSequence
14d7f8d Refactor base_parser_v1 callback traits:
d812344 Add pause option to on_headers interface:
c59bd53 Improve first line serialization
78ff20b Constrain parser_v1 constructor
2765a67 Refine Parser concept:
c329d33 Fix on_headers called twice from basic_parser_v1
55c4c93 Put back missing Design section in docs
90cec54 Make auto_fragment a boolean option
03642fb Rename to write_buffer_size
0ca8964 Frame processing routines are member functions
d99dfb3 Make value optional in param-list
325f579 Set version to 1.0.0-b15
c54762a Fix handling empty HTTP headers in parser_v1.hpp
c39cc06 Regression test for empty headers
60e637b Tidy up error types:
d54d597 Tidy up DynamicBuffer requirements
707fb5e Fix doc reference section
38af0f7 Fix message_v1 constructor
027c4e8 Add Secure WebSocket example
5baaa49 Add HTTPS example
076456b rfc7230 section 3.3.2 compliance
a09a044 Use bin/sh
1ff192d Update README.md for CppCon 2016 presentation
70b8555 Set version to 1.0.0-b14
b4a8342 Update and tidy documentation
8607af5 Update README.md
4abb43e Use BOOST_ASSERT
b5bffee Don't rely on undefined behavior
8ee7a21 Better WebSocket decorator:
38f0d95 Update build scripts for MSVC, MinGW
2a5b116 Fix error handling in server examples
4c7065a Add missing rebind to handler_alloc
git-subtree-dir: src/beast
git-subtree-split: 1ab7a2f04ca9a0b35f2032877cab78d94e96ebad
This commit is contained in:
@@ -10,8 +10,8 @@ env:
|
|||||||
# to boost's .tar.gz.
|
# to boost's .tar.gz.
|
||||||
- LCOV_ROOT=$HOME/lcov
|
- LCOV_ROOT=$HOME/lcov
|
||||||
- VALGRIND_ROOT=$HOME/valgrind-install
|
- VALGRIND_ROOT=$HOME/valgrind-install
|
||||||
- BOOST_ROOT=$HOME/boost_1_60_0
|
- BOOST_ROOT=$HOME/boost_1_61_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'
|
- BOOST_URL='http://downloads.sourceforge.net/project/boost/boost/1.61.0/boost_1_61_0.tar.gz?use_mirror=netix'
|
||||||
packages: &gcc5_pkgs
|
packages: &gcc5_pkgs
|
||||||
- gcc-5
|
- gcc-5
|
||||||
- g++-5
|
- g++-5
|
||||||
|
|||||||
180
CHANGELOG.md
180
CHANGELOG.md
@@ -1,3 +1,183 @@
|
|||||||
|
1.0.0-b22
|
||||||
|
|
||||||
|
* Fix broken Intellisense
|
||||||
|
* Implement the Asio deallocation-before-invocation guarantee
|
||||||
|
* Add handler helpers
|
||||||
|
* Avoid copies in handler_alloc
|
||||||
|
* Update README.md example programs
|
||||||
|
* Fix websocket stream read documentation
|
||||||
|
* Disable Boost.Coroutine deprecation warning
|
||||||
|
* Update documentation examples
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
1.0.0-b21
|
||||||
|
|
||||||
|
* Remove extraneous includes
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
1.0.0-b20
|
||||||
|
|
||||||
|
ZLib
|
||||||
|
|
||||||
|
* Add ZLib module
|
||||||
|
|
||||||
|
API Changes:
|
||||||
|
|
||||||
|
* Rename HTTP identifiers
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
1.0.0-b19
|
||||||
|
|
||||||
|
* Boost library min/max guidance
|
||||||
|
* Improvements to code coverage
|
||||||
|
* Use boost::lexical_cast instead of std::to_string
|
||||||
|
* Fix prepare_buffers value_type
|
||||||
|
* Fix consuming_buffers value_type
|
||||||
|
* Better buffer_cat
|
||||||
|
|
||||||
|
HTTP
|
||||||
|
|
||||||
|
* Make chunk_encode public
|
||||||
|
* Add write, async_write, operator<< for message_headers
|
||||||
|
* Add read, async_read for message_headers
|
||||||
|
* Fix with_body example
|
||||||
|
|
||||||
|
WebSocket
|
||||||
|
|
||||||
|
* Optimize utf8 validation
|
||||||
|
* Optimize mask operations
|
||||||
|
|
||||||
|
API Changes:
|
||||||
|
|
||||||
|
* Refactor message and message_headers declarations
|
||||||
|
* prepared_buffers is private
|
||||||
|
* consume_buffers is removed
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
1.0.0-b18
|
||||||
|
|
||||||
|
* Increase optimization settings for MSVC builds
|
||||||
|
|
||||||
|
HTTP
|
||||||
|
|
||||||
|
* Check invariants in parse_op:
|
||||||
|
* Clean up message docs
|
||||||
|
|
||||||
|
WebSocket
|
||||||
|
|
||||||
|
* Write buffer option does not change capacity
|
||||||
|
* Close connection during async_read on close frame
|
||||||
|
* Add pong, async pong to stream
|
||||||
|
|
||||||
|
Core
|
||||||
|
|
||||||
|
* Meet DynamicBuffer requirements for static_streambuf
|
||||||
|
* Fix write_frame masking and auto-fragment handling
|
||||||
|
|
||||||
|
Extras
|
||||||
|
|
||||||
|
* unit_test::suite fixes:
|
||||||
|
- New overload of fail() specifies file and line
|
||||||
|
- BEAST_EXPECTS only evaluates the reason string on a failure
|
||||||
|
* Add zlib module
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
1.0.0-b17
|
||||||
|
|
||||||
|
* Change implicit to default value in example
|
||||||
|
* Tidy up some declarations
|
||||||
|
* Fix basic_streambuf::capacity
|
||||||
|
* Add basic_streambuf::alloc_size
|
||||||
|
* Parser callbacks may not throw
|
||||||
|
* Fix Reader concept doc typo
|
||||||
|
* Add is_Reader trait
|
||||||
|
* Tidy up basic_headers for documentation
|
||||||
|
* Tidy up documentation
|
||||||
|
* Add basic_parser_v1::reset
|
||||||
|
* Fix handling of body_what::pause in basic_parser_v1
|
||||||
|
* Add headers_parser
|
||||||
|
* Engaged invokable is destructible
|
||||||
|
* Improve websocket example in README.md
|
||||||
|
* Refactor read_size_helper
|
||||||
|
|
||||||
|
API Changes:
|
||||||
|
|
||||||
|
* Added init() to Reader requirements
|
||||||
|
* Reader must be nothrow constructible
|
||||||
|
* Reader is now constructed right before reading the body
|
||||||
|
- The message passed on construction is filled in
|
||||||
|
* Rework HTTP concepts:
|
||||||
|
- Writer uses write instead of operator()
|
||||||
|
- Refactor traits to use void_t
|
||||||
|
- Remove is_ReadableBody, is_WritableBody
|
||||||
|
- Add has_reader, has_writer, is_Reader, is_Writer
|
||||||
|
- More friendly compile errors on failed concept checks
|
||||||
|
* basic_parser_v1 requires all callbacks present
|
||||||
|
* on_headers parser callback now returns void
|
||||||
|
* on_body_what is a new required parser callback returning body_what
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
1.0.0-b16
|
||||||
|
|
||||||
|
* Make value optional in param-list
|
||||||
|
* Frame processing routines are member functions
|
||||||
|
* Fix on_headers called twice from basic_parser_v1
|
||||||
|
* Constrain parser_v1 constructor
|
||||||
|
* Improve first line serialization
|
||||||
|
* Add pause option to on_headers interface
|
||||||
|
* Refactor base_parser_v1 callback traits:
|
||||||
|
* Refine Parser concept
|
||||||
|
* Relax ForwardIterator requirements in FieldSequence
|
||||||
|
* Fix websocket failure testing
|
||||||
|
* Refine Writer concept and fix exemplar in documentation
|
||||||
|
|
||||||
|
API Changes:
|
||||||
|
|
||||||
|
* Rename mask_buffer_size to write_buffer_size
|
||||||
|
* Make auto_fragment a boolean option
|
||||||
|
|
||||||
|
The message class hierarchy is refactored (breaking change):
|
||||||
|
|
||||||
|
* One message class now models both HTTP/1 and HTTP/2 messages
|
||||||
|
* message_v1, request_v1, response_v1 removed
|
||||||
|
* New classes basic_request and basic_response model
|
||||||
|
messages without the body.
|
||||||
|
|
||||||
|
Error resolution: Callers should use message, request,
|
||||||
|
and response instead of message_v1, request_v1, and
|
||||||
|
response_v1 respectively.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
1.0.0-b15
|
||||||
|
|
||||||
|
* rfc7230 section 3.3.2 compliance
|
||||||
|
* Add HTTPS example
|
||||||
|
* Add Secure WebSocket example
|
||||||
|
* Fix message_v1 constructor
|
||||||
|
* Tidy up DynamicBuffer requirements
|
||||||
|
* Tidy up error types and headers
|
||||||
|
* Fix handling empty HTTP headers in parser_v1
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
1.0.0-b14
|
||||||
|
|
||||||
|
* Add missing rebind to handler_alloc
|
||||||
|
* Fix error handling in http server examples
|
||||||
|
* Fix CMake scripts for MinGW
|
||||||
|
* Use BOOST_ASSERT
|
||||||
|
* Better WebSocket decorator
|
||||||
|
* Update and tidy documentation
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
1.0.0-b13
|
1.0.0-b13
|
||||||
|
|
||||||
* dstream improvements
|
* dstream improvements
|
||||||
|
|||||||
@@ -6,21 +6,51 @@ project (Beast)
|
|||||||
|
|
||||||
set_property (GLOBAL PROPERTY USE_FOLDERS ON)
|
set_property (GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
|
|
||||||
if (WIN32)
|
if (MSVC)
|
||||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4 /wd4100 /bigobj /D _WIN32_WINNT=0x0601 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1")
|
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244 /MP /W4 /wd4100 /bigobj /D _WIN32_WINNT=0x0601 /D _SCL_SECURE_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1")
|
||||||
|
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
|
||||||
|
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Ob2 /Oi /Ot /GL /MT")
|
||||||
|
set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Oi /Ot /MT")
|
||||||
|
|
||||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
|
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
|
||||||
|
set (CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG")
|
||||||
|
|
||||||
|
# for RelWithDebInfo builds, disable incremental linking
|
||||||
|
# since CMake sets it ON by default for that build type and it
|
||||||
|
# causes warnings
|
||||||
|
string (REPLACE "/INCREMENTAL" "/INCREMENTAL:NO" replacement_flags
|
||||||
|
${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO})
|
||||||
|
set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO ${replacement_flags})
|
||||||
|
|
||||||
else()
|
else()
|
||||||
set(Boost_USE_STATIC_LIBS ON)
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
set(Boost_USE_MULTITHREADED ON)
|
set(Boost_USE_MULTITHREADED ON)
|
||||||
find_package(Boost REQUIRED COMPONENTS coroutine context thread filesystem program_options system)
|
find_package(Boost REQUIRED COMPONENTS coroutine context thread filesystem program_options system)
|
||||||
include_directories(${Boost_INCLUDE_DIRS})
|
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
|
||||||
link_directories(${Boost_LIBRARY_DIR})
|
link_directories(${Boost_LIBRARY_DIR})
|
||||||
|
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
find_package(Threads)
|
find_package(Threads)
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS
|
set(CMAKE_CXX_FLAGS
|
||||||
"${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wpedantic")
|
"${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra -Wpedantic -Wno-unused-parameter")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_definitions ("-DBOOST_COROUTINES_NO_DEPRECATION_WARNING")
|
||||||
|
|
||||||
|
if (APPLE AND NOT DEFINED ENV{OPENSSL_ROOT_DIR})
|
||||||
|
find_program(HOMEBREW brew)
|
||||||
|
if (NOT HOMEBREW STREQUAL "HOMEBREW-NOTFOUND")
|
||||||
|
execute_process(COMMAND brew --prefix openssl
|
||||||
|
OUTPUT_VARIABLE OPENSSL_ROOT_DIR
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(OpenSSL)
|
||||||
|
|
||||||
|
if (MINGW)
|
||||||
|
link_libraries(${Boost_LIBRARIES} ws2_32 mswsock)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if ("${VARIANT}" STREQUAL "coverage")
|
if ("${VARIANT}" STREQUAL "coverage")
|
||||||
@@ -53,7 +83,6 @@ function(DoGroupSources curdir rootdir folder)
|
|||||||
source_group("" FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
|
source_group("" FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
|
||||||
else()
|
else()
|
||||||
string(REGEX REPLACE ^${rootdir} ${folder} groupname ${curdir})
|
string(REGEX REPLACE ^${rootdir} ${folder} groupname ${curdir})
|
||||||
#set(groupname ${curdir})
|
|
||||||
string(REPLACE "/" "\\" groupname ${groupname})
|
string(REPLACE "/" "\\" groupname ${groupname})
|
||||||
source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
|
source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
|
||||||
endif()
|
endif()
|
||||||
@@ -78,7 +107,14 @@ file(GLOB_RECURSE EXTRAS_INCLUDES
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory (examples)
|
add_subdirectory (examples)
|
||||||
|
if (NOT OPENSSL_FOUND)
|
||||||
|
message("OpenSSL not found. Not building examples/ssl")
|
||||||
|
else()
|
||||||
|
add_subdirectory (examples/ssl)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory (test)
|
add_subdirectory (test)
|
||||||
add_subdirectory (test/core)
|
add_subdirectory (test/core)
|
||||||
add_subdirectory (test/http)
|
add_subdirectory (test/http)
|
||||||
add_subdirectory (test/websocket)
|
add_subdirectory (test/websocket)
|
||||||
|
add_subdirectory (test/zlib)
|
||||||
|
|||||||
19
Jamroot
19
Jamroot
@@ -37,7 +37,7 @@ else if [ os.name ] = HAIKU
|
|||||||
if [ os.name ] = NT
|
if [ os.name ] = NT
|
||||||
{
|
{
|
||||||
lib ssl : : <name>ssleay32 ;
|
lib ssl : : <name>ssleay32 ;
|
||||||
lib crypto : : <name>libeay32 ;
|
lib crypto : : <name>libeay32 ;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -87,20 +87,20 @@ project beast
|
|||||||
<library>/boost/coroutine//boost_coroutine
|
<library>/boost/coroutine//boost_coroutine
|
||||||
<library>/boost/filesystem//boost_filesystem
|
<library>/boost/filesystem//boost_filesystem
|
||||||
<library>/boost/program_options//boost_program_options
|
<library>/boost/program_options//boost_program_options
|
||||||
# <library>ssl
|
|
||||||
# <library>crypto
|
|
||||||
<define>BOOST_ALL_NO_LIB=1
|
<define>BOOST_ALL_NO_LIB=1
|
||||||
<define>BOOST_SYSTEM_NO_DEPRECATED=1
|
|
||||||
<threading>multi
|
<threading>multi
|
||||||
<link>static
|
|
||||||
<runtime-link>shared
|
<runtime-link>shared
|
||||||
<debug-symbols>on
|
<debug-symbols>on
|
||||||
<toolset>gcc:<cxxflags>-std=c++11
|
<toolset>gcc:<cxxflags>-std=c++11
|
||||||
<toolset>gcc:<cxxflags>-Wno-unused-variable
|
<toolset>gcc:<cxxflags>-Wno-unused-parameter
|
||||||
<toolset>clang:<cxxflags>-std=c++11
|
<toolset>clang:<cxxflags>-std=c++11
|
||||||
|
<toolset>clang:<cxxflags>-Wno-unused-parameter
|
||||||
|
<toolset>gcc:<cxxflags>-Wno-unused-variable # Temporary until we can figure out -isystem
|
||||||
|
<toolset>clang:<cxxflags>-Wno-unused-variable # Temporary until we can figure out -isystem
|
||||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
|
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
|
||||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
|
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
|
||||||
<toolset>msvc:<cxxflags>-bigobj
|
<toolset>msvc:<cxxflags>"/wd4100 /bigobj"
|
||||||
|
<toolset>msvc,<variant>release:<cxxflags>"/Ob2 /Oi /Ot"
|
||||||
<os>LINUX:<define>_XOPEN_SOURCE=600
|
<os>LINUX:<define>_XOPEN_SOURCE=600
|
||||||
<os>LINUX:<define>_GNU_SOURCE=1
|
<os>LINUX:<define>_GNU_SOURCE=1
|
||||||
<os>SOLARIS:<define>_XOPEN_SOURCE=500
|
<os>SOLARIS:<define>_XOPEN_SOURCE=500
|
||||||
@@ -113,12 +113,7 @@ project beast
|
|||||||
<os>NT,<toolset>gcc:<library>ws2_32
|
<os>NT,<toolset>gcc:<library>ws2_32
|
||||||
<os>NT,<toolset>gcc:<library>mswsock
|
<os>NT,<toolset>gcc:<library>mswsock
|
||||||
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
|
<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
|
: usage-requirements
|
||||||
<include>.
|
|
||||||
:
|
:
|
||||||
build-dir bin
|
build-dir bin
|
||||||
;
|
;
|
||||||
|
|||||||
32
README.md
32
README.md
@@ -12,16 +12,15 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CppCon 2016
|
## Beast at CppCon 2016
|
||||||
|
|
||||||
I will be giving a lightning talk on Beast at CppCon 2016 in Bellevue,
|
Presentation
|
||||||
Washington from September 18 to September 22. If you'd like to meet me
|
(slides: <a href="https://raw.githubusercontent.com/vinniefalco/Beast/master/doc/images/CppCon2016.pdf">CppCon2016.pdf</a>)
|
||||||
and hear the talk or ask questions about Beast feel free to approach
|
|
||||||
me in person or send me an email at vinnie.falco@gmail.com to schedule
|
|
||||||
some time.
|
|
||||||
|
|
||||||
About CppCon 2016:
|
<a href="https://www.youtube.com/watch?v=uJZgRcvPFwI">
|
||||||
http://cppcon.org
|
<img width="320" height = "180" alt = "Beast"
|
||||||
|
src="https://raw.githubusercontent.com/vinniefalco/Beast/master/doc/images/CppCon2016.png">
|
||||||
|
</a>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -96,7 +95,7 @@ using the `git subtree` or `git submodule` commands). Then, edit your
|
|||||||
build scripts to add the `include/` directory to the list of paths checked
|
build scripts to add the `include/` directory to the list of paths checked
|
||||||
by the C++ compiler when searching for includes. Beast `#include` lines
|
by the C++ compiler when searching for includes. Beast `#include` lines
|
||||||
will look like this:
|
will look like this:
|
||||||
```
|
```C++
|
||||||
#include <beast/http.hpp>
|
#include <beast/http.hpp>
|
||||||
#include <beast/websocket.hpp>
|
#include <beast/websocket.hpp>
|
||||||
```
|
```
|
||||||
@@ -137,6 +136,7 @@ The files in the repository are laid out thusly:
|
|||||||
./
|
./
|
||||||
bin/ Holds executables and project files
|
bin/ Holds executables and project files
|
||||||
bin64/ Holds 64-bit Windows executables and project files
|
bin64/ Holds 64-bit Windows executables and project files
|
||||||
|
doc/ Source code and scripts for the documentation
|
||||||
include/ Add this to your compiler includes
|
include/ Add this to your compiler includes
|
||||||
beast/
|
beast/
|
||||||
extras/ Additional APIs, may change
|
extras/ Additional APIs, may change
|
||||||
@@ -171,14 +171,14 @@ int main()
|
|||||||
// WebSocket connect and send message using beast
|
// WebSocket connect and send message using beast
|
||||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
||||||
ws.handshake(host, "/");
|
ws.handshake(host, "/");
|
||||||
ws.write(boost::asio::buffer("Hello, world!"));
|
ws.write(boost::asio::buffer(std::string("Hello, world!")));
|
||||||
|
|
||||||
// Receive WebSocket message, print and close using beast
|
// Receive WebSocket message, print and close using beast
|
||||||
beast::streambuf sb;
|
beast::streambuf sb;
|
||||||
beast::websocket::opcode op;
|
beast::websocket::opcode op;
|
||||||
ws.read(op, sb);
|
ws.read(op, sb);
|
||||||
ws.close(beast::websocket::close_code::normal);
|
ws.close(beast::websocket::close_code::normal);
|
||||||
std::cout << to_string(sb.data()) << "\n";
|
std::cout << beast::to_string(sb.data()) << "\n";
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -186,6 +186,7 @@ Example HTTP program:
|
|||||||
```C++
|
```C++
|
||||||
#include <beast/http.hpp>
|
#include <beast/http.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -200,18 +201,19 @@ int main()
|
|||||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
|
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
|
||||||
|
|
||||||
// Send HTTP request using beast
|
// Send HTTP request using beast
|
||||||
beast::http::request_v1<beast::http::empty_body> req;
|
beast::http::request<beast::http::empty_body> req;
|
||||||
req.method = "GET";
|
req.method = "GET";
|
||||||
req.url = "/";
|
req.url = "/";
|
||||||
req.version = 11;
|
req.version = 11;
|
||||||
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
|
req.fields.replace("Host", host + ":" +
|
||||||
req.headers.replace("User-Agent", "Beast");
|
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
||||||
|
req.fields.replace("User-Agent", "Beast");
|
||||||
beast::http::prepare(req);
|
beast::http::prepare(req);
|
||||||
beast::http::write(sock, req);
|
beast::http::write(sock, req);
|
||||||
|
|
||||||
// Receive and print HTTP response using beast
|
// Receive and print HTTP response using beast
|
||||||
beast::streambuf sb;
|
beast::streambuf sb;
|
||||||
beast::http::response_v1<beast::http::streambuf_body> resp;
|
beast::http::response<beast::http::streambuf_body> resp;
|
||||||
beast::http::read(sock, sb, resp);
|
beast::http::read(sock, sb, resp);
|
||||||
std::cout << resp;
|
std::cout << resp;
|
||||||
}
|
}
|
||||||
|
|||||||
2
TODO.txt
2
TODO.txt
@@ -18,6 +18,8 @@ Core:
|
|||||||
* Complete allocator testing in basic_streambuf
|
* Complete allocator testing in basic_streambuf
|
||||||
|
|
||||||
WebSocket:
|
WebSocket:
|
||||||
|
* Minimize sizeof(websocket::stream)
|
||||||
|
* Move check for message size limit to account for compression
|
||||||
* more invokable unit test coverage
|
* more invokable unit test coverage
|
||||||
* More control over the HTTP request and response during handshakes
|
* More control over the HTTP request and response during handshakes
|
||||||
* optimized versions of key/masking, choose prepared_key size
|
* optimized versions of key/masking, choose prepared_key size
|
||||||
|
|||||||
@@ -15,15 +15,15 @@ using boostbook ;
|
|||||||
using quickbook ;
|
using quickbook ;
|
||||||
using doxygen ;
|
using doxygen ;
|
||||||
|
|
||||||
xml beast_boostbook : master.qbk ;
|
import quickbook ;
|
||||||
|
|
||||||
path-constant out : . ;
|
path-constant here : . ;
|
||||||
|
|
||||||
install stylesheets
|
install stylesheets
|
||||||
:
|
:
|
||||||
$(broot)/doc/src/boostbook.css
|
$(broot)/doc/src/boostbook.css
|
||||||
:
|
:
|
||||||
<location>$(out)/html
|
<location>$(here)/html
|
||||||
;
|
;
|
||||||
|
|
||||||
explicit stylesheets ;
|
explicit stylesheets ;
|
||||||
@@ -35,7 +35,7 @@ install images
|
|||||||
images/body.png
|
images/body.png
|
||||||
images/message.png
|
images/message.png
|
||||||
:
|
:
|
||||||
<location>$(out)/html/images
|
<location>$(here)/html/images
|
||||||
;
|
;
|
||||||
|
|
||||||
explicit images ;
|
explicit images ;
|
||||||
@@ -44,40 +44,54 @@ install callouts
|
|||||||
:
|
:
|
||||||
[ glob $(broot)/doc/src/images/callouts/*.png ]
|
[ glob $(broot)/doc/src/images/callouts/*.png ]
|
||||||
:
|
:
|
||||||
<location>$(out)/html/images/callouts
|
<location>$(here)/html/images/callouts
|
||||||
;
|
;
|
||||||
|
|
||||||
explicit callout ;
|
explicit callout ;
|
||||||
|
|
||||||
boostbook doc
|
install examples
|
||||||
:
|
:
|
||||||
beast_boostbook
|
[ glob
|
||||||
|
../examples/*.cpp
|
||||||
|
../examples/*.hpp
|
||||||
|
../examples/ssl/*.cpp
|
||||||
|
../examples/ssl/*.hpp
|
||||||
|
]
|
||||||
:
|
:
|
||||||
<xsl:param>chapter.autolabel=0
|
<location>$(here)/html/examples
|
||||||
<xsl:param>boost.image.src=images/beast.png
|
|
||||||
<xsl:param>boost.image.alt="Beast Logo"
|
|
||||||
<xsl:param>boost.image.w=2400
|
|
||||||
<xsl:param>boost.image.h=80
|
|
||||||
<xsl:param>boost.root=$(broot)
|
|
||||||
<xsl:param>chapter.autolabel=0
|
|
||||||
<xsl:param>chunk.first.sections=1 # Chunk the first top-level section?
|
|
||||||
<xsl:param>chunk.section.depth=8 # Depth to which sections should be chunked
|
|
||||||
<xsl:param>generate.section.toc.level=1 # Control depth of TOC generation in sections
|
|
||||||
<xsl:param>toc.max.depth=2 # How many levels should be created for each TOC?
|
|
||||||
<xsl:param>toc.section.depth=2 # How deep should recursive sections appear in the TOC?
|
|
||||||
:
|
|
||||||
<location>temp
|
|
||||||
<dependency>stylesheets
|
|
||||||
<dependency>images
|
|
||||||
;
|
;
|
||||||
|
|
||||||
#explicit doc ;
|
explicit examples ;
|
||||||
# <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"
|
|
||||||
|
|
||||||
|
xml doc
|
||||||
|
:
|
||||||
|
master.qbk
|
||||||
|
:
|
||||||
|
<location>temp
|
||||||
|
<include>$(broot)/tools/boostbook/dtd
|
||||||
|
;
|
||||||
|
|
||||||
|
boostbook boostdoc
|
||||||
|
:
|
||||||
|
doc
|
||||||
|
:
|
||||||
|
<xsl:param>boost.root=$(broot)
|
||||||
|
<xsl:param>boost.image.src=images/beast.png
|
||||||
|
<xsl:param>boost.image.alt="Beast Logo"
|
||||||
|
<xsl:param>boost.image.w=1330
|
||||||
|
<xsl:param>boost.image.h=80
|
||||||
|
<xsl:param>chapter.autolabel=0
|
||||||
|
<xsl:param>chunk.section.depth=8 # Depth to which sections should be chunked
|
||||||
|
<xsl:param>chunk.first.sections=1 # Chunk the first top-level section?
|
||||||
|
<xsl:param>toc.section.depth=8 # How deep should recursive sections appear in the TOC?
|
||||||
|
<xsl:param>toc.max.depth=8 # How many levels should be created for each TOC?
|
||||||
|
<xsl:param>generate.section.toc.level=8 # Control depth of TOC generation in sections
|
||||||
|
<xsl:param>generate.toc="chapter nop section nop"
|
||||||
|
<include>$(broot)/tools/boostbook/dtd
|
||||||
|
:
|
||||||
|
<location>temp
|
||||||
|
<dependency>examples
|
||||||
|
<dependency>images
|
||||||
|
<dependency>stylesheets
|
||||||
|
;
|
||||||
|
|
||||||
#[include reference.qbk]
|
|
||||||
#[xinclude index.xml]
|
|
||||||
198
doc/design.qbk
198
doc/design.qbk
@@ -5,44 +5,52 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:design Design choices]
|
[section:design Design Choices]
|
||||||
|
|
||||||
|
[block '''
|
||||||
|
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
|
||||||
|
<member><link linkend="beast.design.http">HTTP FAQ</link></member>
|
||||||
|
<member><link linkend="beast.design.websocket">WebSocket FAQ</link></member>
|
||||||
|
<member><link linkend="beast.design.websocketpp">Comparison to Zaphoyd Studios WebSocket++</link></member>
|
||||||
|
</simplelist></entry></row></tbody></tgroup></informaltable>
|
||||||
|
''']
|
||||||
|
|
||||||
The implementations are driven by business needs of cryptocurrency server
|
The implementations are driven by business needs of cryptocurrency server
|
||||||
applications (e.g. [@https://ripple.com Ripple]) written in C++. These
|
applications (e.g. [@https://ripple.com Ripple]) written in C++. These
|
||||||
needs were not met by existing solutions so Beast was written from scratch
|
needs were not met by existing solutions so Beast was written from scratch
|
||||||
as a solution. Beast's design philosophy avoid flaws exhibited by other
|
as a solution. Beast's design philosophy avoids flaws exhibited by other
|
||||||
libraries:
|
libraries:
|
||||||
|
|
||||||
|
* Don't try to do too much.
|
||||||
|
|
||||||
* Don't sacrifice performance.
|
* Don't sacrifice performance.
|
||||||
|
|
||||||
* Don't do too much, otherwise interfaces become rigid.
|
* Mimic Boost.Asio; familiarity breeds confidence.
|
||||||
|
|
||||||
* Symmetric interfaces (client and server the same, or close to it).
|
* Role-symmetric interfaces; client and server the same (or close to it).
|
||||||
|
|
||||||
* Emulate Boost.Asio interfaces as much as possible, since Asio is
|
* Leave important decisions to the user, such as allocating memory or
|
||||||
proven and it is familiar to users.
|
managing flow control.
|
||||||
|
|
||||||
* Let library users make the important decisions such as how to
|
Beast uses the __DynamicBuffer__ concept presented in the Networking TS
|
||||||
allocate memory or how to leverage flow control.
|
(__N4588__), and relies heavily on the Boost.Asio __ConstBufferSequence__
|
||||||
|
and __MutableBufferSequence__ concepts for passing buffers to functions.
|
||||||
Beast uses the [link beast.types.DynamicBuffer [*`DynamicBuffer`]] concept
|
The authors have found the dynamic buffer and buffer sequence interfaces to
|
||||||
presented in the Netwoking TS, and relies heavily on the Boost.Asio
|
be optimal for interacting with Asio, and for other tasks such as incremental
|
||||||
[*`ConstBufferSequence`] and [*`MutableBufferSequence`] concepts for passing
|
parsing of data in buffers (for example, parsing websocket frames stored
|
||||||
buffers to functions. The authors have found the dynamic buffer and buffer
|
in a [link beast.ref.static_streambuf `static_streambuf`]).
|
||||||
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
|
During the development of Beast the authors have studied other software
|
||||||
packages and in particular the comments left during the Boost Review process
|
packages and in particular the comments left during the Boost Review process
|
||||||
of other packages offering similar functionality. In this section we attempt
|
of other packages offering similar functionality. In this section and the
|
||||||
to address those issues.
|
FAQs that follow we attempt to answer those questions that are also applicable
|
||||||
|
to Beast.
|
||||||
|
|
||||||
[variablelist
|
[variablelist
|
||||||
[[
|
[[
|
||||||
"I would also like to see instances of this library being used
|
"I would also like to see instances of this library being used
|
||||||
in production. That would give some evidence that the design
|
in production. That would give some evidence that the design
|
||||||
works in practice.""
|
works in practice."
|
||||||
][
|
][
|
||||||
Beast.HTTP and Beast.WebSocket are production ready and currently
|
Beast.HTTP and Beast.WebSocket are production ready and currently
|
||||||
running on public servers receiving traffic and handling millions of
|
running on public servers receiving traffic and handling millions of
|
||||||
@@ -56,25 +64,26 @@ to address those issues.
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
[section:http HTTP]
|
|
||||||
|
|
||||||
For HTTP we to model the message to maximize flexibility of implementation
|
[section:http HTTP FAQ]
|
||||||
|
|
||||||
|
For HTTP we model the message to maximize flexibility of implementation
|
||||||
strategies while allowing familiar verbs such as [*`read`] and [*`write`].
|
strategies while allowing familiar verbs such as [*`read`] and [*`write`].
|
||||||
The HTTP interface is further driven by the needs of the WebSocket module,
|
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
|
as a WebSocket session requires a HTTP Upgrade handshake exchange at the
|
||||||
start. Other design goals:
|
start. Other design goals:
|
||||||
|
|
||||||
* Don't try to invent a complete web server or client
|
* Keep it simple.
|
||||||
|
|
||||||
* Have simple free functions to send and receive messages.
|
* Stay low level; don't invent a whole web server or client.
|
||||||
|
|
||||||
* Allow the message object to be customized,
|
* Allow for customizations, if the user needs it.
|
||||||
|
|
||||||
[variablelist
|
[variablelist
|
||||||
|
|
||||||
[[
|
[[
|
||||||
"Some more advanced examples, e.g. including TLS with client/server
|
"Some more advanced examples, e.g. including TLS with client/server
|
||||||
certificates would help.""
|
certificates would help."
|
||||||
][
|
][
|
||||||
The HTTP interface doesn't try to reinvent the wheel, it just uses
|
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
|
the `boost::asio::ip::tcp::socket` or `boost::asio::ssl::stream` that
|
||||||
@@ -94,7 +103,7 @@ start. Other design goals:
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
[[
|
[[
|
||||||
"Cookies? Forms/File Uploads?""
|
"Cookies? Forms/File Uploads?"
|
||||||
][
|
][
|
||||||
Cookies, or managing these types of HTTP headers in general, is the
|
Cookies, or managing these types of HTTP headers in general, is the
|
||||||
responsibility of higher levels. Beast.HTTP just tries to get complete
|
responsibility of higher levels. Beast.HTTP just tries to get complete
|
||||||
@@ -107,7 +116,7 @@ start. Other design goals:
|
|||||||
|
|
||||||
[[
|
[[
|
||||||
"...supporting TLS (is this a feature? If not this would be a show-stopper),
|
"...supporting TLS (is this a feature? If not this would be a show-stopper),
|
||||||
etc.
|
etc."
|
||||||
][
|
][
|
||||||
Beast.HTTP does not provide direct facilities for implementing TLS
|
Beast.HTTP does not provide direct facilities for implementing TLS
|
||||||
connections; however, the interfaces already existing on the
|
connections; however, the interfaces already existing on the
|
||||||
@@ -126,17 +135,24 @@ start. Other design goals:
|
|||||||
over Asio. Such an implementation should serve as a building block upon
|
over Asio. Such an implementation should serve as a building block upon
|
||||||
which higher abstractions such as the aforementioned HTTP service or
|
which higher abstractions such as the aforementioned HTTP service or
|
||||||
cgi-gateway can be built.
|
cgi-gateway can be built.
|
||||||
|
|
||||||
|
One of the example programs implements a simple HTTP server that
|
||||||
|
delivers files from the filesystem.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
[[
|
[[
|
||||||
"You should send a 100-continue to ask for the rest of the body if required."
|
"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
|
The Beast interface needs to support this functionality (by allowing this
|
||||||
can build on Beast.HTTP to provide these behaviors.
|
special case of partial message parsing and serialization). Specifically,
|
||||||
|
it should let callers read the request up to just before the body,
|
||||||
|
and let callers write the request up to just before the body. However,
|
||||||
|
making use of this behavior should be up to callers (since Beast is low
|
||||||
|
level).
|
||||||
]]
|
]]
|
||||||
|
|
||||||
[[
|
[[
|
||||||
"What about HTTP/2?""
|
"What about HTTP/2?"
|
||||||
][
|
][
|
||||||
Many reviewers feel that HTTP/2 support is an essential feature of
|
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
|
a HTTP library. The authors agree that HTTP/2 is important but also
|
||||||
@@ -145,13 +161,13 @@ start. Other design goals:
|
|||||||
and 1.1.
|
and 1.1.
|
||||||
|
|
||||||
The Beast.HTTP message model is suitable for HTTP/2 and can be re-used.
|
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
|
The IETF HTTP Working Group adopted message compatiblity with HTTP/1.x
|
||||||
as an explicit goal. A parser can simply emit full headers after
|
as an explicit goal. A parser can simply emit full headers after
|
||||||
decoding the compressed HTTP/2 headers. The stream ID is not logicaly
|
decoding the compressed HTTP/2 headers. The stream ID is not logically
|
||||||
part of the message but rather message metadata and should be
|
part of the message but rather message metadata and should be
|
||||||
communicated out-of-band (see below). HTTP/2 sessions begin with a
|
communicated out-of-band (see below). HTTP/2 sessions begin with a
|
||||||
traditional HTTP/1.1 Upgrade similar in fashion to the WebSocket
|
traditional HTTP/1.1 Upgrade similar in fashion to the WebSocket
|
||||||
upgrade. A HTTP/2 implementation can use existing Beast.HTTP primitives
|
upgrade. An HTTP/2 implementation can use existing Beast.HTTP primitives
|
||||||
to perform this handshake.
|
to perform this handshake.
|
||||||
|
|
||||||
Free functions for HTTP/2 sessions are not possible because of the
|
Free functions for HTTP/2 sessions are not possible because of the
|
||||||
@@ -167,10 +183,55 @@ start. Other design goals:
|
|||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:websocket WebSocket FAQ]
|
||||||
|
|
||||||
|
[variablelist
|
||||||
|
|
||||||
|
[[
|
||||||
|
What about message compression?
|
||||||
|
][
|
||||||
|
The author is currently porting ZLib 1.2.8 to modern, header-only C++11
|
||||||
|
that does not use macros or try to support ancient architectures. This
|
||||||
|
deflate implementation will be available as its own individually
|
||||||
|
usable interface, and also will be used to power Beast WebSocket's
|
||||||
|
permessage-deflate implementation, due Q1 of 2017.
|
||||||
|
|
||||||
|
However, Beast currently has sufficient functionality that users can
|
||||||
|
begin taking advantage of the WebSocket protocol using this library
|
||||||
|
immediately.
|
||||||
|
]]
|
||||||
|
|
||||||
|
[[
|
||||||
|
Where is the TLS/SSL interface?
|
||||||
|
][
|
||||||
|
The `websocket::stream` wraps the socket or stream that you provide
|
||||||
|
(for example, a `boost::asio::ip::tcp::socket` or a
|
||||||
|
`boost::asio::ssl::stream`). You establish your TLS connection using the
|
||||||
|
interface on `ssl::stream` like shown in all of the Asio examples, then
|
||||||
|
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] provide support for shutting down
|
||||||
|
the TLS connection through the use of the ADL compile-time virtual functions
|
||||||
|
[link beast.ref.websocket__teardown `teardown`] and
|
||||||
|
[link beast.ref.websocket__async_teardown `async_teardown`]. These will
|
||||||
|
properly close the connection as per rfc6455 and overloads are available
|
||||||
|
for TLS streams. Callers may provide their own overloads of these functions
|
||||||
|
for user-defined next layer types.
|
||||||
|
]]
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
[section:websocket WebSocket]
|
|
||||||
|
|
||||||
|
[section:websocketpp Comparison to Zaphoyd Studios WebSocket++]
|
||||||
|
|
||||||
[variablelist
|
[variablelist
|
||||||
|
|
||||||
@@ -208,21 +269,19 @@ start. Other design goals:
|
|||||||
To get an idea of the complexity involved with implementing a transport,
|
To get an idea of the complexity involved with implementing a transport,
|
||||||
compare the asio transport to the
|
compare the asio transport to the
|
||||||
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59 `iostream` transport]
|
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59 `iostream` transport]
|
||||||
(a layer that allows websocket communication over a std iostream).
|
(a layer that allows websocket communication over a `std::iostream`).
|
||||||
|
|
||||||
In contrast, Beast abstracts the transport by defining just one [*`NextLayer`]
|
In contrast, Beast abstracts the transport by defining just one [*`NextLayer`]
|
||||||
template argument The type requirements for [*`NextLayer`] are
|
template argument The type requirements for [*`NextLayer`] are
|
||||||
already familiar to users as they are documented in Asio:
|
already familiar to users as they are documented in Asio:
|
||||||
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html SyncReadStream],
|
__AsyncReadStream__, __AsyncWriteStream__, __SyncReadStream__, __SyncWriteStream__.
|
||||||
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html SyncWriteStream],
|
|
||||||
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html AsyncReadStream], and
|
|
||||||
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html AsyncWriteStream].
|
|
||||||
The type requirements for instantiating `beast::websocket::stream` versus
|
The type requirements for instantiating `beast::websocket::stream` versus
|
||||||
`websocketpp::connection` with user defined types are vastly reduced
|
`websocketpp::connection` with user defined types are vastly reduced
|
||||||
(18 functions versus 2). Note that websocketpp connections are passed by
|
(18 functions versus 2). Note that websocketpp connections are passed by
|
||||||
`shared_ptr`. Beast does not use `shared_ptr` anywhere in its public interface.
|
`shared_ptr`. Beast does not use `shared_ptr` anywhere in its public interface.
|
||||||
A `beast::websocket::stream` is constructible and movable in a manner identical
|
A `beast::websocket::stream` is constructible and movable in a manner identical
|
||||||
`to a boost::asio::ip::tcp::socket`. Callers can put such objects in a
|
to a `boost::asio::ip::tcp::socket`. Callers can put such objects in a
|
||||||
`shared_ptr` if they want to, but there is no requirement to do so.
|
`shared_ptr` if they want to, but there is no requirement to do so.
|
||||||
|
|
||||||
[table
|
[table
|
||||||
@@ -283,8 +342,7 @@ start. Other design goals:
|
|||||||
implementation. Instead, it follows the Asio pattern. Calls to
|
implementation. Instead, it follows the Asio pattern. Calls to
|
||||||
asynchronous initiation functions use the same method to invoke
|
asynchronous initiation functions use the same method to invoke
|
||||||
intermediate handlers as the method used to invoke the final handler,
|
intermediate handlers as the method used to invoke the final handler,
|
||||||
through the
|
through the __asio_handler_invoke__ mechanism.
|
||||||
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/asio_handler_invoke.html asio_handler_invoke] mechanism.
|
|
||||||
|
|
||||||
The only requirement in Beast is that calls to asynchronous initiation
|
The only requirement in Beast is that calls to asynchronous initiation
|
||||||
functions are made from the same implicit or explicit strand. For
|
functions are made from the same implicit or explicit strand. For
|
||||||
@@ -314,7 +372,7 @@ start. Other design goals:
|
|||||||
|
|
||||||
websocketpp requires a one-time call to set the handler for each event
|
websocketpp requires a one-time call to set the handler for each event
|
||||||
in its interface (for example, upon message receipt). The handler is
|
in its interface (for example, upon message receipt). The handler is
|
||||||
represented by a `std::function equivalent`. Its important to recognize
|
represented by a `std::function` equivalent. Its important to recognize
|
||||||
that the websocketpp interface performs type-erasure on this handler.
|
that the websocketpp interface performs type-erasure on this handler.
|
||||||
|
|
||||||
In comparison, Beast handlers are specified in a manner identical to
|
In comparison, Beast handlers are specified in a manner identical to
|
||||||
@@ -378,29 +436,27 @@ start. Other design goals:
|
|||||||
|
|
||||||
websocketpp defines a message buffer, passed in arguments by
|
websocketpp defines a message buffer, passed in arguments by
|
||||||
`shared_ptr`, and an associated message manager which permits
|
`shared_ptr`, and an associated message manager which permits
|
||||||
aggregation and memory reuse of memory. The implementation of
|
aggregation and reuse of memory. The implementation of
|
||||||
`websocketpp::message` uses a `std::string` to hold the payload. If an
|
`websocketpp::message` uses a `std::string` to hold the payload. If an
|
||||||
incoming message is broken up into multiple frames, the string may be
|
incoming message is broken up into multiple frames, the string may be
|
||||||
reallocated for each continuation frame. The std::string always uses
|
reallocated for each continuation frame. The `std::string` always uses
|
||||||
the standard allocator, it is not possible to customize the choice of
|
the standard allocator, it is not possible to customize the choice of
|
||||||
allocator.
|
allocator.
|
||||||
|
|
||||||
Beast allows callers to specify the object for receiving the message
|
Beast allows callers to specify the object for receiving the message
|
||||||
or frame data, which is of any type meeting the requirements of
|
or frame data, which is of any type meeting the requirements of
|
||||||
[@http://vinniefalco.github.io/beast/beast/types/DynamicBuffer.html [*DynamicBuffer]]
|
__DynamicBuffer__ (modeled after `boost::asio::streambuf`).
|
||||||
(modeled after `boost::asio::streambuf`).
|
|
||||||
|
|
||||||
Beast comes with the class `beast::basic_streambuf`, an efficient
|
Beast comes with the class __basic_streambuf__, an efficient
|
||||||
implementation of the [*DynamicBuffer] concept which makes use of multiple
|
implementation of the __DynamicBuffer__ concept which makes use of multiple
|
||||||
allocated octet arrays. If an incoming message is broken up into
|
allocated octet arrays. If an incoming message is broken up into
|
||||||
multiple pieces, no reallocation occurs. Instead, new allocations are
|
multiple pieces, no reallocation occurs. Instead, new allocations are
|
||||||
appended to the sequence when existing allocations are filled. Beast
|
appended to the sequence when existing allocations are filled. Beast
|
||||||
does not impose any particular memory management model on callers. The
|
does not impose any particular memory management model on callers. The
|
||||||
`basic_streambuf` provided by beast supports standard allocators through
|
__basic_streambuf__ provided by beast supports standard allocators through
|
||||||
a template argument. Use the [*DynamicBuffer] that comes with beast,
|
a template argument. Use the __DynamicBuffer__ that comes with beast,
|
||||||
customize the allocator if you desire, or provide your own type that
|
customize the allocator if you desire, or provide your own type that
|
||||||
meets the
|
meets the requirements.
|
||||||
[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/basic_streambuf.hpp#L21 concept requirements].
|
|
||||||
|
|
||||||
[table
|
[table
|
||||||
[
|
[
|
||||||
@@ -596,44 +652,8 @@ start. Other design goals:
|
|||||||
]
|
]
|
||||||
]]
|
]]
|
||||||
|
|
||||||
[[
|
|
||||||
What about message compression?
|
|
||||||
][
|
|
||||||
The author is currently porting ZLib 1.2.8 to modern, header-only C++11
|
|
||||||
that does not use macros or try to support ancient architectures. This
|
|
||||||
deflate implementation will be available as its own individually
|
|
||||||
usable interface, and also will be used to power Beast WebSocket's
|
|
||||||
permessage-deflate implementation, due Q4 of 2016.
|
|
||||||
|
|
||||||
However, Beast currently has sufficient functionality that users can
|
|
||||||
begin taking advantage of the WebSocket protocol using this library
|
|
||||||
immediately.
|
|
||||||
]]
|
|
||||||
|
|
||||||
[[
|
|
||||||
Where is the TLS/SSL interface?
|
|
||||||
][
|
|
||||||
The `websocket::stream` wraps the socket or stream that you provide
|
|
||||||
(for example, a `boost::asio::ip::tcp::socket` or a
|
|
||||||
`boost::asio::ssl::stream`). You establish your TLS connection using the
|
|
||||||
interface on `ssl::stream` like shown in all of the Asio examples, they
|
|
||||||
construct your `websocket::stream` around it. It works perfectly fine;
|
|
||||||
Beast.WebSocket doesn't try to reinvent the wheel or put a fresh coat of
|
|
||||||
interface paint on the `ssl::stream`.
|
|
||||||
|
|
||||||
The WebSocket implementation [*does] provides support for shutting down
|
|
||||||
the TLS connection through the use of the ADL compile-time virtual functions
|
|
||||||
[link beast.ref.websocket__teardown `teardown`] and
|
|
||||||
[link beast.ref.websocket__async_teardown `async_teardown`]. These will
|
|
||||||
properly close the connection as per rfc6455 and overloads are available
|
|
||||||
for TLS streams. Callers may provide their own overloads of these functions
|
|
||||||
for user-defined next layer types.
|
|
||||||
]]
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|||||||
127
doc/examples.qbk
Normal file
127
doc/examples.qbk
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
[/
|
||||||
|
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
|
||||||
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
]
|
||||||
|
|
||||||
|
[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.
|
||||||
|
|
||||||
|
[heading HTTP GET]
|
||||||
|
|
||||||
|
Use HTTP to request the root page from a website and print the response:
|
||||||
|
|
||||||
|
```
|
||||||
|
#include <beast/http.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Normal boost::asio setup
|
||||||
|
std::string const host = "boost.org";
|
||||||
|
boost::asio::io_service ios;
|
||||||
|
boost::asio::ip::tcp::resolver r{ios};
|
||||||
|
boost::asio::ip::tcp::socket sock{ios};
|
||||||
|
boost::asio::connect(sock,
|
||||||
|
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
|
||||||
|
|
||||||
|
// Send HTTP request using beast
|
||||||
|
beast::http::request<beast::http::empty_body> req;
|
||||||
|
req.method = "GET";
|
||||||
|
req.url = "/";
|
||||||
|
req.version = 11;
|
||||||
|
req.fields.replace("Host", host + ":" +
|
||||||
|
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
||||||
|
req.fields.replace("User-Agent", "Beast");
|
||||||
|
beast::http::prepare(req);
|
||||||
|
beast::http::write(sock, req);
|
||||||
|
|
||||||
|
// Receive and print HTTP response using beast
|
||||||
|
beast::streambuf sb;
|
||||||
|
beast::http::response<beast::http::streambuf_body> resp;
|
||||||
|
beast::http::read(sock, sb, resp);
|
||||||
|
std::cout << resp;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
[heading WebSocket]
|
||||||
|
|
||||||
|
Establish a WebSocket connection, send a message and receive the reply:
|
||||||
|
```
|
||||||
|
#include <beast/core/to_string.hpp>
|
||||||
|
#include <beast/websocket.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Normal boost::asio setup
|
||||||
|
std::string const host = "echo.websocket.org";
|
||||||
|
boost::asio::io_service ios;
|
||||||
|
boost::asio::ip::tcp::resolver r{ios};
|
||||||
|
boost::asio::ip::tcp::socket sock{ios};
|
||||||
|
boost::asio::connect(sock,
|
||||||
|
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
|
||||||
|
|
||||||
|
// WebSocket connect and send message using beast
|
||||||
|
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
||||||
|
ws.handshake(host, "/");
|
||||||
|
ws.write(boost::asio::buffer(std::string("Hello, world!")));
|
||||||
|
|
||||||
|
// Receive WebSocket message, print and close using beast
|
||||||
|
beast::streambuf sb;
|
||||||
|
beast::websocket::opcode op;
|
||||||
|
ws.read(op, sb);
|
||||||
|
ws.close(beast::websocket::close_code::normal);
|
||||||
|
std::cout << beast::to_string(sb.data()) << "\n";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[heading Secure WebSocket]
|
||||||
|
|
||||||
|
Establish a WebSocket connection over an encrypted TLS connection,
|
||||||
|
send a message and receive the reply. Requires OpenSSL to build.
|
||||||
|
|
||||||
|
* [@examples/websocket_ssl_example.cpp]
|
||||||
|
|
||||||
|
[heading HTTPS GET]
|
||||||
|
|
||||||
|
This example demonstrates sending and receiving HTTP messages
|
||||||
|
over a TLS connection. Requires OpenSSL to build.
|
||||||
|
|
||||||
|
* [@examples/http_ssl_example.cpp]
|
||||||
|
|
||||||
|
[heading HTTP Crawl]
|
||||||
|
|
||||||
|
This example retrieves the page at each of the most popular domains
|
||||||
|
as measured by Alexa.
|
||||||
|
|
||||||
|
* [@examples/http_crawl.cpp]
|
||||||
|
|
||||||
|
[heading HTTP Server]
|
||||||
|
|
||||||
|
This example demonstrates both synchronous and asynchronous server
|
||||||
|
implementations. It also provides an example of implementing a [*Body]
|
||||||
|
type, in `file_body`.
|
||||||
|
|
||||||
|
* [@examples/file_body.hpp]
|
||||||
|
* [@examples/http_async_server.hpp]
|
||||||
|
* [@examples/http_sync_server.hpp]
|
||||||
|
* [@examples/http_server.cpp]
|
||||||
|
|
||||||
|
[heading Listings]
|
||||||
|
|
||||||
|
These are stand-alone listings of the HTTP and WebSocket examples.
|
||||||
|
|
||||||
|
* [@examples/http_example.cpp]
|
||||||
|
* [@examples/websocket_example.cpp]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
481
doc/http.qbk
481
doc/http.qbk
@@ -5,138 +5,203 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:http HTTP]
|
[/
|
||||||
|
ideas:
|
||||||
|
- complete send request walkthrough (client)
|
||||||
|
- complete receive response walkthrough (client)
|
||||||
|
- complete receive request walkthrough (server)
|
||||||
|
- complete send response walkthrough (server)
|
||||||
|
|
||||||
Beast.HTTP offers programmers simple and performant models of HTTP messages and
|
- Introduce concepts from simple to complex
|
||||||
|
- Smooth progression of new ideas building on the previous ideas
|
||||||
|
|
||||||
|
- do we show a simplified message with collapsed fields?
|
||||||
|
- do we introduce `header` or `message` first?
|
||||||
|
|
||||||
|
|
||||||
|
contents:
|
||||||
|
Message (and header, fields)
|
||||||
|
Create request
|
||||||
|
Create response
|
||||||
|
Algorithms
|
||||||
|
Write
|
||||||
|
Read
|
||||||
|
Parse
|
||||||
|
Examples
|
||||||
|
Send Request
|
||||||
|
Receive Response
|
||||||
|
Receive Request
|
||||||
|
Send Response
|
||||||
|
Advanced
|
||||||
|
Responding to HEAD
|
||||||
|
Expect: 100-continue
|
||||||
|
Body (user defined)
|
||||||
|
|
||||||
|
|
||||||
|
section beast.http.examples Examples
|
||||||
|
|
||||||
|
note
|
||||||
|
In the example code which follows, `socket` refers to an object of type
|
||||||
|
`boost::asio::ip::tcp::socket` which is currently connected to a remote peer.
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:http Using HTTP]
|
||||||
|
|
||||||
|
[block '''
|
||||||
|
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
|
||||||
|
<member><link linkend="beast.http.message">Message</link></member>
|
||||||
|
<member><link linkend="beast.http.fields">Fields</link></member>
|
||||||
|
<member><link linkend="beast.http.body">Body</link></member>
|
||||||
|
<member><link linkend="beast.http.algorithms">Algorithms</link></member>
|
||||||
|
</simplelist></entry></row></tbody></tgroup></informaltable>
|
||||||
|
''']
|
||||||
|
|
||||||
|
Beast offers programmers simple and performant models of HTTP messages and
|
||||||
their associated operations including synchronous and asynchronous reading and
|
their associated operations including synchronous and asynchronous reading and
|
||||||
writing of messages in the HTTP/1 wire format using Boost.Asio.
|
writing of messages and headers in the HTTP/1 wire format using Boost.Asio.
|
||||||
|
|
||||||
The HTTP protocol is described fully in
|
|
||||||
[@https://tools.ietf.org/html/rfc7230 rfc7230]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:motivation Motivation]
|
|
||||||
|
|
||||||
The HTTP protocol is pervasive in network applications. As C++ is a logical
|
|
||||||
choice for high performance network servers, there is great utility in solid
|
|
||||||
building blocks for manipulating, sending, and receiving HTTP messages
|
|
||||||
compliant with the Hypertext Transfer Protocol and the supplements that
|
|
||||||
follow. Unfortunately reliable implementations or industry standards do not
|
|
||||||
exist in C++.
|
|
||||||
|
|
||||||
Beast.HTTP is built on Boost.Asio and uses its own robust header-only HTTP/1
|
|
||||||
message parser modeled after the nodejs http-parser (written in C). A proposal
|
|
||||||
to add networking functionality to the C++ standard library, based on
|
|
||||||
Boost.Asio, is under consideration by the standards committee. Since the final
|
|
||||||
approved networking interface for the C++ standard library will likely closely
|
|
||||||
resemble the current interface of Boost.Asio, it is logical for Beast.HTTP to
|
|
||||||
use Boost.Asio as its network transport.
|
|
||||||
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:scope Scope]
|
|
||||||
|
|
||||||
This library is designed to be a building block for creating higher level
|
|
||||||
libraries. It is not designed to be end-user facing. There is no convenient
|
|
||||||
class that implements the core of a web server, nor is there a convenient
|
|
||||||
class to quickly perform common operations such as fetching a file or
|
|
||||||
connecting and retrieving a document from a secure connection. These
|
|
||||||
use-cases are important, but this library does not try to do that. Instead,
|
|
||||||
it offers primitives that can be used to build those user-facing algorithms.
|
|
||||||
|
|
||||||
A HTTP message (referred to hereafter as "message") contains request or
|
|
||||||
response specific attributes, a series of zero or more name/value pairs
|
|
||||||
(collectively termed "headers"), and a series of octets called the message
|
|
||||||
body which may be zero in length. The HTTP protocol defines the client and
|
|
||||||
server roles: clients send messages called requests and servers send back
|
|
||||||
messages called responses. `http::message` models both requests and responses.
|
|
||||||
Beast aims to offer this functionality:
|
|
||||||
|
|
||||||
* [*Model]: Provide a universal HTTP message class model.
|
|
||||||
|
|
||||||
* [*Build]: Construct a new message and manipulate its contents.
|
|
||||||
|
|
||||||
* [*Parse]: Deserialize a message from a network or memory stream in HTTP/1 wire format.
|
|
||||||
|
|
||||||
* [*Serialize]: Serialize a message into a network or memory stream in HTTP/1 wire format.
|
|
||||||
|
|
||||||
[note The documentation which follows assumes familiarity with
|
|
||||||
both Boost.Asio and the HTTP protocol specification described in
|
|
||||||
[@https://tools.ietf.org/html/rfc7230 rfc7230] ]
|
|
||||||
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:usage Usage]
|
|
||||||
|
|
||||||
[note
|
[note
|
||||||
Sample code and identifiers mentioned in this section are written
|
The following documentation assumes familiarity with both Boost.Asio
|
||||||
as if the following declarations are in effect:
|
and the HTTP protocol specification described in __rfc7230__. Sample code
|
||||||
|
and identifiers mentioned in this section are written as if the following
|
||||||
|
declarations are in effect:
|
||||||
```
|
```
|
||||||
|
#include <beast/core.hpp>
|
||||||
#include <beast/http.hpp>
|
#include <beast/http.hpp>
|
||||||
using namespace beast;
|
using namespace beast;
|
||||||
|
using namespace beast::http;
|
||||||
```
|
```
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
In the paragraphs that follow we describe the available interfaces for
|
|
||||||
performing typical operations such as interacting with a HTTP server
|
|
||||||
or handling simple requests. Subsequent sections cover the message model
|
|
||||||
and its customization points in more depth, for advanced applications.
|
|
||||||
|
|
||||||
[heading Declarations]
|
|
||||||
|
|
||||||
To do anything, a message must be declared. The message class template
|
[section:message Message]
|
||||||
requires at minimum, a value indicating whether the message is a request
|
|
||||||
(versus a response), and a `Body` type. The choice of `Body` determines the
|
The HTTP protocol defines the client and server roles: clients send messages
|
||||||
kind of container used to represent the message body. Here we will
|
called requests and servers send back messages called responses. A HTTP message
|
||||||
declare a HTTP/1 request that has a `std::string` for the body container:
|
(referred to hereafter as "message") contains request or response specific
|
||||||
|
attributes (contained in the "Start Line"), a series of zero or more name/value
|
||||||
|
pairs (collectively termed "Fields"), and an optional series of octets called
|
||||||
|
the message body which may be zero in length. The start line for a HTTP request
|
||||||
|
includes a string called the method, a string called the URL, and a version
|
||||||
|
number indicating HTTP/1.0 or HTTP/1.1. For a response, the start line contains
|
||||||
|
an integer status code and a string called the reason phrase. Alternatively, a
|
||||||
|
HTTP message can be viewed as two parts: a header, followed by a body.
|
||||||
|
|
||||||
|
[note
|
||||||
|
The Reason-Phrase is obsolete as of rfc7230.
|
||||||
|
]
|
||||||
|
|
||||||
|
The __header__ class template models the header for HTTP/1 and HTTP/2 messages.
|
||||||
|
This class template is a family of specializations, one for requests and one
|
||||||
|
for responses, depending on the [*`isRequest`] template value.
|
||||||
|
The [*`Fields`] template type determines the type of associative container
|
||||||
|
used to store the field values. The provided __basic_fields__ class template
|
||||||
|
and __fields__ type alias are typical choices for the [*`Fields`] type, but
|
||||||
|
advanced applications may supply user defined types which meet the requirements.
|
||||||
|
The __message__ class template models the header and optional body for HTTP/1
|
||||||
|
and HTTP/2 requests and responses. It is derived from the __header__ class
|
||||||
|
template with the same shared template parameters, and adds the `body` data
|
||||||
|
member. The message class template requires an additional template argument
|
||||||
|
type [*`Body`]. This type controls the container used to represent the body,
|
||||||
|
if any, as well as the algorithms needed to serialize and parse bodies of
|
||||||
|
that type.
|
||||||
|
|
||||||
|
This illustration shows the declarations and members of the __header__ and
|
||||||
|
__message__ class templates, as well as the inheritance relationship:
|
||||||
|
|
||||||
|
[$images/message.png [width 650px] [height 390px]]
|
||||||
|
|
||||||
|
For notational convenience, these template type aliases are provided which
|
||||||
|
supply typical choices for the [*`Fields`] type:
|
||||||
```
|
```
|
||||||
http::message_v1<true, http::string_body> req;
|
using request_header = header<true, fields>;
|
||||||
|
using response_header = header<false, fields>;
|
||||||
|
|
||||||
|
template<class Body, class Fields = fields>
|
||||||
|
using request = message<true, Body, Fields>;
|
||||||
|
|
||||||
|
template<class Body, class Fields = fields>
|
||||||
|
using response = message<false, Body, Fields>;
|
||||||
```
|
```
|
||||||
|
|
||||||
Two type aliases are provided for notational convenience when declaring
|
The code examples below show how to create and fill in a request and response
|
||||||
HTTP/1 messages. These two statements declare a request and a response
|
object:
|
||||||
respectively:
|
|
||||||
```
|
|
||||||
http::request_v1<http::string_body> req;
|
|
||||||
http::response_v1<http::string_body> resp;
|
|
||||||
```
|
|
||||||
|
|
||||||
[heading Members]
|
[table Create Message
|
||||||
|
[[HTTP Request] [HTTP Response]]
|
||||||
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:
|
request<empty_body> req;
|
||||||
```
|
req.version = 11; // HTTP/1.1
|
||||||
http::request_v1<http::string_body> req;
|
|
||||||
req.method = "GET";
|
req.method = "GET";
|
||||||
req.url = "/index.html";
|
req.url = "/index.htm"
|
||||||
req.version = 11; // HTTP/1.1
|
req.fields.insert("Accept", "text/html");
|
||||||
req.headers.insert("User-Agent", "hello_world");
|
req.fields.insert("Connection", "keep-alive");
|
||||||
req.body = "";
|
req.fields.insert("User-Agent", "Beast");
|
||||||
|
```
|
||||||
|
][
|
||||||
|
```
|
||||||
|
response<string_body> res;
|
||||||
|
res.version = 11; // HTTP/1.1
|
||||||
|
res.status = 200;
|
||||||
|
res.reason = "OK";
|
||||||
|
res.fields.insert("Sever", "Beast");
|
||||||
|
res.fields.insert("Content-Length", 4);
|
||||||
|
res.body = "****";
|
||||||
|
```
|
||||||
|
]]]
|
||||||
|
|
||||||
http::response_v1<http::string_body> resp;
|
In the serialized format of a HTTP message, the header is represented as a
|
||||||
resp.status = 404;
|
series of text lines ending in CRLF (`"\r\n"`). The end of the header is
|
||||||
resp.reason = "Not Found";
|
indicated by a line containing only CRLF. Here are examples of serialized HTTP
|
||||||
resp.version = 10; // HTTP/1.0
|
request and response objects. The objects created above will produce these
|
||||||
resp.headers.insert("Server", "Beast.HTTP");
|
results when serialized. Note that only the response has a body:
|
||||||
resp.body = "The requested resource was not found.";
|
|
||||||
```
|
|
||||||
|
|
||||||
[heading Headers]
|
[table Serialized HTTP Request and Response
|
||||||
|
[[HTTP Request] [HTTP Response]]
|
||||||
|
[[
|
||||||
|
```
|
||||||
|
GET /index.htm HTTP/1.1\r\n
|
||||||
|
Accept: text/html\r\n
|
||||||
|
Connection: keep-alive\r\n
|
||||||
|
User-Agent: Beast\r\n
|
||||||
|
\r\n
|
||||||
|
```
|
||||||
|
][
|
||||||
|
```
|
||||||
|
200 OK HTTP/1.1\r\n
|
||||||
|
Server: Beast\r\n
|
||||||
|
Content-Length: 4\r\n
|
||||||
|
\r\n
|
||||||
|
****
|
||||||
|
```
|
||||||
|
]]]
|
||||||
|
|
||||||
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:
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:fields Fields]
|
||||||
|
|
||||||
|
The [*`Fields`] type represents a container that can set or retrieve the
|
||||||
|
fields in a message. Beast provides the
|
||||||
|
[link beast.ref.http__basic_fields `basic_fields`] class which serves
|
||||||
|
the needs for most users. It supports modification and inspection of values.
|
||||||
|
The field names are not case-sensitive.
|
||||||
|
|
||||||
|
These statements change the values of the headers in the message passed:
|
||||||
```
|
```
|
||||||
template<class Body>
|
template<class Body>
|
||||||
void set_fields(http::request_v1<Body>& req)
|
void set_fields(request<Body>& req)
|
||||||
{
|
{
|
||||||
if(! req.exists("User-Agent"))
|
if(! req.exists("User-Agent"))
|
||||||
req.insert("User-Agent", "myWebClient");
|
req.insert("User-Agent", "myWebClient");
|
||||||
@@ -148,17 +213,24 @@ in the message passed:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
[heading Body]
|
User defined [*`Fields`] types are possible. To support serialization, the
|
||||||
|
type must meet the requirements of __FieldSequence__. To support parsing using
|
||||||
|
the provided parser, the type must provide the `insert` member function.
|
||||||
|
|
||||||
The `message::body` member represents the message body. Depending on the
|
[endsect]
|
||||||
`Body` template argument type, this could be a writable container. The
|
|
||||||
following types, provided by the library, are suitable choices for the
|
|
||||||
`Body` type:
|
|
||||||
|
[section:body Body]
|
||||||
|
|
||||||
|
The message [*`Body`] template parameter controls both the type of the data
|
||||||
|
member of the resulting message object, and the algorithms used during parsing
|
||||||
|
and serialization. Beast provides three very common [*`Body`] types:
|
||||||
|
|
||||||
* [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
|
* [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
|
||||||
Used in GET requests where there is no message body. Example:
|
Used in GET requests where there is no message body. Example:
|
||||||
```
|
```
|
||||||
http::request_v1<http::empty_body> req;
|
request<empty_body> req;
|
||||||
req.version = 11;
|
req.version = 11;
|
||||||
req.method = "GET";
|
req.method = "GET";
|
||||||
req.url = "/index.html";
|
req.url = "/index.html";
|
||||||
@@ -170,36 +242,77 @@ or response with simple text in the message body (such as an error message).
|
|||||||
Has the same insertion complexity of `std::string`. This is the type of body
|
Has the same insertion complexity of `std::string`. This is the type of body
|
||||||
used in the examples:
|
used in the examples:
|
||||||
```
|
```
|
||||||
http::response_v1<http::string_body> resp;
|
response<string_body> res;
|
||||||
static_assert(std::is_same<decltype(resp.body), std::string>::value);
|
static_assert(std::is_same<decltype(res.body), std::string>::value);
|
||||||
resp.body = "Here is the data you requested";
|
res.body = "Here is the data you requested";
|
||||||
```
|
```
|
||||||
|
|
||||||
* [link beast.ref.http__streambuf_body [*`streambuf_body`:]] A body with a
|
* [link beast.ref.http__streambuf_body [*`streambuf_body`:]] A body with a
|
||||||
`value_type` of [link beast.ref.streambuf `streambuf`]: an efficient storage
|
`value_type` of [link beast.ref.streambuf `streambuf`]: an efficient storage
|
||||||
object which uses multiple octet arrays of varying lengths to represent data.
|
object which uses multiple octet arrays of varying lengths to represent data.
|
||||||
|
|
||||||
[heading Sockets]
|
[heading Advanced]
|
||||||
|
|
||||||
The library provides simple free functions modeled after Boost.Asio to
|
User-defined types are possible for the message body, where the type meets the
|
||||||
send and receive messages on TCP/IP sockets, SSL streams, or any object
|
[link beast.ref.Body [*`Body`]] requirements. This simplified class declaration
|
||||||
which meets the Boost.Asio type requirements (SyncReadStream, SyncWriteStream,
|
shows the customization points available to user-defined body types:
|
||||||
AsyncReadStream, and AsyncWriteStream depending on the types of operations
|
|
||||||
performed). To send messages synchronously, use one of the `http:write`
|
[$images/body.png [width 510px] [height 210px]]
|
||||||
functions:
|
|
||||||
|
* [*`value_type`]: Determines the type of the
|
||||||
|
[link beast.ref.http__message.body `message::body`] member. If this
|
||||||
|
type defines default construction, move, copy, or swap, then message objects
|
||||||
|
declared with this [*`Body`] will have those operations defined.
|
||||||
|
|
||||||
|
* [*`reader`]: An optional nested type meeting the requirements of
|
||||||
|
[link beast.ref.Reader [*`Reader`]]. If present, this defines the algorithm
|
||||||
|
used for parsing bodies of this type.
|
||||||
|
|
||||||
|
* [*`writer`]: An optional nested type meeting the requirements of
|
||||||
|
[link beast.ref.Writer [*`Writer`]]. If present, this defines the algorithm
|
||||||
|
used for serializing bodies of this type.
|
||||||
|
|
||||||
|
The examples included with this library provide a Body implementation that
|
||||||
|
serializing message bodies that come from a file.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:algorithms Algorithms]
|
||||||
|
|
||||||
|
Algorithms are provided to serialize and deserialize HTTP/1 messages on
|
||||||
|
streams.
|
||||||
|
|
||||||
|
* [link beast.ref.http__read [*read]]: Deserialize a HTTP/1 __header__ or __message__ from a stream.
|
||||||
|
* [link beast.ref.http__write [*write]]: Serialize a HTTP/1 __header__ or __message__ to a stream.
|
||||||
|
|
||||||
|
Asynchronous versions of these algorithms are also available:
|
||||||
|
|
||||||
|
* [link beast.ref.http__async_read [*async_read]]: Deserialize a HTTP/1 __header__ or __message__ asynchronously from a stream.
|
||||||
|
* [link beast.ref.http__async_write [*async_write]]: Serialize a HTTP/1 __header__ or __message__ asynchronously to a stream.
|
||||||
|
|
||||||
|
[heading Using Sockets]
|
||||||
|
|
||||||
|
The free function algorithms are 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
|
||||||
|
[link beast.ref.http__write `write`] functions:
|
||||||
```
|
```
|
||||||
void send_request(boost::asio::ip::tcp::socket& sock)
|
void send_request(boost::asio::ip::tcp::socket& sock)
|
||||||
{
|
{
|
||||||
http::request<http::empty_body> req;
|
request<empty_body> req;
|
||||||
req.version = 11;
|
req.version = 11;
|
||||||
req.method = "GET";
|
req.method = "GET";
|
||||||
req.url = "/index.html";
|
req.url = "/index.html";
|
||||||
...
|
...
|
||||||
http::write(sock, req); // Throws exception on error
|
write(sock, req); // Throws exception on error
|
||||||
...
|
...
|
||||||
// Alternatively
|
// Alternatively
|
||||||
boost::system::error:code ec;
|
boost::system::error:code ec;
|
||||||
http::write(sock, req, ec);
|
write(sock, req, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
std::cerr << "error writing http message: " << ec.message();
|
std::cerr << "error writing http message: " << ec.message();
|
||||||
}
|
}
|
||||||
@@ -209,27 +322,27 @@ An asynchronous interface is available:
|
|||||||
```
|
```
|
||||||
void handle_write(boost::system::error_code);
|
void handle_write(boost::system::error_code);
|
||||||
...
|
...
|
||||||
http::request_v1<http::empty_body> req;
|
request<empty_body> req;
|
||||||
...
|
...
|
||||||
http::async_write(sock, req, std::bind(&handle_write, std::placeholders::_1));
|
async_write(sock, req, std::bind(&handle_write, std::placeholders::_1));
|
||||||
```
|
```
|
||||||
|
|
||||||
When the implementation reads messages from a socket, it can read bytes lying
|
When the implementation reads messages from a socket, it can read bytes lying
|
||||||
after the end of the message if they are present (the alternative is to read
|
after the end of the message if they are present (the alternative is to read
|
||||||
a single byte at a time which is unsuitable for performance reasons). To
|
a single byte at a time which is unsuitable for performance reasons). To
|
||||||
store and re-use these extra bytes on subsequent messages, the read interface
|
store and re-use these extra bytes on subsequent messages, the read interface
|
||||||
requires an additional parameter: a [link beast.types.DynamicBuffer [*`DynamicBuffer`]]
|
requires an additional parameter: a [link beast.ref.DynamicBuffer [*`DynamicBuffer`]]
|
||||||
object. This example reads a message from the socket, with the extra bytes
|
object. This example reads a message from the socket, with the extra bytes
|
||||||
stored in the streambuf parameter for use in a subsequent call to read:
|
stored in the streambuf parameter for use in a subsequent call to read:
|
||||||
```
|
```
|
||||||
boost::asio::streambuf sb;
|
boost::asio::streambuf sb;
|
||||||
...
|
...
|
||||||
http::response_v1<http::string_body> resp;
|
response<string_body> res;
|
||||||
http::read(sock, sb, resp); // Throws exception on error
|
read(sock, sb, res); // Throws exception on error
|
||||||
...
|
...
|
||||||
// Alternatively
|
// Alternatively
|
||||||
boost::system::error:code ec;
|
boost::system::error:code ec;
|
||||||
http::read(sock, sb, resp, ec);
|
read(sock, sb, res, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
std::cerr << "error reading http message: " << ec.message();
|
std::cerr << "error reading http message: " << ec.message();
|
||||||
```
|
```
|
||||||
@@ -241,116 +354,28 @@ called:
|
|||||||
void handle_read(boost::system::error_code);
|
void handle_read(boost::system::error_code);
|
||||||
...
|
...
|
||||||
boost::asio::streambuf sb;
|
boost::asio::streambuf sb;
|
||||||
http::response_v1<http::string_body> resp;
|
response<string_body> res;
|
||||||
...
|
...
|
||||||
http::async_read(sock, resp, std::bind(&handle_read, std::placeholders::_1));
|
async_read(sock, res, std::bind(&handle_read, std::placeholders::_1));
|
||||||
```
|
```
|
||||||
|
|
||||||
An alternative to using a `boost::asio::streambuf` is to use a
|
An alternative to using a `boost::asio::streambuf` is to use a
|
||||||
[link beast.ref.streambuf `beast::streambuf`], which meets the requirements of
|
__streambuf__, which meets the requirements of __DynamicBuffer__ and
|
||||||
[*`DynamicBuffer`] and is optimized for performance:
|
is optimized for performance:
|
||||||
```
|
```
|
||||||
void handle_read(boost::system::error_code);
|
void handle_read(boost::system::error_code);
|
||||||
...
|
...
|
||||||
beast::streambuf sb;
|
beast::streambuf sb;
|
||||||
http::response_v1<http::string_body> resp;
|
response<string_body> res;
|
||||||
http::read(sock, sb, resp);
|
read(sock, sb, res);
|
||||||
```
|
```
|
||||||
|
|
||||||
The `read` implementation can use any object meeting the requirements of
|
The `read` implementation can use any object meeting the requirements of
|
||||||
[link beast.types.DynamicBuffer [*`DynamicBuffer`]], allowing callers to define custom
|
__DynamicBuffer__, allowing callers to define custom
|
||||||
memory management strategies used by the implementation.
|
memory management strategies used by the implementation.
|
||||||
|
|
||||||
[endsect]
|
[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]
|
[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/CppCon2016.pdf
Normal file
BIN
doc/images/CppCon2016.pdf
Normal file
Binary file not shown.
BIN
doc/images/CppCon2016.png
Normal file
BIN
doc/images/CppCon2016.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 241 KiB After Width: | Height: | Size: 145 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 30 KiB |
Binary file not shown.
@@ -8,6 +8,7 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<section id="index">
|
<section id="beast.index">
|
||||||
|
<title>Index</title>
|
||||||
<index/>
|
<index/>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
2
doc/makeqbk.sh
Normal file → Executable file
2
doc/makeqbk.sh
Normal file → Executable file
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
#
|
#
|
||||||
|
|||||||
235
doc/master.qbk
235
doc/master.qbk
@@ -8,7 +8,7 @@
|
|||||||
[library Beast
|
[library Beast
|
||||||
[quickbook 1.6]
|
[quickbook 1.6]
|
||||||
[copyright 2013 - 2016 Vinnie Falco]
|
[copyright 2013 - 2016 Vinnie Falco]
|
||||||
[purpose C++ Library]
|
[purpose Networking Protocol Library]
|
||||||
[license
|
[license
|
||||||
Distributed under the Boost Software License, Version 1.0.
|
Distributed under the Boost Software License, Version 1.0.
|
||||||
(See accompanying file LICENSE_1_0.txt or copy at
|
(See accompanying file LICENSE_1_0.txt or copy at
|
||||||
@@ -22,174 +22,85 @@
|
|||||||
[template mdash[] '''— ''']
|
[template mdash[] '''— ''']
|
||||||
[template indexterm1[term1] '''<indexterm><primary>'''[term1]'''</primary></indexterm>''']
|
[template indexterm1[term1] '''<indexterm><primary>'''[term1]'''</primary></indexterm>''']
|
||||||
[template indexterm2[term1 term2] '''<indexterm><primary>'''[term1]'''</primary><secondary>'''[term2]'''</secondary></indexterm>''']
|
[template indexterm2[term1 term2] '''<indexterm><primary>'''[term1]'''</primary><secondary>'''[term2]'''</secondary></indexterm>''']
|
||||||
[def __POSIX__ /POSIX/]
|
|
||||||
[def __Windows__ /Windows/]
|
|
||||||
[def __accept__ [@http://www.opengroup.org/onlinepubs/000095399/functions/accept.html `accept()`]]
|
|
||||||
[def __connect__ [@http://www.opengroup.org/onlinepubs/000095399/functions/connect.html `connect()`]]
|
|
||||||
[def __getpeername__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getpeername.html `getpeername()`]]
|
|
||||||
[def __getsockname__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getsockname.html `getsockname()`]]
|
|
||||||
[def __getsockopt__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getsockopt.html `getsockopt()`]]
|
|
||||||
[def __ioctl__ [@http://www.opengroup.org/onlinepubs/000095399/functions/ioctl.html `ioctl()`]]
|
|
||||||
[def __recvfrom__ [@http://www.opengroup.org/onlinepubs/000095399/functions/recvfrom.html `recvfrom()`]]
|
|
||||||
[def __sendto__ [@http://www.opengroup.org/onlinepubs/000095399/functions/sendto.html `sendto()`]]
|
|
||||||
[def __setsockopt__ [@http://www.opengroup.org/onlinepubs/000095399/functions/setsockopt.html `setsockopt()`]]
|
|
||||||
[def __socket__ [@http://www.opengroup.org/onlinepubs/000095399/functions/socket.html `socket()`]]
|
|
||||||
|
|
||||||
|
[def __N4588__ [@http://cplusplus.github.io/networking-ts/draft.pdf [*N4588]]]
|
||||||
|
[def __rfc6455__ [@https://tools.ietf.org/html/rfc6455 rfc6455]]
|
||||||
|
[def __rfc7230__ [@https://tools.ietf.org/html/rfc7230 rfc7230]]
|
||||||
|
|
||||||
|
[def __asio_handler_invoke__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asio_handler_invoke.html `asio_handler_invoke`]]
|
||||||
|
[def __asio_handler_allocate__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asio_handler_allocate.html `asio_handler_allocate`]]
|
||||||
|
[def __void_or_deduced__ [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.return_type_of_an_initiating_function ['void-or-deduced]]]
|
||||||
|
|
||||||
[section:intro Introduction]
|
[def __AsyncReadStream__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncReadStream.html [*AsyncReadStream]]]
|
||||||
|
[def __AsyncWriteStream__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*AsyncWriteStream]]]
|
||||||
|
[def __CompletionHandler__ [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/CompletionHandler.html [*CompletionHandler]]]
|
||||||
|
[def __ConstBufferSequence__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*ConstBufferSequence]]]
|
||||||
|
[def __MutableBufferSequence__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*MutableBufferSequence]]]
|
||||||
|
[def __SyncReadStream__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncReadStream.html [*SyncReadStream]]]
|
||||||
|
[def __SyncWriteStream__ [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html [*SyncWriteStream]]]
|
||||||
|
|
||||||
Beast is a header-only, cross-platform C++ library built on Boost.Asio and
|
[def __Body__ [link beast.ref.Body [*`Body`]]]
|
||||||
Boost, containing two modules implementing widely used network protocols.
|
[def __DynamicBuffer__ [link beast.ref.DynamicBuffer [*DynamicBuffer]]]
|
||||||
Beast.HTTP offers a universal model for describing, sending, and receiving
|
[def __FieldSequence__ [link beast.ref.FieldSequence [*FieldSequence]]]
|
||||||
HTTP messages while Beast.WebSocket provides a complete implementation of
|
[def __Parser__ [link beast.ref.Parser [*`Parser`]]]
|
||||||
the WebSocket protocol. Their design achieves these goals:
|
|
||||||
|
|
||||||
* [*Symmetry.] Interfaces are role-agnostic; the same interfaces can be
|
[def __basic_fields__ [link beast.ref.http__basic_fields `basic_fields`]]
|
||||||
used to build clients, servers, or both.
|
[def __fields__ [link beast.ref.http__fields `fields`]]
|
||||||
|
[def __header__ [link beast.ref.http__header `header`]]
|
||||||
* [*Ease of Use.] HTTP messages are modeled using simple, readily
|
[def __message__ [link beast.ref.http__message `message`]]
|
||||||
accessible objects. Functions and classes used to send and receive HTTP
|
[def __streambuf__ [link beast.ref.streambuf `streambuf`]]
|
||||||
or WebSocket messages are designed to resemble Boost.Asio as closely as
|
[def __basic_streambuf__ [link beast.ref.basic_streambuf `basic_streambuf`]]
|
||||||
possible. Users familiar with Boost.Asio will be immediately comfortable
|
|
||||||
using this library.
|
|
||||||
|
|
||||||
* [*Flexibility.] Interfaces do not mandate specific implementation
|
|
||||||
strategies; important decisions such as buffer or thread management are
|
|
||||||
left to users of the library.
|
|
||||||
|
|
||||||
* [*Performance.] The implementation performs competitively, making it a
|
|
||||||
realistic choice for building high performance network servers.
|
|
||||||
|
|
||||||
* [*Scalability.] Development of network applications that scale to thousands
|
|
||||||
of concurrent connections is possible with the implementation.
|
|
||||||
|
|
||||||
* [*Basis for further abstraction.] The interfaces facilitate the
|
|
||||||
development of other libraries that provide higher levels of abstraction.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:requirements Requirements]
|
|
||||||
|
|
||||||
Beast requires:
|
|
||||||
|
|
||||||
* [*C++11.] A minimum of C++11 is needed.
|
|
||||||
* [*Boost.] Beast is built on Boost, especially Boost.Asio.
|
|
||||||
* [*OpenSSL.] If using TLS/Secure sockets (optional).
|
|
||||||
|
|
||||||
[note Tested compilers: msvc-14+, gcc 5+, clang 3.6+]
|
|
||||||
|
|
||||||
The library is [*header-only]. It is not necessary to add any .cpp files,
|
|
||||||
or to edit your existing build script or project file except to provide
|
|
||||||
that the include/ directory for beast is searched for include files.
|
|
||||||
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:example Examples]
|
|
||||||
|
|
||||||
These usage examples are intended to quickly impress upon readers the
|
|
||||||
flavor of the library. They are complete programs which may be built
|
|
||||||
and run. Source code and build scripts for these programs may be found
|
|
||||||
in the examples directory.
|
|
||||||
|
|
||||||
Use HTTP to request the root page from a website and print the response:
|
|
||||||
```
|
|
||||||
#include <beast/http.hpp>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
// Normal boost::asio setup
|
|
||||||
std::string const host = "boost.org";
|
|
||||||
boost::asio::io_service ios;
|
|
||||||
boost::asio::ip::tcp::resolver r{ios};
|
|
||||||
boost::asio::ip::tcp::socket sock{ios};
|
|
||||||
boost::asio::connect(sock,
|
|
||||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
|
|
||||||
|
|
||||||
// Send HTTP request using beast
|
|
||||||
beast::http::request_v1<beast::http::empty_body> req;
|
|
||||||
req.method = "GET";
|
|
||||||
req.url = "/";
|
|
||||||
req.version = 11;
|
|
||||||
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
|
|
||||||
req.headers.replace("User-Agent", "Beast");
|
|
||||||
beast::http::prepare(req);
|
|
||||||
beast::http::write(sock, req);
|
|
||||||
|
|
||||||
// Receive and print HTTP response using beast
|
|
||||||
beast::streambuf sb;
|
|
||||||
beast::http::response_v1<beast::http::streambuf_body> resp;
|
|
||||||
beast::http::read(sock, sb, resp);
|
|
||||||
std::cout << resp;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Establish a WebSocket connection, send a message and receive the reply:
|
|
||||||
```
|
|
||||||
#include <beast/core/to_string.hpp>
|
|
||||||
#include <beast/websocket.hpp>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
// Normal boost::asio setup
|
|
||||||
std::string const host = "echo.websocket.org";
|
|
||||||
boost::asio::io_service ios;
|
|
||||||
boost::asio::ip::tcp::resolver r{ios};
|
|
||||||
boost::asio::ip::tcp::socket sock{ios};
|
|
||||||
boost::asio::connect(sock,
|
|
||||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
|
|
||||||
|
|
||||||
// WebSocket connect and send message using beast
|
|
||||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
|
||||||
ws.handshake(host, "/");
|
|
||||||
ws.write(boost::asio::buffer("Hello, world!"));
|
|
||||||
|
|
||||||
// Receive WebSocket message, print and close using beast
|
|
||||||
beast::streambuf sb;
|
|
||||||
beast::websocket::opcode op;
|
|
||||||
ws.read(op, sb);
|
|
||||||
ws.close(beast::websocket::close_code::normal);
|
|
||||||
std::cout << to_string(sb.data()) << "\n";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:credits Credits]
|
|
||||||
|
|
||||||
Boost.Asio is the inspiration behind which all of the interfaces and
|
|
||||||
implementation strategies are built. Some parts of the documentation are
|
|
||||||
written to closely resemble the wording and presentation of Boost.Asio
|
|
||||||
documentation. Credit goes to Christopher Kohloff for the wonderful
|
|
||||||
Asio library and the ideas upon which Beast is built.
|
|
||||||
|
|
||||||
Beast would not be possible without the considerable time and patience
|
|
||||||
contributed by David Schwartz, Edward Hennis, Howard Hinnant, Miguel Portilla,
|
|
||||||
Nikolaos Bougalis, Scott Determan, Scott Schurr, and Ripple Labs for
|
|
||||||
supporting its development.
|
|
||||||
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
|
Beast is a cross-platform, header-only C++ library built on Boost.Asio that
|
||||||
|
provides implementations of the HTTP and WebSocket protocols.
|
||||||
|
|
||||||
|
[variablelist
|
||||||
|
[[
|
||||||
|
[link beast.overview Overview]
|
||||||
|
][
|
||||||
|
An introduction with features, requirements, and credits.
|
||||||
|
]]
|
||||||
|
[[
|
||||||
|
[link beast.http Using HTTP]
|
||||||
|
][
|
||||||
|
How to use Beast's HTTP interfaces in your applications.
|
||||||
|
]]
|
||||||
|
[[
|
||||||
|
[link beast.websocket Using WebSocket]
|
||||||
|
][
|
||||||
|
How to use Beast's WebSocket interfaces in your applications.
|
||||||
|
]]
|
||||||
|
[[
|
||||||
|
[link beast.example Examples]
|
||||||
|
][
|
||||||
|
Examples that illustrate the use of Beast in more complex applications.
|
||||||
|
]]
|
||||||
|
[[
|
||||||
|
[link beast.design Design]
|
||||||
|
][
|
||||||
|
Design rationale, answers to review questions, and
|
||||||
|
other library comparisons.
|
||||||
|
]]
|
||||||
|
[[
|
||||||
|
[link beast.ref Reference]
|
||||||
|
][
|
||||||
|
Detailed class and function reference.
|
||||||
|
]]
|
||||||
|
[[
|
||||||
|
[link beast.index Index]
|
||||||
|
][
|
||||||
|
Book-style text index of Beast documentation.
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
|
||||||
|
[include overview.qbk]
|
||||||
[include http.qbk]
|
[include http.qbk]
|
||||||
[include websocket.qbk]
|
[include websocket.qbk]
|
||||||
|
[include examples.qbk]
|
||||||
|
[include design.qbk]
|
||||||
|
|
||||||
[section:types Type Requirements]
|
[section:ref Reference]
|
||||||
|
[xinclude quickref.xml]
|
||||||
[include types/Body.qbk]
|
[include types/Body.qbk]
|
||||||
[include types/BufferSequence.qbk]
|
[include types/BufferSequence.qbk]
|
||||||
[include types/DynamicBuffer.qbk]
|
[include types/DynamicBuffer.qbk]
|
||||||
@@ -199,13 +110,7 @@ supporting its development.
|
|||||||
[include types/Reader.qbk]
|
[include types/Reader.qbk]
|
||||||
[include types/Streams.qbk]
|
[include types/Streams.qbk]
|
||||||
[include types/Writer.qbk]
|
[include types/Writer.qbk]
|
||||||
|
[include reference.qbk]
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
[include design.qbk]
|
|
||||||
[section:quickref Quick Reference]
|
|
||||||
[xinclude quickref.xml]
|
|
||||||
[endsect]
|
|
||||||
[include reference.qbk]
|
|
||||||
[section:idx Index]
|
|
||||||
[xinclude index.xml]
|
[xinclude index.xml]
|
||||||
[endsect]
|
|
||||||
|
|||||||
114
doc/overview.qbk
Normal file
114
doc/overview.qbk
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
[/
|
||||||
|
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:overview Introduction]
|
||||||
|
|
||||||
|
Beast is a header-only, cross-platform C++ library built on Boost.Asio and
|
||||||
|
parts of Boost, containing two modules implementing widely used network
|
||||||
|
protocols. Beast offers a universal HTTP message model, plus algorithms for
|
||||||
|
parsing and serializing HTTP/1 messages. Beast.WebSocket provides a complete
|
||||||
|
implementation of the WebSocket protocol. Their design achieves these goals:
|
||||||
|
|
||||||
|
* [*Symmetry.] Interfaces are role-agnostic; the same interfaces can be
|
||||||
|
used to build clients, servers, or both.
|
||||||
|
|
||||||
|
* [*Ease of Use.] HTTP messages are modeled using simple, readily
|
||||||
|
accessible objects. Functions and classes used to send and receive HTTP
|
||||||
|
or WebSocket messages are designed to resemble Boost.Asio as closely as
|
||||||
|
possible. Users familiar with Boost.Asio will be immediately comfortable
|
||||||
|
using this library.
|
||||||
|
|
||||||
|
* [*Flexibility.] Interfaces do not mandate specific implementation
|
||||||
|
strategies; important decisions such as buffer or thread management are
|
||||||
|
left to users of the library.
|
||||||
|
|
||||||
|
* [*Performance.] The implementation performs competitively, making it a
|
||||||
|
realistic choice for building high performance network servers.
|
||||||
|
|
||||||
|
* [*Scalability.] Development of network applications that scale to thousands
|
||||||
|
of concurrent connections is possible with the implementation.
|
||||||
|
|
||||||
|
* [*Basis for further abstraction.] The interfaces facilitate the
|
||||||
|
development of other libraries that provide higher levels of abstraction.
|
||||||
|
|
||||||
|
The HTTP portion of Beast is designed to be a low-level building block for
|
||||||
|
creating higher level libraries. It implements only the HTTP protocol, and
|
||||||
|
does not handle domain specific features (for example: cookies, redirects, or
|
||||||
|
deflate content encodings).
|
||||||
|
|
||||||
|
[heading 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 add commands to your build script for building Beast. To link your
|
||||||
|
program successfully, you'll need to add the Boost.System library to link
|
||||||
|
with. If you use coroutines you'll also need the Boost.Coroutine library.
|
||||||
|
Please visit the Boost documentation for instructions on how to do this for
|
||||||
|
your particular build system.
|
||||||
|
|
||||||
|
[heading Motivation]
|
||||||
|
|
||||||
|
Beast is built on Boost.Asio A proposal to add networking functionality to the
|
||||||
|
C++ standard library, based on Boost.Asio, is under consideration by the
|
||||||
|
committee and on track for standardization. Since the final approved networking
|
||||||
|
interface for the C++ standard library will likely closely resemble the current
|
||||||
|
interface of Boost.Asio, the choice of Boost.Asio as the network transport
|
||||||
|
layer is prudent.
|
||||||
|
|
||||||
|
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++. The development of higher level libraries is stymied by the
|
||||||
|
lack of a common set of low-level algorithms and types for interacting with
|
||||||
|
the HTTP protocol.
|
||||||
|
|
||||||
|
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 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 a
|
||||||
|
[link beast.ref.websocket__stream `websocket::stream`].
|
||||||
|
All operations are templated and transparent to the compiler, allowing for
|
||||||
|
maximum inlining and optimization.
|
||||||
|
|
||||||
|
[heading Credits]
|
||||||
|
|
||||||
|
Boost.Asio is the inspiration behind which all of the interfaces and
|
||||||
|
implementation strategies are built. Some parts of the documentation are
|
||||||
|
written to closely resemble the wording and presentation of Boost.Asio
|
||||||
|
documentation. Credit goes to Christopher Kohlhoff for the wonderful
|
||||||
|
Asio library and the ideas upon which Beast is built.
|
||||||
|
|
||||||
|
Beast would not be possible without the considerable time and patience
|
||||||
|
contributed by David Schwartz, Edward Hennis, Howard Hinnant, Miguel Portilla,
|
||||||
|
Nikolaos Bougalis, Scott Determan, Scott Schurr, and Ripple Labs for
|
||||||
|
supporting its development.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
139
doc/quickref.xml
139
doc/quickref.xml
@@ -16,10 +16,10 @@
|
|||||||
<colspec colname="d"/>
|
<colspec colname="d"/>
|
||||||
<thead>
|
<thead>
|
||||||
<row>
|
<row>
|
||||||
<entry valign="center" namest="a" nameend="b">
|
<entry valign="center" namest="a" nameend="c">
|
||||||
<bridgehead renderas="sect2">HTTP</bridgehead>
|
<bridgehead renderas="sect2">HTTP</bridgehead>
|
||||||
</entry>
|
</entry>
|
||||||
<entry valign="center" namest="c" nameend="d">
|
<entry valign="center" namest="d" nameend="d">
|
||||||
<bridgehead renderas="sect2">WebSocket</bridgehead>
|
<bridgehead renderas="sect2">WebSocket</bridgehead>
|
||||||
</entry>
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
@@ -30,53 +30,82 @@
|
|||||||
<bridgehead renderas="sect3">Classes</bridgehead>
|
<bridgehead renderas="sect3">Classes</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.http__basic_dynabuf_body">basic_dynabuf_body</link></member>
|
<member><link linkend="beast.ref.http__basic_dynabuf_body">basic_dynabuf_body</link></member>
|
||||||
<member><link linkend="beast.ref.http__basic_headers">basic_headers</link></member>
|
<member><link linkend="beast.ref.http__basic_fields">basic_fields</link></member>
|
||||||
<member><link linkend="beast.ref.http__basic_parser_v1">basic_parser_v1</link></member>
|
<member><link linkend="beast.ref.http__basic_parser_v1">basic_parser_v1</link></member>
|
||||||
<member><link linkend="beast.ref.http__empty_body">empty_body</link></member>
|
<member><link linkend="beast.ref.http__empty_body">empty_body</link></member>
|
||||||
<member><link linkend="beast.ref.http__headers">headers</link></member>
|
<member><link linkend="beast.ref.http__fields">fields</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__header">header</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__header_parser_v1">header_parser_v1</link></member>
|
||||||
<member><link linkend="beast.ref.http__message">message</link></member>
|
<member><link linkend="beast.ref.http__message">message</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__parser_v1">parser_v1</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__request">request</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__request_header">request_header</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__response">response</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__response_header">response_header</link></member>
|
||||||
<member><link linkend="beast.ref.http__resume_context">resume_context</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__streambuf_body">streambuf_body</link></member>
|
||||||
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
|
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">Options</bridgehead>
|
<bridgehead renderas="sect3">rfc7230</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.http__body_max_size">body_max_size</link></member>
|
|
||||||
<member><link linkend="beast.ref.http__headers_max_size">headers_max_size</link></member>
|
<member><link linkend="beast.ref.http__ext_list">ext_list</link></member>
|
||||||
<member><link linkend="beast.ref.http__skip_body">skip_body</link></member>
|
<member><link linkend="beast.ref.http__param_list">param_list</link></member>
|
||||||
</simplelist>
|
<member><link linkend="beast.ref.http__token_list">token_list</link></member>
|
||||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
|
||||||
<simplelist type="vert" columns="1">
|
|
||||||
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
|
|
||||||
<member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member>
|
|
||||||
<member><link linkend="beast.ref.http__is_ReadableBody">is_ReadableBody</link></member>
|
|
||||||
<member><link linkend="beast.ref.http__is_WritableBody">is_WritableBody</link></member>
|
|
||||||
</simplelist>
|
</simplelist>
|
||||||
</entry>
|
</entry>
|
||||||
<entry valign="top">
|
<entry valign="top">
|
||||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.http__async_parse">async_parse</link></member>
|
|
||||||
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
|
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__async_parse">async_parse</link></member>
|
||||||
<member><link linkend="beast.ref.http__async_write">async_write</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__swap">swap</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__is_keep_alive">is_keep_alive</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__is_upgrade">is_upgrade</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__operator_ls_">operator<<</link></member>
|
||||||
<member><link linkend="beast.ref.http__parse">parse</link></member>
|
<member><link linkend="beast.ref.http__parse">parse</link></member>
|
||||||
<member><link linkend="beast.ref.http__prepare">prepare</link></member>
|
<member><link linkend="beast.ref.http__prepare">prepare</link></member>
|
||||||
<member><link linkend="beast.ref.http__read">read</link></member>
|
<member><link linkend="beast.ref.http__read">read</link></member>
|
||||||
<member><link linkend="beast.ref.http__swap">swap</link></member>
|
<member><link linkend="beast.ref.http__reason_string">reason_string</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__with_body">with_body</link></member>
|
||||||
<member><link linkend="beast.ref.http__write">write</link></member>
|
<member><link linkend="beast.ref.http__write">write</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
|
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__is_Reader">is_Reader</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__is_Writer">is_Writer</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__has_reader">has_reader</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__has_writer">has_writer</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Options</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.http__header_max_size">header_max_size</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__body_max_size">body_max_size</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__skip_body">skip_body</link></member>
|
||||||
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">Constants</bridgehead>
|
<bridgehead renderas="sect3">Constants</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.http__body_what">body_what</link></member>
|
||||||
<member><link linkend="beast.ref.http__connection">connection</link></member>
|
<member><link linkend="beast.ref.http__connection">connection</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__no_content_length">no_content_length</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__parse_error">parse_error</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__parse_flag">parse_flag</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">Concepts</bridgehead>
|
<bridgehead renderas="sect3">Concepts</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.types.Body">Body</link></member>
|
<member><link linkend="beast.ref.Body">Body</link></member>
|
||||||
<member><link linkend="beast.types.Field">Field</link></member>
|
<member><link linkend="beast.ref.Field">Field</link></member>
|
||||||
<member><link linkend="beast.types.FieldSequence">FieldSequence</link></member>
|
<member><link linkend="beast.ref.FieldSequence">FieldSequence</link></member>
|
||||||
<member><link linkend="beast.types.Parser">Parser</link></member>
|
<member><link linkend="beast.ref.Parser">Parser</link></member>
|
||||||
<member><link linkend="beast.types.Reader">Reader</link></member>
|
<member><link linkend="beast.ref.Reader">Reader</link></member>
|
||||||
<member><link linkend="beast.types.Writer">Writer</link></member>
|
<member><link linkend="beast.ref.Writer">Writer</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
</entry>
|
</entry>
|
||||||
<entry valign="top">
|
<entry valign="top">
|
||||||
@@ -88,24 +117,22 @@
|
|||||||
<member><link linkend="beast.ref.websocket__reason_string">reason_string</link></member>
|
<member><link linkend="beast.ref.websocket__reason_string">reason_string</link></member>
|
||||||
<member><link linkend="beast.ref.websocket__teardown_tag">teardown_tag</link></member>
|
<member><link linkend="beast.ref.websocket__teardown_tag">teardown_tag</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">Options</bridgehead>
|
|
||||||
<simplelist type="vert" columns="1">
|
|
||||||
<member><link linkend="beast.ref.websocket__auto_fragment_size">auto_fragment_size</link></member>
|
|
||||||
<member><link linkend="beast.ref.websocket__decorate">decorate</link></member>
|
|
||||||
<member><link linkend="beast.ref.websocket__keep_alive">keep_alive</link></member>
|
|
||||||
<member><link linkend="beast.ref.websocket__mask_buffer_size">mask_buffer_size</link></member>
|
|
||||||
<member><link linkend="beast.ref.websocket__message_type">message_type</link></member>
|
|
||||||
<member><link linkend="beast.ref.websocket__pong_callback">pong_callback</link></member>
|
|
||||||
<member><link linkend="beast.ref.websocket__read_buffer_size">read_buffer_size</link></member>
|
|
||||||
<member><link linkend="beast.ref.websocket__read_message_max">read_message_max</link></member>
|
|
||||||
</simplelist>
|
|
||||||
</entry>
|
|
||||||
<entry valign="top">
|
|
||||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.websocket__async_teardown">async_teardown</link></member>
|
<member><link linkend="beast.ref.websocket__async_teardown">async_teardown</link></member>
|
||||||
<member><link linkend="beast.ref.websocket__teardown">teardown</link></member>
|
<member><link linkend="beast.ref.websocket__teardown">teardown</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
|
<bridgehead renderas="sect3">Options</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.websocket__auto_fragment">auto_fragment</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__message_type">message_type</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__pong_callback">pong_callback</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__read_buffer_size">read_buffer_size</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__read_message_max">read_message_max</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__write_buffer_size">write_buffer_size</link></member>
|
||||||
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">Constants</bridgehead>
|
<bridgehead renderas="sect3">Constants</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.websocket__close_code">close_code</link></member>
|
<member><link linkend="beast.ref.websocket__close_code">close_code</link></member>
|
||||||
@@ -121,11 +148,15 @@
|
|||||||
<colspec colname="b"/>
|
<colspec colname="b"/>
|
||||||
<colspec colname="c"/>
|
<colspec colname="c"/>
|
||||||
<colspec colname="d"/>
|
<colspec colname="d"/>
|
||||||
|
<colspec colname="e"/>
|
||||||
<thead>
|
<thead>
|
||||||
<row>
|
<row>
|
||||||
<entry valign="center" namest="a" nameend="d">
|
<entry valign="center" namest="a" nameend="d">
|
||||||
<bridgehead renderas="sect2">Core</bridgehead>
|
<bridgehead renderas="sect2">Core</bridgehead>
|
||||||
</entry>
|
</entry>
|
||||||
|
<entry valign="center" namest="e" nameend="e">
|
||||||
|
<bridgehead renderas="sect2">ZLib</bridgehead>
|
||||||
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -138,9 +169,12 @@
|
|||||||
<member><link linkend="beast.ref.buffers_adapter">buffers_adapter</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.consuming_buffers">consuming_buffers</link></member>
|
||||||
<member><link linkend="beast.ref.dynabuf_readstream">dynabuf_readstream</link></member>
|
<member><link linkend="beast.ref.dynabuf_readstream">dynabuf_readstream</link></member>
|
||||||
|
<member><link linkend="beast.ref.errc">errc</link></member>
|
||||||
|
<member><link linkend="beast.ref.error_category">error_category</link></member>
|
||||||
<member><link linkend="beast.ref.error_code">error_code</link></member>
|
<member><link linkend="beast.ref.error_code">error_code</link></member>
|
||||||
|
<member><link linkend="beast.ref.error_condition">error_condition</link></member>
|
||||||
<member><link linkend="beast.ref.handler_alloc">handler_alloc</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.handler_ptr">handler_ptr</link></member>
|
||||||
<member><link linkend="beast.ref.static_streambuf">static_streambuf</link></member>
|
<member><link linkend="beast.ref.static_streambuf">static_streambuf</link></member>
|
||||||
<member><link linkend="beast.ref.static_streambuf_n">static_streambuf_n</link></member>
|
<member><link linkend="beast.ref.static_streambuf_n">static_streambuf_n</link></member>
|
||||||
<member><link linkend="beast.ref.static_string">static_string</link></member>
|
<member><link linkend="beast.ref.static_string">static_string</link></member>
|
||||||
@@ -153,18 +187,15 @@
|
|||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.bind_handler">bind_handler</link></member>
|
<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.buffer_cat">buffer_cat</link></member>
|
||||||
<member><link linkend="beast.ref.consumed_buffers">consumed_buffers</link></member>
|
|
||||||
<member><link linkend="beast.ref.prepare_buffer">prepare_buffer</link></member>
|
<member><link linkend="beast.ref.prepare_buffer">prepare_buffer</link></member>
|
||||||
<member><link linkend="beast.ref.prepare_buffers">prepare_buffers</link></member>
|
<member><link linkend="beast.ref.prepare_buffers">prepare_buffers</link></member>
|
||||||
<member><link linkend="beast.ref.to_string">to_string</link></member>
|
<member><link linkend="beast.ref.to_string">to_string</link></member>
|
||||||
|
|
||||||
<member><link linkend="beast.ref.write">write</link></member>
|
<member><link linkend="beast.ref.write">write</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
</entry>
|
</entry>
|
||||||
<entry valign="top">
|
<entry valign="top">
|
||||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
|
|
||||||
<member><link linkend="beast.ref.is_AsyncReadStream">is_AsyncReadStream</link></member>
|
<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_AsyncWriteStream">is_AsyncWriteStream</link></member>
|
||||||
<member><link linkend="beast.ref.is_AsyncStream">is_AsyncStream</link></member>
|
<member><link linkend="beast.ref.is_AsyncStream">is_AsyncStream</link></member>
|
||||||
@@ -181,11 +212,29 @@
|
|||||||
<entry valign="top">
|
<entry valign="top">
|
||||||
<bridgehead renderas="sect3">Concepts</bridgehead>
|
<bridgehead renderas="sect3">Concepts</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.types.streams.AsyncStream">AsyncStream</link></member>
|
<member><link linkend="beast.ref.streams.AsyncStream">AsyncStream</link></member>
|
||||||
<member><link linkend="beast.types.BufferSequence">BufferSequence</link></member>
|
<member><link linkend="beast.ref.BufferSequence">BufferSequence</link></member>
|
||||||
<member><link linkend="beast.types.DynamicBuffer">DynamicBuffer</link></member>
|
<member><link linkend="beast.ref.DynamicBuffer">DynamicBuffer</link></member>
|
||||||
<member><link linkend="beast.types.streams.Stream">Stream</link></member>
|
<member><link linkend="beast.ref.streams.Stream">Stream</link></member>
|
||||||
<member><link linkend="beast.types.streams.SyncStream">SyncStream</link></member>
|
<member><link linkend="beast.ref.streams.SyncStream">SyncStream</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Classes</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.zlib__deflate_stream">deflate_stream</link></member>
|
||||||
|
<member><link linkend="beast.ref.zlib__inflate_stream">inflate_stream</link></member>
|
||||||
|
<member><link linkend="beast.ref.zlib__z_params">z_params</link></member>
|
||||||
|
</simplelist>
|
||||||
|
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.zlib__deflate_upper_bound">deflate_upper_bound</link></member>
|
||||||
|
</simplelist>
|
||||||
|
<bridgehead renderas="sect3">Constants</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.zlib__error">error</link></member>
|
||||||
|
<member><link linkend="beast.ref.zlib__Flush">Flush</link></member>
|
||||||
|
<member><link linkend="beast.ref.zlib__Strategy">Strategy</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
</entry>
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|||||||
@@ -31,8 +31,6 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:ref Reference]
|
|
||||||
|
|
||||||
</xsl:text>
|
</xsl:text>
|
||||||
<xsl:for-each select="
|
<xsl:for-each select="
|
||||||
compounddef[@kind = 'class' or @kind = 'struct'] |
|
compounddef[@kind = 'class' or @kind = 'struct'] |
|
||||||
@@ -59,7 +57,6 @@
|
|||||||
</xsl:otherwise>
|
</xsl:otherwise>
|
||||||
</xsl:choose>
|
</xsl:choose>
|
||||||
</xsl:for-each>
|
</xsl:for-each>
|
||||||
<xsl:text>
[endsect]</xsl:text>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<!--========== Utilities ==========-->
|
<!--========== Utilities ==========-->
|
||||||
@@ -165,7 +162,7 @@
|
|||||||
<xsl:text>``['implementation-defined]``</xsl:text>
|
<xsl:text>``['implementation-defined]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="$type='void_or_deduced'">
|
<xsl:when test="$type='void_or_deduced'">
|
||||||
<xsl:text>``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.return_type_of_an_initiating_function ['void-or-deduced]]``</xsl:text>
|
<xsl:text>__void_or_deduced__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:otherwise>
|
<xsl:otherwise>
|
||||||
<xsl:value-of select="$type"/>
|
<xsl:value-of select="$type"/>
|
||||||
@@ -198,6 +195,18 @@
|
|||||||
select="concat(substring-before($name, '::'), '__', substring-after($name, '::'))"/>
|
select="concat(substring-before($name, '::'), '__', substring-after($name, '::'))"/>
|
||||||
</xsl:call-template>
|
</xsl:call-template>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
|
<xsl:when test="substring($name, string-length($name) - 1) = '<<'">
|
||||||
|
<xsl:call-template name="make-id">
|
||||||
|
<xsl:with-param name="name"
|
||||||
|
select="concat(substring-before($name, '<<'), '_ls_')"/>
|
||||||
|
</xsl:call-template>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:when test="substring($name, string-length($name) - 1) = '>>'">
|
||||||
|
<xsl:call-template name="make-id">
|
||||||
|
<xsl:with-param name="name"
|
||||||
|
select="concat(substring-before($name, '>>'), '_rs_')"/>
|
||||||
|
</xsl:call-template>
|
||||||
|
</xsl:when>
|
||||||
<xsl:when test="contains($name, '=')">
|
<xsl:when test="contains($name, '=')">
|
||||||
<xsl:call-template name="make-id">
|
<xsl:call-template name="make-id">
|
||||||
<xsl:with-param name="name"
|
<xsl:with-param name="name"
|
||||||
@@ -270,10 +279,10 @@
|
|||||||
select="concat(substring-before($name, '*'), '_star_', substring-after($name, '*'))"/>
|
select="concat(substring-before($name, '*'), '_star_', substring-after($name, '*'))"/>
|
||||||
</xsl:call-template>
|
</xsl:call-template>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="contains($name, '~')">
|
<xsl:when test="starts-with($name, '~')">
|
||||||
<xsl:call-template name="make-id">
|
<xsl:call-template name="make-id">
|
||||||
<xsl:with-param name="name"
|
<xsl:with-param name="name"
|
||||||
select="concat(substring-before($name, '~'), '_', substring-after($name, '~'))"/>
|
select="concat(substring-after($name, '~'), '_dtor_')"/>
|
||||||
</xsl:call-template>
|
</xsl:call-template>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="contains($name, ' ')">
|
<xsl:when test="contains($name, ' ')">
|
||||||
@@ -1055,10 +1064,26 @@
|
|||||||
</xsl:for-each>
|
</xsl:for-each>
|
||||||
<xsl:text>]
</xsl:text>
|
<xsl:text>]
</xsl:text>
|
||||||
</xsl:if>
|
</xsl:if>
|
||||||
<xsl:if test="count(sectiondef[@kind='public-attrib' or @kind='public-static-attrib']) > 0">
|
<xsl:if test="count(sectiondef[@kind='public-static-attrib']) > 0">
|
||||||
|
<xsl:text>[heading Static Data Members]
</xsl:text>
|
||||||
|
<xsl:text>[table
 [[Name][Description]]
</xsl:text>
|
||||||
|
<xsl:for-each select="sectiondef[@kind='public-static-attrib']/memberdef" mode="class-table">
|
||||||
|
<xsl:sort select="name"/>
|
||||||
|
<xsl:text> [
</xsl:text>
|
||||||
|
<xsl:text> [[link beast.ref.</xsl:text>
|
||||||
|
<xsl:value-of select="$class-id"/>.<xsl:value-of select="name"/>
|
||||||
|
<xsl:text> [*</xsl:text>
|
||||||
|
<xsl:value-of select="name"/>
|
||||||
|
<xsl:text>]]]
 [
 </xsl:text>
|
||||||
|
<xsl:value-of select="briefdescription"/>
|
||||||
|
<xsl:text>
 ]
 ]
</xsl:text>
|
||||||
|
</xsl:for-each>
|
||||||
|
<xsl:text>]
</xsl:text>
|
||||||
|
</xsl:if>
|
||||||
|
<xsl:if test="count(sectiondef[@kind='public-attrib']) > 0">
|
||||||
<xsl:text>[heading Data Members]
</xsl:text>
|
<xsl:text>[heading Data Members]
</xsl:text>
|
||||||
<xsl:text>[table
 [[Name][Description]]
</xsl:text>
|
<xsl:text>[table
 [[Name][Description]]
</xsl:text>
|
||||||
<xsl:for-each select="sectiondef[@kind='public-attrib' or @kind='public-static-attrib']/memberdef" mode="class-table">
|
<xsl:for-each select="sectiondef[@kind='public-attrib']/memberdef" mode="class-table">
|
||||||
<xsl:sort select="name"/>
|
<xsl:sort select="name"/>
|
||||||
<xsl:text> [
</xsl:text>
|
<xsl:text> [
</xsl:text>
|
||||||
<xsl:text> [[link beast.ref.</xsl:text>
|
<xsl:text> [[link beast.ref.</xsl:text>
|
||||||
@@ -1528,47 +1553,50 @@
|
|||||||
<xsl:text> </xsl:text>
|
<xsl:text> </xsl:text>
|
||||||
<xsl:choose>
|
<xsl:choose>
|
||||||
<xsl:when test="type = 'class AsyncStream'">
|
<xsl:when test="type = 'class AsyncStream'">
|
||||||
<xsl:text>class ``[link beast.types.streams.AsyncStream [*AsyncStream]]``</xsl:text>
|
<xsl:text>class ``[link beast.ref.streams.AsyncStream [*AsyncStream]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class AsyncReadStream'">
|
<xsl:when test="type = 'class AsyncReadStream'">
|
||||||
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html [*AsyncReadStream]]``</xsl:text>
|
<xsl:text>class __AsyncReadStream__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class AsyncWriteStream'">
|
<xsl:when test="type = 'class AsyncWriteStream'">
|
||||||
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*AsyncWriteStream]]``</xsl:text>
|
<xsl:text>class __AsyncWriteStream__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class Body'">
|
<xsl:when test="type = 'class Body'">
|
||||||
<xsl:text>class ``[link beast.types.Body [*Body]]``</xsl:text>
|
<xsl:text>class ``[link beast.ref.Body [*Body]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class BufferSequence'">
|
<xsl:when test="type = 'class BufferSequence'">
|
||||||
<xsl:text>class ``[link beast.types.BufferSequence [*BufferSequence]]``</xsl:text>
|
<xsl:text>class ``[link beast.ref.BufferSequence [*BufferSequence]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="(type = 'class' or type = 'class...') and declname = 'BufferSequence'">
|
<xsl:when test="(type = 'class' or type = 'class...') and declname = 'BufferSequence'">
|
||||||
<xsl:value-of select="type"/>
|
<xsl:value-of select="type"/>
|
||||||
<xsl:text> ``[link beast.types.BufferSequence [*BufferSequence]]``</xsl:text>
|
<xsl:text> ``[link beast.ref.BufferSequence [*BufferSequence]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'CompletionHandler' or type = 'class CompletionHandler'">
|
<xsl:when test="declname = 'CompletionHandler' or type = 'class CompletionHandler'">
|
||||||
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/CompletionHandler.html [*CompletionHandler]]``</xsl:text>
|
<xsl:text>class __CompletionHandler__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'ConstBufferSequence' or type = 'class ConstBufferSequence'">
|
<xsl:when test="declname = 'ConstBufferSequence' or type = 'class ConstBufferSequence'">
|
||||||
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*ConstBufferSequence]]``</xsl:text>
|
<xsl:text>class __ConstBufferSequence__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'DynamicBuffer' or type = 'class DynamicBuffer'">
|
<xsl:when test="declname = 'DynamicBuffer' or type = 'class DynamicBuffer'">
|
||||||
<xsl:text>class ``[link beast.types.DynamicBuffer [*DynamicBuffer]]``</xsl:text>
|
<xsl:text>class ``[link beast.ref.DynamicBuffer [*DynamicBuffer]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'MutableBufferSequence' or type = 'class MutableBufferSequence'">
|
<xsl:when test="declname = 'MutableBufferSequence' or type = 'class MutableBufferSequence'">
|
||||||
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*MutableBufferSequence]]``</xsl:text>
|
<xsl:text>class __MutableBufferSequence__</xsl:text>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:when test="declname = 'Parser' or type = 'class Parser'">
|
||||||
|
<xsl:text>class ``[link beast.ref.Parser [*Parser]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'Stream' or type = 'class Stream'">
|
<xsl:when test="declname = 'Stream' or type = 'class Stream'">
|
||||||
<xsl:text>class ``[link beast.types.streams.Stream [*Stream]]``</xsl:text>
|
<xsl:text>class ``[link beast.ref.streams.Stream [*Stream]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class SyncStream'">
|
<xsl:when test="type = 'class SyncStream'">
|
||||||
<xsl:text>class ``[link beast.types.streams.SyncStream [*SyncStream]]``</xsl:text>
|
<xsl:text>class ``[link beast.ref.streams.SyncStream [*SyncStream]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'SyncReadStream' or type = 'class SyncReadStream'">
|
<xsl:when test="declname = 'SyncReadStream' or type = 'class SyncReadStream'">
|
||||||
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html [*SyncReadStream]]``</xsl:text>
|
<xsl:text>class __SyncReadStream__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'SyncWriteStream' or type = 'class SyncWriteStream'">
|
<xsl:when test="declname = 'SyncWriteStream' or type = 'class SyncWriteStream'">
|
||||||
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html [*SyncWriteStream]]``</xsl:text>
|
<xsl:text>class __SyncWriteStream__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
|
|
||||||
<xsl:when test="declname = 'T'">
|
<xsl:when test="declname = 'T'">
|
||||||
@@ -1682,6 +1710,14 @@
|
|||||||
</xsl:choose>
|
</xsl:choose>
|
||||||
<xsl:text>```
</xsl:text>
|
<xsl:text>```
</xsl:text>
|
||||||
<xsl:for-each select="../memberdef[name = $unqualified-name]">
|
<xsl:for-each select="../memberdef[name = $unqualified-name]">
|
||||||
|
<xsl:if test="position() > 1">
|
||||||
|
<xsl:text>
</xsl:text>
|
||||||
|
<xsl:if test=" not(briefdescription = preceding-sibling::*/briefdescription)">
|
||||||
|
<xsl:text>```
</xsl:text>
|
||||||
|
<xsl:apply-templates select="briefdescription" mode="markup"/>
|
||||||
|
<xsl:text>```
</xsl:text>
|
||||||
|
</xsl:if>
|
||||||
|
</xsl:if>
|
||||||
<xsl:variable name="stripped-type">
|
<xsl:variable name="stripped-type">
|
||||||
<xsl:call-template name="cleanup-type">
|
<xsl:call-template name="cleanup-type">
|
||||||
<xsl:with-param name="name" select="type"/>
|
<xsl:with-param name="name" select="type"/>
|
||||||
|
|||||||
@@ -107,52 +107,8 @@ INPUT = \
|
|||||||
../include/beast/core \
|
../include/beast/core \
|
||||||
../include/beast/http \
|
../include/beast/http \
|
||||||
../include/beast/websocket \
|
../include/beast/websocket \
|
||||||
../include/beast/doc_debug.hpp \
|
../include/beast/zlib \
|
||||||
|
../extras/beast/doc_debug.hpp
|
||||||
../include/beast/async_completion.hpp \
|
|
||||||
../include/beast/basic_streambuf.hpp \
|
|
||||||
../include/beast/bind_handler.hpp \
|
|
||||||
../include/beast/buffer_cat.hpp \
|
|
||||||
../include/beast/buffers_adapter.hpp \
|
|
||||||
../include/beast/consuming_buffers.hpp \
|
|
||||||
../include/beast/handler_alloc.hpp \
|
|
||||||
../include/beast/http.hpp \
|
|
||||||
../include/beast/placeholders.hpp \
|
|
||||||
../include/beast/prepare_buffers.hpp \
|
|
||||||
../include/beast/static_streambuf.hpp \
|
|
||||||
../include/beast/streambuf.hpp \
|
|
||||||
../include/beast/streambuf_readstream.hpp \
|
|
||||||
../include/beast/to_string.hpp \
|
|
||||||
../include/beast/type_check.hpp \
|
|
||||||
../include/beast/websocket.hpp \
|
|
||||||
../include/beast/write_streambuf.hpp \
|
|
||||||
../include/beast/http/basic_headers.hpp \
|
|
||||||
../include/beast/http/basic_parser_v1.hpp \
|
|
||||||
../include/beast/http/body_writer.hpp \
|
|
||||||
../include/beast/http/chunk_encode.hpp \
|
|
||||||
../include/beast/http/empty_body.hpp \
|
|
||||||
../include/beast/http/error.hpp \
|
|
||||||
../include/beast/http/fields.hpp \
|
|
||||||
../include/beast/http/headers.hpp \
|
|
||||||
../include/beast/http/message.hpp \
|
|
||||||
../include/beast/http/message_v1.hpp \
|
|
||||||
../include/beast/http/method.hpp \
|
|
||||||
../include/beast/http/parse_error.hpp \
|
|
||||||
../include/beast/http/parser.hpp \
|
|
||||||
../include/beast/http/read.hpp \
|
|
||||||
../include/beast/http/resume_context.hpp \
|
|
||||||
../include/beast/http/rfc2616.hpp \
|
|
||||||
../include/beast/http/streambuf_body.hpp \
|
|
||||||
../include/beast/http/string_body.hpp \
|
|
||||||
../include/beast/http/type_check.hpp \
|
|
||||||
../include/beast/http/write.hpp \
|
|
||||||
../include/beast/websocket/error.hpp \
|
|
||||||
../include/beast/websocket/option.hpp \
|
|
||||||
../include/beast/websocket/rfc6455.hpp \
|
|
||||||
../include/beast/websocket/ssl.hpp \
|
|
||||||
../include/beast/websocket/static_string.hpp \
|
|
||||||
../include/beast/websocket/stream.hpp \
|
|
||||||
../include/beast/websocket/teardown.hpp \
|
|
||||||
|
|
||||||
INPUT_ENCODING = UTF-8
|
INPUT_ENCODING = UTF-8
|
||||||
FILE_PATTERNS =
|
FILE_PATTERNS =
|
||||||
|
|||||||
@@ -5,7 +5,11 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:Body Body]
|
[section:Body Body requirements]
|
||||||
|
|
||||||
|
A [*Body] type is supplied as a template argument to the __message__ class. It
|
||||||
|
controls both the type of the data member of the resulting message object, and
|
||||||
|
the algorithms used during parsing and serialization.
|
||||||
|
|
||||||
In this table:
|
In this table:
|
||||||
|
|
||||||
@@ -22,17 +26,12 @@ In this table:
|
|||||||
will be not movable or not copyable.
|
will be not movable or not copyable.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
|
||||||
[`X:value_type{}`]
|
|
||||||
[]
|
|
||||||
[`DefaultConstructible`]
|
|
||||||
]
|
|
||||||
[
|
[
|
||||||
[`Body::reader`]
|
[`Body::reader`]
|
||||||
[]
|
[]
|
||||||
[
|
[
|
||||||
If present, a type meeting the requirements of
|
If present, a type meeting the requirements of
|
||||||
[link beast.types.Reader [*`Reader`]].
|
[link beast.ref.Reader [*`Reader`]].
|
||||||
Provides an implementation to parse the body.
|
Provides an implementation to parse the body.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@@ -41,7 +40,7 @@ In this table:
|
|||||||
[]
|
[]
|
||||||
[
|
[
|
||||||
If present, a type meeting the requirements of
|
If present, a type meeting the requirements of
|
||||||
[link beast.types.Writer [*`Writer`]].
|
[link beast.ref.Writer [*`Writer`]].
|
||||||
Provides an implementation to serialize the body.
|
Provides an implementation to serialize the body.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:BufferSequence BufferSequence]
|
[section:BufferSequence BufferSequence requirements]
|
||||||
|
|
||||||
A `BufferSequence` is a type meeting either of the following requirements:
|
A `BufferSequence` is a type meeting either of the following requirements:
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:DynamicBuffer DynamicBuffer]
|
[section:DynamicBuffer DynamicBuffer requirements]
|
||||||
|
|
||||||
A dynamic buffer encapsulates memory storage that may be automatically resized
|
A dynamic buffer encapsulates memory storage that may be automatically resized
|
||||||
as required, where the memory is divided into an input sequence followed by an
|
as required, where the memory is divided into an input sequence followed by an
|
||||||
@@ -27,7 +27,7 @@ implementation strategies:
|
|||||||
|
|
||||||
* A sequence of one or more octet arrays of varying sizes. Additional octet
|
* A sequence of one or more octet arrays of varying sizes. Additional octet
|
||||||
array objects are appended to the sequence to accommodate changes in the
|
array objects are appended to the sequence to accommodate changes in the
|
||||||
size of the character sequence. This is the implementation approached
|
size of the character sequence. This is the implementation approach
|
||||||
currently offered by [link beast.ref.basic_streambuf `basic_streambuf`].
|
currently offered by [link beast.ref.basic_streambuf `basic_streambuf`].
|
||||||
|
|
||||||
In the table below:
|
In the table below:
|
||||||
@@ -88,7 +88,7 @@ In the table below:
|
|||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a.prepare(n)`]
|
[`a.prepare(n)`]
|
||||||
[`X:mutable_buffers_type`]
|
[`X::mutable_buffers_type`]
|
||||||
[
|
[
|
||||||
Returns a mutable buffer sequence u representing the output sequence,
|
Returns a mutable buffer sequence u representing the output sequence,
|
||||||
and where `buffer_size(u) == n`. The dynamic buffer reallocates memory
|
and where `buffer_size(u) == n`. The dynamic buffer reallocates memory
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:Field Field]
|
[section:Field Field requirements]
|
||||||
|
|
||||||
A [*`Field`] represents a single HTTP header field/value pair.
|
A [*`Field`] represents a single HTTP header field/value pair.
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,17 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:FieldSequence FieldSequence]
|
[section:FieldSequence FieldSequence requirements]
|
||||||
|
|
||||||
A [*`FieldSequence`] is an iterable container whose value type meets
|
A [*FieldSequence] is an iterable container whose value type meets
|
||||||
the requirements of [link beast.types.Field [*`Field`]].
|
the requirements of [link beast.ref.Field [*Field]]. Objects that meet
|
||||||
|
these requirements become serializable by the implementation.
|
||||||
|
|
||||||
In this table:
|
In this table:
|
||||||
|
|
||||||
* `X` denotes a type that meets the requirements of [*`FieldSequence`].
|
* `X` denotes a type that meets the requirements of [*FieldSequence].
|
||||||
|
|
||||||
* `a` is a value of type `X`.
|
* `c` is a value of type `X const`.
|
||||||
|
|
||||||
[table FieldSequence requirements
|
[table FieldSequence requirements
|
||||||
[[operation][type][semantics, pre/post-conditions]]
|
[[operation][type][semantics, pre/post-conditions]]
|
||||||
@@ -22,25 +23,33 @@ In this table:
|
|||||||
[`X::value_type`]
|
[`X::value_type`]
|
||||||
[]
|
[]
|
||||||
[
|
[
|
||||||
A type that meets the requirements of `Field`.
|
A type that meets the requirements of [link beast.ref.Field [*Field]].
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`X::const_iterator`]
|
[`X::const_iterator`]
|
||||||
[]
|
[]
|
||||||
[
|
[
|
||||||
A type that meets the requirements of `ForwardIterator`.
|
An iterator type whose `reference` type meets the
|
||||||
|
requirements of [link beast.ref.Field [*Field]], and which
|
||||||
|
satisfies all the requirements of [*ForwardIterator],
|
||||||
|
except that:
|
||||||
|
|
||||||
|
[ordered_list
|
||||||
|
[there is no requirement that `operator->` is provided, and]
|
||||||
|
[there is no requirement that `reference` be a reference type.]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a.begin()`]
|
[`c.begin()`]
|
||||||
[`X::const_iterator`]
|
[`X::const_iterator`]
|
||||||
[
|
[
|
||||||
Returns an iterator to the beginning of the field sequence.
|
Returns an iterator to the beginning of the field sequence.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a.end()`]
|
[`c.end()`]
|
||||||
[`X::const_iterator`]
|
[`X::const_iterator`]
|
||||||
[
|
[
|
||||||
Returns an iterator to the end of the field sequence.
|
Returns an iterator to the end of the field sequence.
|
||||||
|
|||||||
@@ -5,19 +5,22 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:Parser Parser]
|
[section:Parser Parser requirements]
|
||||||
|
|
||||||
A [*`Parser`] is used to deserialize HTTP/1 messages from [link beast.types.streams streams].
|
A [*Parser] is used to deserialize objects from
|
||||||
Objects of this type are used with [link beast.ref.http__parse http::parse] and
|
[link beast.ref.streams streams]. Objects of this type are used with
|
||||||
[link beast.ref.http__async_parse http::async_parse].
|
[link beast.ref.http__parse http::parse] and
|
||||||
|
[link beast.ref.http__async_parse http::async_parse]. The definition of
|
||||||
|
an object, and the predicate defining when the parse is complete, are
|
||||||
|
determined by the implementation.
|
||||||
|
|
||||||
In this table:
|
In this table:
|
||||||
|
|
||||||
* `X` denotes a type meeting the requirements of [*`Parser`].
|
* `X` denotes a type meeting the requirements of [*Parser].
|
||||||
|
|
||||||
* `a` denotes a value of type `X`.
|
* `a` denotes a value of type `X`.
|
||||||
|
|
||||||
* `b` is a value meeting the requirements of [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConvertibleToConstBuffer.html [*`ConvertibleToConstBuffer`]].
|
* `b` is a value meeting the requirements of __ConstBufferSequence__.
|
||||||
|
|
||||||
* `ec` is a value of type [link beast.ref.error_code `error_code&`].
|
* `ec` is a value of type [link beast.ref.error_code `error_code&`].
|
||||||
|
|
||||||
@@ -27,18 +30,18 @@ In this table:
|
|||||||
[`a.complete()`]
|
[`a.complete()`]
|
||||||
[`bool`]
|
[`bool`]
|
||||||
[
|
[
|
||||||
Returns `true` when a complete HTTP/1 message has been parsed.
|
Returns `true` when parsing is complete.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a.write(b, ec)`]
|
[`a.write(b, ec)`]
|
||||||
[`std::size_t`]
|
[`std::size_t`]
|
||||||
[
|
[
|
||||||
Parses the octets in the specified input buffer sequentially until
|
Sequentially parses the octets in the specified input buffer sequence
|
||||||
an error occurs, the end of the buffer is reached, or a complete
|
until an error occurs, the end of the buffer is reached, or parsing is
|
||||||
HTTP/1 message has been parsed. If an error occurs, `ec` is set
|
complete. Upon success, this function returns the number of bytes used
|
||||||
to the error code and parsing stops. This function returns the
|
from the input. If an error occurs, `ec` is set to the error code and
|
||||||
number of bytes consumed from the input buffer.
|
parsing stops.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
@@ -48,9 +51,9 @@ In this table:
|
|||||||
Indicates to the parser that no more octets will be available.
|
Indicates to the parser that no more octets will be available.
|
||||||
Typically this function is called when the end of stream is reached.
|
Typically this function is called when the end of stream is reached.
|
||||||
For example, if a call to `boost::asio::ip::tcp::socket::read_some`
|
For example, if a call to `boost::asio::ip::tcp::socket::read_some`
|
||||||
generates a `boost::asio::error::eof` error. Some HTTP/1 messages
|
generates a `boost::asio::error::eof` error. Some objects, such as
|
||||||
determine the end of the message body by an end of file marker or
|
certain HTTP/1 messages, determine the end of the message body by
|
||||||
closing of the connection.
|
an end of file marker or closing of the connection.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:Reader Reader]
|
[section:Reader Reader requirements]
|
||||||
|
|
||||||
Parser implementations will construct the corresponding `reader` object
|
Parsers provided by the implementation will construct the corresponding
|
||||||
during the parse. This customization point allows the Body to determine
|
`reader` object during parsing. This customization point allows the
|
||||||
the strategy for storing incoming message body data.
|
Body to determine the strategy for storing incoming message body data.
|
||||||
|
|
||||||
In this table:
|
In this table:
|
||||||
|
|
||||||
@@ -17,15 +17,14 @@ In this table:
|
|||||||
|
|
||||||
* `a` denotes a value of type `X`.
|
* `a` denotes a value of type `X`.
|
||||||
|
|
||||||
* `p` is any pointer.
|
|
||||||
|
|
||||||
* `n` is a value convertible to `std::size_t`.
|
* `n` is a value convertible to `std::size_t`.
|
||||||
|
|
||||||
* `ec` is a value of type `error_code&`.
|
* `p` is a `void const*` to valid memory of at least `n` bytes.
|
||||||
|
|
||||||
* `m` denotes a value of type `message const&` where
|
* `ec` is a value of type [link beast.ref.error_code `error_code&`].
|
||||||
`std::is_same<decltype(m.body), Body::value_type>:value == true`
|
|
||||||
|
|
||||||
|
* `m` denotes a value of type `message&` where
|
||||||
|
`std::is_same<decltype(m.body), Body::value_type>::value == true`.
|
||||||
|
|
||||||
[table Reader requirements
|
[table Reader requirements
|
||||||
[[operation] [type] [semantics, pre/post-conditions]]
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
@@ -33,22 +32,38 @@ In this table:
|
|||||||
[`X a(m);`]
|
[`X a(m);`]
|
||||||
[]
|
[]
|
||||||
[
|
[
|
||||||
`a` is constructible from `m`. The lifetime of `m` is
|
`a` is constructible from `m`. The lifetime of `m` is guaranteed
|
||||||
guaranteed to end no earlier than after `a` is destroyed.
|
to end no earlier than after `a` is destroyed. The constructor
|
||||||
|
will be called after all headers have been stored in `m`, and
|
||||||
|
before any body data is deserialized. This function must be
|
||||||
|
`noexcept`.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.init(ec)`]
|
||||||
|
[`void`]
|
||||||
|
[
|
||||||
|
Called immediately after construction. If the function sets
|
||||||
|
an error code in `ec`, the parse is aborted and the error is
|
||||||
|
propagated to the caller. This function must be `noexcept`.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a.write(p, n, ec)`]
|
[`a.write(p, n, ec)`]
|
||||||
[`void`]
|
[`void`]
|
||||||
[
|
[
|
||||||
Deserializes the input sequence into the body.
|
Deserializes the input sequence into the body. If `ec` is set,
|
||||||
If `ec` is set, the deserialization is aborted and the error
|
the deserialization is aborted and the error is propagated to
|
||||||
is returned to the caller.
|
the caller. If the message headers specify a chunked transfer
|
||||||
|
encoding, the reader will receive the decoded version of the
|
||||||
|
body. This function must be `noexcept`.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
[note Definitions for required `Reader` member functions should be declared
|
[note
|
||||||
inline so the generated code becomes part of the implementation. ]
|
Definitions for required `Reader` member functions should be declared
|
||||||
|
inline so the generated code can become part of the implementation.
|
||||||
|
]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:streams Streams]
|
[section:streams Streams requirements]
|
||||||
|
|
||||||
Stream types represent objects capable of performing synchronous or
|
Stream types represent objects capable of performing synchronous or
|
||||||
asynchronous I/O. They are based on concepts from `boost::asio`.
|
asynchronous I/O. They are based on concepts from `boost::asio`.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:Writer Writer]
|
[section:Writer Writer requirements]
|
||||||
|
|
||||||
A `Writer` serializes the message body. The implementation creates an instance
|
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
|
of this type when serializing a message, and calls into it zero or more times
|
||||||
@@ -28,13 +28,13 @@ In this table:
|
|||||||
* `m` denotes a value of type `message const&` where
|
* `m` denotes a value of type `message const&` where
|
||||||
`std::is_same<decltype(m.body), Body::value_type>:value == true`.
|
`std::is_same<decltype(m.body), Body::value_type>:value == true`.
|
||||||
|
|
||||||
* `rc` is an object of type [link beast.ref.http__resume_context resume_context].
|
* `rc` is an object of type [link beast.ref.http__resume_context `resume_context`].
|
||||||
|
|
||||||
* `ec` is a value of type `error_code&`.
|
* `ec` is a value of type [link beast.ref.error_code `error_code&`]
|
||||||
|
|
||||||
* `wf` is a [*write function]: a function object of unspecified type provided
|
* `wf` is a [*write function]: a function object of unspecified type provided
|
||||||
by the implementation which accepts any value meeting the requirements
|
by the implementation which accepts any value meeting the requirements
|
||||||
of `ConstBufferSequence` as its single parameter.
|
of __ConstBufferSequence__ as its single parameter.
|
||||||
|
|
||||||
[table Writer requirements
|
[table Writer requirements
|
||||||
[[operation] [type] [semantics, pre/post-conditions]]
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
@@ -42,17 +42,18 @@ In this table:
|
|||||||
[`X a(m);`]
|
[`X a(m);`]
|
||||||
[]
|
[]
|
||||||
[
|
[
|
||||||
`a` is constructible from `m`. The lifetime of `m` is
|
`a` is constructible from `m`. The lifetime of `m` is guaranteed
|
||||||
guaranteed to end no earlier than after `a` is destroyed.
|
to end no earlier than after `a` is destroyed. This function must
|
||||||
|
be `noexcept`.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a.init(ec)`]
|
[`a.init(ec)`]
|
||||||
[`void`]
|
[`void`]
|
||||||
[
|
[
|
||||||
Called immediately after construction.
|
Called immediately after construction. If the function sets an
|
||||||
If `ec` is set, the serialization is aborted and the error
|
error code in `ec`, the serialization is aborted and the error
|
||||||
is propagated to the caller.
|
is propagated to the caller. This function must be `noexcept`.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
@@ -67,31 +68,33 @@ In this table:
|
|||||||
the serialized message body will be sent unmodified, with the
|
the serialized message body will be sent unmodified, with the
|
||||||
error `boost::asio::error::eof` returned to the caller, to notify
|
error `boost::asio::error::eof` returned to the caller, to notify
|
||||||
they should close the connection to indicate the end of the message.
|
they should close the connection to indicate the end of the message.
|
||||||
|
This function must be `noexcept`.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a(rc, ec, wf)`]
|
[`a.write(rc, ec, wf)`]
|
||||||
[`boost::tribool`]
|
[`boost::tribool`]
|
||||||
[
|
[
|
||||||
Called repeatedly after `init` succeeds.
|
Called repeatedly after `init` succeeds. `wf` is a function object
|
||||||
`wf` is a function object which takes as its single parameter,
|
which takes as its single parameter any value meeting the requirements
|
||||||
any value meeting the requirements of `ConstBufferSequence`.
|
of __ConstBufferSequence__. Buffers provided to this write function
|
||||||
Buffers provided by the `writer` to this [*write function] must
|
must remain valid until the next member function of `writer` is
|
||||||
remain valid until the next member function of `writer` is
|
|
||||||
invoked (which may be the destructor). This function returns `true`
|
invoked (which may be the destructor). This function returns `true`
|
||||||
to indicate all message body data has been written, or `false`
|
to indicate all message body data has been written, or `false` if
|
||||||
if there is more body data. If the return value is
|
there is more body data. If the return value is `boost::indeterminate`,
|
||||||
`boost::indeterminate`, the implementation will suspend the operation
|
the implementation will suspend the operation until the writer invokes
|
||||||
until the writer invokes `rc`. It is the writers responsibility when
|
`rc`. It is the writers responsibility when returning
|
||||||
returning `boost::indeterminate`, to acquire ownership of the
|
`boost::indeterminate`, to acquire ownership of `rc` via move
|
||||||
`resume_context` via move construction and eventually call it or else
|
construction and eventually call it or else undefined behavior
|
||||||
undefined behavior results.
|
results. This function must be `noexcept`.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
[note Definitions for required `Writer` member functions should be declared
|
[note
|
||||||
inline so the generated code becomes part of the implementation. ]
|
Definitions for required `Writer` member functions should be declared
|
||||||
|
inline so the generated code can become part of the implementation.
|
||||||
|
]
|
||||||
|
|
||||||
Exemplar:
|
Exemplar:
|
||||||
```
|
```
|
||||||
@@ -109,7 +112,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
template<bool isRequest, class Body, class Headers>
|
template<bool isRequest, class Body, class Headers>
|
||||||
explicit
|
explicit
|
||||||
writer(message<isRequest, Body, Headers> const& msg);
|
writer(message<isRequest, Body, Headers> const& msg) noexcept;
|
||||||
|
|
||||||
/** Initialize the writer.
|
/** Initialize the writer.
|
||||||
|
|
||||||
@@ -119,7 +122,7 @@ public:
|
|||||||
@param ec Contains the error code if any errors occur.
|
@param ec Contains the error code if any errors occur.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
init(error_code& ec);
|
init(error_code& ec) noexcept;
|
||||||
|
|
||||||
/** Returns the content length.
|
/** Returns the content length.
|
||||||
|
|
||||||
@@ -128,8 +131,8 @@ public:
|
|||||||
use chunk-encoding or terminate the connection to indicate the end
|
use chunk-encoding or terminate the connection to indicate the end
|
||||||
of the message.
|
of the message.
|
||||||
*/
|
*/
|
||||||
std::size_t
|
std::uint64_t
|
||||||
content_length() const;
|
content_length() noexcept;
|
||||||
|
|
||||||
/** Write zero or one buffer representing the message body.
|
/** Write zero or one buffer representing the message body.
|
||||||
|
|
||||||
@@ -172,7 +175,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
template<class WriteFunction>
|
template<class WriteFunction>
|
||||||
boost::tribool
|
boost::tribool
|
||||||
operator()(resume_context&&, error_code&, WriteFunction&& write);
|
write(
|
||||||
|
resume_context&&,
|
||||||
|
error_code&,
|
||||||
|
WriteFunction&& wf) noexcept;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,21 @@
|
|||||||
|
|
||||||
[section:websocket WebSocket]
|
[section:websocket WebSocket]
|
||||||
|
|
||||||
|
[block '''
|
||||||
|
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
|
||||||
|
<member><link linkend="beast.websocket.creation">Creation</link></member>
|
||||||
|
<member><link linkend="beast.websocket.connections">Making connections</link></member>
|
||||||
|
<member><link linkend="beast.websocket.handshaking">Handshaking</link></member>
|
||||||
|
<member><link linkend="beast.websocket.messages">Messages</link></member>
|
||||||
|
<member><link linkend="beast.websocket.frames">Frames</link></member>
|
||||||
|
<member><link linkend="beast.websocket.control">Control Frames</link></member>
|
||||||
|
<member><link linkend="beast.websocket.buffers">Buffers</link></member>
|
||||||
|
<member><link linkend="beast.websocket.async">Asynchronous interface</link></member>
|
||||||
|
<member><link linkend="beast.websocket.io_service">The io_service</link></member>
|
||||||
|
<member><link linkend="beast.websocket.threads">Thread Safety</link></member>
|
||||||
|
</simplelist></entry></row></tbody></tgroup></informaltable>
|
||||||
|
''']
|
||||||
|
|
||||||
The WebSocket Protocol enables two-way communication between a client
|
The WebSocket Protocol enables two-way communication between a client
|
||||||
running untrusted code in a controlled environment to a remote host that has
|
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
|
opted-in to communications from that code. The protocol consists of an opening
|
||||||
@@ -22,48 +37,12 @@ C++ approach.
|
|||||||
The WebSocket protocol is described fully in
|
The WebSocket protocol is described fully in
|
||||||
[@https://tools.ietf.org/html/rfc6455 rfc6455]
|
[@https://tools.ietf.org/html/rfc6455 rfc6455]
|
||||||
|
|
||||||
|
[note
|
||||||
|
The following documentation assumes familiarity with both
|
||||||
|
Boost.Asio and the WebSocket protocol specification described in __rfc6455__.
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
[section:motivation Motivation]
|
|
||||||
|
|
||||||
Today's web applications increasingly rely on alternatives to standard HTTP
|
|
||||||
to achieve performance and/or responsiveness. While WebSocket implementations
|
|
||||||
are widely available in common web development languages such as Javascript,
|
|
||||||
good implementations in C++ are scarce. A survey of existing C++ WebSocket
|
|
||||||
solutions reveals interfaces which lack symmetry, impose performance penalties,
|
|
||||||
and needlessly restrict implementation strategies.
|
|
||||||
|
|
||||||
Beast.WebSocket is built on Boost.Asio, a robust cross platform networking
|
|
||||||
framework that is part of Boost and also offered as a standalone library.
|
|
||||||
A proposal to add networking functionality to the C++ standard library,
|
|
||||||
based on Boost.Asio, is under consideration by the standards committee.
|
|
||||||
Since the final approved networking interface for the C++ standard library
|
|
||||||
will likely closely resemble the current interface of Boost.Asio, it is
|
|
||||||
logical for Beast.WebSocket to use Boost.Asio as its network transport.
|
|
||||||
|
|
||||||
Beast.WebSocket takes advantage of Boost.Asio's extensible asynchronous
|
|
||||||
model, handler allocation, and handler invocation hooks. Calls to
|
|
||||||
Beast.WebSocket asynchronous initiation functions allow callers the choice
|
|
||||||
of using a completion handler, stackful or stackless coroutines, futures,
|
|
||||||
or user defined customizations (for example, Boost.Fiber). The
|
|
||||||
implementation uses handler invocation hooks
|
|
||||||
([@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asio_handler_invoke.html `asio_handler_invoke`]),
|
|
||||||
providing execution guarantees on composed operations in a manner
|
|
||||||
identical to Boost.Asio. The implementation also uses handler allocation hooks
|
|
||||||
([@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asio_handler_allocate.html `asio_handler_allocate`])
|
|
||||||
when allocating memory internally for composed operations.
|
|
||||||
|
|
||||||
There is no need for inheritance or virtual members in a
|
|
||||||
[link beast.ref.websocket__stream `beast::websocket::stream`].
|
|
||||||
All operations are templated and transparent to the compiler, allowing for
|
|
||||||
maximum inlining and optimization.
|
|
||||||
|
|
||||||
[note The documentation which follows assumes familiarity with
|
|
||||||
both Boost.Asio and the WebSocket protocol specification described in
|
|
||||||
[@https://tools.ietf.org/html/rfc6455 rfc6455] ]
|
|
||||||
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:creation Creation]
|
[section:creation Creation]
|
||||||
@@ -71,15 +50,15 @@ both Boost.Asio and the WebSocket protocol specification described in
|
|||||||
The interface to Beast's WebSocket implementation is a single template
|
The interface to Beast's WebSocket implementation is a single template
|
||||||
class [link beast.ref.websocket__stream `beast::websocket::stream`] which
|
class [link beast.ref.websocket__stream `beast::websocket::stream`] which
|
||||||
wraps a "next layer" object. The next layer object must meet the requirements
|
wraps a "next layer" object. The next layer object must meet the requirements
|
||||||
of [link beast.types.streams.SyncStream [*`SyncReadStream`]] if synchronous
|
of [link beast.ref.streams.SyncStream [*`SyncReadStream`]] if synchronous
|
||||||
operations are performed, or
|
operations are performed, or
|
||||||
[link beast.types.streams.AsyncStream [*`AsyncStream`]] if asynchronous
|
[link beast.ref.streams.AsyncStream [*`AsyncStream`]] if asynchronous
|
||||||
operations are performed, or both. Arguments supplied during construction are
|
operations are performed, or both. Arguments supplied during construction are
|
||||||
passed to next layer's constructor. Here we declare a websocket stream over
|
passed to next layer's constructor. Here we declare a websocket stream over
|
||||||
a TCP/IP socket with ownership of the socket:
|
a TCP/IP socket with ownership of the socket:
|
||||||
```
|
```
|
||||||
boost::asio::io_service ios;
|
boost::asio::io_service ios;
|
||||||
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
|
||||||
```
|
```
|
||||||
|
|
||||||
[heading Using SSL]
|
[heading Using SSL]
|
||||||
@@ -92,8 +71,8 @@ argument when constructing the stream.
|
|||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
|
|
||||||
boost::asio::io_service ios;
|
boost::asio::io_service ios;
|
||||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
|
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
|
||||||
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ws(ios, ctx);
|
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ws{ios, ctx};
|
||||||
```
|
```
|
||||||
|
|
||||||
[note
|
[note
|
||||||
@@ -108,7 +87,7 @@ to wrap an object that already exists. This socket can be moved in:
|
|||||||
```
|
```
|
||||||
boost::asio::ip::tcp::socket&& sock;
|
boost::asio::ip::tcp::socket&& sock;
|
||||||
...
|
...
|
||||||
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(std::move(sock));
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{std::move(sock)};
|
||||||
```
|
```
|
||||||
|
|
||||||
Or, the wrapper can be constructed with a non-owning reference. In
|
Or, the wrapper can be constructed with a non-owning reference. In
|
||||||
@@ -117,35 +96,37 @@ underlying socket being wrapped:
|
|||||||
```
|
```
|
||||||
boost::asio::ip::tcp::socket sock;
|
boost::asio::ip::tcp::socket sock;
|
||||||
...
|
...
|
||||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
|
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
||||||
```
|
```
|
||||||
|
|
||||||
The layer being wrapped can be accessed through the websocket's "next layer",
|
The layer being wrapped can be accessed through the websocket's "next layer",
|
||||||
permitting callers to interact directly with its interface.
|
permitting callers to interact directly with its interface.
|
||||||
```
|
```
|
||||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
|
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
|
||||||
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws(ios, ctx);
|
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws{ios, ctx};
|
||||||
...
|
...
|
||||||
ws.next_layer().shutdown(); // ssl::stream shutdown
|
ws.next_layer().shutdown(); // ssl::stream shutdown
|
||||||
```
|
```
|
||||||
|
|
||||||
[important Initiating read and write operations on the next layer while
|
[warning
|
||||||
websocket operations are being performed can break invariants, and
|
Initiating read and write operations on the next layer while
|
||||||
result in undefined behavior. ]
|
stream operations are being performed can break invariants, and
|
||||||
|
result in undefined behavior.
|
||||||
|
]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:connecting Making connections]
|
[section:connections Making connections]
|
||||||
|
|
||||||
Connections are established by using the interfaces which already exist
|
Connections are established by using the interfaces which already exist
|
||||||
for the next layer. For example, making an outgoing connection:
|
for the next layer. For example, making an outgoing connection:
|
||||||
```
|
```
|
||||||
std::string const host = "mywebapp.com";
|
std::string const host = "mywebapp.com";
|
||||||
boost::asio::io_service ios;
|
boost::asio::io_service ios;
|
||||||
boost::asio::ip::tcp::resolver r(ios);
|
boost::asio::ip::tcp::resolver r{ios};
|
||||||
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
|
||||||
boost::asio::connect(ws.next_layer(),
|
boost::asio::connect(ws.next_layer(),
|
||||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "ws"}));
|
r.resolve(boost::asio::ip::tcp::resolver::query{host, "ws"}));
|
||||||
```
|
```
|
||||||
@@ -154,12 +135,14 @@ Accepting an incoming connection:
|
|||||||
```
|
```
|
||||||
void do_accept(boost::asio::ip::tcp::acceptor& acceptor)
|
void do_accept(boost::asio::ip::tcp::acceptor& acceptor)
|
||||||
{
|
{
|
||||||
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(acceptor.get_io_service());
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{acceptor.get_io_service()};
|
||||||
acceptor.accept(ws.next_layer());
|
acceptor.accept(ws.next_layer());
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
[note Examples use synchronous interfaces for clarity of exposition. ]
|
[note
|
||||||
|
Examples use synchronous interfaces for clarity of exposition.
|
||||||
|
]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
@@ -171,16 +154,17 @@ A WebSocket session begins when one side sends the HTTP Upgrade request
|
|||||||
for websocket, and the other side sends an appropriate HTTP response
|
for websocket, and the other side sends an appropriate HTTP response
|
||||||
indicating that the request was accepted and that the connection has
|
indicating that the request was accepted and that the connection has
|
||||||
been upgraded. The HTTP Upgrade request must include the Host HTTP field,
|
been upgraded. The HTTP Upgrade request must include the Host HTTP field,
|
||||||
and the URI of the resource to request. `handshake` is used to send the
|
and the URI of the resource to request.
|
||||||
|
[link beast.ref.websocket__stream.handshake `handshake`] is used to send the
|
||||||
request with the required host and resource strings.
|
request with the required host and resource strings.
|
||||||
```
|
```
|
||||||
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
|
||||||
...
|
...
|
||||||
ws.set_option(beast::websocket::keep_alive(true));
|
ws.set_option(beast::websocket::keep_alive(true));
|
||||||
ws.handshake("ws.example.com:80", "/cgi-bin/bitcoin-prices");
|
ws.handshake("ws.example.com:80", "/cgi-bin/bitcoin-prices");
|
||||||
```
|
```
|
||||||
|
|
||||||
The [link beast.ref.websocket__stream `beast::websocket::stream`] automatically
|
The [link beast.ref.websocket__stream `stream`] automatically
|
||||||
handles receiving and processing the HTTP response to the handshake request.
|
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
|
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
|
101 "Switching Protocols" status code. On failure, an error is returned or an
|
||||||
@@ -190,7 +174,7 @@ open for a subsequent handshake attempt
|
|||||||
Performing a handshake for an incoming websocket upgrade request operates
|
Performing a handshake for an incoming websocket upgrade request operates
|
||||||
similarly. If the handshake fails, an error is returned or exception thrown:
|
similarly. If the handshake fails, an error is returned or exception thrown:
|
||||||
```
|
```
|
||||||
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
|
||||||
...
|
...
|
||||||
ws.accept();
|
ws.accept();
|
||||||
```
|
```
|
||||||
@@ -205,7 +189,7 @@ void do_accept(boost::asio::ip::tcp::socket& sock)
|
|||||||
boost::asio::streambuf sb;
|
boost::asio::streambuf sb;
|
||||||
boost::asio::read_until(sock, sb, "\r\n\r\n");
|
boost::asio::read_until(sock, sb, "\r\n\r\n");
|
||||||
...
|
...
|
||||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
|
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
||||||
ws.accept(sb.data());
|
ws.accept(sb.data());
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
@@ -218,10 +202,10 @@ void do_accept(boost::asio::ip::tcp::socket& sock)
|
|||||||
{
|
{
|
||||||
boost::asio::streambuf sb;
|
boost::asio::streambuf sb;
|
||||||
beast::http::request<http::empty_body> request;
|
beast::http::request<http::empty_body> request;
|
||||||
beast::http::read(sock, request);
|
beast::http::read(sock, sb, request);
|
||||||
if(beast::http::is_upgrade(request))
|
if(beast::http::is_upgrade(request))
|
||||||
{
|
{
|
||||||
websocket::stream<ip::tcp::socket&> ws(sock);
|
websocket::stream<ip::tcp::socket&> ws{sock};
|
||||||
ws.accept(request);
|
ws.accept(request);
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
@@ -242,17 +226,19 @@ void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
|
|||||||
{
|
{
|
||||||
beast::streambuf sb;
|
beast::streambuf sb;
|
||||||
beast::websocket::opcode::value op;
|
beast::websocket::opcode::value op;
|
||||||
ws.read(sb);
|
ws.read(op, sb);
|
||||||
|
|
||||||
ws.set_option(beast::websocket::message_type(op));
|
ws.set_option(beast::websocket::message_type{op});
|
||||||
ws.write(sb.data());
|
ws.write(sb.data());
|
||||||
sb.consume(sb.size());
|
sb.consume(sb.size());
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
[important Calls to [link beast.ref.websocket__stream.set_option `set_option`]
|
[important
|
||||||
must be made from the same implicit or explicit strand as that used to perform
|
Calls to [link beast.ref.websocket__stream.set_option `set_option`]
|
||||||
other operations. ]
|
must be made from the same implicit or explicit strand as that used
|
||||||
|
to perform other operations.
|
||||||
|
]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
@@ -280,9 +266,9 @@ void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
|
|||||||
if(fi.fin)
|
if(fi.fin)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ws.set_option(beast::websocket::message_type(fi.op));
|
ws.set_option(beast::websocket::message_type{fi.op});
|
||||||
beast::consuming_buffers<
|
beast::consuming_buffers<
|
||||||
beast::streambuf::const_buffers_type> cb(sb.data());
|
beast::streambuf::const_buffers_type> cb{sb.data()};
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
using boost::asio::buffer_size;
|
using boost::asio::buffer_size;
|
||||||
@@ -305,46 +291,49 @@ void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:controlframes Control frames]
|
[section:control Control Frames]
|
||||||
|
|
||||||
During read operations, the implementation automatically reads and processes
|
Control frames are small (less than 128 bytes) messages entirely contained
|
||||||
WebSocket control frames such as ping, pong, and close. Pings are replied
|
in an individual WebSocket frame. They may be sent at any time by either
|
||||||
to as soon as possible, pongs are delivered to the pong callback. The receipt
|
peer on an established connection, and can appear in between continuation
|
||||||
of a close frame initiates the WebSocket close procedure, eventually resulting
|
frames for a message. There are three types of control frames: ping, pong,
|
||||||
in the error code [link beast.ref.websocket__error `error::closed`] being
|
and close.
|
||||||
delivered to the caller in a subsequent read operation, assuming no other error
|
|
||||||
|
A sent ping indicates a request that the sender wants to receive a pong. A
|
||||||
|
pong is a response to a ping. Pongs may be sent unsolicited, at any time.
|
||||||
|
One use for an unsolicited pong is to inform the remote peer that the
|
||||||
|
session is still active after a long period of inactivity. A close frame
|
||||||
|
indicates that the remote peer wishes to close the WebSocket connection.
|
||||||
|
The connection is considered gracefully closed when each side has sent
|
||||||
|
and received a close frame.
|
||||||
|
|
||||||
|
During read operations, Beast automatically reads and processes control
|
||||||
|
frames. Pings are replied to as soon as possible with a pong, received
|
||||||
|
pongs are delivered to the pong callback. The receipt of a close frame
|
||||||
|
initiates the WebSocket close procedure, eventually resulting in the error
|
||||||
|
code [link beast.ref.websocket__error `error::closed`] being delivered
|
||||||
|
to the caller in a subsequent read operation, assuming no other error
|
||||||
takes place.
|
takes place.
|
||||||
|
|
||||||
To ensure timely delivery of control frames, large messages are broken up
|
A consequence of this automatic behavior is that caller-initiated read
|
||||||
into smaller sized frames. The implementation chooses the size and number
|
operations can cause socket writes. However, these writes will not
|
||||||
of the frames making up the message. The automatic fragment size option
|
compete with caller-initiated write operations. For the purposes of
|
||||||
gives callers control over the size of these frames:
|
correctness with respect to the stream invariants, caller-initiated
|
||||||
```
|
read operations still only count as a read. This means that callers can
|
||||||
...
|
have a simultaneous active read and write operation in progress, while
|
||||||
ws.set_option(beast::websocket::auto_fragment_size(8192));
|
the implementation also automatically handles control frames.
|
||||||
```
|
|
||||||
|
|
||||||
The WebSocket protocol defines a procedure and control message for initiating
|
[heading Ping and Pong Frames]
|
||||||
a close of the session. Handling of close initiated by the remote end of the
|
|
||||||
connection is performed automatically. To manually initiate a close, use
|
|
||||||
[link beast.ref.websocket__stream.close `close`]:
|
|
||||||
```
|
|
||||||
ws.close();
|
|
||||||
```
|
|
||||||
|
|
||||||
[note To receive the [link beast.ref.websocket__error `error::closed`]
|
Ping and pong messages are control frames which may be sent at any time
|
||||||
error, a read operation is required. ]
|
by either peer on an established WebSocket connection. They are sent
|
||||||
|
using the functions
|
||||||
[endsect]
|
[link beast.ref.websocket__stream.ping `ping`] and
|
||||||
|
[link beast.ref.websocket__stream.pong `pong`].
|
||||||
|
|
||||||
|
|
||||||
[section:pongs Pong messages]
|
|
||||||
|
|
||||||
To receive pong control frames, callers may register a "pong callback" using
|
To receive pong control frames, callers may register a "pong callback" using
|
||||||
[link beast.ref.websocket__stream.set_option `set_option`]:
|
[link beast.ref.websocket__stream.set_option `set_option`]. The object provided
|
||||||
|
with this option should be callable with the following signature:
|
||||||
the following signature:
|
|
||||||
```
|
```
|
||||||
void on_pong(ping_data const& payload);
|
void on_pong(ping_data const& payload);
|
||||||
...
|
...
|
||||||
@@ -361,9 +350,47 @@ reset when a pong is received. The same callback is used for both synchronous
|
|||||||
and asynchronous reads. The pong callback is passive; in order to receive
|
and asynchronous reads. The pong callback is passive; in order to receive
|
||||||
pongs, a synchronous or asynchronous stream read function must be active.
|
pongs, a synchronous or asynchronous stream read function must be active.
|
||||||
|
|
||||||
[note When an asynchronous read function receives a pong, the the pong callback
|
[note
|
||||||
is invoked in the same manner as that used to invoke the final completion
|
When an asynchronous read function receives a pong, the the pong
|
||||||
handler of the corresponding read function.]
|
callback is invoked in the same manner as that used to invoke the
|
||||||
|
final completion handler of the corresponding read function.
|
||||||
|
]
|
||||||
|
|
||||||
|
[heading Close Frames]
|
||||||
|
|
||||||
|
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
|
||||||
|
the [link beast.ref.websocket__stream.close `close`] function:
|
||||||
|
```
|
||||||
|
ws.close();
|
||||||
|
```
|
||||||
|
|
||||||
|
When the remote peer initiates a close by sending a close frame, Beast
|
||||||
|
will handle it for you by causing the next read to return `error::closed`.
|
||||||
|
When this error code is delivered, it indicates to the application that
|
||||||
|
the WebSocket connection has been closed cleanly, and that the TCP/IP
|
||||||
|
connection has been closed. After initiating a close, it is necessary to
|
||||||
|
continue reading messages until receiving the error `error::closed`. This
|
||||||
|
is because the remote peer may still be sending message and control frames
|
||||||
|
before it receives and responds to the close frame.
|
||||||
|
|
||||||
|
[important
|
||||||
|
To receive the [link beast.ref.websocket__error `error::closed`]
|
||||||
|
error, a read operation is required.
|
||||||
|
]
|
||||||
|
|
||||||
|
[heading Auto-fragment]
|
||||||
|
|
||||||
|
To ensure timely delivery of control frames, large messages can be broken up
|
||||||
|
into smaller sized frames. The automatic fragment option turns on this
|
||||||
|
feature, and the write buffer size option determines the maximum size of
|
||||||
|
the fragments:
|
||||||
|
```
|
||||||
|
...
|
||||||
|
ws.set_option(beast::websocket::auto_fragment{true});
|
||||||
|
ws.set_option(beast::websocket::write_buffer_size{16384});
|
||||||
|
```
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
@@ -373,7 +400,7 @@ handler of the corresponding read function.]
|
|||||||
|
|
||||||
Because calls to read data may return a variable amount of bytes, the
|
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
|
interface to calls that read data require an object that meets the requirements
|
||||||
of [link beast.types.DynamicBuffer [*`DynamicBuffer`]]. This concept is modeled on
|
of [link beast.ref.DynamicBuffer [*`DynamicBuffer`]]. This concept is modeled on
|
||||||
[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/basic_streambuf.html `boost::asio::basic_streambuf`].
|
[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/basic_streambuf.html `boost::asio::basic_streambuf`].
|
||||||
|
|
||||||
The implementation does not perform queueing or buffering of messages. If
|
The implementation does not perform queueing or buffering of messages. If
|
||||||
@@ -429,7 +456,7 @@ use or require threads.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:safety Thread Safety]
|
[section:threads Thread Safety]
|
||||||
|
|
||||||
Like a regular asio socket, a [link beast.ref.websocket__stream `stream`] is
|
Like a regular asio socket, a [link beast.ref.websocket__stream `stream`] is
|
||||||
not thread safe. Callers are responsible for synchronizing operations on the
|
not thread safe. Callers are responsible for synchronizing operations on the
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
#
|
#
|
||||||
|
|
||||||
import os ;
|
|
||||||
|
|
||||||
exe http-crawl :
|
exe http-crawl :
|
||||||
http_crawl.cpp
|
http_crawl.cpp
|
||||||
urls_large_data.cpp
|
urls_large_data.cpp
|
||||||
@@ -23,4 +21,3 @@ exe http-example :
|
|||||||
exe websocket-example :
|
exe websocket-example :
|
||||||
websocket_example.cpp
|
websocket_example.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -8,9 +8,12 @@
|
|||||||
#ifndef BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
|
#ifndef BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
|
||||||
#define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
|
#define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
|
||||||
|
|
||||||
#include <beast/http/body_type.hpp>
|
#include <beast/core/error.hpp>
|
||||||
|
#include <beast/http/message.hpp>
|
||||||
|
#include <beast/http/resume_context.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/logic/tribool.hpp>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -34,8 +37,8 @@ struct file_body
|
|||||||
writer(writer const&) = delete;
|
writer(writer const&) = delete;
|
||||||
writer& operator=(writer const&) = delete;
|
writer& operator=(writer const&) = delete;
|
||||||
|
|
||||||
template<bool isRequest, class Headers>
|
template<bool isRequest, class Fields>
|
||||||
writer(message<isRequest, file_body, Headers> const& m) noexcept
|
writer(message<isRequest, file_body, Fields> const& m) noexcept
|
||||||
: path_(m.body)
|
: path_(m.body)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -58,14 +61,15 @@ struct file_body
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::uint64_t
|
std::uint64_t
|
||||||
content_length() const
|
content_length() const noexcept
|
||||||
{
|
{
|
||||||
return size_;
|
return size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Write>
|
template<class WriteFunction>
|
||||||
boost::tribool
|
boost::tribool
|
||||||
operator()(resume_context&&, error_code&, Write&& write)
|
write(resume_context&&, error_code&,
|
||||||
|
WriteFunction&& wf) noexcept
|
||||||
{
|
{
|
||||||
if(size_ - offset_ < sizeof(buf_))
|
if(size_ - offset_ < sizeof(buf_))
|
||||||
buf_len_ = static_cast<std::size_t>(
|
buf_len_ = static_cast<std::size_t>(
|
||||||
@@ -75,7 +79,7 @@ struct file_body
|
|||||||
auto const nread = fread(buf_, 1, sizeof(buf_), file_);
|
auto const nread = fread(buf_, 1, sizeof(buf_), file_);
|
||||||
(void)nread;
|
(void)nread;
|
||||||
offset_ += buf_len_;
|
offset_ += buf_len_;
|
||||||
write(boost::asio::buffer(buf_, buf_len_));
|
wf(boost::asio::buffer(buf_, buf_len_));
|
||||||
return offset_ >= size_;
|
return offset_ >= size_;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include "mime_type.hpp"
|
#include "mime_type.hpp"
|
||||||
|
|
||||||
#include <beast/http.hpp>
|
#include <beast/http.hpp>
|
||||||
|
#include <beast/core/handler_helpers.hpp>
|
||||||
|
#include <beast/core/handler_ptr.hpp>
|
||||||
#include <beast/core/placeholders.hpp>
|
#include <beast/core/placeholders.hpp>
|
||||||
#include <beast/core/streambuf.hpp>
|
#include <beast/core/streambuf.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
@@ -32,8 +34,8 @@ class http_async_server
|
|||||||
using address_type = boost::asio::ip::address;
|
using address_type = boost::asio::ip::address;
|
||||||
using socket_type = boost::asio::ip::tcp::socket;
|
using socket_type = boost::asio::ip::tcp::socket;
|
||||||
|
|
||||||
using req_type = request_v1<string_body>;
|
using req_type = request<string_body>;
|
||||||
using resp_type = response_v1<file_body>;
|
using resp_type = response<file_body>;
|
||||||
|
|
||||||
std::mutex m_;
|
std::mutex m_;
|
||||||
bool log_ = true;
|
bool log_ = true;
|
||||||
@@ -85,32 +87,26 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
template<class Stream, class Handler,
|
template<class Stream, class Handler,
|
||||||
bool isRequest, class Body, class Headers>
|
bool isRequest, class Body, class Fields>
|
||||||
class write_op
|
class write_op
|
||||||
{
|
{
|
||||||
using alloc_type =
|
|
||||||
handler_alloc<char, Handler>;
|
|
||||||
|
|
||||||
struct data
|
struct data
|
||||||
{
|
{
|
||||||
Stream& s;
|
|
||||||
message_v1<isRequest, Body, Headers> m;
|
|
||||||
Handler h;
|
|
||||||
bool cont;
|
bool cont;
|
||||||
|
Stream& s;
|
||||||
|
message<isRequest, Body, Fields> m;
|
||||||
|
|
||||||
template<class DeducedHandler>
|
data(Handler& handler, Stream& s_,
|
||||||
data(DeducedHandler&& h_, Stream& s_,
|
message<isRequest, Body, Fields>&& m_)
|
||||||
message_v1<isRequest, Body, Headers>&& m_)
|
: cont(beast_asio_helpers::
|
||||||
: s(s_)
|
is_continuation(handler))
|
||||||
|
, s(s_)
|
||||||
, m(std::move(m_))
|
, m(std::move(m_))
|
||||||
, h(std::forward<DeducedHandler>(h_))
|
|
||||||
, cont(boost_asio_handler_cont_helpers::
|
|
||||||
is_continuation(h))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<data> d_;
|
handler_ptr<data, Handler> d_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
write_op(write_op&&) = default;
|
write_op(write_op&&) = default;
|
||||||
@@ -118,7 +114,7 @@ private:
|
|||||||
|
|
||||||
template<class DeducedHandler, class... Args>
|
template<class DeducedHandler, class... Args>
|
||||||
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
: d_(make_handler_ptr<data, Handler>(
|
||||||
std::forward<DeducedHandler>(h), s,
|
std::forward<DeducedHandler>(h), s,
|
||||||
std::forward<Args>(args)...))
|
std::forward<Args>(args)...))
|
||||||
{
|
{
|
||||||
@@ -135,23 +131,23 @@ private:
|
|||||||
beast::http::async_write(d.s, d.m, std::move(*this));
|
beast::http::async_write(d.s, d.m, std::move(*this));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
d.h(ec);
|
d_.invoke(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
friend
|
friend
|
||||||
void* asio_handler_allocate(
|
void* asio_handler_allocate(
|
||||||
std::size_t size, write_op* op)
|
std::size_t size, write_op* op)
|
||||||
{
|
{
|
||||||
return boost_asio_handler_alloc_helpers::
|
return beast_asio_helpers::
|
||||||
allocate(size, op->d_->h);
|
allocate(size, op->d_.handler());
|
||||||
}
|
}
|
||||||
|
|
||||||
friend
|
friend
|
||||||
void asio_handler_deallocate(
|
void asio_handler_deallocate(
|
||||||
void* p, std::size_t size, write_op* op)
|
void* p, std::size_t size, write_op* op)
|
||||||
{
|
{
|
||||||
return boost_asio_handler_alloc_helpers::
|
return beast_asio_helpers::
|
||||||
deallocate(p, size, op->d_->h);
|
deallocate(p, size, op->d_.handler());
|
||||||
}
|
}
|
||||||
|
|
||||||
friend
|
friend
|
||||||
@@ -164,22 +160,22 @@ private:
|
|||||||
friend
|
friend
|
||||||
void asio_handler_invoke(Function&& f, write_op* op)
|
void asio_handler_invoke(Function&& f, write_op* op)
|
||||||
{
|
{
|
||||||
return boost_asio_handler_invoke_helpers::
|
return beast_asio_helpers::
|
||||||
invoke(f, op->d_->h);
|
invoke(f, op->d_.handler());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class Stream,
|
template<class Stream,
|
||||||
bool isRequest, class Body, class Headers,
|
bool isRequest, class Body, class Fields,
|
||||||
class DeducedHandler>
|
class DeducedHandler>
|
||||||
static
|
static
|
||||||
void
|
void
|
||||||
async_write(Stream& stream, message_v1<
|
async_write(Stream& stream, message<
|
||||||
isRequest, Body, Headers>&& msg,
|
isRequest, Body, Fields>&& msg,
|
||||||
DeducedHandler&& handler)
|
DeducedHandler&& handler)
|
||||||
{
|
{
|
||||||
write_op<Stream, typename std::decay<DeducedHandler>::type,
|
write_op<Stream, typename std::decay<DeducedHandler>::type,
|
||||||
isRequest, Body, Headers>{std::forward<DeducedHandler>(
|
isRequest, Body, Fields>{std::forward<DeducedHandler>(
|
||||||
handler), stream, std::move(msg)};
|
handler), stream, std::move(msg)};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,12 +232,12 @@ private:
|
|||||||
path = server_.root_ + path;
|
path = server_.root_ + path;
|
||||||
if(! boost::filesystem::exists(path))
|
if(! boost::filesystem::exists(path))
|
||||||
{
|
{
|
||||||
response_v1<string_body> res;
|
response<string_body> res;
|
||||||
res.status = 404;
|
res.status = 404;
|
||||||
res.reason = "Not Found";
|
res.reason = "Not Found";
|
||||||
res.version = req_.version;
|
res.version = req_.version;
|
||||||
res.headers.insert("Server", "http_async_server");
|
res.fields.insert("Server", "http_async_server");
|
||||||
res.headers.insert("Content-Type", "text/html");
|
res.fields.insert("Content-Type", "text/html");
|
||||||
res.body = "The file '" + path + "' was not found";
|
res.body = "The file '" + path + "' was not found";
|
||||||
prepare(res);
|
prepare(res);
|
||||||
async_write(sock_, std::move(res),
|
async_write(sock_, std::move(res),
|
||||||
@@ -249,32 +245,35 @@ private:
|
|||||||
asio::placeholders::error));
|
asio::placeholders::error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resp_type res;
|
|
||||||
res.status = 200;
|
|
||||||
res.reason = "OK";
|
|
||||||
res.version = req_.version;
|
|
||||||
res.headers.insert("Server", "http_async_server");
|
|
||||||
res.headers.insert("Content-Type", mime_type(path));
|
|
||||||
res.body = path;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
resp_type res;
|
||||||
|
res.status = 200;
|
||||||
|
res.reason = "OK";
|
||||||
|
res.version = req_.version;
|
||||||
|
res.fields.insert("Server", "http_async_server");
|
||||||
|
res.fields.insert("Content-Type", mime_type(path));
|
||||||
|
res.body = path;
|
||||||
prepare(res);
|
prepare(res);
|
||||||
|
async_write(sock_, std::move(res),
|
||||||
|
std::bind(&peer::on_write, shared_from_this(),
|
||||||
|
asio::placeholders::error));
|
||||||
}
|
}
|
||||||
catch(std::exception const& e)
|
catch(std::exception const& e)
|
||||||
{
|
{
|
||||||
res = {};
|
response<string_body> res;
|
||||||
res.status = 500;
|
res.status = 500;
|
||||||
res.reason = "Internal Error";
|
res.reason = "Internal Error";
|
||||||
res.version = req_.version;
|
res.version = req_.version;
|
||||||
res.headers.insert("Server", "http_async_server");
|
res.fields.insert("Server", "http_async_server");
|
||||||
res.headers.insert("Content-Type", "text/html");
|
res.fields.insert("Content-Type", "text/html");
|
||||||
res.body =
|
res.body =
|
||||||
std::string{"An internal error occurred"} + e.what();
|
std::string{"An internal error occurred"} + e.what();
|
||||||
prepare(res);
|
prepare(res);
|
||||||
|
async_write(sock_, std::move(res),
|
||||||
|
std::bind(&peer::on_write, shared_from_this(),
|
||||||
|
asio::placeholders::error));
|
||||||
}
|
}
|
||||||
async_write(sock_, std::move(res),
|
|
||||||
std::bind(&peer::on_write, shared_from_this(),
|
|
||||||
asio::placeholders::error));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_write(error_code ec)
|
void on_write(error_code ec)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <beast/core/streambuf.hpp>
|
#include <beast/core/streambuf.hpp>
|
||||||
#include <beast/http.hpp>
|
#include <beast/http.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using namespace beast::http;
|
using namespace beast::http;
|
||||||
@@ -35,21 +36,21 @@ int main(int, char const*[])
|
|||||||
ip::tcp::socket sock(ios);
|
ip::tcp::socket sock(ios);
|
||||||
connect(sock, it);
|
connect(sock, it);
|
||||||
auto ep = sock.remote_endpoint();
|
auto ep = sock.remote_endpoint();
|
||||||
request_v1<empty_body> req;
|
request<empty_body> req;
|
||||||
req.method = "GET";
|
req.method = "GET";
|
||||||
req.url = "/";
|
req.url = "/";
|
||||||
req.version = 11;
|
req.version = 11;
|
||||||
req.headers.insert("Host", host +
|
req.fields.insert("Host", host + std::string(":") +
|
||||||
std::string(":") + std::to_string(ep.port()));
|
boost::lexical_cast<std::string>(ep.port()));
|
||||||
req.headers.insert("User-Agent", "beast/http");
|
req.fields.insert("User-Agent", "beast/http");
|
||||||
prepare(req);
|
prepare(req);
|
||||||
write(sock, req);
|
write(sock, req);
|
||||||
response_v1<string_body> res;
|
response<string_body> res;
|
||||||
streambuf sb;
|
streambuf sb;
|
||||||
beast::http::read(sock, sb, res);
|
beast::http::read(sock, sb, res);
|
||||||
std::cout << res;
|
std::cout << res;
|
||||||
}
|
}
|
||||||
catch(boost::system::system_error const& ec)
|
catch(beast::system_error const& ec)
|
||||||
{
|
{
|
||||||
std::cerr << host << ": " << ec.what();
|
std::cerr << host << ": " << ec.what();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <beast/http.hpp>
|
#include <beast/http.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -21,18 +22,19 @@ int main()
|
|||||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
|
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
|
||||||
|
|
||||||
// Send HTTP request using beast
|
// Send HTTP request using beast
|
||||||
beast::http::request_v1<beast::http::empty_body> req;
|
beast::http::request<beast::http::empty_body> req;
|
||||||
req.method = "GET";
|
req.method = "GET";
|
||||||
req.url = "/";
|
req.url = "/";
|
||||||
req.version = 11;
|
req.version = 11;
|
||||||
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
|
req.fields.replace("Host", host + ":" +
|
||||||
req.headers.replace("User-Agent", "Beast");
|
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
||||||
|
req.fields.replace("User-Agent", "Beast");
|
||||||
beast::http::prepare(req);
|
beast::http::prepare(req);
|
||||||
beast::http::write(sock, req);
|
beast::http::write(sock, req);
|
||||||
|
|
||||||
// Receive and print HTTP response using beast
|
// Receive and print HTTP response using beast
|
||||||
beast::streambuf sb;
|
beast::streambuf sb;
|
||||||
beast::http::response_v1<beast::http::streambuf_body> resp;
|
beast::http::response<beast::http::streambuf_body> resp;
|
||||||
beast::http::read(sock, sb, resp);
|
beast::http::read(sock, sb, resp);
|
||||||
std::cout << resp;
|
std::cout << resp;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,34 +20,26 @@ int main(int ac, char const* av[])
|
|||||||
po::options_description desc("Options");
|
po::options_description desc("Options");
|
||||||
|
|
||||||
desc.add_options()
|
desc.add_options()
|
||||||
("root,r", po::value<std::string>()->implicit_value("."),
|
("root,r", po::value<std::string>()->default_value("."),
|
||||||
"Set the root directory for serving files")
|
"Set the root directory for serving files")
|
||||||
("port,p", po::value<std::uint16_t>()->implicit_value(8080),
|
("port,p", po::value<std::uint16_t>()->default_value(8080),
|
||||||
"Set the port number for the server")
|
"Set the port number for the server")
|
||||||
("ip", po::value<std::string>()->implicit_value("0.0.0.0"),
|
("ip", po::value<std::string>()->default_value("0.0.0.0"),
|
||||||
"Set the IP address to bind to, \"0.0.0.0\" for all")
|
"Set the IP address to bind to, \"0.0.0.0\" for all")
|
||||||
("threads,n", po::value<std::size_t>()->implicit_value(4),
|
("threads,n", po::value<std::size_t>()->default_value(4),
|
||||||
"Set the number of threads to use")
|
"Set the number of threads to use")
|
||||||
("sync,s", "Launch a synchronous server")
|
("sync,s", "Launch a synchronous server")
|
||||||
;
|
;
|
||||||
po::variables_map vm;
|
po::variables_map vm;
|
||||||
po::store(po::parse_command_line(ac, av, desc), vm);
|
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||||
|
|
||||||
std::string root = ".";
|
std::string root = vm["root"].as<std::string>();
|
||||||
if(vm.count("root"))
|
|
||||||
root = vm["root"].as<std::string>();
|
|
||||||
|
|
||||||
std::uint16_t port = 8080;
|
std::uint16_t port = vm["port"].as<std::uint16_t>();
|
||||||
if(vm.count("port"))
|
|
||||||
port = vm["port"].as<std::uint16_t>();
|
|
||||||
|
|
||||||
std::string ip = "0.0.0.0";
|
std::string ip = vm["ip"].as<std::string>();
|
||||||
if(vm.count("ip"))
|
|
||||||
ip = vm["ip"].as<std::string>();
|
|
||||||
|
|
||||||
std::size_t threads = 4;
|
std::size_t threads = vm["threads"].as<std::size_t>();
|
||||||
if(vm.count("threads"))
|
|
||||||
threads = vm["threads"].as<std::size_t>();
|
|
||||||
|
|
||||||
bool sync = vm.count("sync") > 0;
|
bool sync = vm.count("sync") > 0;
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include "file_body.hpp"
|
#include "file_body.hpp"
|
||||||
#include "mime_type.hpp"
|
#include "mime_type.hpp"
|
||||||
|
|
||||||
|
#include <beast/http.hpp>
|
||||||
|
#include <beast/core/placeholders.hpp>
|
||||||
#include <beast/core/streambuf.hpp>
|
#include <beast/core/streambuf.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -19,6 +21,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -32,8 +35,8 @@ class http_sync_server
|
|||||||
using address_type = boost::asio::ip::address;
|
using address_type = boost::asio::ip::address;
|
||||||
using socket_type = boost::asio::ip::tcp::socket;
|
using socket_type = boost::asio::ip::tcp::socket;
|
||||||
|
|
||||||
using req_type = request_v1<string_body>;
|
using req_type = request<string_body>;
|
||||||
using resp_type = response_v1<file_body>;
|
using resp_type = response<file_body>;
|
||||||
|
|
||||||
bool log_ = true;
|
bool log_ = true;
|
||||||
std::mutex m_;
|
std::mutex m_;
|
||||||
@@ -161,44 +164,48 @@ private:
|
|||||||
path = root_ + path;
|
path = root_ + path;
|
||||||
if(! boost::filesystem::exists(path))
|
if(! boost::filesystem::exists(path))
|
||||||
{
|
{
|
||||||
response_v1<string_body> res;
|
response<string_body> res;
|
||||||
res.status = 404;
|
res.status = 404;
|
||||||
res.reason = "Not Found";
|
res.reason = "Not Found";
|
||||||
res.version = req.version;
|
res.version = req.version;
|
||||||
res.headers.insert("Server", "http_sync_server");
|
res.fields.insert("Server", "http_sync_server");
|
||||||
res.headers.insert("Content-Type", "text/html");
|
res.fields.insert("Content-Type", "text/html");
|
||||||
res.body = "The file '" + path + "' was not found";
|
res.body = "The file '" + path + "' was not found";
|
||||||
prepare(res);
|
prepare(res);
|
||||||
write(sock, res, ec);
|
write(sock, res, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
break;
|
break;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
resp_type res;
|
|
||||||
res.status = 200;
|
|
||||||
res.reason = "OK";
|
|
||||||
res.version = req.version;
|
|
||||||
res.headers.insert("Server", "http_sync_server");
|
|
||||||
res.headers.insert("Content-Type", mime_type(path));
|
|
||||||
res.body = path;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
resp_type res;
|
||||||
|
res.status = 200;
|
||||||
|
res.reason = "OK";
|
||||||
|
res.version = req.version;
|
||||||
|
res.fields.insert("Server", "http_sync_server");
|
||||||
|
res.fields.insert("Content-Type", mime_type(path));
|
||||||
|
res.body = path;
|
||||||
prepare(res);
|
prepare(res);
|
||||||
|
write(sock, res, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
catch(std::exception const& e)
|
catch(std::exception const& e)
|
||||||
{
|
{
|
||||||
res = {};
|
response<string_body> res;
|
||||||
res.status = 500;
|
res.status = 500;
|
||||||
res.reason = "Internal Error";
|
res.reason = "Internal Error";
|
||||||
res.version = req.version;
|
res.version = req.version;
|
||||||
res.headers.insert("Server", "http_sync_server");
|
res.fields.insert("Server", "http_sync_server");
|
||||||
res.headers.insert("Content-Type", "text/html");
|
res.fields.insert("Content-Type", "text/html");
|
||||||
res.body =
|
res.body =
|
||||||
std::string{"An internal error occurred"} + e.what();
|
std::string{"An internal error occurred: "} + e.what();
|
||||||
prepare(res);
|
prepare(res);
|
||||||
|
write(sock, res, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
write(sock, res, ec);
|
|
||||||
if(ec)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
fail(id, ec);
|
fail(id, ec);
|
||||||
}
|
}
|
||||||
|
|||||||
32
examples/ssl/CMakeLists.txt
Normal file
32
examples/ssl/CMakeLists.txt
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Part of Beast
|
||||||
|
|
||||||
|
GroupSources(extras/beast extras)
|
||||||
|
GroupSources(include/beast beast)
|
||||||
|
|
||||||
|
GroupSources(examples/ssl "/")
|
||||||
|
|
||||||
|
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||||
|
|
||||||
|
add_executable (http-ssl-example
|
||||||
|
${BEAST_INCLUDES}
|
||||||
|
${EXTRAS_INCLUDES}
|
||||||
|
http_ssl_example.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(http-ssl-example ${OPENSSL_LIBRARIES})
|
||||||
|
|
||||||
|
if (NOT WIN32)
|
||||||
|
target_link_libraries(http-ssl-example ${Boost_LIBRARIES} Threads::Threads)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable (websocket-ssl-example
|
||||||
|
${BEAST_INCLUDES}
|
||||||
|
${EXTRAS_INCLUDES}
|
||||||
|
websocket_ssl_example.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(websocket-ssl-example ${OPENSSL_LIBRARIES})
|
||||||
|
|
||||||
|
if (NOT WIN32)
|
||||||
|
target_link_libraries(websocket-ssl-example ${Boost_LIBRARIES} Threads::Threads)
|
||||||
|
endif()
|
||||||
54
examples/ssl/Jamfile.v2
Normal file
54
examples/ssl/Jamfile.v2
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
#
|
||||||
|
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
#
|
||||||
|
|
||||||
|
import os ;
|
||||||
|
|
||||||
|
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 ] = HAIKU
|
||||||
|
{
|
||||||
|
lib network ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ os.name ] = NT
|
||||||
|
{
|
||||||
|
lib ssl : : <name>ssleay32 ;
|
||||||
|
lib crypto : : <name>libeay32 ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lib ssl ;
|
||||||
|
lib crypto ;
|
||||||
|
}
|
||||||
|
|
||||||
|
project
|
||||||
|
: requirements
|
||||||
|
<library>ssl
|
||||||
|
<library>crypto
|
||||||
|
;
|
||||||
|
|
||||||
|
exe http-ssl-example
|
||||||
|
:
|
||||||
|
http_ssl_example.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
exe websocket-ssl-example
|
||||||
|
:
|
||||||
|
websocket_ssl_example.cpp
|
||||||
|
;
|
||||||
58
examples/ssl/http_ssl_example.cpp
Normal file
58
examples/ssl/http_ssl_example.cpp
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <beast/http.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
using boost::asio::connect;
|
||||||
|
using socket = boost::asio::ip::tcp::socket;
|
||||||
|
using resolver = boost::asio::ip::tcp::resolver;
|
||||||
|
using io_service = boost::asio::io_service;
|
||||||
|
namespace ssl = boost::asio::ssl;
|
||||||
|
|
||||||
|
// Normal boost::asio setup
|
||||||
|
std::string const host = "github.com";
|
||||||
|
io_service ios;
|
||||||
|
resolver r{ios};
|
||||||
|
socket sock{ios};
|
||||||
|
connect(sock, r.resolve(resolver::query{host, "https"}));
|
||||||
|
|
||||||
|
// Perform SSL handshaking
|
||||||
|
ssl::context ctx{ssl::context::sslv23};
|
||||||
|
ssl::stream<socket&> stream{sock, ctx};
|
||||||
|
stream.set_verify_mode(ssl::verify_none);
|
||||||
|
stream.handshake(ssl::stream_base::client);
|
||||||
|
|
||||||
|
// Send HTTP request over SSL using Beast
|
||||||
|
beast::http::request<beast::http::empty_body> req;
|
||||||
|
req.method = "GET";
|
||||||
|
req.url = "/";
|
||||||
|
req.version = 11;
|
||||||
|
req.fields.insert("Host", host + ":" +
|
||||||
|
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
||||||
|
req.fields.insert("User-Agent", "Beast");
|
||||||
|
beast::http::prepare(req);
|
||||||
|
beast::http::write(stream, req);
|
||||||
|
|
||||||
|
// Receive and print HTTP response using Beast
|
||||||
|
beast::streambuf sb;
|
||||||
|
beast::http::response<beast::http::streambuf_body> resp;
|
||||||
|
beast::http::read(stream, sb, resp);
|
||||||
|
std::cout << resp;
|
||||||
|
|
||||||
|
// Shut down SSL on the stream
|
||||||
|
boost::system::error_code ec;
|
||||||
|
stream.shutdown(ec);
|
||||||
|
if(ec && ec != boost::asio::error::eof)
|
||||||
|
std::cout << "error: " << ec.message();
|
||||||
|
}
|
||||||
49
examples/ssl/websocket_ssl_example.cpp
Normal file
49
examples/ssl/websocket_ssl_example.cpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <beast/core/to_string.hpp>
|
||||||
|
#include <beast/websocket.hpp>
|
||||||
|
#include <beast/websocket/ssl.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
using boost::asio::connect;
|
||||||
|
using socket = boost::asio::ip::tcp::socket;
|
||||||
|
using resolver = boost::asio::ip::tcp::resolver;
|
||||||
|
using io_service = boost::asio::io_service;
|
||||||
|
namespace ssl = boost::asio::ssl;
|
||||||
|
|
||||||
|
// Normal boost::asio setup
|
||||||
|
std::string const host = "echo.websocket.org";
|
||||||
|
io_service ios;
|
||||||
|
resolver r{ios};
|
||||||
|
socket sock{ios};
|
||||||
|
connect(sock, r.resolve(resolver::query{host, "https"}));
|
||||||
|
|
||||||
|
// Perform SSL handshaking
|
||||||
|
using stream_type = ssl::stream<socket&>;
|
||||||
|
ssl::context ctx{ssl::context::sslv23};
|
||||||
|
stream_type stream{sock, ctx};
|
||||||
|
stream.set_verify_mode(ssl::verify_none);
|
||||||
|
stream.handshake(ssl::stream_base::client);
|
||||||
|
|
||||||
|
// Secure WebSocket connect and send message using Beast
|
||||||
|
beast::websocket::stream<stream_type&> ws{stream};
|
||||||
|
ws.handshake(host, "/");
|
||||||
|
ws.write(boost::asio::buffer("Hello, world!"));
|
||||||
|
|
||||||
|
// Receive Secure WebSocket message, print and close using Beast
|
||||||
|
beast::streambuf sb;
|
||||||
|
beast::websocket::opcode op;
|
||||||
|
ws.read(op, sb);
|
||||||
|
ws.close(beast::websocket::close_code::normal);
|
||||||
|
std::cout << to_string(sb.data()) << "\n";
|
||||||
|
}
|
||||||
@@ -24,12 +24,12 @@ int main()
|
|||||||
// WebSocket connect and send message using beast
|
// WebSocket connect and send message using beast
|
||||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
||||||
ws.handshake(host, "/");
|
ws.handshake(host, "/");
|
||||||
ws.write(boost::asio::buffer("Hello, world!"));
|
ws.write(boost::asio::buffer(std::string("Hello, world!")));
|
||||||
|
|
||||||
// Receive WebSocket message, print and close using beast
|
// Receive WebSocket message, print and close using beast
|
||||||
beast::streambuf sb;
|
beast::streambuf sb;
|
||||||
beast::websocket::opcode op;
|
beast::websocket::opcode op;
|
||||||
ws.read(op, sb);
|
ws.read(op, sb);
|
||||||
ws.close(beast::websocket::close_code::normal);
|
ws.close(beast::websocket::close_code::normal);
|
||||||
std::cout << to_string(sb.data()) << "\n";
|
std::cout << beast::to_string(sb.data()) << "\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,9 @@
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
enum error
|
enum class error
|
||||||
{
|
{
|
||||||
success = 0,
|
fail_error = 1
|
||||||
|
|
||||||
fail_error
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
@@ -79,14 +77,15 @@ inline
|
|||||||
error_code
|
error_code
|
||||||
make_error_code(error ev)
|
make_error_code(error ev)
|
||||||
{
|
{
|
||||||
return error_code{static_cast<int>(ev),
|
return error_code{
|
||||||
detail::get_error_category()};
|
static_cast<std::underlying_type<error>::type>(ev),
|
||||||
|
detail::get_error_category()};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A countdown to simulated failure.
|
/** A countdown to simulated failure.
|
||||||
|
|
||||||
On the Nth operation, the class will fail with the specified
|
On the Nth operation, the class will fail with the specified
|
||||||
error code, or the default error code of @ref fail_error.
|
error code, or the default error code of @ref error::fail_error.
|
||||||
*/
|
*/
|
||||||
class fail_counter
|
class fail_counter
|
||||||
{
|
{
|
||||||
@@ -102,7 +101,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
explicit
|
explicit
|
||||||
fail_counter(std::size_t n,
|
fail_counter(std::size_t n,
|
||||||
error_code ev = make_error_code(fail_error))
|
error_code ev = make_error_code(error::fail_error))
|
||||||
: n_(n)
|
: n_(n)
|
||||||
, ec_(ev)
|
, ec_(ev)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#ifndef BEAST_TEST_STRING_STREAM_HPP
|
#ifndef BEAST_TEST_STRING_STREAM_HPP
|
||||||
#define BEAST_TEST_STRING_STREAM_HPP
|
#define BEAST_TEST_STRING_STREAM_HPP
|
||||||
|
|
||||||
|
#include <beast/core/async_completion.hpp>
|
||||||
#include <beast/core/bind_handler.hpp>
|
#include <beast/core/bind_handler.hpp>
|
||||||
#include <beast/core/error.hpp>
|
#include <beast/core/error.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <beast/unit_test/amount.hpp>
|
#include <beast/unit_test/amount.hpp>
|
||||||
#include <beast/unit_test/recorder.hpp>
|
#include <beast/unit_test/recorder.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -214,7 +215,8 @@ reporter<_>::fmtdur(typename clock_type::duration const& d)
|
|||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
auto const ms = duration_cast<milliseconds>(d);
|
auto const ms = duration_cast<milliseconds>(d);
|
||||||
if(ms < seconds{1})
|
if(ms < seconds{1})
|
||||||
return std::to_string(ms.count()) + "ms";
|
return boost::lexical_cast<std::string>(
|
||||||
|
ms.count()) + "ms";
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << std::fixed << std::setprecision(1) <<
|
ss << std::fixed << std::setprecision(1) <<
|
||||||
(ms.count()/1000.) << "s";
|
(ms.count()/1000.) << "s";
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#define BEAST_UNIT_TEST_RUNNER_H_INCLUDED
|
#define BEAST_UNIT_TEST_RUNNER_H_INCLUDED
|
||||||
|
|
||||||
#include <beast/unit_test/suite_info.hpp>
|
#include <beast/unit_test/suite_info.hpp>
|
||||||
#include <cassert>
|
#include <boost/assert.hpp>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -185,7 +185,7 @@ runner::run(suite_info const& s)
|
|||||||
on_suite_begin(s);
|
on_suite_begin(s);
|
||||||
s.run(*this);
|
s.run(*this);
|
||||||
// Forgot to call pass or fail.
|
// Forgot to call pass or fail.
|
||||||
assert(cond_);
|
BOOST_ASSERT(cond_);
|
||||||
on_case_end();
|
on_case_end();
|
||||||
on_suite_end();
|
on_suite_end();
|
||||||
return failed_;
|
return failed_;
|
||||||
@@ -239,9 +239,9 @@ runner::testcase(std::string const& name)
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||||
// Name may not be empty
|
// Name may not be empty
|
||||||
assert(default_ || ! name.empty());
|
BOOST_ASSERT(default_ || ! name.empty());
|
||||||
// Forgot to call pass or fail
|
// Forgot to call pass or fail
|
||||||
assert(default_ || cond_);
|
BOOST_ASSERT(default_ || cond_);
|
||||||
if(! default_)
|
if(! default_)
|
||||||
on_case_end();
|
on_case_end();
|
||||||
default_ = false;
|
default_ = false;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <beast/unit_test/runner.hpp>
|
#include <beast/unit_test/runner.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -17,6 +18,27 @@
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace unit_test {
|
namespace unit_test {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class String>
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
make_reason(String const& reason,
|
||||||
|
char const* file, int line)
|
||||||
|
{
|
||||||
|
std::string s(reason);
|
||||||
|
if(! s.empty())
|
||||||
|
s.append(": ");
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
s.append(fs::path{file}.filename().string());
|
||||||
|
s.append("(");
|
||||||
|
s.append(boost::lexical_cast<std::string>(line));
|
||||||
|
s.append(")");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
class thread;
|
class thread;
|
||||||
|
|
||||||
enum abort_t
|
enum abort_t
|
||||||
@@ -178,11 +200,21 @@ public:
|
|||||||
|
|
||||||
/** Record a failure.
|
/** Record a failure.
|
||||||
|
|
||||||
@param reason
|
@param reason Optional text added to the output on a failure.
|
||||||
|
|
||||||
|
@param file The source code file where the test failed.
|
||||||
|
|
||||||
|
@param line The source code line number where the test failed.
|
||||||
*/
|
*/
|
||||||
|
/** @{ */
|
||||||
|
template<class String>
|
||||||
|
void
|
||||||
|
fail(String const& reason, char const* file, int line);
|
||||||
|
|
||||||
template<class = void>
|
template<class = void>
|
||||||
void
|
void
|
||||||
fail(std::string const& reason = "");
|
fail(std::string const& reason = "");
|
||||||
|
/** @} */
|
||||||
|
|
||||||
/** Evaluate a test condition.
|
/** Evaluate a test condition.
|
||||||
|
|
||||||
@@ -206,25 +238,25 @@ public:
|
|||||||
bool
|
bool
|
||||||
expect(Condition const& shouldBeTrue)
|
expect(Condition const& shouldBeTrue)
|
||||||
{
|
{
|
||||||
return expect(shouldBeTrue, {});
|
return expect(shouldBeTrue, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Condition>
|
template<class Condition, class String>
|
||||||
bool
|
bool
|
||||||
expect(Condition const& shouldBeTrue, std::string const& reason);
|
expect(Condition const& shouldBeTrue, String const& reason);
|
||||||
|
|
||||||
template<class Condition>
|
template<class Condition>
|
||||||
bool
|
bool
|
||||||
expect(Condition const& shouldBeTrue,
|
expect(Condition const& shouldBeTrue,
|
||||||
char const* file, int line)
|
char const* file, int line)
|
||||||
{
|
{
|
||||||
return expect(shouldBeTrue, {}, file, line);
|
return expect(shouldBeTrue, "", file, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Condition>
|
template<class Condition, class String>
|
||||||
bool
|
bool
|
||||||
expect(Condition const& shouldBeTrue,
|
expect(Condition const& shouldBeTrue,
|
||||||
std::string const& reason, char const* file, int line);
|
String const& reason, char const* file, int line);
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -402,11 +434,11 @@ operator()(runner& r)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Condition>
|
template<class Condition, class String>
|
||||||
bool
|
bool
|
||||||
suite::
|
suite::
|
||||||
expect(
|
expect(
|
||||||
Condition const& shouldBeTrue, std::string const& reason)
|
Condition const& shouldBeTrue, String const& reason)
|
||||||
{
|
{
|
||||||
if(shouldBeTrue)
|
if(shouldBeTrue)
|
||||||
{
|
{
|
||||||
@@ -417,26 +449,18 @@ expect(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Condition>
|
template<class Condition, class String>
|
||||||
bool
|
bool
|
||||||
suite::
|
suite::
|
||||||
expect(Condition const& shouldBeTrue,
|
expect(Condition const& shouldBeTrue,
|
||||||
std::string const& reason, char const* file, int line)
|
String const& reason, char const* file, int line)
|
||||||
{
|
{
|
||||||
if(shouldBeTrue)
|
if(shouldBeTrue)
|
||||||
{
|
{
|
||||||
pass();
|
pass();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
std::string s;
|
fail(detail::make_reason(reason, file, line));
|
||||||
if(! reason.empty())
|
|
||||||
{
|
|
||||||
s += reason;
|
|
||||||
s += " ";
|
|
||||||
}
|
|
||||||
s += boost::filesystem::path{file}.filename().string() +
|
|
||||||
"(" + std::to_string(line) + ")";
|
|
||||||
fail(s);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,6 +559,14 @@ fail(std::string const& reason)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class String>
|
||||||
|
void
|
||||||
|
suite::
|
||||||
|
fail(String const& reason, char const* file, int line)
|
||||||
|
{
|
||||||
|
fail(detail::make_reason(reason, file, line));
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void
|
void
|
||||||
suite::
|
suite::
|
||||||
@@ -583,7 +615,8 @@ run(runner& r)
|
|||||||
|
|
||||||
If the condition is false, the file and line number are reported.
|
If the condition is false, the file and line number are reported.
|
||||||
*/
|
*/
|
||||||
#define BEAST_EXPECTS(cond, reason) expect(cond, reason, __FILE__, __LINE__)
|
#define BEAST_EXPECTS(cond, reason) ((cond) ? (pass(), true) : \
|
||||||
|
(fail((reason), __FILE__, __LINE__), false))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // unit_test
|
} // unit_test
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
#include <beast/unit_test/suite_info.hpp>
|
#include <beast/unit_test/suite_info.hpp>
|
||||||
#include <beast/unit_test/detail/const_container.hpp>
|
#include <beast/unit_test/detail/const_container.hpp>
|
||||||
#include <cassert>
|
#include <boost/assert.hpp>
|
||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
@@ -57,13 +57,13 @@ suite_list::insert(
|
|||||||
std::string s;
|
std::string s;
|
||||||
s = std::string(library) + "." + module + "." + name;
|
s = std::string(library) + "." + module + "." + name;
|
||||||
auto const result(names_.insert(s));
|
auto const result(names_.insert(s));
|
||||||
assert(result.second); // Duplicate name
|
BOOST_ASSERT(result.second); // Duplicate name
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto const result(classes_.insert(
|
auto const result(classes_.insert(
|
||||||
std::type_index(typeid(Suite))));
|
std::type_index(typeid(Suite))));
|
||||||
assert(result.second); // Duplicate type
|
BOOST_ASSERT(result.second); // Duplicate type
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
cont().emplace(make_suite_info<Suite>(
|
cont().emplace(make_suite_info<Suite>(
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
#include <beast/core/error.hpp>
|
#include <beast/core/error.hpp>
|
||||||
#include <beast/core/handler_alloc.hpp>
|
#include <beast/core/handler_alloc.hpp>
|
||||||
#include <beast/core/handler_concepts.hpp>
|
#include <beast/core/handler_concepts.hpp>
|
||||||
|
#include <beast/core/handler_helpers.hpp>
|
||||||
|
#include <beast/core/handler_ptr.hpp>
|
||||||
#include <beast/core/placeholders.hpp>
|
#include <beast/core/placeholders.hpp>
|
||||||
#include <beast/core/prepare_buffers.hpp>
|
#include <beast/core/prepare_buffers.hpp>
|
||||||
#include <beast/core/static_streambuf.hpp>
|
#include <beast/core/static_streambuf.hpp>
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ namespace beast {
|
|||||||
...
|
...
|
||||||
template<class CompletionHandler>
|
template<class CompletionHandler>
|
||||||
typename async_completion<CompletionHandler,
|
typename async_completion<CompletionHandler,
|
||||||
void(boost::system::error_code)>::result_type
|
void(error_code)>::result_type
|
||||||
async_initfn(..., CompletionHandler&& handler)
|
async_initfn(..., CompletionHandler&& handler)
|
||||||
{
|
{
|
||||||
async_completion<CompletionHandler,
|
async_completion<CompletionHandler,
|
||||||
void(boost::system::error_code)> completion(handler);
|
void(error_code)> completion(handler);
|
||||||
...
|
...
|
||||||
return completion.result.get();
|
return completion.result.get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace beast {
|
|||||||
the sequence to accommodate changes in the size of the character
|
the sequence to accommodate changes in the size of the character
|
||||||
sequence.
|
sequence.
|
||||||
|
|
||||||
@note Meets the requirements of @b `DynamicBuffer`.
|
@note Meets the requirements of @b DynamicBuffer.
|
||||||
|
|
||||||
@tparam Allocator The allocator to use for managing memory.
|
@tparam Allocator The allocator to use for managing memory.
|
||||||
*/
|
*/
|
||||||
@@ -209,6 +209,36 @@ public:
|
|||||||
return this->member();
|
return this->member();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the default allocation size.
|
||||||
|
|
||||||
|
This is the smallest size that the stream buffer will allocate.
|
||||||
|
The size of the allocation can influence capacity, which will
|
||||||
|
affect algorithms that use capacity to efficiently read from
|
||||||
|
streams.
|
||||||
|
*/
|
||||||
|
std::size_t
|
||||||
|
alloc_size() const
|
||||||
|
{
|
||||||
|
return alloc_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the default allocation size.
|
||||||
|
|
||||||
|
This is the smallest size that the stream buffer will allocate.
|
||||||
|
The size of the allocation can influence capacity, which will
|
||||||
|
affect algorithms that use capacity to efficiently read from
|
||||||
|
streams.
|
||||||
|
|
||||||
|
@note This will not affect any already-existing allocations.
|
||||||
|
|
||||||
|
@param n The number of bytes.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
alloc_size(std::size_t n)
|
||||||
|
{
|
||||||
|
alloc_size_ = n;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the size of the input sequence.
|
/// Returns the size of the input sequence.
|
||||||
size_type
|
size_type
|
||||||
size() const
|
size() const
|
||||||
@@ -220,7 +250,7 @@ public:
|
|||||||
size_type
|
size_type
|
||||||
max_size() const
|
max_size() const
|
||||||
{
|
{
|
||||||
return std::numeric_limits<std::size_t>::max();
|
return (std::numeric_limits<std::size_t>::max)();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
|
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
|
||||||
|
|||||||
@@ -19,18 +19,21 @@
|
|||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
/** Concatenate 2 or more buffer sequences to form a `ConstBufferSequence`.
|
/** Concatenate 2 or more buffer sequences.
|
||||||
|
|
||||||
This function returns a @b `ConstBufferSequence` that when iterated,
|
This function returns a constant or mutable buffer sequence which,
|
||||||
efficiently concatenates the input buffer sequences. Copies of the
|
when iterated, efficiently concatenates the input buffer sequences.
|
||||||
arguments passed will be made; however, the returned object does
|
Copies of the arguments passed will be made; however, the returned
|
||||||
not take ownership of the underlying memory. The application is still
|
object does not take ownership of the underlying memory. The application
|
||||||
responsible for managing the lifetime of the referenced memory.
|
is still responsible for managing the lifetime of the referenced memory.
|
||||||
|
|
||||||
@param buffers The list of buffer sequences to concatenate.
|
@param buffers The list of buffer sequences to concatenate.
|
||||||
|
|
||||||
@return A new @b `ConstBufferSequence` that represents the
|
@return A new buffer sequence that represents the concatenation of
|
||||||
concatenation of the input buffer sequences.
|
the input buffer sequences. This buffer sequence will be a
|
||||||
|
@b MutableBufferSequence if each of the passed buffer sequences is
|
||||||
|
also a @b MutableBufferSequence, else the returned buffer sequence
|
||||||
|
will be a @b ConstBufferSequence.
|
||||||
*/
|
*/
|
||||||
#if GENERATING_DOCS
|
#if GENERATING_DOCS
|
||||||
template<class... BufferSequence>
|
template<class... BufferSequence>
|
||||||
@@ -38,14 +41,15 @@ implementation_defined
|
|||||||
buffer_cat(BufferSequence const&... buffers)
|
buffer_cat(BufferSequence const&... buffers)
|
||||||
#else
|
#else
|
||||||
template<class B1, class B2, class... Bn>
|
template<class B1, class B2, class... Bn>
|
||||||
detail::buffer_cat_helper<
|
detail::buffer_cat_helper<B1, B2, Bn...>
|
||||||
boost::asio::const_buffer, B1, B2, Bn...>
|
|
||||||
buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
|
buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
static_assert(
|
||||||
|
detail::is_all_ConstBufferSequence<B1, B2, Bn...>::value,
|
||||||
|
"BufferSequence requirements not met");
|
||||||
return detail::buffer_cat_helper<
|
return detail::buffer_cat_helper<
|
||||||
boost::asio::const_buffer,
|
B1, B2, Bn...>{b1, b2, bn...};
|
||||||
B1, B2, Bn...>(b1, b2, bn...);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // beast
|
} // beast
|
||||||
|
|||||||
@@ -37,20 +37,12 @@ namespace beast {
|
|||||||
consumable `ConstBufferSequence`. Violations of buffer const safety
|
consumable `ConstBufferSequence`. Violations of buffer const safety
|
||||||
are not permitted, and will result in a compile error.
|
are not permitted, and will result in a compile error.
|
||||||
*/
|
*/
|
||||||
template<class BufferSequence,
|
template<class BufferSequence>
|
||||||
class ValueType = typename BufferSequence::value_type>
|
|
||||||
class consuming_buffers
|
class consuming_buffers
|
||||||
{
|
{
|
||||||
using iter_type =
|
using iter_type =
|
||||||
typename BufferSequence::const_iterator;
|
typename BufferSequence::const_iterator;
|
||||||
|
|
||||||
static_assert(is_BufferSequence<BufferSequence, ValueType>::value,
|
|
||||||
"BufferSequence requirements not met");
|
|
||||||
|
|
||||||
static_assert(std::is_constructible<ValueType,
|
|
||||||
typename std::iterator_traits<iter_type>::value_type>::value,
|
|
||||||
"ValueType requirements not met");
|
|
||||||
|
|
||||||
BufferSequence bs_;
|
BufferSequence bs_;
|
||||||
iter_type begin_;
|
iter_type begin_;
|
||||||
std::size_t skip_ = 0;
|
std::size_t skip_ = 0;
|
||||||
@@ -65,7 +57,12 @@ class consuming_buffers
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
/// The type for each element in the list of buffers.
|
/// The type for each element in the list of buffers.
|
||||||
using value_type = ValueType;
|
using value_type = typename std::conditional<
|
||||||
|
std::is_convertible<typename
|
||||||
|
std::iterator_traits<iter_type>::value_type,
|
||||||
|
boost::asio::mutable_buffer>::value,
|
||||||
|
boost::asio::mutable_buffer,
|
||||||
|
boost::asio::const_buffer>::type;
|
||||||
|
|
||||||
#if GENERATING_DOCS
|
#if GENERATING_DOCS
|
||||||
/// A bidirectional iterator type that may be used to read elements.
|
/// A bidirectional iterator type that may be used to read elements.
|
||||||
@@ -100,7 +97,7 @@ public:
|
|||||||
const_iterator
|
const_iterator
|
||||||
begin() const;
|
begin() const;
|
||||||
|
|
||||||
/// Get a bidirectional iterator for one past the last element.
|
/// Get a bidirectional iterator to one past the last element.
|
||||||
const_iterator
|
const_iterator
|
||||||
end() const;
|
end() const;
|
||||||
|
|
||||||
@@ -114,25 +111,6 @@ public:
|
|||||||
consume(std::size_t n);
|
consume(std::size_t n);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Returns a new, consumed buffer sequence.
|
|
||||||
|
|
||||||
This function returns a new buffer sequence which when iterated,
|
|
||||||
efficiently represents the portion of the original buffer sequence
|
|
||||||
with `n` bytes removed from the beginning.
|
|
||||||
|
|
||||||
Copies will be made of the buffer sequence passed, but ownership
|
|
||||||
of the underlying memory is not transferred.
|
|
||||||
|
|
||||||
@param buffers The buffer sequence to consume.
|
|
||||||
|
|
||||||
@param n The number of bytes to remove from the front. If this is
|
|
||||||
larger than the size of the buffer sequence, an empty buffer sequence
|
|
||||||
is returned.
|
|
||||||
*/
|
|
||||||
template<class BufferSequence>
|
|
||||||
consuming_buffers<BufferSequence, typename BufferSequence::value_type>
|
|
||||||
consumed_buffers(BufferSequence const& buffers, std::size_t n);
|
|
||||||
|
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
#include <beast/core/impl/consuming_buffers.ipp>
|
#include <beast/core/impl/consuming_buffers.ipp>
|
||||||
|
|||||||
@@ -8,10 +8,8 @@
|
|||||||
#ifndef BEAST_BIND_DETAIL_HANDLER_HPP
|
#ifndef BEAST_BIND_DETAIL_HANDLER_HPP
|
||||||
#define BEAST_BIND_DETAIL_HANDLER_HPP
|
#define BEAST_BIND_DETAIL_HANDLER_HPP
|
||||||
|
|
||||||
|
#include <beast/core/handler_helpers.hpp>
|
||||||
#include <beast/core/detail/integer_sequence.hpp>
|
#include <beast/core/detail/integer_sequence.hpp>
|
||||||
#include <boost/asio/detail/handler_alloc_helpers.hpp>
|
|
||||||
#include <boost/asio/detail/handler_cont_helpers.hpp>
|
|
||||||
#include <boost/asio/detail/handler_invoke_helpers.hpp>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
@@ -69,7 +67,7 @@ public:
|
|||||||
asio_handler_allocate(
|
asio_handler_allocate(
|
||||||
std::size_t size, bound_handler* h)
|
std::size_t size, bound_handler* h)
|
||||||
{
|
{
|
||||||
return boost_asio_handler_alloc_helpers::
|
return beast_asio_helpers::
|
||||||
allocate(size, h->h_);
|
allocate(size, h->h_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +76,7 @@ public:
|
|||||||
asio_handler_deallocate(
|
asio_handler_deallocate(
|
||||||
void* p, std::size_t size, bound_handler* h)
|
void* p, std::size_t size, bound_handler* h)
|
||||||
{
|
{
|
||||||
boost_asio_handler_alloc_helpers::
|
beast_asio_helpers::
|
||||||
deallocate(p, size, h->h_);
|
deallocate(p, size, h->h_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +84,7 @@ public:
|
|||||||
bool
|
bool
|
||||||
asio_handler_is_continuation(bound_handler* h)
|
asio_handler_is_continuation(bound_handler* h)
|
||||||
{
|
{
|
||||||
return boost_asio_handler_cont_helpers::
|
return beast_asio_helpers::
|
||||||
is_continuation (h->h_);
|
is_continuation (h->h_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +93,7 @@ public:
|
|||||||
void
|
void
|
||||||
asio_handler_invoke(F&& f, bound_handler* h)
|
asio_handler_invoke(F&& f, bound_handler* h)
|
||||||
{
|
{
|
||||||
boost_asio_handler_invoke_helpers::
|
beast_asio_helpers::
|
||||||
invoke(f, h->h_);
|
invoke(f, h->h_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#ifndef BEAST_DETAIL_BUFFER_CAT_HPP
|
#ifndef BEAST_DETAIL_BUFFER_CAT_HPP
|
||||||
#define BEAST_DETAIL_BUFFER_CAT_HPP
|
#define BEAST_DETAIL_BUFFER_CAT_HPP
|
||||||
|
|
||||||
|
#include <beast/core/buffer_concepts.hpp>
|
||||||
|
#include <beast/core/detail/type_traits.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
@@ -19,24 +21,36 @@
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
|
struct common_buffers_type
|
||||||
|
{
|
||||||
|
using type = typename std::conditional<
|
||||||
|
std::is_convertible<std::tuple<Bn...>,
|
||||||
|
typename repeat_tuple<sizeof...(Bn),
|
||||||
|
boost::asio::mutable_buffer>::type>::value,
|
||||||
|
boost::asio::mutable_buffer,
|
||||||
|
boost::asio::const_buffer>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class... Bn>
|
||||||
class buffer_cat_helper
|
class buffer_cat_helper
|
||||||
{
|
{
|
||||||
std::tuple<Bs...> bs_;
|
std::tuple<Bn...> bn_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_type = ValueType;
|
using value_type = typename
|
||||||
|
common_buffers_type<Bn...>::type;
|
||||||
|
|
||||||
class const_iterator;
|
class const_iterator;
|
||||||
|
|
||||||
buffer_cat_helper(buffer_cat_helper&&) = default;
|
buffer_cat_helper(buffer_cat_helper&&) = default;
|
||||||
buffer_cat_helper(buffer_cat_helper const&) = default;
|
buffer_cat_helper(buffer_cat_helper const&) = default;
|
||||||
buffer_cat_helper& operator=(buffer_cat_helper&&) = default;
|
buffer_cat_helper& operator=(buffer_cat_helper&&) = delete;
|
||||||
buffer_cat_helper& operator=(buffer_cat_helper const&) = default;
|
buffer_cat_helper& operator=(buffer_cat_helper const&) = delete;
|
||||||
|
|
||||||
explicit
|
explicit
|
||||||
buffer_cat_helper(Bs const&... bs)
|
buffer_cat_helper(Bn const&... bn)
|
||||||
: bs_(bs...)
|
: bn_(bn...)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,39 +61,22 @@ public:
|
|||||||
end() const;
|
end() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class U>
|
template<class... Bn>
|
||||||
std::size_t constexpr
|
class buffer_cat_helper<Bn...>::const_iterator
|
||||||
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::size_t n_;
|
||||||
std::tuple<Bs...> const* bs_;
|
std::tuple<Bn...> const* bn_;
|
||||||
std::array<std::uint8_t,
|
std::array<std::uint8_t,
|
||||||
max_sizeof<typename Bs::const_iterator...>()> buf_;
|
max_sizeof<typename Bn::const_iterator...>()> buf_;
|
||||||
|
|
||||||
friend class buffer_cat_helper<ValueType, Bs...>;
|
friend class buffer_cat_helper<Bn...>;
|
||||||
|
|
||||||
template<std::size_t I>
|
template<std::size_t I>
|
||||||
using C = std::integral_constant<std::size_t, I>;
|
using C = std::integral_constant<std::size_t, I>;
|
||||||
|
|
||||||
template<std::size_t I>
|
template<std::size_t I>
|
||||||
using iter_t = typename std::tuple_element<
|
using iter_t = typename std::tuple_element<
|
||||||
I, std::tuple<Bs...>>::type::const_iterator;
|
I, std::tuple<Bn...>>::type::const_iterator;
|
||||||
|
|
||||||
template<std::size_t I>
|
template<std::size_t I>
|
||||||
iter_t<I>&
|
iter_t<I>&
|
||||||
@@ -98,7 +95,8 @@ class buffer_cat_helper<
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_type = ValueType;
|
using value_type = typename
|
||||||
|
common_buffers_type<Bn...>::type;
|
||||||
using pointer = value_type const*;
|
using pointer = value_type const*;
|
||||||
using reference = value_type;
|
using reference = value_type;
|
||||||
using difference_type = std::ptrdiff_t;
|
using difference_type = std::ptrdiff_t;
|
||||||
@@ -151,39 +149,39 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
const_iterator(
|
const_iterator(
|
||||||
std::tuple<Bs...> const& bs, bool at_end);
|
std::tuple<Bn...> const& bn, bool at_end);
|
||||||
|
|
||||||
void
|
void
|
||||||
construct(C<sizeof...(Bs)>)
|
construct(C<sizeof...(Bn)> const&)
|
||||||
{
|
{
|
||||||
auto constexpr I = sizeof...(Bs);
|
auto constexpr I = sizeof...(Bn);
|
||||||
n_ = I;
|
n_ = I;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t I>
|
template<std::size_t I>
|
||||||
void
|
void
|
||||||
construct(C<I>)
|
construct(C<I> const&)
|
||||||
{
|
{
|
||||||
if(std::get<I>(*bs_).begin() !=
|
if(std::get<I>(*bn_).begin() !=
|
||||||
std::get<I>(*bs_).end())
|
std::get<I>(*bn_).end())
|
||||||
{
|
{
|
||||||
n_ = I;
|
n_ = I;
|
||||||
new(buf_.data()) iter_t<I>{
|
new(buf_.data()) iter_t<I>{
|
||||||
std::get<I>(*bs_).begin()};
|
std::get<I>(*bn_).begin()};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
construct(C<I+1>{});
|
construct(C<I+1>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
destroy(C<sizeof...(Bs)>)
|
destroy(C<sizeof...(Bn)> const&)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t I>
|
template<std::size_t I>
|
||||||
void
|
void
|
||||||
destroy(C<I>)
|
destroy(C<I> const&)
|
||||||
{
|
{
|
||||||
if(n_ == I)
|
if(n_ == I)
|
||||||
{
|
{
|
||||||
@@ -195,13 +193,15 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
move(C<sizeof...(Bs)>, const_iterator&&)
|
move(const_iterator&&,
|
||||||
|
C<sizeof...(Bn)> const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t I>
|
template<std::size_t I>
|
||||||
void
|
void
|
||||||
move(C<I>, const_iterator&& other)
|
move(const_iterator&& other,
|
||||||
|
C<I> const&)
|
||||||
{
|
{
|
||||||
if(n_ == I)
|
if(n_ == I)
|
||||||
{
|
{
|
||||||
@@ -209,17 +209,19 @@ private:
|
|||||||
std::move(other.iter<I>())};
|
std::move(other.iter<I>())};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
move(C<I+1>{}, std::move(other));
|
move(std::move(other), C<I+1>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
copy(C<sizeof...(Bs)>, const_iterator const&)
|
copy(const_iterator const&,
|
||||||
|
C<sizeof...(Bn)> const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t I>
|
template<std::size_t I>
|
||||||
void
|
void
|
||||||
copy(C<I>, const_iterator const& other)
|
copy(const_iterator const& other,
|
||||||
|
C<I> const&)
|
||||||
{
|
{
|
||||||
if(n_ == I)
|
if(n_ == I)
|
||||||
{
|
{
|
||||||
@@ -227,35 +229,36 @@ private:
|
|||||||
other.iter<I>()};
|
other.iter<I>()};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
copy(C<I+1>{}, other);
|
copy(other, C<I+1>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
equal(C<sizeof...(Bs)>,
|
equal(const_iterator const&,
|
||||||
const_iterator const&) const
|
C<sizeof...(Bn)> const&) const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t I>
|
template<std::size_t I>
|
||||||
bool
|
bool
|
||||||
equal(C<I>, const_iterator const& other) const
|
equal(const_iterator const& other,
|
||||||
|
C<I> const&) const
|
||||||
{
|
{
|
||||||
if(n_ == I)
|
if(n_ == I)
|
||||||
return iter<I>() == other.iter<I>();
|
return iter<I>() == other.iter<I>();
|
||||||
return equal(C<I+1>{}, other);
|
return equal(other, C<I+1>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]]
|
[[noreturn]]
|
||||||
reference
|
reference
|
||||||
dereference(C<sizeof...(Bs)>) const
|
dereference(C<sizeof...(Bn)> const&) const
|
||||||
{
|
{
|
||||||
throw std::logic_error("invalid iterator");
|
throw std::logic_error("invalid iterator");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t I>
|
template<std::size_t I>
|
||||||
reference
|
reference
|
||||||
dereference(C<I>) const
|
dereference(C<I> const&) const
|
||||||
{
|
{
|
||||||
if(n_ == I)
|
if(n_ == I)
|
||||||
return *iter<I>();
|
return *iter<I>();
|
||||||
@@ -264,19 +267,19 @@ private:
|
|||||||
|
|
||||||
[[noreturn]]
|
[[noreturn]]
|
||||||
void
|
void
|
||||||
increment(C<sizeof...(Bs)>)
|
increment(C<sizeof...(Bn)> const&)
|
||||||
{
|
{
|
||||||
throw std::logic_error("invalid iterator");
|
throw std::logic_error("invalid iterator");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t I>
|
template<std::size_t I>
|
||||||
void
|
void
|
||||||
increment(C<I>)
|
increment(C<I> const&)
|
||||||
{
|
{
|
||||||
if(n_ == I)
|
if(n_ == I)
|
||||||
{
|
{
|
||||||
if(++iter<I>() !=
|
if(++iter<I>() !=
|
||||||
std::get<I>(*bs_).end())
|
std::get<I>(*bn_).end())
|
||||||
return;
|
return;
|
||||||
using Iter = iter_t<I>;
|
using Iter = iter_t<I>;
|
||||||
iter<I>().~Iter();
|
iter<I>().~Iter();
|
||||||
@@ -286,23 +289,23 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
decrement(C<sizeof...(Bs)>)
|
decrement(C<sizeof...(Bn)> const&)
|
||||||
{
|
{
|
||||||
auto constexpr I = sizeof...(Bs);
|
auto constexpr I = sizeof...(Bn);
|
||||||
if(n_ == I)
|
if(n_ == I)
|
||||||
{
|
{
|
||||||
--n_;
|
--n_;
|
||||||
new(buf_.data()) iter_t<I-1>{
|
new(buf_.data()) iter_t<I-1>{
|
||||||
std::get<I-1>(*bs_).end()};
|
std::get<I-1>(*bn_).end()};
|
||||||
}
|
}
|
||||||
decrement(C<I-1>{});
|
decrement(C<I-1>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
decrement(C<0>)
|
decrement(C<0> const&)
|
||||||
{
|
{
|
||||||
auto constexpr I = 0;
|
auto constexpr I = 0;
|
||||||
if(iter<I>() != std::get<I>(*bs_).begin())
|
if(iter<I>() != std::get<I>(*bn_).begin())
|
||||||
{
|
{
|
||||||
--iter<I>();
|
--iter<I>();
|
||||||
return;
|
return;
|
||||||
@@ -312,11 +315,11 @@ private:
|
|||||||
|
|
||||||
template<std::size_t I>
|
template<std::size_t I>
|
||||||
void
|
void
|
||||||
decrement(C<I>)
|
decrement(C<I> const&)
|
||||||
{
|
{
|
||||||
if(n_ == I)
|
if(n_ == I)
|
||||||
{
|
{
|
||||||
if(iter<I>() != std::get<I>(*bs_).begin())
|
if(iter<I>() != std::get<I>(*bn_).begin())
|
||||||
{
|
{
|
||||||
--iter<I>();
|
--iter<I>();
|
||||||
return;
|
return;
|
||||||
@@ -325,7 +328,7 @@ private:
|
|||||||
using Iter = iter_t<I>;
|
using Iter = iter_t<I>;
|
||||||
iter<I>().~Iter();
|
iter<I>().~Iter();
|
||||||
new(buf_.data()) iter_t<I-1>{
|
new(buf_.data()) iter_t<I-1>{
|
||||||
std::get<I-1>(*bs_).end()};
|
std::get<I-1>(*bn_).end()};
|
||||||
}
|
}
|
||||||
decrement(C<I-1>{});
|
decrement(C<I-1>{});
|
||||||
}
|
}
|
||||||
@@ -333,54 +336,54 @@ private:
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
buffer_cat_helper<ValueType, Bs...>::
|
buffer_cat_helper<Bn...>::
|
||||||
const_iterator::~const_iterator()
|
const_iterator::~const_iterator()
|
||||||
{
|
{
|
||||||
destroy(C<0>{});
|
destroy(C<0>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
buffer_cat_helper<ValueType, Bs...>::
|
buffer_cat_helper<Bn...>::
|
||||||
const_iterator::const_iterator()
|
const_iterator::const_iterator()
|
||||||
: n_(sizeof...(Bs))
|
: n_(sizeof...(Bn))
|
||||||
, bs_(nullptr)
|
, bn_(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
buffer_cat_helper<ValueType, Bs...>::
|
buffer_cat_helper<Bn...>::
|
||||||
const_iterator::const_iterator(
|
const_iterator::const_iterator(
|
||||||
std::tuple<Bs...> const& bs, bool at_end)
|
std::tuple<Bn...> const& bn, bool at_end)
|
||||||
: bs_(&bs)
|
: bn_(&bn)
|
||||||
{
|
{
|
||||||
if(at_end)
|
if(at_end)
|
||||||
n_ = sizeof...(Bs);
|
n_ = sizeof...(Bn);
|
||||||
else
|
else
|
||||||
construct(C<0>{});
|
construct(C<0>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
buffer_cat_helper<ValueType, Bs...>::
|
buffer_cat_helper<Bn...>::
|
||||||
const_iterator::const_iterator(const_iterator&& other)
|
const_iterator::const_iterator(const_iterator&& other)
|
||||||
: n_(other.n_)
|
: n_(other.n_)
|
||||||
, bs_(other.bs_)
|
, bn_(other.bn_)
|
||||||
{
|
{
|
||||||
move(C<0>{}, std::move(other));
|
move(std::move(other), C<0>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
buffer_cat_helper<ValueType, Bs...>::
|
buffer_cat_helper<Bn...>::
|
||||||
const_iterator::const_iterator(const_iterator const& other)
|
const_iterator::const_iterator(const_iterator const& other)
|
||||||
: n_(other.n_)
|
: n_(other.n_)
|
||||||
, bs_(other.bs_)
|
, bn_(other.bn_)
|
||||||
{
|
{
|
||||||
copy(C<0>{}, other);
|
copy(other, C<0>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
auto
|
auto
|
||||||
buffer_cat_helper<ValueType, Bs...>::
|
buffer_cat_helper<Bn...>::
|
||||||
const_iterator::operator=(const_iterator&& other) ->
|
const_iterator::operator=(const_iterator&& other) ->
|
||||||
const_iterator&
|
const_iterator&
|
||||||
{
|
{
|
||||||
@@ -388,14 +391,14 @@ const_iterator::operator=(const_iterator&& other) ->
|
|||||||
return *this;
|
return *this;
|
||||||
destroy(C<0>{});
|
destroy(C<0>{});
|
||||||
n_ = other.n_;
|
n_ = other.n_;
|
||||||
bs_ = other.bs_;
|
bn_ = other.bn_;
|
||||||
move(C<0>{}, std::move(other));
|
move(std::move(other), C<0>{});
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
auto
|
auto
|
||||||
buffer_cat_helper<ValueType, Bs...>::
|
buffer_cat_helper<Bn...>::
|
||||||
const_iterator::operator=(const_iterator const& other) ->
|
const_iterator::operator=(const_iterator const& other) ->
|
||||||
const_iterator&
|
const_iterator&
|
||||||
{
|
{
|
||||||
@@ -403,35 +406,35 @@ const_iterator&
|
|||||||
return *this;
|
return *this;
|
||||||
destroy(C<0>{});
|
destroy(C<0>{});
|
||||||
n_ = other.n_;
|
n_ = other.n_;
|
||||||
bs_ = other.bs_;
|
bn_ = other.bn_;
|
||||||
copy(C<0>{}, other);
|
copy(other, C<0>{});
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
bool
|
bool
|
||||||
buffer_cat_helper<ValueType, Bs...>::
|
buffer_cat_helper<Bn...>::
|
||||||
const_iterator::operator==(const_iterator const& other) const
|
const_iterator::operator==(const_iterator const& other) const
|
||||||
{
|
{
|
||||||
if(bs_ != other.bs_)
|
if(bn_ != other.bn_)
|
||||||
return false;
|
return false;
|
||||||
if(n_ != other.n_)
|
if(n_ != other.n_)
|
||||||
return false;
|
return false;
|
||||||
return equal(C<0>{}, other);
|
return equal(other, C<0>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
auto
|
auto
|
||||||
buffer_cat_helper<ValueType, Bs...>::
|
buffer_cat_helper<Bn...>::
|
||||||
const_iterator::operator*() const ->
|
const_iterator::operator*() const ->
|
||||||
reference
|
reference
|
||||||
{
|
{
|
||||||
return dereference(C<0>{});
|
return dereference(C<0>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
auto
|
auto
|
||||||
buffer_cat_helper<ValueType, Bs...>::
|
buffer_cat_helper<Bn...>::
|
||||||
const_iterator::operator++() ->
|
const_iterator::operator++() ->
|
||||||
const_iterator&
|
const_iterator&
|
||||||
{
|
{
|
||||||
@@ -439,30 +442,32 @@ const_iterator::operator++() ->
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
auto
|
auto
|
||||||
buffer_cat_helper<ValueType, Bs...>::
|
buffer_cat_helper<Bn...>::
|
||||||
const_iterator::operator--() ->
|
const_iterator::operator--() ->
|
||||||
const_iterator&
|
const_iterator&
|
||||||
{
|
{
|
||||||
decrement(C<sizeof...(Bs)>{});
|
decrement(C<sizeof...(Bn)>{});
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
|
inline
|
||||||
auto
|
auto
|
||||||
buffer_cat_helper<ValueType, Bs...>::begin() const ->
|
buffer_cat_helper<Bn...>::begin() const ->
|
||||||
const_iterator
|
const_iterator
|
||||||
{
|
{
|
||||||
return const_iterator(bs_, false);
|
return const_iterator{bn_, false};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ValueType, class... Bs>
|
template<class... Bn>
|
||||||
|
inline
|
||||||
auto
|
auto
|
||||||
buffer_cat_helper<ValueType, Bs...>::end() const ->
|
buffer_cat_helper<Bn...>::end() const ->
|
||||||
const_iterator
|
const_iterator
|
||||||
{
|
{
|
||||||
return const_iterator(bs_, true);
|
return const_iterator{bn_, true};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
|||||||
@@ -85,53 +85,93 @@ public:
|
|||||||
type3::value && type4::value>;
|
type3::value && type4::value>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class B1, class... Bn>
|
||||||
|
struct is_all_ConstBufferSequence
|
||||||
|
: std::integral_constant<bool,
|
||||||
|
is_BufferSequence<B1, boost::asio::const_buffer>::type::value &&
|
||||||
|
is_all_ConstBufferSequence<Bn...>::value>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class B1>
|
||||||
|
struct is_all_ConstBufferSequence<B1>
|
||||||
|
: is_BufferSequence<B1, boost::asio::const_buffer>::type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
class is_DynamicBuffer
|
class is_DynamicBuffer
|
||||||
{
|
{
|
||||||
template<class U, class R = std::integral_constant<
|
// size()
|
||||||
bool, is_BufferSequence<decltype(
|
template<class U, class R = std::is_convertible<decltype(
|
||||||
std::declval<U>().prepare(1)),
|
std::declval<U const>().size()), std::size_t>>
|
||||||
boost::asio::mutable_buffer>::type::value>>
|
|
||||||
static R check1(int);
|
static R check1(int);
|
||||||
template<class>
|
template<class>
|
||||||
static std::false_type check1(...);
|
static std::false_type check1(...);
|
||||||
using type1 = decltype(check1<T>(0));
|
using type1 = decltype(check1<T>(0));
|
||||||
|
|
||||||
template<class U, class R = std::integral_constant<
|
// max_size()
|
||||||
bool, is_BufferSequence<decltype(
|
template<class U, class R = std::is_convertible<decltype(
|
||||||
std::declval<U>().data()),
|
std::declval<U const>().max_size()), std::size_t>>
|
||||||
boost::asio::const_buffer>::type::value>>
|
|
||||||
static R check2(int);
|
static R check2(int);
|
||||||
template<class>
|
template<class>
|
||||||
static std::false_type check2(...);
|
static std::false_type check2(...);
|
||||||
using type2 = decltype(check2<T>(0));
|
using type2 = decltype(check2<T>(0));
|
||||||
|
|
||||||
template<class U, class R = decltype(
|
// capacity()
|
||||||
std::declval<U>().commit(1), std::true_type{})>
|
template<class U, class R = std::is_convertible<decltype(
|
||||||
|
std::declval<U const>().capacity()), std::size_t>>
|
||||||
static R check3(int);
|
static R check3(int);
|
||||||
template<class>
|
template<class>
|
||||||
static std::false_type check3(...);
|
static std::false_type check3(...);
|
||||||
using type3 = decltype(check3<T>(0));
|
using type3 = decltype(check3<T>(0));
|
||||||
|
|
||||||
template<class U, class R = decltype(
|
// data()
|
||||||
std::declval<U>().consume(1), std::true_type{})>
|
template<class U, class R = std::integral_constant<
|
||||||
|
bool, is_BufferSequence<decltype(
|
||||||
|
std::declval<U const>().data()),
|
||||||
|
boost::asio::const_buffer>::type::value>>
|
||||||
static R check4(int);
|
static R check4(int);
|
||||||
template<class>
|
template<class>
|
||||||
static std::false_type check4(...);
|
static std::false_type check4(...);
|
||||||
using type4 = decltype(check4<T>(0));
|
using type4 = decltype(check4<T>(0));
|
||||||
|
|
||||||
template<class U, class R = std::is_same<decltype(
|
// prepare()
|
||||||
std::declval<U>().size()), std::size_t>>
|
template<class U, class R = std::integral_constant<
|
||||||
|
bool, is_BufferSequence<decltype(
|
||||||
|
std::declval<U>().prepare(1)),
|
||||||
|
boost::asio::mutable_buffer>::type::value>>
|
||||||
static R check5(int);
|
static R check5(int);
|
||||||
template<class>
|
template<class>
|
||||||
static std::false_type check5(...);
|
static std::false_type check5(...);
|
||||||
using type5 = decltype(check5<T>(0));
|
using type5 = decltype(check5<T>(0));
|
||||||
|
|
||||||
|
// commit()
|
||||||
|
template<class U, class R = decltype(
|
||||||
|
std::declval<U>().commit(1), std::true_type{})>
|
||||||
|
static R check6(int);
|
||||||
|
template<class>
|
||||||
|
static std::false_type check6(...);
|
||||||
|
using type6 = decltype(check6<T>(0));
|
||||||
|
|
||||||
|
// consume
|
||||||
|
template<class U, class R = decltype(
|
||||||
|
std::declval<U>().consume(1), std::true_type{})>
|
||||||
|
static R check7(int);
|
||||||
|
template<class>
|
||||||
|
static std::false_type check7(...);
|
||||||
|
using type7 = decltype(check7<T>(0));
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using type = std::integral_constant<bool,
|
using type = std::integral_constant<bool,
|
||||||
type1::value && type2::value &&
|
type1::value
|
||||||
type3::value && type4::value &&
|
&& type2::value
|
||||||
type5::value>;
|
//&& type3::value // Networking TS
|
||||||
|
&& type4::value
|
||||||
|
&& type5::value
|
||||||
|
&& type6::value
|
||||||
|
&& type7::value
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
|||||||
40
include/beast/core/detail/clamp.hpp
Normal file
40
include/beast/core/detail/clamp.hpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_CORE_DETAIL_CLAMP_HPP
|
||||||
|
#define BEAST_CORE_DETAIL_CLAMP_HPP
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class UInt>
|
||||||
|
static
|
||||||
|
std::size_t
|
||||||
|
clamp(UInt x)
|
||||||
|
{
|
||||||
|
if(x >= (std::numeric_limits<std::size_t>::max)())
|
||||||
|
return (std::numeric_limits<std::size_t>::max)();
|
||||||
|
return static_cast<std::size_t>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class UInt>
|
||||||
|
static
|
||||||
|
std::size_t
|
||||||
|
clamp(UInt x, std::size_t limit)
|
||||||
|
{
|
||||||
|
if(x >= limit)
|
||||||
|
return limit;
|
||||||
|
return static_cast<std::size_t>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -61,6 +61,8 @@ struct is_call_possible_udt3
|
|||||||
int operator()(int);
|
int operator()(int);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef __INTELLISENSE__
|
||||||
|
// VFALCO Fails to compile with Intellisense
|
||||||
static_assert(is_call_possible<
|
static_assert(is_call_possible<
|
||||||
is_call_possible_udt1, void(int)>::value, "");
|
is_call_possible_udt1, void(int)>::value, "");
|
||||||
|
|
||||||
@@ -81,6 +83,7 @@ static_assert(is_call_possible<
|
|||||||
|
|
||||||
static_assert(! is_call_possible<
|
static_assert(! is_call_possible<
|
||||||
is_call_possible_udt3 const, int(int)>::value, "");
|
is_call_possible_udt3 const, int(int)>::value, "");
|
||||||
|
#endif
|
||||||
|
|
||||||
} // test
|
} // test
|
||||||
|
|
||||||
|
|||||||
@@ -5,38 +5,104 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef BEAST_IMPL_PREPARE_BUFFERS_IPP
|
#ifndef BEAST_DETAIL_PREPARED_BUFFERS_HPP
|
||||||
#define BEAST_IMPL_PREPARE_BUFFERS_IPP
|
#define BEAST_DETAIL_PREPARED_BUFFERS_HPP
|
||||||
|
|
||||||
|
#include <beast/core/prepare_buffer.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/** A buffer sequence adapter that shortens the sequence size.
|
||||||
|
|
||||||
|
The class adapts a buffer sequence to efficiently represent
|
||||||
|
a shorter subset of the original list of buffers starting
|
||||||
|
with the first byte of the original sequence.
|
||||||
|
|
||||||
|
@tparam BufferSequence The buffer sequence to adapt.
|
||||||
|
*/
|
||||||
template<class BufferSequence>
|
template<class BufferSequence>
|
||||||
void
|
class prepared_buffers
|
||||||
prepared_buffers<BufferSequence>::
|
|
||||||
setup(std::size_t n)
|
|
||||||
{
|
{
|
||||||
for(end_ = bs_.begin(); end_ != bs_.end(); ++end_)
|
using iter_type =
|
||||||
|
typename BufferSequence::const_iterator;
|
||||||
|
|
||||||
|
BufferSequence bs_;
|
||||||
|
iter_type back_;
|
||||||
|
iter_type end_;
|
||||||
|
std::size_t size_;
|
||||||
|
|
||||||
|
template<class Deduced>
|
||||||
|
prepared_buffers(Deduced&& other,
|
||||||
|
std::size_t nback, std::size_t nend)
|
||||||
|
: bs_(std::forward<Deduced>(other).bs_)
|
||||||
|
, back_(std::next(bs_.begin(), nback))
|
||||||
|
, end_(std::next(bs_.begin(), nend))
|
||||||
|
, size_(other.size_)
|
||||||
{
|
{
|
||||||
auto const len =
|
|
||||||
boost::asio::buffer_size(*end_);
|
|
||||||
if(n <= len)
|
|
||||||
{
|
|
||||||
size_ = n;
|
|
||||||
back_ = end_++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
n -= len;
|
|
||||||
}
|
}
|
||||||
size_ = 0;
|
|
||||||
back_ = end_;
|
void
|
||||||
}
|
setup(std::size_t n);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// The type for each element in the list of buffers.
|
||||||
|
using value_type = typename std::conditional<
|
||||||
|
std::is_convertible<typename
|
||||||
|
std::iterator_traits<iter_type>::value_type,
|
||||||
|
boost::asio::mutable_buffer>::value,
|
||||||
|
boost::asio::mutable_buffer,
|
||||||
|
boost::asio::const_buffer>::type;
|
||||||
|
|
||||||
|
#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.
|
||||||
|
prepared_buffers(prepared_buffers&&);
|
||||||
|
|
||||||
|
/// Copy constructor.
|
||||||
|
prepared_buffers(prepared_buffers const&);
|
||||||
|
|
||||||
|
/// Move assignment.
|
||||||
|
prepared_buffers& operator=(prepared_buffers&&);
|
||||||
|
|
||||||
|
/// Copy assignment.
|
||||||
|
prepared_buffers& operator=(prepared_buffers const&);
|
||||||
|
|
||||||
|
/** Construct a shortened buffer sequence.
|
||||||
|
|
||||||
|
@param n The maximum number of bytes in the wrapped
|
||||||
|
sequence. If this is larger than the size of passed,
|
||||||
|
buffers, the resulting sequence will represent the
|
||||||
|
entire input sequence.
|
||||||
|
|
||||||
|
@param buffers The buffer sequence to adapt. A copy of
|
||||||
|
the sequence will be made, but ownership of the underlying
|
||||||
|
memory is not transferred.
|
||||||
|
*/
|
||||||
|
prepared_buffers(std::size_t n, BufferSequence const& buffers);
|
||||||
|
|
||||||
|
/// Get a bidirectional iterator to the first element.
|
||||||
|
const_iterator
|
||||||
|
begin() const;
|
||||||
|
|
||||||
|
/// Get a bidirectional iterator to one past the last element.
|
||||||
|
const_iterator
|
||||||
|
end() const;
|
||||||
|
};
|
||||||
|
|
||||||
template<class BufferSequence>
|
template<class BufferSequence>
|
||||||
class prepared_buffers<BufferSequence>::const_iterator
|
class prepared_buffers<BufferSequence>::const_iterator
|
||||||
@@ -50,8 +116,12 @@ class prepared_buffers<BufferSequence>::const_iterator
|
|||||||
typename BufferSequence::const_iterator it_;
|
typename BufferSequence::const_iterator it_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_type =
|
using value_type = typename std::conditional<
|
||||||
typename std::iterator_traits<iter_type>::value_type;
|
std::is_convertible<typename
|
||||||
|
std::iterator_traits<iter_type>::value_type,
|
||||||
|
boost::asio::mutable_buffer>::value,
|
||||||
|
boost::asio::mutable_buffer,
|
||||||
|
boost::asio::const_buffer>::type;
|
||||||
using pointer = value_type const*;
|
using pointer = value_type const*;
|
||||||
using reference = value_type;
|
using reference = value_type;
|
||||||
using difference_type = std::ptrdiff_t;
|
using difference_type = std::ptrdiff_t;
|
||||||
@@ -59,10 +129,10 @@ public:
|
|||||||
std::bidirectional_iterator_tag;
|
std::bidirectional_iterator_tag;
|
||||||
|
|
||||||
const_iterator() = default;
|
const_iterator() = default;
|
||||||
const_iterator(const_iterator&& other) = default;
|
const_iterator(const_iterator&& other);
|
||||||
const_iterator(const_iterator const& other) = default;
|
const_iterator(const_iterator const& other);
|
||||||
const_iterator& operator=(const_iterator&& other) = default;
|
const_iterator& operator=(const_iterator&& other);
|
||||||
const_iterator& operator=(const_iterator const& other) = default;
|
const_iterator& operator=(const_iterator const& other);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
operator==(const_iterator const& other) const
|
operator==(const_iterator const& other) const
|
||||||
@@ -126,6 +196,67 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class BufferSequence>
|
||||||
|
void
|
||||||
|
prepared_buffers<BufferSequence>::
|
||||||
|
setup(std::size_t n)
|
||||||
|
{
|
||||||
|
for(end_ = bs_.begin(); end_ != bs_.end(); ++end_)
|
||||||
|
{
|
||||||
|
auto const len =
|
||||||
|
boost::asio::buffer_size(*end_);
|
||||||
|
if(n <= len)
|
||||||
|
{
|
||||||
|
size_ = n;
|
||||||
|
back_ = end_++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
n -= len;
|
||||||
|
}
|
||||||
|
size_ = 0;
|
||||||
|
back_ = end_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class BufferSequence>
|
||||||
|
prepared_buffers<BufferSequence>::const_iterator::
|
||||||
|
const_iterator(const_iterator&& other)
|
||||||
|
: b_(other.b_)
|
||||||
|
, it_(std::move(other.it_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class BufferSequence>
|
||||||
|
prepared_buffers<BufferSequence>::const_iterator::
|
||||||
|
const_iterator(const_iterator const& other)
|
||||||
|
: b_(other.b_)
|
||||||
|
, it_(other.it_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class BufferSequence>
|
||||||
|
auto
|
||||||
|
prepared_buffers<BufferSequence>::const_iterator::
|
||||||
|
operator=(const_iterator&& other) ->
|
||||||
|
const_iterator&
|
||||||
|
{
|
||||||
|
b_ = other.b_;
|
||||||
|
it_ = std::move(other.it_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class BufferSequence>
|
||||||
|
auto
|
||||||
|
prepared_buffers<BufferSequence>::const_iterator::
|
||||||
|
operator=(const_iterator const& other) ->
|
||||||
|
const_iterator&
|
||||||
|
{
|
||||||
|
if(&other == this)
|
||||||
|
return *this;
|
||||||
|
b_ = other.b_;
|
||||||
|
it_ = other.it_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template<class BufferSequence>
|
template<class BufferSequence>
|
||||||
prepared_buffers<BufferSequence>::
|
prepared_buffers<BufferSequence>::
|
||||||
prepared_buffers(prepared_buffers&& other)
|
prepared_buffers(prepared_buffers&& other)
|
||||||
@@ -187,6 +318,7 @@ prepared_buffers(std::size_t n, BufferSequence const& bs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class BufferSequence>
|
template<class BufferSequence>
|
||||||
|
inline
|
||||||
auto
|
auto
|
||||||
prepared_buffers<BufferSequence>::begin() const ->
|
prepared_buffers<BufferSequence>::begin() const ->
|
||||||
const_iterator
|
const_iterator
|
||||||
@@ -195,6 +327,7 @@ prepared_buffers<BufferSequence>::begin() const ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class BufferSequence>
|
template<class BufferSequence>
|
||||||
|
inline
|
||||||
auto
|
auto
|
||||||
prepared_buffers<BufferSequence>::end() const ->
|
prepared_buffers<BufferSequence>::end() const ->
|
||||||
const_iterator
|
const_iterator
|
||||||
@@ -202,14 +335,7 @@ prepared_buffers<BufferSequence>::end() const ->
|
|||||||
return const_iterator{*this, true};
|
return const_iterator{*this, true};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class BufferSequence>
|
} // detail
|
||||||
inline
|
|
||||||
prepared_buffers<BufferSequence>
|
|
||||||
prepare_buffers(std::size_t n, BufferSequence const& buffers)
|
|
||||||
{
|
|
||||||
return prepared_buffers<BufferSequence>(n, buffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -249,7 +249,7 @@ update(sha1_context& ctx,
|
|||||||
std::uint8_t const*>(message);
|
std::uint8_t const*>(message);
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
auto const n = std::min(
|
auto const n = (std::min)(
|
||||||
size, sizeof(ctx.buf) - ctx.buflen);
|
size, sizeof(ctx.buf) - ctx.buflen);
|
||||||
std::memcpy(ctx.buf + ctx.buflen, p, n);
|
std::memcpy(ctx.buf + ctx.buflen, p, n);
|
||||||
ctx.buflen += n;
|
ctx.buflen += n;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
#define BEAST_DETAIL_STREAM_CONCEPTS_HPP
|
#define BEAST_DETAIL_STREAM_CONCEPTS_HPP
|
||||||
|
|
||||||
#include <beast/core/buffer_concepts.hpp>
|
#include <beast/core/buffer_concepts.hpp>
|
||||||
|
#include <beast/core/error.hpp>
|
||||||
#include <boost/asio/io_service.hpp>
|
#include <boost/asio/io_service.hpp>
|
||||||
#include <boost/system/error_code.hpp>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ namespace detail {
|
|||||||
struct StreamHandler
|
struct StreamHandler
|
||||||
{
|
{
|
||||||
StreamHandler(StreamHandler const&) = default;
|
StreamHandler(StreamHandler const&) = default;
|
||||||
void operator()(boost::system::error_code ec, std::size_t);
|
void operator()(error_code ec, std::size_t);
|
||||||
};
|
};
|
||||||
using ReadHandler = StreamHandler;
|
using ReadHandler = StreamHandler;
|
||||||
using WriteHandler = StreamHandler;
|
using WriteHandler = StreamHandler;
|
||||||
@@ -79,9 +79,6 @@ public:
|
|||||||
template<class T>
|
template<class T>
|
||||||
class is_SyncReadStream
|
class is_SyncReadStream
|
||||||
{
|
{
|
||||||
using error_code =
|
|
||||||
boost::system::error_code;
|
|
||||||
|
|
||||||
template<class U, class R = std::is_same<decltype(
|
template<class U, class R = std::is_same<decltype(
|
||||||
std::declval<U>().read_some(
|
std::declval<U>().read_some(
|
||||||
std::declval<MutableBufferSequence>())),
|
std::declval<MutableBufferSequence>())),
|
||||||
@@ -108,9 +105,6 @@ public:
|
|||||||
template<class T>
|
template<class T>
|
||||||
class is_SyncWriteStream
|
class is_SyncWriteStream
|
||||||
{
|
{
|
||||||
using error_code =
|
|
||||||
boost::system::error_code;
|
|
||||||
|
|
||||||
template<class U, class R = std::is_same<decltype(
|
template<class U, class R = std::is_same<decltype(
|
||||||
std::declval<U>().write_some(
|
std::declval<U>().write_some(
|
||||||
std::declval<ConstBufferSequence>())),
|
std::declval<ConstBufferSequence>())),
|
||||||
|
|||||||
93
include/beast/core/detail/sync_ostream.hpp
Normal file
93
include/beast/core/detail/sync_ostream.hpp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
//
|
||||||
|
// 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_SYNC_OSTREAM_HPP
|
||||||
|
#define BEAST_DETAIL_SYNC_OSTREAM_HPP
|
||||||
|
|
||||||
|
#include <beast/core/buffer_concepts.hpp>
|
||||||
|
#include <beast/core/error.hpp>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/** A SyncWriteStream which outputs to a `std::ostream`
|
||||||
|
|
||||||
|
Objects of this type meet the requirements of @b SyncWriteStream.
|
||||||
|
*/
|
||||||
|
class sync_ostream
|
||||||
|
{
|
||||||
|
std::ostream& os_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Construct the stream.
|
||||||
|
|
||||||
|
@param os The associated `std::ostream`. All buffers
|
||||||
|
written will be passed to the associated output stream.
|
||||||
|
*/
|
||||||
|
sync_ostream(std::ostream& os)
|
||||||
|
: os_(os)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
write_some(ConstBufferSequence const& buffers);
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
write_some(ConstBufferSequence const& buffers,
|
||||||
|
error_code& ec);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
sync_ostream::
|
||||||
|
write_some(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
static_assert(
|
||||||
|
is_ConstBufferSequence<ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
auto const n = write_some(buffers, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
sync_ostream::
|
||||||
|
write_some(ConstBufferSequence const& buffers,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(
|
||||||
|
is_ConstBufferSequence<ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
std::size_t n = 0;
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
for(auto const& buffer : buffers)
|
||||||
|
{
|
||||||
|
os_.write(buffer_cast<char const*>(buffer),
|
||||||
|
buffer_size(buffer));
|
||||||
|
if(os_.fail())
|
||||||
|
{
|
||||||
|
ec = errc::make_error_code(
|
||||||
|
errc::no_stream_resources);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
n += buffer_size(buffer);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
84
include/beast/core/detail/type_traits.hpp
Normal file
84
include/beast/core/detail/type_traits.hpp
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
//
|
||||||
|
// 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_TYPE_TRAITS_HPP
|
||||||
|
#define BEAST_DETAIL_TYPE_TRAITS_HPP
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class... Ts>
|
||||||
|
struct make_void
|
||||||
|
{
|
||||||
|
using type = void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class... Ts>
|
||||||
|
using void_t = typename make_void<Ts...>::type;
|
||||||
|
|
||||||
|
template<class... Ts>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
ignore_unused(Ts const& ...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class... Ts>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
ignore_unused()
|
||||||
|
{}
|
||||||
|
|
||||||
|
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<unsigned N, class T, class... Tn>
|
||||||
|
struct repeat_tuple_impl
|
||||||
|
{
|
||||||
|
using type = typename repeat_tuple_impl<
|
||||||
|
N - 1, T, T, Tn...>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, class... Tn>
|
||||||
|
struct repeat_tuple_impl<0, T, Tn...>
|
||||||
|
{
|
||||||
|
using type = std::tuple<T, Tn...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<unsigned N, class T>
|
||||||
|
struct repeat_tuple
|
||||||
|
{
|
||||||
|
using type =
|
||||||
|
typename repeat_tuple_impl<N-1, T>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct repeat_tuple<0, T>
|
||||||
|
{
|
||||||
|
using type = std::tuple<>;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
#include <beast/core/detail/get_lowest_layer.hpp>
|
#include <beast/core/detail/get_lowest_layer.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
#include <boost/asio/io_service.hpp>
|
#include <boost/asio/io_service.hpp>
|
||||||
#include <boost/system/error_code.hpp>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -47,7 +46,7 @@ namespace beast {
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
@code
|
@code
|
||||||
// Process the next HTTP headers on the stream,
|
// Process the next HTTP header on the stream,
|
||||||
// leaving excess bytes behind for the next call.
|
// leaving excess bytes behind for the next call.
|
||||||
//
|
//
|
||||||
template<class DynamicBuffer>
|
template<class DynamicBuffer>
|
||||||
@@ -55,7 +54,7 @@ namespace beast {
|
|||||||
dynabuf_readstream<DynamicBuffer>& stream)
|
dynabuf_readstream<DynamicBuffer>& stream)
|
||||||
{
|
{
|
||||||
// Read up to and including the end of the HTTP
|
// Read up to and including the end of the HTTP
|
||||||
// headers, leaving the sequence in the stream's
|
// header, leaving the sequence in the stream's
|
||||||
// buffer. read_until may read past the end of the
|
// buffer. read_until may read past the end of the
|
||||||
// headers; the return value will include only the
|
// headers; the return value will include only the
|
||||||
// part up to the end of the delimiter.
|
// part up to the end of the delimiter.
|
||||||
|
|||||||
@@ -19,6 +19,18 @@ using error_code = boost::system::error_code;
|
|||||||
/// The type of system error thrown by the library
|
/// The type of system error thrown by the library
|
||||||
using system_error = boost::system::system_error;
|
using system_error = boost::system::system_error;
|
||||||
|
|
||||||
|
/// The type of error category used by the library
|
||||||
|
using error_category = boost::system::error_category;
|
||||||
|
|
||||||
|
/// The type of error condition used by the library
|
||||||
|
using error_condition = boost::system::error_condition;
|
||||||
|
|
||||||
|
/// The set of constants used for cross-platform error codes
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
enum errc{};
|
||||||
|
#else
|
||||||
|
namespace errc = boost::system::errc;
|
||||||
|
#endif
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#ifndef BEAST_HANDLER_ALLOC_HPP
|
#ifndef BEAST_HANDLER_ALLOC_HPP
|
||||||
#define BEAST_HANDLER_ALLOC_HPP
|
#define BEAST_HANDLER_ALLOC_HPP
|
||||||
|
|
||||||
#include <boost/asio/detail/handler_alloc_helpers.hpp>
|
#include <beast/core/handler_helpers.hpp>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@@ -23,15 +23,15 @@ namespace beast {
|
|||||||
|
|
||||||
This allocator uses the handler customizations `asio_handler_allocate`
|
This allocator uses the handler customizations `asio_handler_allocate`
|
||||||
and `asio_handler_deallocate` to manage memory. It meets the requirements
|
and `asio_handler_deallocate` to manage memory. It meets the requirements
|
||||||
of `Allocator` and can be used anywhere a `std::allocator` is
|
of @b Allocator and can be used anywhere a `std::allocator` is
|
||||||
accepted.
|
accepted.
|
||||||
|
|
||||||
@tparam T The type of objects allocated by the allocator.
|
@tparam T The type of objects allocated by the allocator.
|
||||||
|
|
||||||
@tparam CompletionHandler The type of handler.
|
@tparam CompletionHandler The type of handler.
|
||||||
|
|
||||||
@note Allocated memory is only valid until the handler is called. The
|
@note Memory allocated by this allocator must be freed before
|
||||||
caller is still responsible for freeing memory.
|
the handler is invoked or undefined behavior results.
|
||||||
*/
|
*/
|
||||||
#if GENERATING_DOCS
|
#if GENERATING_DOCS
|
||||||
template<class T, class CompletionHandler>
|
template<class T, class CompletionHandler>
|
||||||
@@ -49,12 +49,18 @@ private:
|
|||||||
template<class U, class H>
|
template<class U, class H>
|
||||||
friend class handler_alloc;
|
friend class handler_alloc;
|
||||||
|
|
||||||
CompletionHandler h_;
|
CompletionHandler& h_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using is_always_equal = std::true_type;
|
using is_always_equal = std::true_type;
|
||||||
|
|
||||||
|
template<class U>
|
||||||
|
struct rebind
|
||||||
|
{
|
||||||
|
using other = handler_alloc<U, CompletionHandler>;
|
||||||
|
};
|
||||||
|
|
||||||
handler_alloc() = delete;
|
handler_alloc() = delete;
|
||||||
handler_alloc(handler_alloc&&) = default;
|
handler_alloc(handler_alloc&&) = default;
|
||||||
handler_alloc(handler_alloc const&) = default;
|
handler_alloc(handler_alloc const&) = default;
|
||||||
@@ -63,31 +69,16 @@ public:
|
|||||||
|
|
||||||
/** Construct the allocator.
|
/** Construct the allocator.
|
||||||
|
|
||||||
The handler is moved or copied into the allocator.
|
A reference of the handler is stored. The handler must
|
||||||
|
remain valid for at least the lifetime of the allocator.
|
||||||
*/
|
*/
|
||||||
explicit
|
explicit
|
||||||
handler_alloc(CompletionHandler&& h)
|
handler_alloc(CompletionHandler& h)
|
||||||
: h_(std::move(h))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Construct the allocator.
|
|
||||||
|
|
||||||
A copy of the handler is made.
|
|
||||||
*/
|
|
||||||
explicit
|
|
||||||
handler_alloc(CompletionHandler const& h)
|
|
||||||
: h_(h)
|
: h_(h)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class U>
|
/// Copy constructor
|
||||||
handler_alloc(
|
|
||||||
handler_alloc<U, CompletionHandler>&& other)
|
|
||||||
: h_(std::move(other.h_))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class U>
|
template<class U>
|
||||||
handler_alloc(
|
handler_alloc(
|
||||||
handler_alloc<U, CompletionHandler> const& other)
|
handler_alloc<U, CompletionHandler> const& other)
|
||||||
@@ -100,7 +91,7 @@ public:
|
|||||||
{
|
{
|
||||||
auto const size = n * sizeof(T);
|
auto const size = n * sizeof(T);
|
||||||
return static_cast<value_type*>(
|
return static_cast<value_type*>(
|
||||||
boost_asio_handler_alloc_helpers::allocate(
|
beast_asio_helpers::allocate(
|
||||||
size, h_));
|
size, h_));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +99,7 @@ public:
|
|||||||
deallocate(value_type* p, std::ptrdiff_t n)
|
deallocate(value_type* p, std::ptrdiff_t n)
|
||||||
{
|
{
|
||||||
auto const size = n * sizeof(T);
|
auto const size = n * sizeof(T);
|
||||||
boost_asio_handler_alloc_helpers::deallocate(
|
beast_asio_helpers::deallocate(
|
||||||
p, size, h_);
|
p, size, h_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
104
include/beast/core/handler_helpers.hpp
Normal file
104
include/beast/core/handler_helpers.hpp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
//
|
||||||
|
// 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_HELPERS_HPP
|
||||||
|
#define BEAST_HANDLER_HELPERS_HPP
|
||||||
|
|
||||||
|
#include <boost/asio/handler_alloc_hook.hpp>
|
||||||
|
#include <boost/asio/handler_continuation_hook.hpp>
|
||||||
|
#include <boost/asio/handler_invoke_hook.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
/* Calls to:
|
||||||
|
|
||||||
|
* asio_handler_allocate
|
||||||
|
* asio_handler_deallocate
|
||||||
|
* asio_handler_invoke
|
||||||
|
* asio_handler_is_continuation
|
||||||
|
|
||||||
|
must be made from a namespace that does not
|
||||||
|
contain overloads of this function. The beast_asio_helpers
|
||||||
|
namespace is defined here for that purpose.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace beast_asio_helpers {
|
||||||
|
|
||||||
|
/// Allocation function for handlers.
|
||||||
|
template <class Handler>
|
||||||
|
inline
|
||||||
|
void*
|
||||||
|
allocate(std::size_t s, Handler& handler)
|
||||||
|
{
|
||||||
|
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||||
|
return ::operator new(s);
|
||||||
|
#else
|
||||||
|
using boost::asio::asio_handler_allocate;
|
||||||
|
return asio_handler_allocate(s, std::addressof(handler));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deallocation function for handlers.
|
||||||
|
template<class Handler>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
deallocate(void* p, std::size_t s, Handler& handler)
|
||||||
|
{
|
||||||
|
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||||
|
::operator delete(p);
|
||||||
|
#else
|
||||||
|
using boost::asio::asio_handler_deallocate;
|
||||||
|
asio_handler_deallocate(p, s, std::addressof(handler));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoke function for handlers.
|
||||||
|
template<class Function, class Handler>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
invoke(Function& function, Handler& handler)
|
||||||
|
{
|
||||||
|
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||||
|
Function tmp(function);
|
||||||
|
tmp();
|
||||||
|
#else
|
||||||
|
using boost::asio::asio_handler_invoke;
|
||||||
|
asio_handler_invoke(function, std::addressof(handler));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoke function for handlers.
|
||||||
|
template<class Function, class Handler>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
invoke(Function const& function, Handler& handler)
|
||||||
|
{
|
||||||
|
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||||
|
Function tmp(function);
|
||||||
|
tmp();
|
||||||
|
#else
|
||||||
|
using boost::asio::asio_handler_invoke;
|
||||||
|
asio_handler_invoke(function, std::addressof(handler));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if handler represents a continuation of the asynchronous operation
|
||||||
|
template<class Handler>
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_continuation(Handler& handler)
|
||||||
|
{
|
||||||
|
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
using boost::asio::asio_handler_is_continuation;
|
||||||
|
return asio_handler_is_continuation(std::addressof(handler));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // beast_asio_helpers
|
||||||
|
|
||||||
|
#endif
|
||||||
173
include/beast/core/handler_ptr.hpp
Normal file
173
include/beast/core/handler_ptr.hpp
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HANDLER_PTR_HPP
|
||||||
|
#define BEAST_HANDLER_PTR_HPP
|
||||||
|
|
||||||
|
#include <beast/core/detail/type_traits.hpp>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
/** A smart pointer container.
|
||||||
|
|
||||||
|
This is a smart pointer that retains shared ownership of an
|
||||||
|
object through a pointer. Memory is managed using the allocation
|
||||||
|
and deallocation functions associated with a completion handler,
|
||||||
|
which is also stored in the object. The object is destroyed and
|
||||||
|
its memory deallocated when one of the following happens:
|
||||||
|
|
||||||
|
@li The function @ref invoke is called.
|
||||||
|
|
||||||
|
@li The function @ref release_handler is called
|
||||||
|
|
||||||
|
@li The last remaining container owning the object is destroyed
|
||||||
|
|
||||||
|
Objects of this type are used in the implementation of
|
||||||
|
composed operations. Typically the composed operation's shared
|
||||||
|
state is managed by the @ref handler_ptr and an allocator
|
||||||
|
associated with the final handler is used to create the managed
|
||||||
|
object.
|
||||||
|
|
||||||
|
@note The reference count is stored using a 16 bit unsigned
|
||||||
|
integer. Making more than 2^16 copies of one object results
|
||||||
|
in undefined behavior.
|
||||||
|
*/
|
||||||
|
template<class T, class Handler>
|
||||||
|
class handler_ptr
|
||||||
|
{
|
||||||
|
struct P
|
||||||
|
{
|
||||||
|
T* t;
|
||||||
|
std::atomic<std::uint16_t> n;
|
||||||
|
|
||||||
|
// There's no way to put the handler anywhere else
|
||||||
|
// without exposing ourselves to race conditions
|
||||||
|
// and all sorts of ugliness.
|
||||||
|
// See:
|
||||||
|
// https://github.com/vinniefalco/Beast/issues/215
|
||||||
|
Handler handler;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
P(DeducedHandler&& handler, Args&&... args);
|
||||||
|
};
|
||||||
|
|
||||||
|
P* p_;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
handler_ptr(int, DeducedHandler&& handler, Args&&... args);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// The type of handler this object stores
|
||||||
|
using handler_type = Handler;
|
||||||
|
|
||||||
|
/// Copy assignment (disallowed).
|
||||||
|
handler_ptr& operator=(handler_ptr const&) = delete;
|
||||||
|
|
||||||
|
/** Destructs the owned object if no more @ref handler_ptr link to it.
|
||||||
|
|
||||||
|
If `*this` owns an object and it is the last @ref handler_ptr
|
||||||
|
owning it, the object is destroyed and the memory deallocated
|
||||||
|
using the associated deallocator.
|
||||||
|
*/
|
||||||
|
~handler_ptr();
|
||||||
|
|
||||||
|
/** Move constructor.
|
||||||
|
|
||||||
|
When this call returns, the moved-from container
|
||||||
|
will have no owned object.
|
||||||
|
*/
|
||||||
|
handler_ptr(handler_ptr&& other);
|
||||||
|
|
||||||
|
/// Copy constructor
|
||||||
|
handler_ptr(handler_ptr const& other);
|
||||||
|
|
||||||
|
/// Returns a reference to the handler
|
||||||
|
handler_type&
|
||||||
|
handler() const
|
||||||
|
{
|
||||||
|
return p_->handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a pointer to the owned object
|
||||||
|
T*
|
||||||
|
get() const
|
||||||
|
{
|
||||||
|
return p_->t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a reference to the owned object.
|
||||||
|
T&
|
||||||
|
operator*() const
|
||||||
|
{
|
||||||
|
return *get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a pointer to the owned object.
|
||||||
|
T*
|
||||||
|
operator->() const
|
||||||
|
{
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Release ownership of the handler
|
||||||
|
|
||||||
|
If `*this` owns an object, it is first destroyed.
|
||||||
|
|
||||||
|
@return The released handler.
|
||||||
|
*/
|
||||||
|
handler_type
|
||||||
|
release_handler();
|
||||||
|
|
||||||
|
/** Invoke the handler in the owned object.
|
||||||
|
|
||||||
|
This function invokes the handler in the owned object
|
||||||
|
with a forwarded argument list. Before the invocation,
|
||||||
|
the owned object is destroyed, satisfying the
|
||||||
|
deallocation-before-invocation Asio guarantee. All
|
||||||
|
instances of @ref handler_ptr which refer to the
|
||||||
|
same owned object will be reset, including this instance.
|
||||||
|
*/
|
||||||
|
template<class... Args>
|
||||||
|
void
|
||||||
|
invoke(Args&&... args);
|
||||||
|
|
||||||
|
// VFALCO The free function interface works around
|
||||||
|
// a horrible Visual Studio 15 Update 3 bug
|
||||||
|
|
||||||
|
/** Construct a new `handler_ptr`.
|
||||||
|
|
||||||
|
@param handler The handler. The allocator associated with
|
||||||
|
the handler will be used to allocate memory for the owned
|
||||||
|
object. This argument will be forwarded to the owned object's
|
||||||
|
constructor.
|
||||||
|
|
||||||
|
@param args Optional arguments forwarded to
|
||||||
|
the owned object's constructor.
|
||||||
|
*/
|
||||||
|
/** @{ */
|
||||||
|
template<class U, class CompletionHandler, class... Args>
|
||||||
|
friend
|
||||||
|
handler_ptr<U, CompletionHandler>
|
||||||
|
make_handler_ptr(
|
||||||
|
CompletionHandler&& handler, Args&&... args);
|
||||||
|
|
||||||
|
template<class U, class CompletionHandler, class... Args>
|
||||||
|
friend
|
||||||
|
handler_ptr<U, CompletionHandler>
|
||||||
|
make_handler_ptr(
|
||||||
|
CompletionHandler const& handler, Args&&... args);
|
||||||
|
/** @} */
|
||||||
|
};
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/core/impl/handler_ptr.ipp>
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -8,9 +8,10 @@
|
|||||||
#ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP
|
#ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP
|
||||||
#define BEAST_IMPL_BASIC_STREAMBUF_IPP
|
#define BEAST_IMPL_BASIC_STREAMBUF_IPP
|
||||||
|
|
||||||
|
#include <beast/core/detail/type_traits.hpp>
|
||||||
#include <beast/core/detail/write_dynabuf.hpp>
|
#include <beast/core/detail/write_dynabuf.hpp>
|
||||||
|
#include <boost/assert.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -533,7 +534,7 @@ basic_streambuf<Allocator>::capacity() const
|
|||||||
{
|
{
|
||||||
auto pos = out_;
|
auto pos = out_;
|
||||||
if(pos == list_.end())
|
if(pos == list_.end())
|
||||||
return 0;
|
return in_size_;
|
||||||
auto n = pos->size() - out_pos_;
|
auto n = pos->size() - out_pos_;
|
||||||
while(++pos != list_.end())
|
while(++pos != list_.end())
|
||||||
n += pos->size();
|
n += pos->size();
|
||||||
@@ -656,7 +657,7 @@ basic_streambuf<Allocator>::commit(size_type n)
|
|||||||
debug_check();
|
debug_check();
|
||||||
}
|
}
|
||||||
|
|
||||||
n = std::min(n, out_end_ - out_pos_);
|
n = (std::min)(n, out_end_ - out_pos_);
|
||||||
out_pos_ += n;
|
out_pos_ += n;
|
||||||
in_size_ += n;
|
in_size_ += n;
|
||||||
if(out_pos_ == out_->size())
|
if(out_pos_ == out_->size())
|
||||||
@@ -786,6 +787,7 @@ void
|
|||||||
basic_streambuf<Allocator>::
|
basic_streambuf<Allocator>::
|
||||||
copy_assign(basic_streambuf const& other, std::false_type)
|
copy_assign(basic_streambuf const& other, std::false_type)
|
||||||
{
|
{
|
||||||
|
beast::detail::ignore_unused(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
@@ -816,36 +818,36 @@ basic_streambuf<Allocator>::debug_check() const
|
|||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
using boost::asio::buffer_size;
|
using boost::asio::buffer_size;
|
||||||
assert(buffer_size(data()) == in_size_);
|
BOOST_ASSERT(buffer_size(data()) == in_size_);
|
||||||
if(list_.empty())
|
if(list_.empty())
|
||||||
{
|
{
|
||||||
assert(in_pos_ == 0);
|
BOOST_ASSERT(in_pos_ == 0);
|
||||||
assert(in_size_ == 0);
|
BOOST_ASSERT(in_size_ == 0);
|
||||||
assert(out_pos_ == 0);
|
BOOST_ASSERT(out_pos_ == 0);
|
||||||
assert(out_end_ == 0);
|
BOOST_ASSERT(out_end_ == 0);
|
||||||
assert(out_ == list_.end());
|
BOOST_ASSERT(out_ == list_.end());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const& front = list_.front();
|
auto const& front = list_.front();
|
||||||
|
|
||||||
assert(in_pos_ < front.size());
|
BOOST_ASSERT(in_pos_ < front.size());
|
||||||
|
|
||||||
if(out_ == list_.end())
|
if(out_ == list_.end())
|
||||||
{
|
{
|
||||||
assert(out_pos_ == 0);
|
BOOST_ASSERT(out_pos_ == 0);
|
||||||
assert(out_end_ == 0);
|
BOOST_ASSERT(out_end_ == 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto const& out = *out_;
|
auto const& out = *out_;
|
||||||
auto const& back = list_.back();
|
auto const& back = list_.back();
|
||||||
|
|
||||||
assert(out_end_ <= back.size());
|
BOOST_ASSERT(out_end_ <= back.size());
|
||||||
assert(out_pos_ < out.size());
|
BOOST_ASSERT(out_pos_ < out.size());
|
||||||
assert(&out != &front || out_pos_ >= in_pos_);
|
BOOST_ASSERT(&out != &front || out_pos_ >= in_pos_);
|
||||||
assert(&out != &front || out_pos_ - in_pos_ == in_size_);
|
BOOST_ASSERT(&out != &front || out_pos_ - in_pos_ == in_size_);
|
||||||
assert(&out != &back || out_pos_ <= out_end_);
|
BOOST_ASSERT(&out != &back || out_pos_ <= out_end_);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -855,11 +857,18 @@ std::size_t
|
|||||||
read_size_helper(basic_streambuf<
|
read_size_helper(basic_streambuf<
|
||||||
Allocator> const& streambuf, std::size_t max_size)
|
Allocator> const& streambuf, std::size_t max_size)
|
||||||
{
|
{
|
||||||
|
BOOST_ASSERT(max_size >= 1);
|
||||||
|
// If we already have an allocated
|
||||||
|
// buffer, try to fill that up first
|
||||||
auto const avail = streambuf.capacity() - streambuf.size();
|
auto const avail = streambuf.capacity() - streambuf.size();
|
||||||
if(avail == 0)
|
if (avail > 0)
|
||||||
return std::min(max_size,
|
return (std::min)(avail, max_size);
|
||||||
std::max<std::size_t>(512, streambuf.alloc_size_));
|
// Try to have just one new block allocated
|
||||||
return std::min(max_size, avail);
|
constexpr std::size_t low = 512;
|
||||||
|
if (streambuf.alloc_size_ > low)
|
||||||
|
return (std::min)(max_size, streambuf.alloc_size_);
|
||||||
|
// ...but enforce a 512 byte minimum.
|
||||||
|
return (std::min)(max_size, low);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Alloc, class T>
|
template<class Alloc, class T>
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ buffers_adapter<MutableBufferSequence>::commit(std::size_t n)
|
|||||||
max_size_ -= avail;
|
max_size_ -= avail;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = std::min(n, out_end_ - out_pos_);
|
n = (std::min)(n, out_end_ - out_pos_);
|
||||||
out_pos_ += n;
|
out_pos_ += n;
|
||||||
in_size_ += n;
|
in_size_ += n;
|
||||||
max_size_ -= n;
|
max_size_ -= n;
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
template<class BufferSequence, class ValueType>
|
template<class BufferSequence>
|
||||||
class consuming_buffers<BufferSequence, ValueType>::const_iterator
|
class consuming_buffers<BufferSequence>::const_iterator
|
||||||
{
|
{
|
||||||
friend class consuming_buffers<BufferSequence, ValueType>;
|
friend class consuming_buffers<BufferSequence>;
|
||||||
|
|
||||||
using iter_type =
|
using iter_type =
|
||||||
typename BufferSequence::const_iterator;
|
typename BufferSequence::const_iterator;
|
||||||
@@ -30,8 +30,12 @@ class consuming_buffers<BufferSequence, ValueType>::const_iterator
|
|||||||
consuming_buffers const* b_ = nullptr;
|
consuming_buffers const* b_ = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_type =
|
using value_type = typename std::conditional<
|
||||||
typename std::iterator_traits<iter_type>::value_type;
|
std::is_convertible<typename
|
||||||
|
std::iterator_traits<iter_type>::value_type,
|
||||||
|
boost::asio::mutable_buffer>::value,
|
||||||
|
boost::asio::mutable_buffer,
|
||||||
|
boost::asio::const_buffer>::type;
|
||||||
using pointer = value_type const*;
|
using pointer = value_type const*;
|
||||||
using reference = value_type;
|
using reference = value_type;
|
||||||
using difference_type = std::ptrdiff_t;
|
using difference_type = std::ptrdiff_t;
|
||||||
@@ -59,8 +63,9 @@ public:
|
|||||||
reference
|
reference
|
||||||
operator*() const
|
operator*() const
|
||||||
{
|
{
|
||||||
return it_ == b_->begin_ ?
|
return it_ == b_->begin_
|
||||||
*it_ + b_->skip_ : *it_;
|
? value_type{*it_} + b_->skip_
|
||||||
|
: *it_;
|
||||||
}
|
}
|
||||||
|
|
||||||
pointer
|
pointer
|
||||||
@@ -105,8 +110,8 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class BufferSequence, class ValueType>
|
template<class BufferSequence>
|
||||||
consuming_buffers<BufferSequence, ValueType>::
|
consuming_buffers<BufferSequence>::
|
||||||
consuming_buffers(consuming_buffers&& other)
|
consuming_buffers(consuming_buffers&& other)
|
||||||
: consuming_buffers(std::move(other),
|
: consuming_buffers(std::move(other),
|
||||||
std::distance<iter_type>(
|
std::distance<iter_type>(
|
||||||
@@ -114,8 +119,8 @@ consuming_buffers(consuming_buffers&& other)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class BufferSequence, class ValueType>
|
template<class BufferSequence>
|
||||||
consuming_buffers<BufferSequence, ValueType>::
|
consuming_buffers<BufferSequence>::
|
||||||
consuming_buffers(consuming_buffers const& other)
|
consuming_buffers(consuming_buffers const& other)
|
||||||
: consuming_buffers(other,
|
: consuming_buffers(other,
|
||||||
std::distance<iter_type>(
|
std::distance<iter_type>(
|
||||||
@@ -123,9 +128,9 @@ consuming_buffers(consuming_buffers const& other)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class BufferSequence, class ValueType>
|
template<class BufferSequence>
|
||||||
auto
|
auto
|
||||||
consuming_buffers<BufferSequence, ValueType>::
|
consuming_buffers<BufferSequence>::
|
||||||
operator=(consuming_buffers&& other) ->
|
operator=(consuming_buffers&& other) ->
|
||||||
consuming_buffers&
|
consuming_buffers&
|
||||||
{
|
{
|
||||||
@@ -137,9 +142,9 @@ operator=(consuming_buffers&& other) ->
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class BufferSequence, class ValueType>
|
template<class BufferSequence>
|
||||||
auto
|
auto
|
||||||
consuming_buffers<BufferSequence, ValueType>::
|
consuming_buffers<BufferSequence>::
|
||||||
operator=(consuming_buffers const& other) ->
|
operator=(consuming_buffers const& other) ->
|
||||||
consuming_buffers&
|
consuming_buffers&
|
||||||
{
|
{
|
||||||
@@ -151,35 +156,41 @@ operator=(consuming_buffers const& other) ->
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class BufferSequence, class ValueType>
|
template<class BufferSequence>
|
||||||
consuming_buffers<BufferSequence, ValueType>::
|
consuming_buffers<BufferSequence>::
|
||||||
consuming_buffers(BufferSequence const& bs)
|
consuming_buffers(BufferSequence const& bs)
|
||||||
: bs_(bs)
|
: bs_(bs)
|
||||||
, begin_(bs_.begin())
|
, begin_(bs_.begin())
|
||||||
{
|
{
|
||||||
static_assert(is_BufferSequence<BufferSequence, ValueType>::value,
|
static_assert(
|
||||||
"BufferSequence requirements not met");
|
is_BufferSequence<BufferSequence, value_type>::value,
|
||||||
|
"BufferSequence requirements not met");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class BufferSequence, class ValueType>
|
template<class BufferSequence>
|
||||||
|
inline
|
||||||
auto
|
auto
|
||||||
consuming_buffers<BufferSequence, ValueType>::begin() const ->
|
consuming_buffers<BufferSequence>::
|
||||||
|
begin() const ->
|
||||||
const_iterator
|
const_iterator
|
||||||
{
|
{
|
||||||
return const_iterator{*this, begin_};
|
return const_iterator{*this, begin_};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class BufferSequence, class ValueType>
|
template<class BufferSequence>
|
||||||
|
inline
|
||||||
auto
|
auto
|
||||||
consuming_buffers<BufferSequence, ValueType>::end() const ->
|
consuming_buffers<BufferSequence>::
|
||||||
|
end() const ->
|
||||||
const_iterator
|
const_iterator
|
||||||
{
|
{
|
||||||
return const_iterator{*this, bs_.end()};
|
return const_iterator{*this, bs_.end()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class BufferSequence, class ValueType>
|
template<class BufferSequence>
|
||||||
void
|
void
|
||||||
consuming_buffers<BufferSequence, ValueType>::consume(std::size_t n)
|
consuming_buffers<BufferSequence>::
|
||||||
|
consume(std::size_t n)
|
||||||
{
|
{
|
||||||
using boost::asio::buffer_size;
|
using boost::asio::buffer_size;
|
||||||
for(;n > 0 && begin_ != bs_.end(); ++begin_)
|
for(;n > 0 && begin_ != bs_.end(); ++begin_)
|
||||||
@@ -196,15 +207,6 @@ consuming_buffers<BufferSequence, ValueType>::consume(std::size_t n)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class BufferSequence>
|
|
||||||
consuming_buffers<BufferSequence, typename BufferSequence::value_type>
|
|
||||||
consumed_buffers(BufferSequence const& bs, std::size_t n)
|
|
||||||
{
|
|
||||||
consuming_buffers<BufferSequence> cb(bs);
|
|
||||||
cb.consume(n);
|
|
||||||
return cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
#define BEAST_IMPL_DYNABUF_READSTREAM_HPP
|
#define BEAST_IMPL_DYNABUF_READSTREAM_HPP
|
||||||
|
|
||||||
#include <beast/core/bind_handler.hpp>
|
#include <beast/core/bind_handler.hpp>
|
||||||
|
#include <beast/core/error.hpp>
|
||||||
#include <beast/core/handler_concepts.hpp>
|
#include <beast/core/handler_concepts.hpp>
|
||||||
#include <beast/core/handler_alloc.hpp>
|
#include <beast/core/handler_helpers.hpp>
|
||||||
#include <boost/system/error_code.hpp>
|
#include <beast/core/handler_ptr.hpp>
|
||||||
#include <boost/system/system_error.hpp>
|
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
@@ -21,28 +21,22 @@ template<class MutableBufferSequence, class Handler>
|
|||||||
class dynabuf_readstream<
|
class dynabuf_readstream<
|
||||||
Stream, DynamicBuffer>::read_some_op
|
Stream, DynamicBuffer>::read_some_op
|
||||||
{
|
{
|
||||||
using alloc_type =
|
// VFALCO What about bool cont for is_continuation?
|
||||||
handler_alloc<char, Handler>;
|
|
||||||
|
|
||||||
struct data
|
struct data
|
||||||
{
|
{
|
||||||
dynabuf_readstream& srs;
|
dynabuf_readstream& srs;
|
||||||
MutableBufferSequence bs;
|
MutableBufferSequence bs;
|
||||||
Handler h;
|
|
||||||
int state = 0;
|
int state = 0;
|
||||||
|
|
||||||
template<class DeducedHandler>
|
data(Handler&, dynabuf_readstream& srs_,
|
||||||
data(DeducedHandler&& h_,
|
|
||||||
dynabuf_readstream& srs_,
|
|
||||||
MutableBufferSequence const& bs_)
|
MutableBufferSequence const& bs_)
|
||||||
: srs(srs_)
|
: srs(srs_)
|
||||||
, bs(bs_)
|
, bs(bs_)
|
||||||
, h(std::forward<DeducedHandler>(h_))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<data> d_;
|
handler_ptr<data, Handler> d_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
read_some_op(read_some_op&&) = default;
|
read_some_op(read_some_op&&) = default;
|
||||||
@@ -51,7 +45,7 @@ public:
|
|||||||
template<class DeducedHandler, class... Args>
|
template<class DeducedHandler, class... Args>
|
||||||
read_some_op(DeducedHandler&& h,
|
read_some_op(DeducedHandler&& h,
|
||||||
dynabuf_readstream& srs, Args&&... args)
|
dynabuf_readstream& srs, Args&&... args)
|
||||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
: d_(make_handler_ptr<data, Handler>(
|
||||||
std::forward<DeducedHandler>(h), srs,
|
std::forward<DeducedHandler>(h), srs,
|
||||||
std::forward<Args>(args)...))
|
std::forward<Args>(args)...))
|
||||||
{
|
{
|
||||||
@@ -66,31 +60,31 @@ public:
|
|||||||
void* asio_handler_allocate(
|
void* asio_handler_allocate(
|
||||||
std::size_t size, read_some_op* op)
|
std::size_t size, read_some_op* op)
|
||||||
{
|
{
|
||||||
return boost_asio_handler_alloc_helpers::
|
return beast_asio_helpers::
|
||||||
allocate(size, op->d_->h);
|
allocate(size, op->d_.handler());
|
||||||
}
|
}
|
||||||
|
|
||||||
friend
|
friend
|
||||||
void asio_handler_deallocate(
|
void asio_handler_deallocate(
|
||||||
void* p, std::size_t size, read_some_op* op)
|
void* p, std::size_t size, read_some_op* op)
|
||||||
{
|
{
|
||||||
return boost_asio_handler_alloc_helpers::
|
return beast_asio_helpers::
|
||||||
deallocate(p, size, op->d_->h);
|
deallocate(p, size, op->d_.handler());
|
||||||
}
|
}
|
||||||
|
|
||||||
friend
|
friend
|
||||||
bool asio_handler_is_continuation(read_some_op* op)
|
bool asio_handler_is_continuation(read_some_op* op)
|
||||||
{
|
{
|
||||||
return boost_asio_handler_cont_helpers::
|
return beast_asio_helpers::
|
||||||
is_continuation(op->d_->h);
|
is_continuation(op->d_.handler());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Function>
|
template<class Function>
|
||||||
friend
|
friend
|
||||||
void asio_handler_invoke(Function&& f, read_some_op* op)
|
void asio_handler_invoke(Function&& f, read_some_op* op)
|
||||||
{
|
{
|
||||||
return boost_asio_handler_invoke_helpers::
|
return beast_asio_helpers::
|
||||||
invoke(f, op->d_->h);
|
invoke(f, op->d_.handler());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -150,7 +144,7 @@ read_some_op<MutableBufferSequence, Handler>::operator()(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.h(ec, bytes_transferred);
|
d_.invoke(ec, bytes_transferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
138
include/beast/core/impl/handler_ptr.ipp
Normal file
138
include/beast/core/impl/handler_ptr.ipp
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_IMPL_HANDLER_PTR_HPP
|
||||||
|
#define BEAST_IMPL_HANDLER_PTR_HPP
|
||||||
|
|
||||||
|
#include <beast/core/handler_helpers.hpp>
|
||||||
|
#include <boost/asio/detail/handler_alloc_helpers.hpp>
|
||||||
|
#include <boost/assert.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
template<class T, class Handler>
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
inline
|
||||||
|
handler_ptr<T, Handler>::P::
|
||||||
|
P(DeducedHandler&& h, Args&&... args)
|
||||||
|
: n(1)
|
||||||
|
, handler(std::forward<DeducedHandler>(h))
|
||||||
|
{
|
||||||
|
t = reinterpret_cast<T*>(
|
||||||
|
beast_asio_helpers::
|
||||||
|
allocate(sizeof(T), handler));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
t = new(t) T{handler,
|
||||||
|
std::forward<Args>(args)...};
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
beast_asio_helpers::
|
||||||
|
deallocate(t, sizeof(T), handler);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, class Handler>
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
handler_ptr<T, Handler>::
|
||||||
|
handler_ptr(int, DeducedHandler&& handler, Args&&... args)
|
||||||
|
: p_(new P(std::forward<DeducedHandler>(handler),
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, class Handler>
|
||||||
|
handler_ptr<T, Handler>::
|
||||||
|
~handler_ptr()
|
||||||
|
{
|
||||||
|
if(! p_)
|
||||||
|
return;
|
||||||
|
if(--p_->n)
|
||||||
|
return;
|
||||||
|
if(p_->t)
|
||||||
|
{
|
||||||
|
p_->t->~T();
|
||||||
|
beast_asio_helpers::
|
||||||
|
deallocate(p_->t, sizeof(T), p_->handler);
|
||||||
|
}
|
||||||
|
delete p_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, class Handler>
|
||||||
|
handler_ptr<T, Handler>::
|
||||||
|
handler_ptr(handler_ptr&& other)
|
||||||
|
: p_(other.p_)
|
||||||
|
{
|
||||||
|
other.p_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, class Handler>
|
||||||
|
handler_ptr<T, Handler>::
|
||||||
|
handler_ptr(handler_ptr const& other)
|
||||||
|
: p_(other.p_)
|
||||||
|
{
|
||||||
|
if(p_)
|
||||||
|
++p_->n;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, class Handler>
|
||||||
|
auto
|
||||||
|
handler_ptr<T, Handler>::
|
||||||
|
release_handler() ->
|
||||||
|
handler_type
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(p_);
|
||||||
|
BOOST_ASSERT(p_->t);
|
||||||
|
p_->t->~T();
|
||||||
|
beast_asio_helpers::
|
||||||
|
deallocate(p_->t, sizeof(T), p_->handler);
|
||||||
|
p_->t = nullptr;
|
||||||
|
return std::move(p_->handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, class Handler>
|
||||||
|
template<class... Args>
|
||||||
|
void
|
||||||
|
handler_ptr<T, Handler>::
|
||||||
|
invoke(Args&&... args)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(p_);
|
||||||
|
BOOST_ASSERT(p_->t);
|
||||||
|
p_->t->~T();
|
||||||
|
beast_asio_helpers::
|
||||||
|
deallocate(p_->t, sizeof(T), p_->handler);
|
||||||
|
p_->t = nullptr;
|
||||||
|
p_->handler(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
class T, class CompletionHandler, class... Args>
|
||||||
|
handler_ptr<T, CompletionHandler>
|
||||||
|
make_handler_ptr(
|
||||||
|
CompletionHandler&& handler, Args&&... args)
|
||||||
|
{
|
||||||
|
return handler_ptr<T, CompletionHandler>{0,
|
||||||
|
std::move(handler),
|
||||||
|
std::forward<Args>(args)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
class T, class CompletionHandler, class... Args>
|
||||||
|
handler_ptr<T, CompletionHandler>
|
||||||
|
make_handler_ptr(
|
||||||
|
CompletionHandler const& handler, Args&&... args)
|
||||||
|
{
|
||||||
|
return handler_ptr<T, CompletionHandler>{0,
|
||||||
|
handler, std::forward<Args>(args)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -279,6 +279,16 @@ static_streambuf::mutable_buffers_type::end() const ->
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
static_streambuf::data() const ->
|
||||||
|
const_buffers_type
|
||||||
|
{
|
||||||
|
return const_buffers_type{in_,
|
||||||
|
static_cast<std::size_t>(out_ - in_)};
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
auto
|
auto
|
||||||
static_streambuf::prepare(std::size_t n) ->
|
static_streambuf::prepare(std::size_t n) ->
|
||||||
@@ -290,15 +300,6 @@ static_streambuf::prepare(std::size_t n) ->
|
|||||||
return mutable_buffers_type{out_, n};
|
return mutable_buffers_type{out_, n};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
|
||||||
auto
|
|
||||||
static_streambuf::data() const ->
|
|
||||||
const_buffers_type
|
|
||||||
{
|
|
||||||
return const_buffers_type{in_,
|
|
||||||
static_cast<std::size_t>(out_ - in_)};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
68
include/beast/core/prepare_buffer.hpp
Normal file
68
include/beast/core/prepare_buffer.hpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_PREPARE_BUFFER_HPP
|
||||||
|
#define BEAST_PREPARE_BUFFER_HPP
|
||||||
|
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
/** Return a shortened buffer.
|
||||||
|
|
||||||
|
The returned buffer points to the same memory as the
|
||||||
|
passed buffer, but with a size that is equal to or less
|
||||||
|
than the size of the original buffer.
|
||||||
|
|
||||||
|
@param n The size of the returned buffer.
|
||||||
|
|
||||||
|
@param buffer The buffer to shorten. Ownership of the
|
||||||
|
underlying memory is not transferred.
|
||||||
|
|
||||||
|
@return A new buffer that points to the first `n` bytes
|
||||||
|
of the original buffer.
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
boost::asio::const_buffer
|
||||||
|
prepare_buffer(std::size_t n,
|
||||||
|
boost::asio::const_buffer buffer)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
return { buffer_cast<void const*>(buffer),
|
||||||
|
(std::min)(n, buffer_size(buffer)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return a shortened buffer.
|
||||||
|
|
||||||
|
The returned buffer points to the same memory as the
|
||||||
|
passed buffer, but with a size that is equal to or less
|
||||||
|
than the size of the original buffer.
|
||||||
|
|
||||||
|
@param n The size of the returned buffer.
|
||||||
|
|
||||||
|
@param buffer The buffer to shorten. Ownership of the
|
||||||
|
underlying memory is not transferred.
|
||||||
|
|
||||||
|
@return A new buffer that points to the first `n` bytes
|
||||||
|
of the original buffer.
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
boost::asio::mutable_buffer
|
||||||
|
prepare_buffer(std::size_t n,
|
||||||
|
boost::asio::mutable_buffer buffer)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
return { buffer_cast<void*>(buffer),
|
||||||
|
(std::min)(n, buffer_size(buffer)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -8,150 +8,45 @@
|
|||||||
#ifndef BEAST_PREPARE_BUFFERS_HPP
|
#ifndef BEAST_PREPARE_BUFFERS_HPP
|
||||||
#define BEAST_PREPARE_BUFFERS_HPP
|
#define BEAST_PREPARE_BUFFERS_HPP
|
||||||
|
|
||||||
|
#include <beast/core/detail/prepare_buffers.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
/** Get a trimmed const buffer.
|
/** Return a shortened buffer sequence.
|
||||||
|
|
||||||
The new buffer starts at the beginning of the passed
|
This function returns a new buffer sequence which adapts the
|
||||||
buffer. Ownership of the underlying memory is not
|
passed buffer sequence and efficiently presents a shorter subset
|
||||||
transferred.
|
of the original list of buffers starting with the first byte of
|
||||||
*/
|
the original sequence.
|
||||||
inline
|
|
||||||
boost::asio::const_buffer
|
|
||||||
prepare_buffer(std::size_t n,
|
|
||||||
boost::asio::const_buffer buffer)
|
|
||||||
{
|
|
||||||
using boost::asio::buffer_cast;
|
|
||||||
using boost::asio::buffer_size;
|
|
||||||
return { buffer_cast<void const*>(buffer),
|
|
||||||
std::min(n, buffer_size(buffer)) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get a trimmed mutable buffer.
|
@param n The maximum number of bytes in the wrapped
|
||||||
|
sequence. If this is larger than the size of passed,
|
||||||
|
buffers, the resulting sequence will represent the
|
||||||
|
entire input sequence.
|
||||||
|
|
||||||
The new buffer starts at the beginning of the passed
|
@param buffers The buffer sequence to adapt. A copy of
|
||||||
buffer. Ownership of the underlying memory is not
|
the sequence will be made, but ownership of the underlying
|
||||||
transferred.
|
memory is not transferred.
|
||||||
*/
|
|
||||||
inline
|
|
||||||
boost::asio::mutable_buffer
|
|
||||||
prepare_buffer(std::size_t n,
|
|
||||||
boost::asio::mutable_buffer buffer)
|
|
||||||
{
|
|
||||||
using boost::asio::buffer_cast;
|
|
||||||
using boost::asio::buffer_size;
|
|
||||||
return { buffer_cast<void*>(buffer),
|
|
||||||
std::min(n, buffer_size(buffer)) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Wrapper to produce a trimmed buffer sequence.
|
|
||||||
|
|
||||||
This wraps a buffer sequence to efficiently present a shorter
|
|
||||||
subset of the original list of buffers starting with the first
|
|
||||||
byte of the original sequence.
|
|
||||||
|
|
||||||
@tparam BufferSequence The buffer sequence to wrap.
|
|
||||||
*/
|
*/
|
||||||
template<class BufferSequence>
|
template<class BufferSequence>
|
||||||
class prepared_buffers
|
|
||||||
{
|
|
||||||
using iter_type =
|
|
||||||
typename BufferSequence::const_iterator;
|
|
||||||
|
|
||||||
BufferSequence bs_;
|
|
||||||
iter_type back_;
|
|
||||||
iter_type end_;
|
|
||||||
std::size_t size_;
|
|
||||||
|
|
||||||
template<class Deduced>
|
|
||||||
prepared_buffers(Deduced&& other,
|
|
||||||
std::size_t nback, std::size_t nend)
|
|
||||||
: bs_(std::forward<Deduced>(other).bs_)
|
|
||||||
, back_(std::next(bs_.begin(), nback))
|
|
||||||
, end_(std::next(bs_.begin(), nend))
|
|
||||||
, size_(other.size_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// The type for each element in the list of buffers.
|
|
||||||
using value_type =
|
|
||||||
typename std::iterator_traits<iter_type>::value_type;
|
|
||||||
|
|
||||||
#if GENERATING_DOCS
|
#if GENERATING_DOCS
|
||||||
/// A bidirectional iterator type that may be used to read elements.
|
implementation_defined
|
||||||
using const_iterator = implementation_defined;
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
class const_iterator;
|
inline
|
||||||
|
detail::prepared_buffers<BufferSequence>
|
||||||
#endif
|
#endif
|
||||||
|
prepare_buffers(std::size_t n, BufferSequence const& buffers)
|
||||||
/// Move constructor.
|
{
|
||||||
prepared_buffers(prepared_buffers&&);
|
return detail::prepared_buffers<BufferSequence>(n, buffers);
|
||||||
|
}
|
||||||
/// Copy constructor.
|
|
||||||
prepared_buffers(prepared_buffers const&);
|
|
||||||
|
|
||||||
/// Move assignment.
|
|
||||||
prepared_buffers& operator=(prepared_buffers&&);
|
|
||||||
|
|
||||||
/// Copy assignment.
|
|
||||||
prepared_buffers& operator=(prepared_buffers const&);
|
|
||||||
|
|
||||||
/** Construct a wrapped buffer sequence.
|
|
||||||
|
|
||||||
@param n The maximum number of bytes in the wrapped sequence.
|
|
||||||
If this is larger than the size of buffers, the wrapped
|
|
||||||
sequence will represent the entire input sequence.
|
|
||||||
|
|
||||||
@param buffers The buffer sequence to wrap. A copy of the sequence
|
|
||||||
will be made, but ownership of the underlying memory is not transferred.
|
|
||||||
*/
|
|
||||||
prepared_buffers(std::size_t n, BufferSequence const& buffers);
|
|
||||||
|
|
||||||
/// Get a bidirectional iterator to the first element.
|
|
||||||
const_iterator
|
|
||||||
begin() const;
|
|
||||||
|
|
||||||
/// Get a bidirectional iterator for one past the last element.
|
|
||||||
const_iterator
|
|
||||||
end() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void
|
|
||||||
setup(std::size_t n);
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/** Return a trimmed, wrapped buffer sequence.
|
|
||||||
|
|
||||||
This function returns a new buffer sequence which wraps the provided
|
|
||||||
buffer sequence and efficiently presents a shorter subset of the
|
|
||||||
original list of buffers starting with the first byte of the original
|
|
||||||
sequence.
|
|
||||||
|
|
||||||
@param n The maximum number of bytes in the wrapped sequence. If this
|
|
||||||
is larger than the size of buffers, the wrapped sequence will represent
|
|
||||||
the entire input sequence.
|
|
||||||
|
|
||||||
@param buffers The buffer sequence to wrap. A copy of the sequence
|
|
||||||
will be made, but ownership of the underlying memory is not transferred.
|
|
||||||
*/
|
|
||||||
template<class BufferSequence>
|
|
||||||
prepared_buffers<BufferSequence>
|
|
||||||
prepare_buffers(std::size_t n, BufferSequence const& buffers);
|
|
||||||
|
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
#include <beast/core/impl/prepare_buffers.ipp>
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ private:
|
|||||||
#else
|
#else
|
||||||
protected:
|
protected:
|
||||||
#endif
|
#endif
|
||||||
|
std::uint8_t* begin_;
|
||||||
std::uint8_t* in_;
|
std::uint8_t* in_;
|
||||||
std::uint8_t* out_;
|
std::uint8_t* out_;
|
||||||
std::uint8_t* last_;
|
std::uint8_t* last_;
|
||||||
@@ -57,21 +58,35 @@ public:
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Returns the largest size output sequence possible.
|
/// Return the size of the input sequence.
|
||||||
std::size_t
|
|
||||||
max_size() const
|
|
||||||
{
|
|
||||||
return end_ - in_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the size of the input sequence.
|
|
||||||
std::size_t
|
std::size_t
|
||||||
size() const
|
size() const
|
||||||
{
|
{
|
||||||
return out_ - in_;
|
return out_ - in_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get a list of buffers that represents the output sequence, with the given size.
|
/// Return the maximum sum of the input and output sequence sizes.
|
||||||
|
std::size_t
|
||||||
|
max_size() const
|
||||||
|
{
|
||||||
|
return end_ - begin_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the maximum sum of input and output sizes that can be held without an allocation.
|
||||||
|
std::size_t
|
||||||
|
capacity() const
|
||||||
|
{
|
||||||
|
return end_ - in_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a list of buffers that represent the input sequence.
|
||||||
|
|
||||||
|
@note These buffers remain valid across subsequent calls to `prepare`.
|
||||||
|
*/
|
||||||
|
const_buffers_type
|
||||||
|
data() const;
|
||||||
|
|
||||||
|
/** Get a list of buffers that represent the output sequence, with the given size.
|
||||||
|
|
||||||
@throws std::length_error if the size would exceed the limit
|
@throws std::length_error if the size would exceed the limit
|
||||||
imposed by the underlying mutable buffer sequence.
|
imposed by the underlying mutable buffer sequence.
|
||||||
@@ -93,13 +108,6 @@ public:
|
|||||||
out_ += std::min<std::size_t>(n, last_ - out_);
|
out_ += std::min<std::size_t>(n, last_ - out_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get a list of buffers that represents the input sequence.
|
|
||||||
|
|
||||||
@note These buffers remain valid across subsequent calls to `prepare`.
|
|
||||||
*/
|
|
||||||
const_buffers_type
|
|
||||||
data() const;
|
|
||||||
|
|
||||||
/// Remove bytes from the input sequence.
|
/// Remove bytes from the input sequence.
|
||||||
void
|
void
|
||||||
consume(std::size_t n)
|
consume(std::size_t n)
|
||||||
@@ -120,6 +128,7 @@ protected:
|
|||||||
void
|
void
|
||||||
reset(std::uint8_t* p, std::size_t n)
|
reset(std::uint8_t* p, std::size_t n)
|
||||||
{
|
{
|
||||||
|
begin_ = p;
|
||||||
in_ = p;
|
in_ = p;
|
||||||
out_ = p;
|
out_ = p;
|
||||||
last_ = p;
|
last_ = p;
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
#ifndef BEAST_HTTP_HPP
|
#ifndef BEAST_HTTP_HPP
|
||||||
#define BEAST_HTTP_HPP
|
#define BEAST_HTTP_HPP
|
||||||
|
|
||||||
#include <beast/http/basic_headers.hpp>
|
#include <beast/http/basic_fields.hpp>
|
||||||
#include <beast/http/basic_parser_v1.hpp>
|
#include <beast/http/basic_parser_v1.hpp>
|
||||||
#include <beast/http/body_type.hpp>
|
#include <beast/http/chunk_encode.hpp>
|
||||||
#include <beast/http/empty_body.hpp>
|
#include <beast/http/empty_body.hpp>
|
||||||
#include <beast/http/headers.hpp>
|
#include <beast/http/fields.hpp>
|
||||||
#include <beast/http/message.hpp>
|
#include <beast/http/message.hpp>
|
||||||
#include <beast/http/message_v1.hpp>
|
#include <beast/http/parse.hpp>
|
||||||
#include <beast/http/parse_error.hpp>
|
#include <beast/http/parse_error.hpp>
|
||||||
#include <beast/http/parser_v1.hpp>
|
#include <beast/http/parser_v1.hpp>
|
||||||
#include <beast/http/read.hpp>
|
#include <beast/http/read.hpp>
|
||||||
|
|||||||
@@ -8,8 +8,12 @@
|
|||||||
#ifndef BEAST_HTTP_BASIC_DYNABUF_BODY_HPP
|
#ifndef BEAST_HTTP_BASIC_DYNABUF_BODY_HPP
|
||||||
#define BEAST_HTTP_BASIC_DYNABUF_BODY_HPP
|
#define BEAST_HTTP_BASIC_DYNABUF_BODY_HPP
|
||||||
|
|
||||||
#include <beast/http/body_type.hpp>
|
#include <beast/core/error.hpp>
|
||||||
|
#include <beast/http/message.hpp>
|
||||||
|
#include <beast/http/resume_context.hpp>
|
||||||
|
#include <beast/core/detail/type_traits.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/logic/tribool.hpp>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
@@ -33,14 +37,19 @@ private:
|
|||||||
value_type& sb_;
|
value_type& sb_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<bool isRequest, class Headers>
|
template<bool isRequest, class Fields>
|
||||||
explicit
|
explicit
|
||||||
reader(message<isRequest,
|
reader(message<isRequest,
|
||||||
basic_dynabuf_body, Headers>& m) noexcept
|
basic_dynabuf_body, Fields>& m) noexcept
|
||||||
: sb_(m.body)
|
: sb_(m.body)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(error_code&) noexcept
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
write(void const* data,
|
write(void const* data,
|
||||||
std::size_t size, error_code&) noexcept
|
std::size_t size, error_code&) noexcept
|
||||||
@@ -57,33 +66,32 @@ private:
|
|||||||
DynamicBuffer const& body_;
|
DynamicBuffer const& body_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
writer(writer const&) = delete;
|
template<bool isRequest, class Fields>
|
||||||
writer& operator=(writer const&) = delete;
|
|
||||||
|
|
||||||
template<bool isRequest, class Headers>
|
|
||||||
explicit
|
explicit
|
||||||
writer(message<
|
writer(message<
|
||||||
isRequest, basic_dynabuf_body, Headers> const& m)
|
isRequest, basic_dynabuf_body, Fields> const& m) noexcept
|
||||||
: body_(m.body)
|
: body_(m.body)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
init(error_code& ec)
|
init(error_code& ec) noexcept
|
||||||
{
|
{
|
||||||
|
beast::detail::ignore_unused(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint64_t
|
std::uint64_t
|
||||||
content_length() const
|
content_length() const noexcept
|
||||||
{
|
{
|
||||||
return body_.size();
|
return body_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Write>
|
template<class WriteFunction>
|
||||||
boost::tribool
|
boost::tribool
|
||||||
operator()(resume_context&&, error_code&, Write&& write)
|
write(resume_context&&, error_code&,
|
||||||
|
WriteFunction&& wf) noexcept
|
||||||
{
|
{
|
||||||
write(body_.data());
|
wf(body_.data());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
306
include/beast/http/basic_fields.hpp
Normal file
306
include/beast/http/basic_fields.hpp
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
//
|
||||||
|
// 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_FIELDS_HPP
|
||||||
|
#define BEAST_HTTP_BASIC_FIELDS_HPP
|
||||||
|
|
||||||
|
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||||
|
#include <beast/http/detail/basic_fields.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** A container for storing HTTP header fields.
|
||||||
|
|
||||||
|
This container is designed to store the field value pairs that make
|
||||||
|
up the fields and trailers in a HTTP message. Objects of this type
|
||||||
|
are iterable, with each element holding the field name and field
|
||||||
|
value.
|
||||||
|
|
||||||
|
Field names are stored as-is, but comparisons are case-insensitive.
|
||||||
|
When the container is iterated, the fields are presented in the order
|
||||||
|
of insertion. For fields with the same name, the container behaves
|
||||||
|
as a `std::multiset`; there will be a separate value for each occurrence
|
||||||
|
of the field name.
|
||||||
|
|
||||||
|
@note Meets the requirements of @b FieldSequence.
|
||||||
|
*/
|
||||||
|
template<class Allocator>
|
||||||
|
class basic_fields :
|
||||||
|
#if ! GENERATING_DOCS
|
||||||
|
private beast::detail::empty_base_optimization<
|
||||||
|
typename std::allocator_traits<Allocator>::
|
||||||
|
template rebind_alloc<
|
||||||
|
detail::basic_fields_base::element>>,
|
||||||
|
#endif
|
||||||
|
public detail::basic_fields_base
|
||||||
|
{
|
||||||
|
using alloc_type = typename
|
||||||
|
std::allocator_traits<Allocator>::
|
||||||
|
template rebind_alloc<
|
||||||
|
detail::basic_fields_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_fields&, std::false_type);
|
||||||
|
|
||||||
|
void
|
||||||
|
move_assign(basic_fields&, std::true_type);
|
||||||
|
|
||||||
|
void
|
||||||
|
copy_assign(basic_fields const&, std::false_type);
|
||||||
|
|
||||||
|
void
|
||||||
|
copy_assign(basic_fields 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;
|
||||||
|
|
||||||
|
/** The value type of the field sequence.
|
||||||
|
|
||||||
|
Meets the requirements of @b Field.
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using value_type = implementation_defined;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// A const iterator to the field sequence
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using iterator = implementation_defined;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// A const iterator to the field sequence
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using const_iterator = implementation_defined;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Default constructor.
|
||||||
|
basic_fields() = default;
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
~basic_fields();
|
||||||
|
|
||||||
|
/** Construct the fields.
|
||||||
|
|
||||||
|
@param alloc The allocator to use.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
basic_fields(Allocator const& alloc);
|
||||||
|
|
||||||
|
/** Move constructor.
|
||||||
|
|
||||||
|
The moved-from object becomes an empty field sequence.
|
||||||
|
|
||||||
|
@param other The object to move from.
|
||||||
|
*/
|
||||||
|
basic_fields(basic_fields&& other);
|
||||||
|
|
||||||
|
/** Move assignment.
|
||||||
|
|
||||||
|
The moved-from object becomes an empty field sequence.
|
||||||
|
|
||||||
|
@param other The object to move from.
|
||||||
|
*/
|
||||||
|
basic_fields& operator=(basic_fields&& other);
|
||||||
|
|
||||||
|
/// Copy constructor.
|
||||||
|
basic_fields(basic_fields const&);
|
||||||
|
|
||||||
|
/// Copy assignment.
|
||||||
|
basic_fields& operator=(basic_fields const&);
|
||||||
|
|
||||||
|
/// Copy constructor.
|
||||||
|
template<class OtherAlloc>
|
||||||
|
basic_fields(basic_fields<OtherAlloc> const&);
|
||||||
|
|
||||||
|
/// Copy assignment.
|
||||||
|
template<class OtherAlloc>
|
||||||
|
basic_fields& operator=(basic_fields<OtherAlloc> const&);
|
||||||
|
|
||||||
|
/// Construct from a field sequence.
|
||||||
|
template<class FwdIt>
|
||||||
|
basic_fields(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 a const iterator to the beginning of the field sequence.
|
||||||
|
const_iterator
|
||||||
|
begin() const
|
||||||
|
{
|
||||||
|
return list_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a const iterator to the end of the field sequence.
|
||||||
|
const_iterator
|
||||||
|
end() const
|
||||||
|
{
|
||||||
|
return list_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a const iterator to the beginning of the field sequence.
|
||||||
|
const_iterator
|
||||||
|
cbegin() const
|
||||||
|
{
|
||||||
|
return list_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a const iterator to the end of the field sequence.
|
||||||
|
const_iterator
|
||||||
|
cend() const
|
||||||
|
{
|
||||||
|
return list_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the specified field exists.
|
||||||
|
bool
|
||||||
|
exists(boost::string_ref const& name) const
|
||||||
|
{
|
||||||
|
return set_.find(name, less{}) != set_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of values for the specified field.
|
||||||
|
std::size_t
|
||||||
|
count(boost::string_ref const& name) const;
|
||||||
|
|
||||||
|
/** Returns an iterator to the case-insensitive matching field name.
|
||||||
|
|
||||||
|
If more than one field with the specified name exists, the
|
||||||
|
first field defined by insertion order is returned.
|
||||||
|
*/
|
||||||
|
iterator
|
||||||
|
find(boost::string_ref const& name) const;
|
||||||
|
|
||||||
|
/** Returns the value for a case-insensitive matching header, or `""`.
|
||||||
|
|
||||||
|
If more than one field with the specified name exists, the
|
||||||
|
first field defined by insertion order is returned.
|
||||||
|
*/
|
||||||
|
boost::string_ref
|
||||||
|
operator[](boost::string_ref const& name) const;
|
||||||
|
|
||||||
|
/// Clear the contents of the basic_fields.
|
||||||
|
void
|
||||||
|
clear() noexcept;
|
||||||
|
|
||||||
|
/** Remove a field.
|
||||||
|
|
||||||
|
If more than one field with the specified name exists, all
|
||||||
|
matching fields will be removed.
|
||||||
|
|
||||||
|
@param name The name of the field(s) to remove.
|
||||||
|
|
||||||
|
@return The number of fields removed.
|
||||||
|
*/
|
||||||
|
std::size_t
|
||||||
|
erase(boost::string_ref const& name);
|
||||||
|
|
||||||
|
/** Insert a field value.
|
||||||
|
|
||||||
|
If a field with the same name already exists, the
|
||||||
|
existing field is untouched and a new field value pair
|
||||||
|
is inserted into the container.
|
||||||
|
|
||||||
|
@param name The name of the field.
|
||||||
|
|
||||||
|
@param value A string holding the value of the field.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
insert(boost::string_ref const& name, boost::string_ref value);
|
||||||
|
|
||||||
|
/** Insert a field value.
|
||||||
|
|
||||||
|
If a field with the same name already exists, the
|
||||||
|
existing field is untouched and a new field value pair
|
||||||
|
is inserted into the container.
|
||||||
|
|
||||||
|
@param name The name of the field
|
||||||
|
|
||||||
|
@param value The value of the field. The object will be
|
||||||
|
converted to a string using `boost::lexical_cast`.
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
typename std::enable_if<
|
||||||
|
! std::is_constructible<boost::string_ref, T>::value>::type
|
||||||
|
insert(boost::string_ref name, T const& value)
|
||||||
|
{
|
||||||
|
insert(name, boost::lexical_cast<std::string>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Replace a field value.
|
||||||
|
|
||||||
|
First removes any values with matching field names, then
|
||||||
|
inserts the new field value.
|
||||||
|
|
||||||
|
@param name The name of the field.
|
||||||
|
|
||||||
|
@param value A string holding the value of the field.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
replace(boost::string_ref const& name, boost::string_ref value);
|
||||||
|
|
||||||
|
/** Replace a field value.
|
||||||
|
|
||||||
|
First removes any values with matching field names, then
|
||||||
|
inserts the new field value.
|
||||||
|
|
||||||
|
@param name The name of the field
|
||||||
|
|
||||||
|
@param value The value of the field. The object will be
|
||||||
|
converted to a string using `boost::lexical_cast`.
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
typename std::enable_if<
|
||||||
|
! std::is_constructible<boost::string_ref, T>::value>::type
|
||||||
|
replace(boost::string_ref const& name, T const& value)
|
||||||
|
{
|
||||||
|
replace(name,
|
||||||
|
boost::lexical_cast<std::string>(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/basic_fields.ipp>
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,474 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_BASIC_HEADERS_HPP
|
|
||||||
#define BEAST_HTTP_BASIC_HEADERS_HPP
|
|
||||||
|
|
||||||
#include <beast/core/detail/ci_char_traits.hpp>
|
|
||||||
#include <beast/core/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_multiset<
|
|
||||||
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
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/** A container for storing HTTP headers.
|
|
||||||
|
|
||||||
This container is designed to store the field value pairs that make
|
|
||||||
up the headers and trailers in a HTTP message. Objects of this type
|
|
||||||
are iterable, which each element holding the field name and field
|
|
||||||
value.
|
|
||||||
|
|
||||||
Field names are stored as-is, but comparison are case-insensitive.
|
|
||||||
When the container is iterated, the fields are presented in the order
|
|
||||||
of insertion. For fields with the same name, the container behaves
|
|
||||||
as a std::multiset; there will be a separate value for each occurrence
|
|
||||||
of the field name.
|
|
||||||
|
|
||||||
@note Meets the requirements of @b `FieldSequence`.
|
|
||||||
*/
|
|
||||||
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 the number of values for the specified field.
|
|
||||||
std::size_t
|
|
||||||
count(boost::string_ref const& name) const;
|
|
||||||
|
|
||||||
/** Returns an iterator to the case-insensitive matching field name.
|
|
||||||
|
|
||||||
If more than one field with the specified name exists, the
|
|
||||||
first field defined by insertion order is returned.
|
|
||||||
*/
|
|
||||||
iterator
|
|
||||||
find(boost::string_ref const& name) const;
|
|
||||||
|
|
||||||
/** Returns the value for a case-insensitive matching header, or `""`.
|
|
||||||
|
|
||||||
If more than one field with the specified name exists, the
|
|
||||||
first field defined by insertion order is returned.
|
|
||||||
*/
|
|
||||||
boost::string_ref
|
|
||||||
operator[](boost::string_ref const& name) const;
|
|
||||||
|
|
||||||
/// Clear the contents of the basic_headers.
|
|
||||||
void
|
|
||||||
clear() noexcept;
|
|
||||||
|
|
||||||
/** Remove a field.
|
|
||||||
|
|
||||||
If more than one field with the specified name exists, all
|
|
||||||
matching fields will be removed.
|
|
||||||
|
|
||||||
@param name The name of the field(s) to remove.
|
|
||||||
|
|
||||||
@return The number of fields removed.
|
|
||||||
*/
|
|
||||||
std::size_t
|
|
||||||
erase(boost::string_ref const& name);
|
|
||||||
|
|
||||||
/** Insert a field value.
|
|
||||||
|
|
||||||
If a field with the same name already exists, the
|
|
||||||
existing field is untouched and a new field value pair
|
|
||||||
is inserted into the container.
|
|
||||||
|
|
||||||
@param name The name of the field.
|
|
||||||
|
|
||||||
@param value A string holding the value of the field.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
insert(boost::string_ref const& name, boost::string_ref value);
|
|
||||||
|
|
||||||
/** Insert a field value.
|
|
||||||
|
|
||||||
If a field with the same name already exists, the
|
|
||||||
existing field is untouched and a new field value pair
|
|
||||||
is inserted into the container.
|
|
||||||
|
|
||||||
@param name The name of the field
|
|
||||||
|
|
||||||
@param value The value of the field. The object will be
|
|
||||||
converted to a string using `boost::lexical_cast`.
|
|
||||||
*/
|
|
||||||
template<class T>
|
|
||||||
typename std::enable_if<
|
|
||||||
! std::is_constructible<boost::string_ref, T>::value>::type
|
|
||||||
insert(boost::string_ref name, T const& value)
|
|
||||||
{
|
|
||||||
insert(name, boost::lexical_cast<std::string>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Replace a field value.
|
|
||||||
|
|
||||||
First removes any values with matching field names, then
|
|
||||||
inserts the new field value.
|
|
||||||
|
|
||||||
@param name The name of the field.
|
|
||||||
|
|
||||||
@param value A string holding the value of the field.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
replace(boost::string_ref const& name, boost::string_ref value);
|
|
||||||
|
|
||||||
/** Replace a field value.
|
|
||||||
|
|
||||||
First removes any values with matching field names, then
|
|
||||||
inserts the new field value.
|
|
||||||
|
|
||||||
@param name The name of the field
|
|
||||||
|
|
||||||
@param value The value of the field. The object will be
|
|
||||||
converted to a string using `boost::lexical_cast`.
|
|
||||||
*/
|
|
||||||
template<class T>
|
|
||||||
typename std::enable_if<
|
|
||||||
! std::is_constructible<boost::string_ref, T>::value>::type
|
|
||||||
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
|
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
#include <beast/http/rfc7230.hpp>
|
#include <beast/http/rfc7230.hpp>
|
||||||
#include <beast/http/detail/basic_parser_v1.hpp>
|
#include <beast/http/detail/basic_parser_v1.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/assert.hpp>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cassert>
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@@ -22,8 +22,11 @@
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
namespace parse_flag {
|
/** Parse flags
|
||||||
enum values
|
|
||||||
|
The set of parser bit flags are returned by @ref basic_parser_v1::flags.
|
||||||
|
*/
|
||||||
|
enum parse_flag
|
||||||
{
|
{
|
||||||
chunked = 1,
|
chunked = 1,
|
||||||
connection_keep_alive = 2,
|
connection_keep_alive = 2,
|
||||||
@@ -34,28 +37,6 @@ enum values
|
|||||||
skipbody = 64,
|
skipbody = 64,
|
||||||
contentlength = 128
|
contentlength = 128
|
||||||
};
|
};
|
||||||
} // parse_flag
|
|
||||||
|
|
||||||
/** Headers maximum size option.
|
|
||||||
|
|
||||||
Sets the maximum number of cumulative bytes allowed
|
|
||||||
including all header octets. A value of zero indicates
|
|
||||||
no limit on the number of header octets
|
|
||||||
|
|
||||||
The default headers maximum size is 16KB (16,384 bytes).
|
|
||||||
|
|
||||||
@note Objects of this type are passed to @ref set_option.
|
|
||||||
*/
|
|
||||||
struct headers_max_size
|
|
||||||
{
|
|
||||||
std::size_t value;
|
|
||||||
|
|
||||||
explicit
|
|
||||||
headers_max_size(std::size_t v)
|
|
||||||
: value(v)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Body maximum size option.
|
/** Body maximum size option.
|
||||||
|
|
||||||
@@ -67,7 +48,7 @@ struct headers_max_size
|
|||||||
The default body maximum size for requests is 4MB (four
|
The default body maximum size for requests is 4MB (four
|
||||||
megabytes or 4,194,304 bytes) and unlimited for responses.
|
megabytes or 4,194,304 bytes) and unlimited for responses.
|
||||||
|
|
||||||
@note Objects of this type are passed to @ref set_option.
|
@note Objects of this type are used with @ref basic_parser_v1::set_option.
|
||||||
*/
|
*/
|
||||||
struct body_max_size
|
struct body_max_size
|
||||||
{
|
{
|
||||||
@@ -80,9 +61,73 @@ struct body_max_size
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Header maximum size option.
|
||||||
|
|
||||||
|
Sets the maximum number of cumulative bytes allowed
|
||||||
|
including all header octets. A value of zero indicates
|
||||||
|
no limit on the number of header octets.
|
||||||
|
|
||||||
|
The default header maximum size is 16KB (16,384 bytes).
|
||||||
|
|
||||||
|
@note Objects of this type are used with @ref basic_parser_v1::set_option.
|
||||||
|
*/
|
||||||
|
struct header_max_size
|
||||||
|
{
|
||||||
|
std::size_t value;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
header_max_size(std::size_t v)
|
||||||
|
: value(v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A value indicating how the parser should treat the body.
|
||||||
|
|
||||||
|
This value is returned from the `on_header` callback in
|
||||||
|
the derived class. It controls what the parser does next
|
||||||
|
in terms of the message body.
|
||||||
|
*/
|
||||||
|
enum class body_what
|
||||||
|
{
|
||||||
|
/** The parser should expect a body, keep reading.
|
||||||
|
*/
|
||||||
|
normal,
|
||||||
|
|
||||||
|
/** Skip parsing of the body.
|
||||||
|
|
||||||
|
When returned by `on_header` this causes parsing to
|
||||||
|
complete and control to return to the caller. This
|
||||||
|
could be used when sending a response to a HEAD
|
||||||
|
request, for example.
|
||||||
|
*/
|
||||||
|
skip,
|
||||||
|
|
||||||
|
/** The message represents an UPGRADE request.
|
||||||
|
|
||||||
|
When returned by `on_body_prepare` this causes parsing
|
||||||
|
to complete and control to return to the caller.
|
||||||
|
*/
|
||||||
|
upgrade,
|
||||||
|
|
||||||
|
/** Suspend parsing before reading the body.
|
||||||
|
|
||||||
|
When returned by `on_body_prepare` this causes parsing
|
||||||
|
to pause. Control is returned to the caller, and the
|
||||||
|
parser state is preserved such that a subsequent call
|
||||||
|
to the parser will begin reading the message body.
|
||||||
|
|
||||||
|
This could be used by callers to inspect the HTTP
|
||||||
|
header before committing to read the body. For example,
|
||||||
|
to choose the body type based on the fields. Or to
|
||||||
|
respond to an Expect: 100-continue request.
|
||||||
|
*/
|
||||||
|
pause
|
||||||
|
};
|
||||||
|
|
||||||
/// The value returned when no content length is known or applicable.
|
/// The value returned when no content length is known or applicable.
|
||||||
static std::uint64_t constexpr no_content_length =
|
static std::uint64_t constexpr no_content_length =
|
||||||
std::numeric_limits<std::uint64_t>::max();
|
(std::numeric_limits<std::uint64_t>::max)();
|
||||||
|
|
||||||
/** A parser for decoding HTTP/1 wire format messages.
|
/** A parser for decoding HTTP/1 wire format messages.
|
||||||
|
|
||||||
@@ -97,86 +142,95 @@ static std::uint64_t constexpr no_content_length =
|
|||||||
more calls to derived class members functions (referred to as
|
more calls to derived class members functions (referred to as
|
||||||
"callbacks" from here on) matching a specific signature.
|
"callbacks" from here on) matching a specific signature.
|
||||||
|
|
||||||
Callbacks are detected through SFINAE. The derived class may
|
Every callback must be provided by the derived class, or else
|
||||||
implement as few or as many of the members as needed.
|
a compilation error will be generated. This exemplar shows
|
||||||
These are the signatures of the callbacks:<br>
|
the signature and description of the callbacks required in
|
||||||
|
the derived class.
|
||||||
|
|
||||||
@li `void on_start(error_code&)`
|
@code
|
||||||
|
template<bool isRequest>
|
||||||
|
struct exemplar : basic_parser_v1<isRequest, exemplar>
|
||||||
|
{
|
||||||
|
// Called when the first valid octet of a new message is received
|
||||||
|
//
|
||||||
|
void on_start(error_code&);
|
||||||
|
|
||||||
Called when the first valid octet of a new message is received
|
// Called for each piece of the Request-Method
|
||||||
|
//
|
||||||
|
void on_method(boost::string_ref const&, error_code&);
|
||||||
|
|
||||||
@li `void on_method(boost::string_ref const&, error_code&)`
|
// Called for each piece of the Request-URI
|
||||||
|
//
|
||||||
|
void on_uri(boost::string_ref const&, error_code&);
|
||||||
|
|
||||||
Called for each piece of the Request-Method
|
// Called for each piece of the reason-phrase
|
||||||
|
//
|
||||||
|
void on_reason(boost::string_ref const&, error_code&);
|
||||||
|
|
||||||
@li `void on_uri(boost::string_ref const&, error_code&)`
|
// Called after the entire Request-Line has been parsed successfully.
|
||||||
|
//
|
||||||
|
void on_request(error_code&);
|
||||||
|
|
||||||
Called for each piece of the Request-URI
|
// Called after the entire Response-Line has been parsed successfully.
|
||||||
|
//
|
||||||
|
void on_response(error_code&);
|
||||||
|
|
||||||
@li `void on_reason(boost::string_ref const&, error_code&)`
|
// Called for each piece of the current header field.
|
||||||
|
//
|
||||||
|
void on_field(boost::string_ref const&, error_code&);
|
||||||
|
|
||||||
Called for each piece of the reason-phrase
|
// Called for each piece of the current header value.
|
||||||
|
//
|
||||||
|
void on_value(boost::string_ref const&, error_code&)
|
||||||
|
|
||||||
@li `void on_request(error_code&)`
|
// Called when the entire header has been parsed successfully.
|
||||||
|
//
|
||||||
|
void
|
||||||
|
on_header(std::uint64_t content_length, error_code&);
|
||||||
|
|
||||||
Called after the entire Request-Line has been parsed successfully.
|
// Called after on_header, before the body is parsed
|
||||||
|
//
|
||||||
|
body_what
|
||||||
|
on_body_what(std::uint64_t content_length, error_code&);
|
||||||
|
|
||||||
@li `void on_response(error_code&)`
|
// Called for each piece of the body.
|
||||||
|
//
|
||||||
|
// If the header indicates chunk encoding, the chunk
|
||||||
|
// encoding is removed from the buffer before being
|
||||||
|
// passed to the callback.
|
||||||
|
//
|
||||||
|
void on_body(boost::string_ref const&, error_code&);
|
||||||
|
|
||||||
Called after the entire Response-Line has been parsed successfully.
|
// Called when the entire message has been parsed successfully.
|
||||||
|
// At this point, @ref complete returns `true`, and the parser
|
||||||
|
// is ready to parse another message if `keep_alive` would
|
||||||
|
// return `true`.
|
||||||
|
//
|
||||||
|
void on_complete(error_code&) {}
|
||||||
|
};
|
||||||
|
@endcode
|
||||||
|
|
||||||
@li `void on_field(boost::string_ref const&, error_code&)`
|
The return value of `on_body_what` is special, it controls
|
||||||
|
whether or not the parser should expect a body. See @ref body_what
|
||||||
Called for each piece of the current header field.
|
for choices of the return value.
|
||||||
|
|
||||||
@li `void on_value(boost::string_ref const&, error_code&)`
|
|
||||||
|
|
||||||
Called for each piece of the current header value.
|
|
||||||
|
|
||||||
@li `int on_headers(std::uint64_t content_length, error_code&)`
|
|
||||||
|
|
||||||
Called when all the headers have been parsed successfully.
|
|
||||||
|
|
||||||
@li `void on_body(boost::string_ref const&, error_code&)`
|
|
||||||
|
|
||||||
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&)`
|
|
||||||
|
|
||||||
Called when the entire message has been parsed successfully.
|
|
||||||
At this point, @ref basic_parser_v1::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
|
If a callback sets an error, parsing stops at the current octet
|
||||||
and the error is returned to the caller.
|
and the error is returned to the caller. Callbacks must not throw
|
||||||
|
exceptions.
|
||||||
|
|
||||||
@tparam isRequest A `bool` indicating whether the parser will be
|
@tparam isRequest A `bool` indicating whether the parser will be
|
||||||
presented with request or response message.
|
presented with request or response message.
|
||||||
|
|
||||||
|
@tparam Derived The derived class type. This is part of the
|
||||||
|
Curiously Recurring Template Pattern interface.
|
||||||
*/
|
*/
|
||||||
template<bool isRequest, class Derived>
|
template<bool isRequest, class Derived>
|
||||||
class basic_parser_v1 : public detail::parser_base
|
class basic_parser_v1 : public detail::parser_base
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
template<bool, class>
|
||||||
|
friend class basic_parser_v1;
|
||||||
|
|
||||||
using self = basic_parser_v1;
|
using self = basic_parser_v1;
|
||||||
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
||||||
|
|
||||||
@@ -235,15 +289,19 @@ private:
|
|||||||
bool upgrade_ : 1; // true if parser exited for upgrade
|
bool upgrade_ : 1; // true if parser exited for upgrade
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Copy constructor.
|
|
||||||
basic_parser_v1(basic_parser_v1 const&) = default;
|
|
||||||
|
|
||||||
/// Copy assignment.
|
|
||||||
basic_parser_v1& operator=(basic_parser_v1 const&) = default;
|
|
||||||
|
|
||||||
/// Default constructor
|
/// Default constructor
|
||||||
basic_parser_v1();
|
basic_parser_v1();
|
||||||
|
|
||||||
|
/// Copy constructor.
|
||||||
|
template<class OtherDerived>
|
||||||
|
basic_parser_v1(basic_parser_v1<
|
||||||
|
isRequest, OtherDerived> const& other);
|
||||||
|
|
||||||
|
/// Copy assignment.
|
||||||
|
template<class OtherDerived>
|
||||||
|
basic_parser_v1& operator=(basic_parser_v1<
|
||||||
|
isRequest, OtherDerived> const& other);
|
||||||
|
|
||||||
/** Set options on the parser.
|
/** Set options on the parser.
|
||||||
|
|
||||||
@param args One or more parser options to set.
|
@param args One or more parser options to set.
|
||||||
@@ -263,9 +321,9 @@ public:
|
|||||||
std::forward<An>(an)...);
|
std::forward<An>(an)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the headers maximum size option
|
/// Set the header maximum size option
|
||||||
void
|
void
|
||||||
set_option(headers_max_size const& o)
|
set_option(header_max_size const& o)
|
||||||
{
|
{
|
||||||
h_max_ = o.value;
|
h_max_ = o.value;
|
||||||
h_left_ = h_max_;
|
h_left_ = h_max_;
|
||||||
@@ -373,7 +431,10 @@ public:
|
|||||||
bool
|
bool
|
||||||
complete() const
|
complete() const
|
||||||
{
|
{
|
||||||
return s_ == s_restart || s_ == s_closed_complete;
|
return
|
||||||
|
s_ == s_restart ||
|
||||||
|
s_ == s_closed_complete ||
|
||||||
|
s_ == s_body_pause;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Write a sequence of buffers to the parser.
|
/** Write a sequence of buffers to the parser.
|
||||||
@@ -418,6 +479,15 @@ public:
|
|||||||
void
|
void
|
||||||
write_eof(error_code& ec);
|
write_eof(error_code& ec);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Reset the parsing state.
|
||||||
|
|
||||||
|
The state of the parser is reset to expect the beginning of
|
||||||
|
a new request or response. The old state is discarded.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Derived&
|
Derived&
|
||||||
impl()
|
impl()
|
||||||
@@ -437,18 +507,10 @@ private:
|
|||||||
s_ = s_res_start;
|
s_ = s_res_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
reset()
|
|
||||||
{
|
|
||||||
h_left_ = h_max_;
|
|
||||||
b_left_ = b_max_;
|
|
||||||
reset(std::integral_constant<bool, isRequest>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
init(std::true_type)
|
init(std::true_type)
|
||||||
{
|
{
|
||||||
// 16KB max headers, 4MB max body
|
// Request: 16KB max header, 4MB max body
|
||||||
h_max_ = 16 * 1024;
|
h_max_ = 16 * 1024;
|
||||||
b_max_ = 4 * 1024 * 1024;
|
b_max_ = 4 * 1024 * 1024;
|
||||||
}
|
}
|
||||||
@@ -456,7 +518,7 @@ private:
|
|||||||
void
|
void
|
||||||
init(std::false_type)
|
init(std::false_type)
|
||||||
{
|
{
|
||||||
// 16KB max headers, unlimited body
|
// Response: 16KB max header, unlimited body
|
||||||
h_max_ = 16 * 1024;
|
h_max_ = 16 * 1024;
|
||||||
b_max_ = 0;
|
b_max_ = 0;
|
||||||
}
|
}
|
||||||
@@ -474,13 +536,102 @@ private:
|
|||||||
bool
|
bool
|
||||||
needs_eof(std::false_type) const;
|
needs_eof(std::false_type) const;
|
||||||
|
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct check_on_start : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct check_on_start<T, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().on_start(
|
||||||
|
std::declval<error_code&>())
|
||||||
|
)>> : std::true_type { };
|
||||||
|
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct check_on_method : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct check_on_method<T, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().on_method(
|
||||||
|
std::declval<boost::string_ref>(),
|
||||||
|
std::declval<error_code&>())
|
||||||
|
)>> : std::true_type {};
|
||||||
|
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct check_on_uri : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct check_on_uri<T, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().on_uri(
|
||||||
|
std::declval<boost::string_ref>(),
|
||||||
|
std::declval<error_code&>())
|
||||||
|
)>> : std::true_type {};
|
||||||
|
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct check_on_reason : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct check_on_reason<T, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().on_reason(
|
||||||
|
std::declval<boost::string_ref>(),
|
||||||
|
std::declval<error_code&>())
|
||||||
|
)>> : std::true_type {};
|
||||||
|
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct check_on_request : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct check_on_request<T, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().on_request(
|
||||||
|
std::declval<error_code&>())
|
||||||
|
)>> : std::true_type {};
|
||||||
|
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct check_on_response : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct check_on_response<T, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().on_response(
|
||||||
|
std::declval<error_code&>())
|
||||||
|
)>> : std::true_type {};
|
||||||
|
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct check_on_field : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct check_on_field<T, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().on_field(
|
||||||
|
std::declval<boost::string_ref>(),
|
||||||
|
std::declval<error_code&>())
|
||||||
|
)>> : std::true_type {};
|
||||||
|
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct check_on_value : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct check_on_value<T, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().on_value(
|
||||||
|
std::declval<boost::string_ref>(),
|
||||||
|
std::declval<error_code&>())
|
||||||
|
)>> : std::true_type {};
|
||||||
|
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct check_on_headers : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct check_on_headers<T, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().on_header(
|
||||||
|
std::declval<std::uint64_t>(),
|
||||||
|
std::declval<error_code&>())
|
||||||
|
)>> : std::true_type {};
|
||||||
|
|
||||||
|
// VFALCO Can we use std::is_detected? Is C++11 capable?
|
||||||
template<class C>
|
template<class C>
|
||||||
class has_on_start_t
|
class check_on_body_what_t
|
||||||
{
|
{
|
||||||
template<class T, class R =
|
template<class T, class R = std::is_convertible<decltype(
|
||||||
decltype(std::declval<T>().on_start(
|
std::declval<T>().on_body_what(
|
||||||
std::declval<error_code&>()),
|
std::declval<std::uint64_t>(),
|
||||||
std::true_type{})>
|
std::declval<error_code&>())),
|
||||||
|
body_what>>
|
||||||
static R check(int);
|
static R check(int);
|
||||||
template<class>
|
template<class>
|
||||||
static std::false_type check(...);
|
static std::false_type check(...);
|
||||||
@@ -489,211 +640,46 @@ private:
|
|||||||
static bool const value = type::value;
|
static bool const value = type::value;
|
||||||
};
|
};
|
||||||
template<class C>
|
template<class C>
|
||||||
using has_on_start =
|
using check_on_body_what =
|
||||||
std::integral_constant<bool, has_on_start_t<C>::value>;
|
std::integral_constant<bool, check_on_body_what_t<C>::value>;
|
||||||
|
|
||||||
template<class C>
|
template<class T, class = beast::detail::void_t<>>
|
||||||
class has_on_method_t
|
struct check_on_body : std::false_type {};
|
||||||
{
|
|
||||||
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>
|
template<class T>
|
||||||
class has_on_uri_t
|
struct check_on_body<T, beast::detail::void_t<decltype(
|
||||||
{
|
std::declval<T>().on_body(
|
||||||
template<class T, class R =
|
std::declval<boost::string_ref>(),
|
||||||
decltype(std::declval<T>().on_uri(
|
std::declval<error_code&>())
|
||||||
std::declval<boost::string_ref const&>(),
|
)>> : std::true_type {};
|
||||||
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>
|
template<class T, class = beast::detail::void_t<>>
|
||||||
class has_on_reason_t
|
struct check_on_complete : std::false_type {};
|
||||||
{
|
|
||||||
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>
|
template<class T>
|
||||||
class has_on_request_t
|
struct check_on_complete<T, beast::detail::void_t<decltype(
|
||||||
{
|
std::declval<T>().on_complete(
|
||||||
template<class T, class R =
|
std::declval<error_code&>())
|
||||||
decltype(std::declval<T>().on_request(
|
)>> : std::true_type {};
|
||||||
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<std::uint64_t>(), 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_start(error_code& ec, std::true_type)
|
|
||||||
{
|
|
||||||
impl().on_start(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void call_on_start(error_code& ec, std::false_type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void call_on_start(error_code& ec)
|
void call_on_start(error_code& ec)
|
||||||
{
|
{
|
||||||
call_on_start(ec, has_on_start<Derived>{});
|
static_assert(check_on_start<Derived>::value,
|
||||||
|
"on_start requirements not met");
|
||||||
|
impl().on_start(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_method(error_code& ec,
|
void call_on_method(error_code& ec,
|
||||||
boost::string_ref const& s, std::true_type)
|
boost::string_ref const& s, std::true_type)
|
||||||
{
|
{
|
||||||
|
static_assert(check_on_method<Derived>::value,
|
||||||
|
"on_method requirements not met");
|
||||||
|
if(h_max_ && s.size() > h_left_)
|
||||||
|
{
|
||||||
|
ec = parse_error::header_too_big;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
h_left_ -= s.size();
|
||||||
impl().on_method(s, ec);
|
impl().on_method(s, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,21 +691,21 @@ private:
|
|||||||
void call_on_method(error_code& ec,
|
void call_on_method(error_code& ec,
|
||||||
boost::string_ref const& s)
|
boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
if(! h_max_ || s.size() <= h_left_)
|
call_on_method(ec, s,
|
||||||
{
|
std::integral_constant<bool, isRequest>{});
|
||||||
h_left_ -= s.size();
|
|
||||||
call_on_method(ec, s, std::integral_constant<bool,
|
|
||||||
isRequest && has_on_method<Derived>::value>{});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ec = parse_error::headers_too_big;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_uri(error_code& ec,
|
void call_on_uri(error_code& ec,
|
||||||
boost::string_ref const& s, std::true_type)
|
boost::string_ref const& s, std::true_type)
|
||||||
{
|
{
|
||||||
|
static_assert(check_on_uri<Derived>::value,
|
||||||
|
"on_uri requirements not met");
|
||||||
|
if(h_max_ && s.size() > h_left_)
|
||||||
|
{
|
||||||
|
ec = parse_error::header_too_big;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
h_left_ -= s.size();
|
||||||
impl().on_uri(s, ec);
|
impl().on_uri(s, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -728,23 +714,24 @@ private:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_uri(error_code& ec, boost::string_ref const& s)
|
void call_on_uri(error_code& ec,
|
||||||
|
boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
if(! h_max_ || s.size() <= h_left_)
|
call_on_uri(ec, s,
|
||||||
{
|
std::integral_constant<bool, isRequest>{});
|
||||||
h_left_ -= s.size();
|
|
||||||
call_on_uri(ec, s, std::integral_constant<bool,
|
|
||||||
isRequest && has_on_uri<Derived>::value>{});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ec = parse_error::headers_too_big;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_reason(error_code& ec,
|
void call_on_reason(error_code& ec,
|
||||||
boost::string_ref const& s, std::true_type)
|
boost::string_ref const& s, std::true_type)
|
||||||
{
|
{
|
||||||
|
static_assert(check_on_reason<Derived>::value,
|
||||||
|
"on_reason requirements not met");
|
||||||
|
if(h_max_ && s.size() > h_left_)
|
||||||
|
{
|
||||||
|
ec = parse_error::header_too_big;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
h_left_ -= s.size();
|
||||||
impl().on_reason(s, ec);
|
impl().on_reason(s, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -755,20 +742,14 @@ private:
|
|||||||
|
|
||||||
void call_on_reason(error_code& ec, boost::string_ref const& s)
|
void call_on_reason(error_code& ec, boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
if(! h_max_ || s.size() <= h_left_)
|
call_on_reason(ec, s,
|
||||||
{
|
std::integral_constant<bool, ! isRequest>{});
|
||||||
h_left_ -= s.size();
|
|
||||||
call_on_reason(ec, s, std::integral_constant<bool,
|
|
||||||
! isRequest && has_on_reason<Derived>::value>{});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ec = parse_error::headers_too_big;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_request(error_code& ec, std::true_type)
|
void call_on_request(error_code& ec, std::true_type)
|
||||||
{
|
{
|
||||||
|
static_assert(check_on_request<Derived>::value,
|
||||||
|
"on_request requirements not met");
|
||||||
impl().on_request(ec);
|
impl().on_request(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -778,12 +759,14 @@ private:
|
|||||||
|
|
||||||
void call_on_request(error_code& ec)
|
void call_on_request(error_code& ec)
|
||||||
{
|
{
|
||||||
call_on_request(ec, std::integral_constant<bool,
|
call_on_request(ec,
|
||||||
isRequest && has_on_request<Derived>::value>{});
|
std::integral_constant<bool, isRequest>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_response(error_code& ec, std::true_type)
|
void call_on_response(error_code& ec, std::true_type)
|
||||||
{
|
{
|
||||||
|
static_assert(check_on_response<Derived>::value,
|
||||||
|
"on_response requirements not met");
|
||||||
impl().on_response(ec);
|
impl().on_response(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -793,111 +776,73 @@ private:
|
|||||||
|
|
||||||
void call_on_response(error_code& ec)
|
void call_on_response(error_code& ec)
|
||||||
{
|
{
|
||||||
call_on_response(ec, std::integral_constant<bool,
|
call_on_response(ec,
|
||||||
! isRequest && has_on_response<Derived>::value>{});
|
std::integral_constant<bool, ! isRequest>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_field(error_code& ec,
|
void call_on_field(error_code& ec,
|
||||||
boost::string_ref const& s, std::true_type)
|
boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
|
static_assert(check_on_field<Derived>::value,
|
||||||
|
"on_field requirements not met");
|
||||||
|
if(h_max_ && s.size() > h_left_)
|
||||||
|
{
|
||||||
|
ec = parse_error::header_too_big;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
h_left_ -= s.size();
|
||||||
impl().on_field(s, ec);
|
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)
|
|
||||||
{
|
|
||||||
if(! h_max_ || s.size() <= h_left_)
|
|
||||||
{
|
|
||||||
h_left_ -= s.size();
|
|
||||||
call_on_field(ec, s, has_on_field<Derived>{});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ec = parse_error::headers_too_big;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void call_on_value(error_code& ec,
|
void call_on_value(error_code& ec,
|
||||||
boost::string_ref const& s, std::true_type)
|
boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
|
static_assert(check_on_value<Derived>::value,
|
||||||
|
"on_value requirements not met");
|
||||||
|
if(h_max_ && s.size() > h_left_)
|
||||||
|
{
|
||||||
|
ec = parse_error::header_too_big;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
h_left_ -= s.size();
|
||||||
impl().on_value(s, ec);
|
impl().on_value(s, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_value(error_code&,
|
void
|
||||||
boost::string_ref const&, std::false_type)
|
call_on_headers(error_code& ec)
|
||||||
{
|
{
|
||||||
|
static_assert(check_on_headers<Derived>::value,
|
||||||
|
"on_header requirements not met");
|
||||||
|
impl().on_header(content_length_, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_value(error_code& ec, boost::string_ref const& s)
|
body_what
|
||||||
|
call_on_body_what(error_code& ec)
|
||||||
{
|
{
|
||||||
if(! h_max_ || s.size() <= h_left_)
|
static_assert(check_on_body_what<Derived>::value,
|
||||||
{
|
"on_body_what requirements not met");
|
||||||
h_left_ -= s.size();
|
return impl().on_body_what(content_length_, ec);
|
||||||
call_on_value(ec, s, has_on_value<Derived>{});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ec = parse_error::headers_too_big;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int call_on_headers(error_code& ec,
|
|
||||||
std::uint64_t content_length, std::true_type)
|
|
||||||
{
|
|
||||||
return impl().on_headers(content_length, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
int call_on_headers(error_code& ec, std::uint64_t, std::false_type)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int call_on_headers(error_code& ec)
|
|
||||||
{
|
|
||||||
return call_on_headers(ec, content_length_,
|
|
||||||
has_on_headers<Derived>{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_body(error_code& ec,
|
void call_on_body(error_code& ec,
|
||||||
boost::string_ref const& s, std::true_type)
|
boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
impl().on_body(s, ec);
|
static_assert(check_on_body<Derived>::value,
|
||||||
}
|
"on_body requirements not met");
|
||||||
|
if(b_max_ && s.size() > b_left_)
|
||||||
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)
|
|
||||||
{
|
|
||||||
if(! b_max_ || s.size() <= b_left_)
|
|
||||||
{
|
|
||||||
b_left_ -= s.size();
|
|
||||||
call_on_body(ec, s, has_on_body<Derived>{});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
ec = parse_error::body_too_big;
|
ec = parse_error::body_too_big;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
b_left_ -= s.size();
|
||||||
|
impl().on_body(s, ec);
|
||||||
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)
|
void call_on_complete(error_code& ec)
|
||||||
{
|
{
|
||||||
call_on_complete(ec, has_on_complete<Derived>{});
|
static_assert(check_on_complete<Derived>::value,
|
||||||
|
"on_complete requirements not met");
|
||||||
|
impl().on_complete(ec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_BODY_TYPE_HPP
|
|
||||||
#define BEAST_HTTP_BODY_TYPE_HPP
|
|
||||||
|
|
||||||
// Convenience header to include everything
|
|
||||||
// needed when declarating a user defined Body type.
|
|
||||||
|
|
||||||
#include <beast/core/error.hpp>
|
|
||||||
#include <beast/http/message.hpp>
|
|
||||||
#include <beast/http/resume_context.hpp>
|
|
||||||
#include <boost/logic/tribool.hpp>
|
|
||||||
|
|
||||||
#endif
|
|
||||||
74
include/beast/http/chunk_encode.hpp
Normal file
74
include/beast/http/chunk_encode.hpp
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
//
|
||||||
|
// 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 <beast/core/buffer_cat.hpp>
|
||||||
|
#include <beast/http/detail/chunk_encode.hpp>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/assert.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iterator>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** Returns a chunk-encoded ConstBufferSequence.
|
||||||
|
|
||||||
|
This returns a buffer sequence representing the
|
||||||
|
first chunk of a chunked transfer coded body.
|
||||||
|
|
||||||
|
@param fin `true` if this is the last chunk.
|
||||||
|
|
||||||
|
@param buffers The input buffer sequence.
|
||||||
|
|
||||||
|
@return A chunk-encoded ConstBufferSequence representing the input.
|
||||||
|
|
||||||
|
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
|
||||||
|
*/
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
implementation_defined
|
||||||
|
#else
|
||||||
|
beast::detail::buffer_cat_helper<
|
||||||
|
detail::chunk_encode_delim,
|
||||||
|
ConstBufferSequence,
|
||||||
|
boost::asio::const_buffers_1>
|
||||||
|
#endif
|
||||||
|
chunk_encode(bool fin, ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
return buffer_cat(
|
||||||
|
detail::chunk_encode_delim{buffer_size(buffers)},
|
||||||
|
buffers,
|
||||||
|
fin ? boost::asio::const_buffers_1{"\r\n0\r\n\r\n", 7}
|
||||||
|
: boost::asio::const_buffers_1{"\r\n", 2});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a chunked encoding final chunk.
|
||||||
|
|
||||||
|
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
|
||||||
|
*/
|
||||||
|
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
|
||||||
@@ -9,7 +9,10 @@
|
|||||||
#define BEAST_HTTP_TYPE_CHECK_HPP
|
#define BEAST_HTTP_TYPE_CHECK_HPP
|
||||||
|
|
||||||
#include <beast/core/error.hpp>
|
#include <beast/core/error.hpp>
|
||||||
|
#include <beast/core/detail/type_traits.hpp>
|
||||||
|
#include <beast/http/resume_context.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/logic/tribool.hpp>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -18,65 +21,107 @@ namespace http {
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<class T>
|
struct write_function
|
||||||
class has_value_type
|
|
||||||
{
|
{
|
||||||
template<class U, class R =
|
template<class ConstBufferSequence>
|
||||||
typename U::value_type>
|
void
|
||||||
static std::true_type check(int);
|
operator()(ConstBufferSequence const&);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct has_value_type : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct has_value_type<T, beast::detail::void_t<
|
||||||
|
typename T::value_type
|
||||||
|
> > : std::true_type {};
|
||||||
|
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct has_content_length : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct has_content_length<T, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().content_length()
|
||||||
|
)> > : std::true_type
|
||||||
|
{
|
||||||
|
static_assert(std::is_convertible<
|
||||||
|
decltype(std::declval<T>().content_length()),
|
||||||
|
std::uint64_t>::value,
|
||||||
|
"Writer::content_length requirements not met");
|
||||||
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
template<class T, class M, class = beast::detail::void_t<>>
|
||||||
|
struct is_Writer : std::false_type {};
|
||||||
|
|
||||||
|
template<class T, class M>
|
||||||
|
struct is_Writer<T, M, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().init(
|
||||||
|
std::declval<error_code&>())
|
||||||
|
// VFALCO This is unfortunate, we have to provide the template
|
||||||
|
// argument type because this is not a deduced context?
|
||||||
|
//
|
||||||
|
,std::declval<T>().template write<detail::write_function>(
|
||||||
|
std::declval<resume_context>(),
|
||||||
|
std::declval<error_code&>(),
|
||||||
|
std::declval<detail::write_function>())
|
||||||
|
)> > : std::integral_constant<bool,
|
||||||
|
std::is_nothrow_constructible<T, M const&>::value &&
|
||||||
|
std::is_convertible<decltype(
|
||||||
|
std::declval<T>().template write<detail::write_function>(
|
||||||
|
std::declval<resume_context>(),
|
||||||
|
std::declval<error_code&>(),
|
||||||
|
std::declval<detail::write_function>())),
|
||||||
|
boost::tribool>::value
|
||||||
|
>
|
||||||
|
{
|
||||||
|
static_assert(std::is_same<
|
||||||
|
typename M::body_type::writer, T>::value,
|
||||||
|
"Mismatched writer and message");
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template<class T, class M>
|
||||||
|
class is_Writer
|
||||||
|
{
|
||||||
|
template<class U, class R = decltype(
|
||||||
|
std::declval<U>().init(std::declval<error_code&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check1(int);
|
||||||
template<class>
|
template<class>
|
||||||
static std::false_type check(...);
|
static std::false_type check1(...);
|
||||||
using type = decltype(check<T>(0));
|
using type1 = decltype(check1<T>(0));
|
||||||
public:
|
|
||||||
static bool constexpr value = type::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T, bool B = has_value_type<T>::value>
|
// VFALCO This is unfortunate, we have to provide the template
|
||||||
struct extract_value_type
|
// argument type because this is not a deduced context?
|
||||||
{
|
//
|
||||||
using type = void;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
struct extract_value_type<T, true>
|
|
||||||
{
|
|
||||||
using type = typename T::value_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
class has_reader
|
|
||||||
{
|
|
||||||
template<class U, class R =
|
template<class U, class R =
|
||||||
typename U::reader>
|
std::is_convertible<decltype(
|
||||||
static std::true_type check(int);
|
std::declval<U>().template write<detail::write_function>(
|
||||||
|
std::declval<resume_context>(),
|
||||||
|
std::declval<error_code&>(),
|
||||||
|
std::declval<detail::write_function>()))
|
||||||
|
, boost::tribool>>
|
||||||
|
static R check2(int);
|
||||||
template<class>
|
template<class>
|
||||||
static std::false_type check(...);
|
static std::false_type check2(...);
|
||||||
public:
|
using type2 = decltype(check2<T>(0));
|
||||||
using type = decltype(check<T>(0));
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
class has_writer
|
|
||||||
{
|
|
||||||
template<class U, class R =
|
|
||||||
typename U::writer>
|
|
||||||
static std::true_type check(int);
|
|
||||||
template<class>
|
|
||||||
static std::false_type check(...);
|
|
||||||
public:
|
public:
|
||||||
using type = decltype(check<T>(0));
|
static_assert(std::is_same<
|
||||||
};
|
typename M::body_type::writer, T>::value,
|
||||||
|
"Mismatched writer and message");
|
||||||
|
|
||||||
template<class T>
|
|
||||||
struct is_Body
|
|
||||||
{
|
|
||||||
using type = std::integral_constant<bool,
|
using type = std::integral_constant<bool,
|
||||||
has_value_type<T>::value &&
|
std::is_nothrow_constructible<T, M const&>::value
|
||||||
std::is_default_constructible<
|
&& type1::value
|
||||||
typename extract_value_type<T>::type>::value
|
&& type2::value
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
class is_Parser
|
class is_Parser
|
||||||
{
|
{
|
||||||
@@ -92,7 +137,7 @@ class is_Parser
|
|||||||
template<class U, class R =
|
template<class U, class R =
|
||||||
std::is_convertible<decltype(
|
std::is_convertible<decltype(
|
||||||
std::declval<U>().write(
|
std::declval<U>().write(
|
||||||
std::declval<boost::asio::const_buffer const&>(),
|
std::declval<boost::asio::const_buffers_1 const&>(),
|
||||||
std::declval<error_code&>())),
|
std::declval<error_code&>())),
|
||||||
std::size_t>>
|
std::size_t>>
|
||||||
static R check2(int);
|
static R check2(int);
|
||||||
@@ -118,31 +163,97 @@ public:
|
|||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
|
||||||
/// Determine if `T` meets the requirements of `Body`.
|
/// Determine if `T` meets the requirements of @b Body.
|
||||||
template<class T>
|
template<class T>
|
||||||
#if GENERATING_DOCS
|
#if GENERATING_DOCS
|
||||||
struct is_Body : std::integral_constant<bool, ...>{};
|
struct is_Body : std::integral_constant<bool, ...>{};
|
||||||
#else
|
#else
|
||||||
using is_Body = typename detail::is_Body<T>::type;
|
using is_Body = detail::has_value_type<T>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Determine if `T` meets the requirements of `ReadableBody`.
|
/** Determine if a @b Body has a nested type `reader`.
|
||||||
template<class T>
|
|
||||||
|
@tparam T The type to check, which must meet the
|
||||||
|
requirements of @b Body.
|
||||||
|
*/
|
||||||
#if GENERATING_DOCS
|
#if GENERATING_DOCS
|
||||||
struct is_ReadableBody : std::integral_constant<bool, ...>{};
|
|
||||||
#else
|
|
||||||
using is_ReadableBody = typename detail::has_reader<T>::type;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Determine if `T` meets the requirements of `WritableBody`.
|
|
||||||
template<class T>
|
template<class T>
|
||||||
#if GENERATING_DOCS
|
struct has_reader : std::integral_constant<bool, ...>{};
|
||||||
struct is_WritableBody : std::integral_constant<bool, ...>{};
|
|
||||||
#else
|
#else
|
||||||
using is_WritableBody = typename detail::has_writer<T>::type;
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct has_reader : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct has_reader<T, beast::detail::void_t<
|
||||||
|
typename T::reader
|
||||||
|
> > : std::true_type {};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Determine if `T` meets the requirements of `Parser`.
|
/** Determine if a @b Body has a nested type `writer`.
|
||||||
|
|
||||||
|
@tparam T The type to check, which must meet the
|
||||||
|
requirements of @b Body.
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
template<class T>
|
||||||
|
struct has_writer : std::integral_constant<bool, ...>{};
|
||||||
|
#else
|
||||||
|
template<class T, class = beast::detail::void_t<>>
|
||||||
|
struct has_writer : std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct has_writer<T, beast::detail::void_t<
|
||||||
|
typename T::writer
|
||||||
|
> > : std::true_type {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Determine if `T` meets the requirements of @b Reader for `M`.
|
||||||
|
|
||||||
|
@tparam T The type to test.
|
||||||
|
|
||||||
|
@tparam M The message type to test with, which must be of
|
||||||
|
type `message`.
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
template<class T, class M>
|
||||||
|
struct is_Reader : std::integral_constant<bool, ...> {};
|
||||||
|
#else
|
||||||
|
template<class T, class M, class = beast::detail::void_t<>>
|
||||||
|
struct is_Reader : std::false_type {};
|
||||||
|
|
||||||
|
template<class T, class M>
|
||||||
|
struct is_Reader<T, M, beast::detail::void_t<decltype(
|
||||||
|
std::declval<T>().init(
|
||||||
|
std::declval<error_code&>()),
|
||||||
|
std::declval<T>().write(
|
||||||
|
std::declval<void const*>(),
|
||||||
|
std::declval<std::size_t>(),
|
||||||
|
std::declval<error_code&>())
|
||||||
|
)> > : std::integral_constant<bool,
|
||||||
|
std::is_nothrow_constructible<T, M&>::value
|
||||||
|
>
|
||||||
|
{
|
||||||
|
static_assert(std::is_same<
|
||||||
|
typename M::body_type::reader, T>::value,
|
||||||
|
"Mismatched reader and message");
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Determine if `T` meets the requirements of @b Writer for `M`.
|
||||||
|
|
||||||
|
@tparam T The type to test.
|
||||||
|
|
||||||
|
@tparam M The message type to test with, which must be of
|
||||||
|
type `message`.
|
||||||
|
*/
|
||||||
|
template<class T, class M>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
struct is_Writer : std::integral_constant<bool, ...> {};
|
||||||
|
#else
|
||||||
|
using is_Writer = typename detail::is_Writer<T, M>::type;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Determine if `T` meets the requirements of @b Parser.
|
||||||
template<class T>
|
template<class T>
|
||||||
#if GENERATING_DOCS
|
#if GENERATING_DOCS
|
||||||
struct is_Parser : std::integral_constant<bool, ...>{};
|
struct is_Parser : std::integral_constant<bool, ...>{};
|
||||||
|
|||||||
214
include/beast/http/detail/basic_fields.hpp
Normal file
214
include/beast/http/detail/basic_fields.hpp
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
//
|
||||||
|
// 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_FIELDS_HPP
|
||||||
|
#define BEAST_HTTP_DETAIL_BASIC_FIELDS_HPP
|
||||||
|
|
||||||
|
#include <beast/core/detail/ci_char_traits.hpp>
|
||||||
|
#include <boost/intrusive/list.hpp>
|
||||||
|
#include <boost/intrusive/set.hpp>
|
||||||
|
#include <boost/utility/string_ref.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
class basic_fields;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class basic_fields_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_fields;
|
||||||
|
|
||||||
|
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_multiset<
|
||||||
|
element, boost::intrusive::constant_time_size<true>,
|
||||||
|
boost::intrusive::compare<less>>::type;
|
||||||
|
|
||||||
|
// data
|
||||||
|
set_t set_;
|
||||||
|
list_t list_;
|
||||||
|
|
||||||
|
basic_fields_base(set_t&& set, list_t&& list)
|
||||||
|
: set_(std::move(set))
|
||||||
|
, list_(std::move(list))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
class const_iterator;
|
||||||
|
|
||||||
|
using iterator = const_iterator;
|
||||||
|
|
||||||
|
basic_fields_base() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class basic_fields_base::const_iterator
|
||||||
|
{
|
||||||
|
using iter_type = list_t::const_iterator;
|
||||||
|
|
||||||
|
iter_type it_;
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
friend class beast::http::basic_fields;
|
||||||
|
|
||||||
|
friend class basic_fields_base;
|
||||||
|
|
||||||
|
const_iterator(iter_type it)
|
||||||
|
: it_(it)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type =
|
||||||
|
typename basic_fields_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
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -8,9 +8,6 @@
|
|||||||
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
|
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
|
||||||
#define BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
|
#define BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
|
||||||
|
|
||||||
#include <boost/system/error_code.hpp>
|
|
||||||
#include <boost/utility/string_ref.hpp>
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
@@ -130,6 +127,7 @@ protected:
|
|||||||
s_chunk_data_cr,
|
s_chunk_data_cr,
|
||||||
s_chunk_data_lf,
|
s_chunk_data_lf,
|
||||||
|
|
||||||
|
s_body_pause,
|
||||||
s_body_identity0,
|
s_body_identity0,
|
||||||
s_body_identity,
|
s_body_identity,
|
||||||
s_body_identity_eof0,
|
s_body_identity_eof0,
|
||||||
|
|||||||
@@ -8,27 +8,30 @@
|
|||||||
#ifndef BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
|
#ifndef BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
|
||||||
#define BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
|
#define BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
|
||||||
|
|
||||||
#include <beast/core/buffer_cat.hpp>
|
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
#include <boost/logic/tribool.hpp>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cassert>
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <iterator>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class chunk_encode_text
|
class chunk_encode_delim
|
||||||
{
|
{
|
||||||
boost::asio::const_buffer cb_;
|
boost::asio::const_buffer cb_;
|
||||||
|
|
||||||
// Storage for the longest hex string we might need, plus delimiters.
|
// Storage for the longest hex string we might need, plus delimiters.
|
||||||
std::array<char, 2 * sizeof(std::size_t) + 2> buf_;
|
std::array<char, 2 * sizeof(std::size_t) + 2> buf_;
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
copy(chunk_encode_delim const& other);
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
setup(std::size_t n);
|
||||||
|
|
||||||
template<class OutIter>
|
template<class OutIter>
|
||||||
static
|
static
|
||||||
OutIter
|
OutIter
|
||||||
@@ -52,24 +55,15 @@ public:
|
|||||||
|
|
||||||
using const_iterator = value_type const*;
|
using const_iterator = value_type const*;
|
||||||
|
|
||||||
chunk_encode_text(chunk_encode_text const& other)
|
chunk_encode_delim(chunk_encode_delim const& other)
|
||||||
{
|
{
|
||||||
auto const n =
|
copy(other);
|
||||||
boost::asio::buffer_size(other.cb_);
|
|
||||||
buf_ = other.buf_;
|
|
||||||
cb_ = boost::asio::const_buffer(
|
|
||||||
&buf_[buf_.size() - n], n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit
|
explicit
|
||||||
chunk_encode_text(std::size_t n)
|
chunk_encode_delim(std::size_t n)
|
||||||
{
|
{
|
||||||
buf_[buf_.size() - 2] = '\r';
|
setup(n);
|
||||||
buf_[buf_.size() - 1] = '\n';
|
|
||||||
auto it = to_hex(buf_.end() - 2, n);
|
|
||||||
cb_ = boost::asio::const_buffer{&*it,
|
|
||||||
static_cast<std::size_t>(
|
|
||||||
std::distance(it, buf_.end()))};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const_iterator
|
const_iterator
|
||||||
@@ -85,44 +79,29 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Returns a chunk-encoded ConstBufferSequence.
|
template<class>
|
||||||
|
void
|
||||||
This returns a buffer sequence representing the
|
chunk_encode_delim::
|
||||||
first chunk of a chunked transfer coded body.
|
copy(chunk_encode_delim const& other)
|
||||||
|
|
||||||
@param buffers The input buffer sequence.
|
|
||||||
|
|
||||||
@return A chunk-encoded ConstBufferSequence representing the input.
|
|
||||||
|
|
||||||
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
|
|
||||||
*/
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
#if GENERATING_DOCS
|
|
||||||
implementation_defined
|
|
||||||
#else
|
|
||||||
beast::detail::buffer_cat_helper<boost::asio::const_buffer,
|
|
||||||
chunk_encode_text, ConstBufferSequence, boost::asio::const_buffers_1>
|
|
||||||
#endif
|
|
||||||
chunk_encode(ConstBufferSequence const& buffers)
|
|
||||||
{
|
{
|
||||||
using boost::asio::buffer_size;
|
auto const n =
|
||||||
return buffer_cat(
|
boost::asio::buffer_size(other.cb_);
|
||||||
chunk_encode_text{buffer_size(buffers)},
|
buf_ = other.buf_;
|
||||||
buffers,
|
cb_ = boost::asio::const_buffer(
|
||||||
boost::asio::const_buffers_1{"\r\n", 2});
|
&buf_[buf_.size() - n], n);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a chunked encoding final chunk.
|
template<class>
|
||||||
inline
|
void
|
||||||
#if GENERATING_DOCS
|
chunk_encode_delim::
|
||||||
implementation_defined
|
setup(std::size_t n)
|
||||||
#else
|
|
||||||
boost::asio::const_buffers_1
|
|
||||||
#endif
|
|
||||||
chunk_encode_final()
|
|
||||||
{
|
{
|
||||||
return boost::asio::const_buffers_1(
|
buf_[buf_.size() - 2] = '\r';
|
||||||
"0\r\n\r\n", 5);
|
buf_[buf_.size() - 1] = '\n';
|
||||||
|
auto it = to_hex(buf_.end() - 2, n);
|
||||||
|
cb_ = boost::asio::const_buffer{&*it,
|
||||||
|
static_cast<std::size_t>(
|
||||||
|
std::distance(it, buf_.end()))};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_DETAIL_HAS_CONTENT_LENGTH_HPP
|
|
||||||
#define BEAST_HTTP_DETAIL_HAS_CONTENT_LENGTH_HPP
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
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::uint64_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>;
|
|
||||||
|
|
||||||
} // detail
|
|
||||||
} // http
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -233,6 +233,14 @@ skip_ows(FwdIt& it, FwdIt const& end)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class FwdIt>
|
||||||
|
void
|
||||||
|
skip_token(FwdIt& it, FwdIt const& last)
|
||||||
|
{
|
||||||
|
while(it != last && is_tchar(*it))
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
boost::string_ref
|
boost::string_ref
|
||||||
trim(boost::string_ref const& s)
|
trim(boost::string_ref const& s)
|
||||||
@@ -258,14 +266,14 @@ struct param_iter
|
|||||||
using iter_type = boost::string_ref::const_iterator;
|
using iter_type = boost::string_ref::const_iterator;
|
||||||
|
|
||||||
iter_type it;
|
iter_type it;
|
||||||
iter_type begin;
|
iter_type first;
|
||||||
iter_type end;
|
iter_type last;
|
||||||
std::pair<boost::string_ref, boost::string_ref> v;
|
std::pair<boost::string_ref, boost::string_ref> v;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
empty() const
|
empty() const
|
||||||
{
|
{
|
||||||
return begin == it;
|
return first == it;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class = void>
|
template<class = void>
|
||||||
@@ -279,59 +287,48 @@ param_iter::
|
|||||||
increment()
|
increment()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
param-list = *( OWS ";" OWS param )
|
||||||
ext = token param-list
|
param = token OWS [ "=" OWS ( token / quoted-string ) ]
|
||||||
param-list = *( OWS ";" OWS param )
|
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||||
param = token OWS "=" OWS ( token / quoted-string )
|
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||||
|
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||||
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
obs-text = %x80-FF
|
||||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
|
||||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
|
||||||
obs-text = %x80-FF
|
|
||||||
|
|
||||||
Example:
|
|
||||||
chunked;a=b;i=j,gzip;windowBits=12
|
|
||||||
x,y
|
|
||||||
*/
|
*/
|
||||||
auto const err =
|
auto const err =
|
||||||
[&]
|
[&]
|
||||||
{
|
{
|
||||||
it = begin;
|
it = first;
|
||||||
};
|
};
|
||||||
v.first.clear();
|
v.first.clear();
|
||||||
v.second.clear();
|
v.second.clear();
|
||||||
detail::skip_ows(it, end);
|
detail::skip_ows(it, last);
|
||||||
begin = it;
|
first = it;
|
||||||
if(it == end)
|
if(it == last)
|
||||||
return err();
|
return err();
|
||||||
if(*it != ';')
|
if(*it != ';')
|
||||||
return err();
|
return err();
|
||||||
++it;
|
++it;
|
||||||
detail::skip_ows(it, end);
|
detail::skip_ows(it, last);
|
||||||
if(it == end)
|
if(it == last)
|
||||||
return err();
|
return err();
|
||||||
// param
|
// param
|
||||||
if(! detail::is_tchar(*it))
|
if(! detail::is_tchar(*it))
|
||||||
return err();
|
return err();
|
||||||
auto const p0 = it;
|
auto const p0 = it;
|
||||||
for(;;)
|
skip_token(++it, last);
|
||||||
{
|
|
||||||
++it;
|
|
||||||
if(it == end)
|
|
||||||
return err();
|
|
||||||
if(! detail::is_tchar(*it))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto const p1 = it;
|
auto const p1 = it;
|
||||||
detail::skip_ows(it, end);
|
v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
|
||||||
if(it == end)
|
detail::skip_ows(it, last);
|
||||||
return err();
|
if(it == last)
|
||||||
|
return;
|
||||||
|
if(*it == ';')
|
||||||
|
return;
|
||||||
if(*it != '=')
|
if(*it != '=')
|
||||||
return err();
|
return err();
|
||||||
++it;
|
++it;
|
||||||
detail::skip_ows(it, end);
|
detail::skip_ows(it, last);
|
||||||
if(it == end)
|
if(it == last)
|
||||||
return err();
|
return;
|
||||||
if(*it == '"')
|
if(*it == '"')
|
||||||
{
|
{
|
||||||
// quoted-string
|
// quoted-string
|
||||||
@@ -339,7 +336,7 @@ increment()
|
|||||||
++it;
|
++it;
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
if(it == end)
|
if(it == last)
|
||||||
return err();
|
return err();
|
||||||
auto c = *it++;
|
auto c = *it++;
|
||||||
if(c == '"')
|
if(c == '"')
|
||||||
@@ -348,13 +345,12 @@ increment()
|
|||||||
continue;
|
continue;
|
||||||
if(c != '\\')
|
if(c != '\\')
|
||||||
return err();
|
return err();
|
||||||
if(it == end)
|
if(it == last)
|
||||||
return err();
|
return err();
|
||||||
c = *it++;
|
c = *it++;
|
||||||
if(! detail::is_qpchar(c))
|
if(! detail::is_qpchar(c))
|
||||||
return err();
|
return err();
|
||||||
}
|
}
|
||||||
v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
|
|
||||||
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
|
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -363,15 +359,7 @@ increment()
|
|||||||
if(! detail::is_tchar(*it))
|
if(! detail::is_tchar(*it))
|
||||||
return err();
|
return err();
|
||||||
auto const p2 = it;
|
auto const p2 = it;
|
||||||
for(;;)
|
skip_token(++it, last);
|
||||||
{
|
|
||||||
it++;
|
|
||||||
if(it == end)
|
|
||||||
break;
|
|
||||||
if(! detail::is_tchar(*it))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
|
|
||||||
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
|
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,12 @@
|
|||||||
#ifndef BEAST_HTTP_EMPTY_BODY_HPP
|
#ifndef BEAST_HTTP_EMPTY_BODY_HPP
|
||||||
#define BEAST_HTTP_EMPTY_BODY_HPP
|
#define BEAST_HTTP_EMPTY_BODY_HPP
|
||||||
|
|
||||||
#include <beast/http/body_type.hpp>
|
#include <beast/core/error.hpp>
|
||||||
|
#include <beast/http/message.hpp>
|
||||||
|
#include <beast/http/resume_context.hpp>
|
||||||
|
#include <beast/core/detail/type_traits.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/logic/tribool.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -35,31 +39,31 @@ private:
|
|||||||
|
|
||||||
struct writer
|
struct writer
|
||||||
{
|
{
|
||||||
writer(writer const&) = delete;
|
template<bool isRequest, class Fields>
|
||||||
writer& operator=(writer const&) = delete;
|
|
||||||
|
|
||||||
template<bool isRequest, class Headers>
|
|
||||||
explicit
|
explicit
|
||||||
writer(message<isRequest, empty_body, Headers> const& m)
|
writer(message<isRequest, empty_body, Fields> const& m) noexcept
|
||||||
{
|
{
|
||||||
|
beast::detail::ignore_unused(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
init(error_code& ec)
|
init(error_code& ec) noexcept
|
||||||
{
|
{
|
||||||
|
beast::detail::ignore_unused(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint64_t
|
std::uint64_t
|
||||||
content_length() const
|
content_length() const noexcept
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Write>
|
template<class WriteFunction>
|
||||||
boost::tribool
|
boost::tribool
|
||||||
operator()(resume_context&&, error_code&, Write&& write)
|
write(resume_context&&, error_code&,
|
||||||
|
WriteFunction&& wf) noexcept
|
||||||
{
|
{
|
||||||
write(boost::asio::null_buffers{});
|
wf(boost::asio::null_buffers{});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,17 +5,18 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_HEADERS_HPP
|
#ifndef BEAST_HTTP_FIELDS_HPP
|
||||||
#define BEAST_HTTP_HEADERS_HPP
|
#define BEAST_HTTP_FIELDS_HPP
|
||||||
|
|
||||||
#include <beast/http/basic_headers.hpp>
|
#include <beast/http/basic_fields.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
using headers =
|
/// A typical HTTP header fields container
|
||||||
basic_headers<std::allocator<char>>;
|
using fields =
|
||||||
|
basic_fields<std::allocator<char>>;
|
||||||
|
|
||||||
} // http
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
232
include/beast/http/header_parser_v1.hpp
Normal file
232
include/beast/http/header_parser_v1.hpp
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
//
|
||||||
|
// 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_PARSER_V1_HPP
|
||||||
|
#define BEAST_HTTP_HEADERS_PARSER_V1_HPP
|
||||||
|
|
||||||
|
#include <beast/http/basic_parser_v1.hpp>
|
||||||
|
#include <beast/http/concepts.hpp>
|
||||||
|
#include <beast/http/message.hpp>
|
||||||
|
#include <beast/core/error.hpp>
|
||||||
|
#include <boost/assert.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
struct request_parser_base
|
||||||
|
{
|
||||||
|
std::string method_;
|
||||||
|
std::string uri_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct response_parser_base
|
||||||
|
{
|
||||||
|
std::string reason_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
/** A parser for a HTTP/1 request or response header.
|
||||||
|
|
||||||
|
This class uses the HTTP/1 wire format parser to
|
||||||
|
convert a series of octets into a request or
|
||||||
|
response @ref header.
|
||||||
|
|
||||||
|
@note A new instance of the parser is required for each message.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Fields>
|
||||||
|
class header_parser_v1
|
||||||
|
: public basic_parser_v1<isRequest,
|
||||||
|
header_parser_v1<isRequest, Fields>>
|
||||||
|
, private std::conditional<isRequest,
|
||||||
|
detail::request_parser_base,
|
||||||
|
detail::response_parser_base>::type
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The type of the header this parser produces.
|
||||||
|
using header_type = header<isRequest, Fields>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// VFALCO Check Fields requirements?
|
||||||
|
|
||||||
|
std::string field_;
|
||||||
|
std::string value_;
|
||||||
|
header_type h_;
|
||||||
|
bool flush_ = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Default constructor
|
||||||
|
header_parser_v1() = default;
|
||||||
|
|
||||||
|
/// Move constructor
|
||||||
|
header_parser_v1(header_parser_v1&&) = default;
|
||||||
|
|
||||||
|
/// Copy constructor (disallowed)
|
||||||
|
header_parser_v1(header_parser_v1 const&) = delete;
|
||||||
|
|
||||||
|
/// Move assignment (disallowed)
|
||||||
|
header_parser_v1& operator=(header_parser_v1&&) = delete;
|
||||||
|
|
||||||
|
/// Copy assignment (disallowed)
|
||||||
|
header_parser_v1& operator=(header_parser_v1 const&) = delete;
|
||||||
|
|
||||||
|
/** Construct the parser.
|
||||||
|
|
||||||
|
@param args Forwarded to the header constructor.
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
template<class... Args>
|
||||||
|
explicit
|
||||||
|
header_parser_v1(Args&&... args);
|
||||||
|
#else
|
||||||
|
template<class Arg1, class... ArgN,
|
||||||
|
class = typename std::enable_if<! std::is_same<
|
||||||
|
typename std::decay<Arg1>::type, header_parser_v1>::value>>
|
||||||
|
explicit
|
||||||
|
header_parser_v1(Arg1&& arg1, ArgN&&... argn)
|
||||||
|
: h_(std::forward<Arg1>(arg1),
|
||||||
|
std::forward<ArgN>(argn)...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Returns the parsed header
|
||||||
|
|
||||||
|
Only valid if @ref complete would return `true`.
|
||||||
|
*/
|
||||||
|
header_type const&
|
||||||
|
get() const
|
||||||
|
{
|
||||||
|
return h_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the parsed header.
|
||||||
|
|
||||||
|
Only valid if @ref complete would return `true`.
|
||||||
|
*/
|
||||||
|
header_type&
|
||||||
|
get()
|
||||||
|
{
|
||||||
|
return h_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns ownership of the parsed header.
|
||||||
|
|
||||||
|
Ownership is transferred to the caller. Only
|
||||||
|
valid if @ref complete would return `true`.
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
@ref header_type is @b MoveConstructible
|
||||||
|
*/
|
||||||
|
header_type
|
||||||
|
release()
|
||||||
|
{
|
||||||
|
static_assert(std::is_move_constructible<decltype(h_)>::value,
|
||||||
|
"MoveConstructible requirements not met");
|
||||||
|
return std::move(h_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class basic_parser_v1<isRequest, header_parser_v1>;
|
||||||
|
|
||||||
|
void flush()
|
||||||
|
{
|
||||||
|
if(! flush_)
|
||||||
|
return;
|
||||||
|
flush_ = false;
|
||||||
|
BOOST_ASSERT(! field_.empty());
|
||||||
|
h_.fields.insert(field_, value_);
|
||||||
|
field_.clear();
|
||||||
|
value_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_start(error_code&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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_request_or_response(std::true_type)
|
||||||
|
{
|
||||||
|
h_.method = std::move(this->method_);
|
||||||
|
h_.url = std::move(this->uri_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_request_or_response(std::false_type)
|
||||||
|
{
|
||||||
|
h_.status = this->status_code();
|
||||||
|
h_.reason = std::move(this->reason_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_request(error_code& ec)
|
||||||
|
{
|
||||||
|
on_request_or_response(
|
||||||
|
std::integral_constant<bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_response(error_code& ec)
|
||||||
|
{
|
||||||
|
on_request_or_response(
|
||||||
|
std::integral_constant<bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
flush_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_header(std::uint64_t, error_code&)
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
h_.version = 10 * this->http_major() + this->http_minor();
|
||||||
|
}
|
||||||
|
|
||||||
|
body_what
|
||||||
|
on_body_what(std::uint64_t, error_code&)
|
||||||
|
{
|
||||||
|
return body_what::pause;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_body(boost::string_ref const&, error_code&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_complete(error_code&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
|
#ifndef BEAST_HTTP_IMPL_BASIC_FIELDS_IPP
|
||||||
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
|
#define BEAST_HTTP_IMPL_BASIC_FIELDS_IPP
|
||||||
|
|
||||||
#include <beast/http/detail/rfc7230.hpp>
|
#include <beast/http/detail/rfc7230.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -14,47 +14,9 @@
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
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>
|
template<class Allocator>
|
||||||
void
|
void
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
delete_all()
|
delete_all()
|
||||||
{
|
{
|
||||||
for(auto it = list_.begin(); it != list_.end();)
|
for(auto it = list_.begin(); it != list_.end();)
|
||||||
@@ -69,8 +31,8 @@ delete_all()
|
|||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
inline
|
inline
|
||||||
void
|
void
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
move_assign(basic_headers& other, std::false_type)
|
move_assign(basic_fields& other, std::false_type)
|
||||||
{
|
{
|
||||||
if(this->member() != other.member())
|
if(this->member() != other.member())
|
||||||
{
|
{
|
||||||
@@ -87,8 +49,8 @@ move_assign(basic_headers& other, std::false_type)
|
|||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
inline
|
inline
|
||||||
void
|
void
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
move_assign(basic_headers& other, std::true_type)
|
move_assign(basic_fields& other, std::true_type)
|
||||||
{
|
{
|
||||||
this->member() = std::move(other.member());
|
this->member() = std::move(other.member());
|
||||||
set_ = std::move(other.set_);
|
set_ = std::move(other.set_);
|
||||||
@@ -98,8 +60,8 @@ move_assign(basic_headers& other, std::true_type)
|
|||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
inline
|
inline
|
||||||
void
|
void
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
copy_assign(basic_headers const& other, std::false_type)
|
copy_assign(basic_fields const& other, std::false_type)
|
||||||
{
|
{
|
||||||
copy_from(other);
|
copy_from(other);
|
||||||
}
|
}
|
||||||
@@ -107,8 +69,8 @@ copy_assign(basic_headers const& other, std::false_type)
|
|||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
inline
|
inline
|
||||||
void
|
void
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
copy_assign(basic_headers const& other, std::true_type)
|
copy_assign(basic_fields const& other, std::true_type)
|
||||||
{
|
{
|
||||||
this->member() = other.member();
|
this->member() = other.member();
|
||||||
copy_from(other);
|
copy_from(other);
|
||||||
@@ -117,35 +79,35 @@ copy_assign(basic_headers const& other, std::true_type)
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
~basic_headers()
|
~basic_fields()
|
||||||
{
|
{
|
||||||
delete_all();
|
delete_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
basic_headers(Allocator const& alloc)
|
basic_fields(Allocator const& alloc)
|
||||||
: beast::detail::empty_base_optimization<
|
: beast::detail::empty_base_optimization<
|
||||||
alloc_type>(alloc)
|
alloc_type>(alloc)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
basic_headers(basic_headers&& other)
|
basic_fields(basic_fields&& other)
|
||||||
: beast::detail::empty_base_optimization<alloc_type>(
|
: beast::detail::empty_base_optimization<alloc_type>(
|
||||||
std::move(other.member()))
|
std::move(other.member()))
|
||||||
, detail::basic_headers_base(
|
, detail::basic_fields_base(
|
||||||
std::move(other.set_), std::move(other.list_))
|
std::move(other.set_), std::move(other.list_))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
auto
|
auto
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
operator=(basic_headers&& other) ->
|
operator=(basic_fields&& other) ->
|
||||||
basic_headers&
|
basic_fields&
|
||||||
{
|
{
|
||||||
if(this == &other)
|
if(this == &other)
|
||||||
return *this;
|
return *this;
|
||||||
@@ -156,9 +118,9 @@ operator=(basic_headers&& other) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
basic_headers(basic_headers const& other)
|
basic_fields(basic_fields const& other)
|
||||||
: basic_headers(alloc_traits::
|
: basic_fields(alloc_traits::
|
||||||
select_on_container_copy_construction(other.member()))
|
select_on_container_copy_construction(other.member()))
|
||||||
{
|
{
|
||||||
copy_from(other);
|
copy_from(other);
|
||||||
@@ -166,9 +128,9 @@ basic_headers(basic_headers const& other)
|
|||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
auto
|
auto
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
operator=(basic_headers const& other) ->
|
operator=(basic_fields const& other) ->
|
||||||
basic_headers&
|
basic_fields&
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
copy_assign(other, std::integral_constant<bool,
|
copy_assign(other, std::integral_constant<bool,
|
||||||
@@ -178,8 +140,8 @@ operator=(basic_headers const& other) ->
|
|||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
template<class OtherAlloc>
|
template<class OtherAlloc>
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
basic_headers(basic_headers<OtherAlloc> const& other)
|
basic_fields(basic_fields<OtherAlloc> const& other)
|
||||||
{
|
{
|
||||||
copy_from(other);
|
copy_from(other);
|
||||||
}
|
}
|
||||||
@@ -187,9 +149,9 @@ basic_headers(basic_headers<OtherAlloc> const& other)
|
|||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
template<class OtherAlloc>
|
template<class OtherAlloc>
|
||||||
auto
|
auto
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
operator=(basic_headers<OtherAlloc> const& other) ->
|
operator=(basic_fields<OtherAlloc> const& other) ->
|
||||||
basic_headers&
|
basic_fields&
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
copy_from(other);
|
copy_from(other);
|
||||||
@@ -198,8 +160,8 @@ operator=(basic_headers<OtherAlloc> const& other) ->
|
|||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
template<class FwdIt>
|
template<class FwdIt>
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
basic_headers(FwdIt first, FwdIt last)
|
basic_fields(FwdIt first, FwdIt last)
|
||||||
{
|
{
|
||||||
for(;first != last; ++first)
|
for(;first != last; ++first)
|
||||||
insert(first->name(), first->value());
|
insert(first->name(), first->value());
|
||||||
@@ -207,7 +169,7 @@ basic_headers(FwdIt first, FwdIt last)
|
|||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
std::size_t
|
std::size_t
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
count(boost::string_ref const& name) const
|
count(boost::string_ref const& name) const
|
||||||
{
|
{
|
||||||
auto const it = set_.find(name, less{});
|
auto const it = set_.find(name, less{});
|
||||||
@@ -219,7 +181,7 @@ count(boost::string_ref const& name) const
|
|||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
auto
|
auto
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
find(boost::string_ref const& name) const ->
|
find(boost::string_ref const& name) const ->
|
||||||
iterator
|
iterator
|
||||||
{
|
{
|
||||||
@@ -231,7 +193,7 @@ find(boost::string_ref const& name) const ->
|
|||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
boost::string_ref
|
boost::string_ref
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
operator[](boost::string_ref const& name) const
|
operator[](boost::string_ref const& name) const
|
||||||
{
|
{
|
||||||
auto const it = find(name);
|
auto const it = find(name);
|
||||||
@@ -242,7 +204,7 @@ operator[](boost::string_ref const& name) const
|
|||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
void
|
void
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
clear() noexcept
|
clear() noexcept
|
||||||
{
|
{
|
||||||
delete_all();
|
delete_all();
|
||||||
@@ -252,7 +214,7 @@ clear() noexcept
|
|||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
std::size_t
|
std::size_t
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
erase(boost::string_ref const& name)
|
erase(boost::string_ref const& name)
|
||||||
{
|
{
|
||||||
auto it = set_.find(name, less{});
|
auto it = set_.find(name, less{});
|
||||||
@@ -276,7 +238,7 @@ erase(boost::string_ref const& name)
|
|||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
void
|
void
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
insert(boost::string_ref const& name,
|
insert(boost::string_ref const& name,
|
||||||
boost::string_ref value)
|
boost::string_ref value)
|
||||||
{
|
{
|
||||||
@@ -289,7 +251,7 @@ insert(boost::string_ref const& name,
|
|||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
void
|
void
|
||||||
basic_headers<Allocator>::
|
basic_fields<Allocator>::
|
||||||
replace(boost::string_ref const& name,
|
replace(boost::string_ref const& name,
|
||||||
boost::string_ref value)
|
boost::string_ref value)
|
||||||
{
|
{
|
||||||
@@ -5,6 +5,16 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP
|
||||||
|
#define BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP
|
||||||
|
|
||||||
|
#include <beast/http/detail/rfc7230.hpp>
|
||||||
|
#include <beast/core/buffer_concepts.hpp>
|
||||||
|
#include <boost/assert.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
|
/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
|
||||||
*
|
*
|
||||||
* Additional changes are licensed under the same terms as NGINX and
|
* Additional changes are licensed under the same terms as NGINX and
|
||||||
@@ -28,21 +38,10 @@
|
|||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
/*
|
/* This code is a modified version of nodejs/http-parser, copyright above:
|
||||||
This code is a modified version of nodejs/http-parser, copyright above:
|
https://github.com/nodejs/http-parser
|
||||||
https://github.com/nodejs/http-parser
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP
|
|
||||||
#define BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP
|
|
||||||
|
|
||||||
#include <beast/http/detail/rfc7230.hpp>
|
|
||||||
#include <beast/core/buffer_concepts.hpp>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace http {
|
|
||||||
|
|
||||||
template<bool isRequest, class Derived>
|
template<bool isRequest, class Derived>
|
||||||
basic_parser_v1<isRequest, Derived>::
|
basic_parser_v1<isRequest, Derived>::
|
||||||
basic_parser_v1()
|
basic_parser_v1()
|
||||||
@@ -50,6 +49,55 @@ basic_parser_v1()
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Derived>
|
||||||
|
template<class OtherDerived>
|
||||||
|
basic_parser_v1<isRequest, Derived>::
|
||||||
|
basic_parser_v1(basic_parser_v1<
|
||||||
|
isRequest, OtherDerived> const& other)
|
||||||
|
: h_max_(other.h_max_)
|
||||||
|
, h_left_(other.h_left_)
|
||||||
|
, b_max_(other.b_max_)
|
||||||
|
, b_left_(other.b_left_)
|
||||||
|
, content_length_(other.content_length_)
|
||||||
|
, cb_(nullptr)
|
||||||
|
, s_(other.s_)
|
||||||
|
, flags_(other.flags_)
|
||||||
|
, fs_(other.fs_)
|
||||||
|
, pos_(other.pos_)
|
||||||
|
, http_major_(other.http_major_)
|
||||||
|
, http_minor_(other.http_minor_)
|
||||||
|
, status_code_(other.status_code_)
|
||||||
|
, upgrade_(other.upgrade_)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(! other.cb_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Derived>
|
||||||
|
template<class OtherDerived>
|
||||||
|
auto
|
||||||
|
basic_parser_v1<isRequest, Derived>::
|
||||||
|
operator=(basic_parser_v1<
|
||||||
|
isRequest, OtherDerived> const& other) ->
|
||||||
|
basic_parser_v1&
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(! other.cb_);
|
||||||
|
h_max_ = other.h_max_;
|
||||||
|
h_left_ = other.h_left_;
|
||||||
|
b_max_ = other.b_max_;
|
||||||
|
b_left_ = other.b_left_;
|
||||||
|
content_length_ = other.content_length_;
|
||||||
|
cb_ = nullptr;
|
||||||
|
s_ = other.s_;
|
||||||
|
flags_ = other.flags_;
|
||||||
|
fs_ = other.fs_;
|
||||||
|
pos_ = other.pos_;
|
||||||
|
http_major_ = other.http_major_;
|
||||||
|
http_minor_ = other.http_minor_;
|
||||||
|
status_code_ = other.status_code_;
|
||||||
|
upgrade_ = other.upgrade_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template<bool isRequest, class Derived>
|
template<bool isRequest, class Derived>
|
||||||
bool
|
bool
|
||||||
basic_parser_v1<isRequest, Derived>::
|
basic_parser_v1<isRequest, Derived>::
|
||||||
@@ -170,7 +218,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
call_on_start(ec);
|
call_on_start(ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
return errc();
|
return errc();
|
||||||
assert(! cb_);
|
BOOST_ASSERT(! cb_);
|
||||||
cb(&self::call_on_method);
|
cb(&self::call_on_method);
|
||||||
s_ = s_req_method;
|
s_ = s_req_method;
|
||||||
break;
|
break;
|
||||||
@@ -194,7 +242,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
// VFALCO TODO Better checking for valid URL characters
|
// VFALCO TODO Better checking for valid URL characters
|
||||||
if(! is_text(ch))
|
if(! is_text(ch))
|
||||||
return err(parse_error::bad_uri);
|
return err(parse_error::bad_uri);
|
||||||
assert(! cb_);
|
BOOST_ASSERT(! cb_);
|
||||||
cb(&self::call_on_uri);
|
cb(&self::call_on_uri);
|
||||||
s_ = s_req_url;
|
s_ = s_req_url;
|
||||||
break;
|
break;
|
||||||
@@ -377,7 +425,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
}
|
}
|
||||||
if(! is_text(ch))
|
if(! is_text(ch))
|
||||||
return err(parse_error::bad_reason);
|
return err(parse_error::bad_reason);
|
||||||
assert(! cb_);
|
BOOST_ASSERT(! cb_);
|
||||||
cb(&self::call_on_reason);
|
cb(&self::call_on_reason);
|
||||||
s_ = s_res_reason;
|
s_ = s_res_reason;
|
||||||
break;
|
break;
|
||||||
@@ -429,7 +477,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
fs_ = h_general;
|
fs_ = h_general;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(! cb_);
|
BOOST_ASSERT(! cb_);
|
||||||
cb(&self::call_on_field);
|
cb(&self::call_on_field);
|
||||||
s_ = s_header_name;
|
s_ = s_header_name;
|
||||||
break;
|
break;
|
||||||
@@ -549,7 +597,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
content_length_ = 0;
|
content_length_ = 0;
|
||||||
flags_ |= parse_flag::contentlength;
|
flags_ |= parse_flag::contentlength;
|
||||||
}
|
}
|
||||||
assert(! cb_);
|
BOOST_ASSERT(! cb_);
|
||||||
cb(&self::call_on_value);
|
cb(&self::call_on_value);
|
||||||
s_ = s_header_value;
|
s_ = s_header_value;
|
||||||
// fall through
|
// fall through
|
||||||
@@ -801,7 +849,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
return err(parse_error::bad_content_length);
|
return err(parse_error::bad_content_length);
|
||||||
if(fs_ == h_upgrade)
|
if(fs_ == h_upgrade)
|
||||||
flags_ |= parse_flag::upgrade;
|
flags_ |= parse_flag::upgrade;
|
||||||
assert(! cb_);
|
BOOST_ASSERT(! cb_);
|
||||||
call_on_value(ec, boost::string_ref{"", 0});
|
call_on_value(ec, boost::string_ref{"", 0});
|
||||||
if(ec)
|
if(ec)
|
||||||
return errc();
|
return errc();
|
||||||
@@ -880,7 +928,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
goto redo;
|
goto redo;
|
||||||
|
|
||||||
case s_header_value_unfold:
|
case s_header_value_unfold:
|
||||||
assert(! cb_);
|
BOOST_ASSERT(! cb_);
|
||||||
cb(&self::call_on_value);
|
cb(&self::call_on_value);
|
||||||
s_ = s_header_value;
|
s_ = s_header_value;
|
||||||
goto redo;
|
goto redo;
|
||||||
@@ -899,30 +947,57 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
return err(parse_error::illegal_content_length);
|
return err(parse_error::illegal_content_length);
|
||||||
upgrade_ = ((flags_ & (parse_flag::upgrade | parse_flag::connection_upgrade)) ==
|
upgrade_ = ((flags_ & (parse_flag::upgrade | parse_flag::connection_upgrade)) ==
|
||||||
(parse_flag::upgrade | parse_flag::connection_upgrade)) /*|| method == "connect"*/;
|
(parse_flag::upgrade | parse_flag::connection_upgrade)) /*|| method == "connect"*/;
|
||||||
auto const maybe_skip = call_on_headers(ec);
|
call_on_headers(ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
return errc();
|
return errc();
|
||||||
switch(maybe_skip)
|
auto const what = call_on_body_what(ec);
|
||||||
|
if(ec)
|
||||||
|
return errc();
|
||||||
|
switch(what)
|
||||||
{
|
{
|
||||||
case 0:
|
case body_what::normal:
|
||||||
break;
|
break;
|
||||||
case 2:
|
case body_what::upgrade:
|
||||||
upgrade_ = true;
|
upgrade_ = true;
|
||||||
// fall through
|
// fall through
|
||||||
case 1:
|
case body_what::skip:
|
||||||
flags_ |= parse_flag::skipbody;
|
flags_ |= parse_flag::skipbody;
|
||||||
break;
|
break;
|
||||||
default:
|
case body_what::pause:
|
||||||
return err(parse_error::bad_on_headers_rv);
|
++p;
|
||||||
|
s_ = s_body_pause;
|
||||||
|
return used();
|
||||||
}
|
}
|
||||||
s_ = s_headers_done;
|
s_ = s_headers_done;
|
||||||
goto redo;
|
goto redo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case s_body_pause:
|
||||||
|
{
|
||||||
|
auto const what = call_on_body_what(ec);
|
||||||
|
if(ec)
|
||||||
|
return errc();
|
||||||
|
switch(what)
|
||||||
|
{
|
||||||
|
case body_what::normal:
|
||||||
|
break;
|
||||||
|
case body_what::upgrade:
|
||||||
|
upgrade_ = true;
|
||||||
|
// fall through
|
||||||
|
case body_what::skip:
|
||||||
|
flags_ |= parse_flag::skipbody;
|
||||||
|
break;
|
||||||
|
case body_what::pause:
|
||||||
|
return used();
|
||||||
|
}
|
||||||
|
--p;
|
||||||
|
s_ = s_headers_done;
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
|
||||||
case s_headers_done:
|
case s_headers_done:
|
||||||
{
|
{
|
||||||
assert(! cb_);
|
BOOST_ASSERT(! cb_);
|
||||||
call_on_headers(ec);
|
|
||||||
if(ec)
|
if(ec)
|
||||||
return errc();
|
return errc();
|
||||||
bool const hasBody =
|
bool const hasBody =
|
||||||
@@ -959,7 +1034,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case s_body_identity0:
|
case s_body_identity0:
|
||||||
assert(! cb_);
|
BOOST_ASSERT(! cb_);
|
||||||
cb(&self::call_on_body);
|
cb(&self::call_on_body);
|
||||||
s_ = s_body_identity;
|
s_ = s_body_identity;
|
||||||
// fall through
|
// fall through
|
||||||
@@ -971,7 +1046,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
n = end - p;
|
n = end - p;
|
||||||
else
|
else
|
||||||
n = static_cast<std::size_t>(content_length_);
|
n = static_cast<std::size_t>(content_length_);
|
||||||
assert(content_length_ != 0 && content_length_ != no_content_length);
|
BOOST_ASSERT(content_length_ != 0 && content_length_ != no_content_length);
|
||||||
content_length_ -= n;
|
content_length_ -= n;
|
||||||
if(content_length_ == 0)
|
if(content_length_ == 0)
|
||||||
{
|
{
|
||||||
@@ -984,7 +1059,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case s_body_identity_eof0:
|
case s_body_identity_eof0:
|
||||||
assert(! cb_);
|
BOOST_ASSERT(! cb_);
|
||||||
cb(&self::call_on_body);
|
cb(&self::call_on_body);
|
||||||
s_ = s_body_identity_eof;
|
s_ = s_body_identity_eof;
|
||||||
// fall through
|
// fall through
|
||||||
@@ -1073,7 +1148,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case s_chunk_data0:
|
case s_chunk_data0:
|
||||||
assert(! cb_);
|
BOOST_ASSERT(! cb_);
|
||||||
cb(&self::call_on_body);
|
cb(&self::call_on_body);
|
||||||
s_ = s_chunk_data;
|
s_ = s_chunk_data;
|
||||||
goto redo; // VFALCO fall through?
|
goto redo; // VFALCO fall through?
|
||||||
@@ -1167,6 +1242,17 @@ write_eof(error_code& ec)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Derived>
|
||||||
|
void
|
||||||
|
basic_parser_v1<isRequest, Derived>::
|
||||||
|
reset()
|
||||||
|
{
|
||||||
|
cb_ = nullptr;
|
||||||
|
h_left_ = h_max_;
|
||||||
|
b_left_ = b_max_;
|
||||||
|
reset(std::integral_constant<bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
template<bool isRequest, class Derived>
|
template<bool isRequest, class Derived>
|
||||||
bool
|
bool
|
||||||
basic_parser_v1<isRequest, Derived>::
|
basic_parser_v1<isRequest, Derived>::
|
||||||
|
|||||||
264
include/beast/http/impl/message.ipp
Normal file
264
include/beast/http/impl/message.ipp
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
//
|
||||||
|
// 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/core/error.hpp>
|
||||||
|
#include <beast/http/concepts.hpp>
|
||||||
|
#include <beast/http/rfc7230.hpp>
|
||||||
|
#include <beast/core/detail/ci_char_traits.hpp>
|
||||||
|
#include <beast/core/detail/type_traits.hpp>
|
||||||
|
#include <boost/assert.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
template<class Fields>
|
||||||
|
void
|
||||||
|
swap(
|
||||||
|
header<true, Fields>& m1,
|
||||||
|
header<true, Fields>& m2)
|
||||||
|
{
|
||||||
|
using std::swap;
|
||||||
|
swap(m1.version, m2.version);
|
||||||
|
swap(m1.method, m2.method);
|
||||||
|
swap(m1.url, m2.url);
|
||||||
|
swap(m1.fields, m2.fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Fields>
|
||||||
|
void
|
||||||
|
swap(
|
||||||
|
header<false, Fields>& a,
|
||||||
|
header<false, Fields>& b)
|
||||||
|
{
|
||||||
|
using std::swap;
|
||||||
|
swap(a.version, b.version);
|
||||||
|
swap(a.status, b.status);
|
||||||
|
swap(a.reason, b.reason);
|
||||||
|
swap(a.fields, b.fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
void
|
||||||
|
swap(
|
||||||
|
message<isRequest, Body, Fields>& m1,
|
||||||
|
message<isRequest, Body, Fields>& m2)
|
||||||
|
{
|
||||||
|
using std::swap;
|
||||||
|
swap(m1.base(), m2.base());
|
||||||
|
swap(m1.body, m2.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Fields>
|
||||||
|
bool
|
||||||
|
is_keep_alive(header<isRequest, Fields> const& msg)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||||
|
if(msg.version == 11)
|
||||||
|
{
|
||||||
|
if(token_list{msg.fields["Connection"]}.exists("close"))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(token_list{msg.fields["Connection"]}.exists("keep-alive"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Fields>
|
||||||
|
bool
|
||||||
|
is_upgrade(header<isRequest, Fields> const& msg)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||||
|
if(msg.version == 10)
|
||||||
|
return false;
|
||||||
|
if(token_list{msg.fields["Connection"]}.exists("upgrade"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
struct prepare_info
|
||||||
|
{
|
||||||
|
boost::optional<connection> connection_value;
|
||||||
|
boost::optional<std::uint64_t> content_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
prepare_options(prepare_info& pi,
|
||||||
|
message<isRequest, Body, Fields>& msg)
|
||||||
|
{
|
||||||
|
beast::detail::ignore_unused(pi, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
void
|
||||||
|
prepare_option(prepare_info& pi,
|
||||||
|
message<isRequest, Body, Fields>& msg,
|
||||||
|
connection value)
|
||||||
|
{
|
||||||
|
beast::detail::ignore_unused(msg);
|
||||||
|
pi.connection_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
bool isRequest, class Body, class Fields,
|
||||||
|
class Opt, class... Opts>
|
||||||
|
void
|
||||||
|
prepare_options(prepare_info& pi,
|
||||||
|
message<isRequest, Body, Fields>& msg,
|
||||||
|
Opt&& opt, Opts&&... opts)
|
||||||
|
{
|
||||||
|
prepare_option(pi, msg, opt);
|
||||||
|
prepare_options(pi, msg,
|
||||||
|
std::forward<Opts>(opts)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
void
|
||||||
|
prepare_content_length(prepare_info& pi,
|
||||||
|
message<isRequest, Body, Fields> const& msg,
|
||||||
|
std::true_type)
|
||||||
|
{
|
||||||
|
typename Body::writer w(msg);
|
||||||
|
// VFALCO This is a design problem!
|
||||||
|
error_code ec;
|
||||||
|
w.init(ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
pi.content_length = w.content_length();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
void
|
||||||
|
prepare_content_length(prepare_info& pi,
|
||||||
|
message<isRequest, Body, Fields> const& msg,
|
||||||
|
std::false_type)
|
||||||
|
{
|
||||||
|
beast::detail::ignore_unused(msg);
|
||||||
|
pi.content_length = boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
template<
|
||||||
|
bool isRequest, class Body, class Fields,
|
||||||
|
class... Options>
|
||||||
|
void
|
||||||
|
prepare(message<isRequest, Body, Fields>& msg,
|
||||||
|
Options&&... options)
|
||||||
|
{
|
||||||
|
// VFALCO TODO
|
||||||
|
static_assert(is_Body<Body>::value,
|
||||||
|
"Body requirements not met");
|
||||||
|
static_assert(has_writer<Body>::value,
|
||||||
|
"Body has no writer");
|
||||||
|
static_assert(is_Writer<typename Body::writer,
|
||||||
|
message<isRequest, Body, Fields>>::value,
|
||||||
|
"Writer requirements not met");
|
||||||
|
detail::prepare_info pi;
|
||||||
|
detail::prepare_content_length(pi, msg,
|
||||||
|
detail::has_content_length<typename Body::writer>{});
|
||||||
|
detail::prepare_options(pi, msg,
|
||||||
|
std::forward<Options>(options)...);
|
||||||
|
|
||||||
|
if(msg.fields.exists("Connection"))
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"prepare called with Connection field set");
|
||||||
|
|
||||||
|
if(msg.fields.exists("Content-Length"))
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"prepare called with Content-Length field set");
|
||||||
|
|
||||||
|
if(token_list{msg.fields["Transfer-Encoding"]}.exists("chunked"))
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"prepare called with Transfer-Encoding: chunked set");
|
||||||
|
|
||||||
|
if(pi.connection_value != connection::upgrade)
|
||||||
|
{
|
||||||
|
if(pi.content_length)
|
||||||
|
{
|
||||||
|
struct set_field
|
||||||
|
{
|
||||||
|
void
|
||||||
|
operator()(message<true, Body, Fields>& msg,
|
||||||
|
detail::prepare_info const& pi) const
|
||||||
|
{
|
||||||
|
using beast::detail::ci_equal;
|
||||||
|
if(*pi.content_length > 0 ||
|
||||||
|
ci_equal(msg.method, "POST"))
|
||||||
|
{
|
||||||
|
msg.fields.insert(
|
||||||
|
"Content-Length", *pi.content_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(message<false, Body, Fields>& msg,
|
||||||
|
detail::prepare_info const& pi) const
|
||||||
|
{
|
||||||
|
if((msg.status / 100 ) != 1 &&
|
||||||
|
msg.status != 204 &&
|
||||||
|
msg.status != 304)
|
||||||
|
{
|
||||||
|
msg.fields.insert(
|
||||||
|
"Content-Length", *pi.content_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
set_field{}(msg, pi);
|
||||||
|
}
|
||||||
|
else if(msg.version >= 11)
|
||||||
|
{
|
||||||
|
msg.fields.insert("Transfer-Encoding", "chunked");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const content_length =
|
||||||
|
msg.fields.exists("Content-Length");
|
||||||
|
|
||||||
|
if(pi.connection_value)
|
||||||
|
{
|
||||||
|
switch(*pi.connection_value)
|
||||||
|
{
|
||||||
|
case connection::upgrade:
|
||||||
|
msg.fields.insert("Connection", "upgrade");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case connection::keep_alive:
|
||||||
|
if(msg.version < 11)
|
||||||
|
{
|
||||||
|
if(content_length)
|
||||||
|
msg.fields.insert("Connection", "keep-alive");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case connection::close:
|
||||||
|
if(msg.version >= 11)
|
||||||
|
msg.fields.insert("Connection", "close");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rfc7230 6.7.
|
||||||
|
if(msg.version < 11 && token_list{
|
||||||
|
msg.fields["Connection"]}.exists("upgrade"))
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"invalid version for Connection: upgrade");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // 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