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:
Vinnie Falco
2017-01-11 16:50:38 -05:00
parent 2f9a8440c2
commit af4fe24939
437 changed files with 87677 additions and 7035 deletions

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

@@ -0,0 +1,127 @@
[/
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[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]

View File

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

Binary file not shown.

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.

View File

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

View File

@@ -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[] '''&mdash; '''] [template mdash[] '''&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
View 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]

View File

@@ -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&lt;&lt;</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>

View File

@@ -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>&#xd;[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) = '&lt;&lt;'">
<xsl:call-template name="make-id">
<xsl:with-param name="name"
select="concat(substring-before($name, '&lt;&lt;'), '_ls_')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="substring($name, string-length($name) - 1) = '&gt;&gt;'">
<xsl:call-template name="make-id">
<xsl:with-param name="name"
select="concat(substring-before($name, '&gt;&gt;'), '_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>]&#xd;</xsl:text> <xsl:text>]&#xd;</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']) &gt; 0">
<xsl:text>[heading Static Data Members]&#xd;</xsl:text>
<xsl:text>[table&#xd; [[Name][Description]]&#xd;</xsl:text>
<xsl:for-each select="sectiondef[@kind='public-static-attrib']/memberdef" mode="class-table">
<xsl:sort select="name"/>
<xsl:text> [&#xd;</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>]]]&#xd; [&#xd; </xsl:text>
<xsl:value-of select="briefdescription"/>
<xsl:text>&#xd; ]&#xd; ]&#xd;</xsl:text>
</xsl:for-each>
<xsl:text>]&#xd;</xsl:text>
</xsl:if>
<xsl:if test="count(sectiondef[@kind='public-attrib']) &gt; 0">
<xsl:text>[heading Data Members]&#xd;</xsl:text> <xsl:text>[heading Data Members]&#xd;</xsl:text>
<xsl:text>[table&#xd; [[Name][Description]]&#xd;</xsl:text> <xsl:text>[table&#xd; [[Name][Description]]&#xd;</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> [&#xd;</xsl:text> <xsl:text> [&#xd;</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>```&#xd;</xsl:text> <xsl:text>```&#xd;</xsl:text>
<xsl:for-each select="../memberdef[name = $unqualified-name]"> <xsl:for-each select="../memberdef[name = $unqualified-name]">
<xsl:if test="position() &gt; 1">
<xsl:text>&#xd;</xsl:text>
<xsl:if test=" not(briefdescription = preceding-sibling::*/briefdescription)">
<xsl:text>```&#xd;</xsl:text>
<xsl:apply-templates select="briefdescription" mode="markup"/>
<xsl:text>```&#xd;</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"/>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;
}; };
``` ```

View File

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

View File

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

View File

@@ -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_;
} }
}; };

View File

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

View File

@@ -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();
} }

View File

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

View File

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

View File

@@ -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);
} }

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

@@ -0,0 +1,54 @@
#
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
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
;

View File

@@ -0,0 +1,58 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#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();
}

View 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";
}

View File

@@ -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";
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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();
} }

View File

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

View File

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

View File

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

View File

@@ -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_);
} }
}; };

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

@@ -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>())),

View 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

View 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

View File

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

View File

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

View File

@@ -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_);
} }

View 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

View File

@@ -0,0 +1,173 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_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

View File

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

View File

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

View File

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

View File

@@ -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);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View 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

View File

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

View File

@@ -0,0 +1,68 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

@@ -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);
} }
}; };

View File

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

View 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

View File

@@ -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, ...>{};

View 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

View File

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

View File

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

View File

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

View File

@@ -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) };
} }
} }

View File

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

View File

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

View 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

View File

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

View File

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

View 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