Merge commit 'af4fe2493925bc57c5c3343c383719fa72dea262' into b4.2

This commit is contained in:
Vinnie Falco
2017-01-11 16:50:38 -05:00
437 changed files with 87677 additions and 7035 deletions

View File

@@ -10,8 +10,8 @@ env:
# to boost's .tar.gz.
- LCOV_ROOT=$HOME/lcov
- VALGRIND_ROOT=$HOME/valgrind-install
- BOOST_ROOT=$HOME/boost_1_60_0
- BOOST_URL='http://downloads.sourceforge.net/project/boost/boost/1.60.0/boost_1_60_0.tar.gz?r=https%3A%2F%2Fsourceforge.net%2Fprojects%2Fboost%2Ffiles%2Fboost%2F1.60.0%2Fboost_1_60_0.tar.gz&ts=1460417589&use_mirror=netix'
- BOOST_ROOT=$HOME/boost_1_61_0
- BOOST_URL='http://downloads.sourceforge.net/project/boost/boost/1.61.0/boost_1_61_0.tar.gz?use_mirror=netix'
packages: &gcc5_pkgs
- gcc-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
* dstream improvements

View File

@@ -6,21 +6,51 @@ project (Beast)
set_property (GLOBAL PROPERTY USE_FOLDERS ON)
if (WIN32)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4 /wd4100 /bigobj /D _WIN32_WINNT=0x0601 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1")
if (MSVC)
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_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()
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
find_package(Boost REQUIRED COMPONENTS coroutine context thread filesystem program_options system)
include_directories(${Boost_INCLUDE_DIRS})
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIR})
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads)
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wpedantic")
"${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()
if ("${VARIANT}" STREQUAL "coverage")
@@ -53,7 +83,6 @@ function(DoGroupSources curdir rootdir folder)
source_group("" FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
else()
string(REGEX REPLACE ^${rootdir} ${folder} groupname ${curdir})
#set(groupname ${curdir})
string(REPLACE "/" "\\" groupname ${groupname})
source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
endif()
@@ -78,7 +107,14 @@ file(GLOB_RECURSE EXTRAS_INCLUDES
)
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/core)
add_subdirectory (test/http)
add_subdirectory (test/websocket)
add_subdirectory (test/zlib)

View File

@@ -37,7 +37,7 @@ else if [ os.name ] = HAIKU
if [ os.name ] = NT
{
lib ssl : : <name>ssleay32 ;
lib crypto : : <name>libeay32 ;
lib crypto : : <name>libeay32 ;
}
else
{
@@ -87,20 +87,20 @@ project beast
<library>/boost/coroutine//boost_coroutine
<library>/boost/filesystem//boost_filesystem
<library>/boost/program_options//boost_program_options
# <library>ssl
# <library>crypto
<define>BOOST_ALL_NO_LIB=1
<define>BOOST_SYSTEM_NO_DEPRECATED=1
<threading>multi
<link>static
<runtime-link>shared
<debug-symbols>on
<toolset>gcc:<cxxflags>-std=c++11
<toolset>gcc:<cxxflags>-Wno-unused-variable
<toolset>gcc:<cxxflags>-Wno-unused-parameter
<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>_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>_GNU_SOURCE=1
<os>SOLARIS:<define>_XOPEN_SOURCE=500
@@ -113,12 +113,7 @@ project beast
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>QNXNTO:<library>socket
<os>HAIKU:<library>network
: usage-requirements
<include>.
:
build-dir bin
;

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,
Washington from September 18 to September 22. If you'd like to meet me
and hear the talk or ask questions about Beast feel free to approach
me in person or send me an email at vinnie.falco@gmail.com to schedule
some time.
Presentation
(slides: <a href="https://raw.githubusercontent.com/vinniefalco/Beast/master/doc/images/CppCon2016.pdf">CppCon2016.pdf</a>)
About CppCon 2016:
http://cppcon.org
<a href="https://www.youtube.com/watch?v=uJZgRcvPFwI">
<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
by the C++ compiler when searching for includes. Beast `#include` lines
will look like this:
```
```C++
#include <beast/http.hpp>
#include <beast/websocket.hpp>
```
@@ -137,6 +136,7 @@ The files in the repository are laid out thusly:
./
bin/ Holds 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
beast/
extras/ Additional APIs, may change
@@ -171,14 +171,14 @@ int main()
// 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!"));
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 << to_string(sb.data()) << "\n";
std::cout << beast::to_string(sb.data()) << "\n";
}
```
@@ -186,6 +186,7 @@ Example HTTP program:
```C++
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <string>
@@ -200,18 +201,19 @@ int main()
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
// 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.url = "/";
req.version = 11;
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast");
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_v1<beast::http::streambuf_body> resp;
beast::http::response<beast::http::streambuf_body> resp;
beast::http::read(sock, sb, resp);
std::cout << resp;
}

View File

@@ -18,6 +18,8 @@ Core:
* Complete allocator testing in basic_streambuf
WebSocket:
* Minimize sizeof(websocket::stream)
* Move check for message size limit to account for compression
* more invokable unit test coverage
* More control over the HTTP request and response during handshakes
* optimized versions of key/masking, choose prepared_key size

View File

@@ -15,15 +15,15 @@ using boostbook ;
using quickbook ;
using doxygen ;
xml beast_boostbook : master.qbk ;
import quickbook ;
path-constant out : . ;
path-constant here : . ;
install stylesheets
:
$(broot)/doc/src/boostbook.css
:
<location>$(out)/html
<location>$(here)/html
;
explicit stylesheets ;
@@ -35,7 +35,7 @@ install images
images/body.png
images/message.png
:
<location>$(out)/html/images
<location>$(here)/html/images
;
explicit images ;
@@ -44,40 +44,54 @@ install callouts
:
[ glob $(broot)/doc/src/images/callouts/*.png ]
:
<location>$(out)/html/images/callouts
<location>$(here)/html/images/callouts
;
explicit callout ;
boostbook doc
install examples
:
beast_boostbook
[ glob
../examples/*.cpp
../examples/*.hpp
../examples/ssl/*.cpp
../examples/ssl/*.hpp
]
:
<xsl:param>chapter.autolabel=0
<xsl:param>boost.image.src=images/beast.png
<xsl:param>boost.image.alt="Beast Logo"
<xsl:param>boost.image.w=2400
<xsl:param>boost.image.h=80
<xsl:param>boost.root=$(broot)
<xsl:param>chapter.autolabel=0
<xsl:param>chunk.first.sections=1 # Chunk the first top-level section?
<xsl:param>chunk.section.depth=8 # Depth to which sections should be chunked
<xsl:param>generate.section.toc.level=1 # Control depth of TOC generation in sections
<xsl:param>toc.max.depth=2 # How many levels should be created for each TOC?
<xsl:param>toc.section.depth=2 # How deep should recursive sections appear in the TOC?
:
<location>temp
<dependency>stylesheets
<dependency>images
<location>$(here)/html/examples
;
#explicit doc ;
# <xsl:param>nav.layout=none
# <format>html:<xsl:param>location=../bin/doc/html
# <xsl:param>generate.toc="chapter nop section nop"
# <xsl:param>root.filename=index
# <xsl:param>output-root="../bin/html"
explicit examples ;
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)
]
[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
applications (e.g. [@https://ripple.com Ripple]) written in C++. These
needs were not met by existing solutions so Beast was written from scratch
as a solution. Beast's design philosophy avoid flaws exhibited by other
as a solution. Beast's design philosophy avoids flaws exhibited by other
libraries:
* Don't try to do too much.
* 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
proven and it is familiar to users.
* Leave important decisions to the user, such as allocating memory or
managing flow control.
* Let library users make the important decisions such as how to
allocate memory or how to leverage flow control.
Beast uses the [link beast.types.DynamicBuffer [*`DynamicBuffer`]] concept
presented in the Netwoking TS, and relies heavily on the Boost.Asio
[*`ConstBufferSequence`] and [*`MutableBufferSequence`] concepts for passing
buffers to functions. The authors have found the dynamic buffer and buffer
sequence interfaces to be optimal for interacting with Asio, and for other
tasks such as incremental parsing of data in buffers (for example, parsing
websocket frames stored in a [link beast.ref.static_streambuf `static_streambuf`]).
Beast uses the __DynamicBuffer__ concept presented in the Networking TS
(__N4588__), and relies heavily on the Boost.Asio __ConstBufferSequence__
and __MutableBufferSequence__ concepts for passing buffers to functions.
The authors have found the dynamic buffer and buffer sequence interfaces to
be optimal for interacting with Asio, and for other tasks such as incremental
parsing of data in buffers (for example, parsing websocket frames stored
in a [link beast.ref.static_streambuf `static_streambuf`]).
During the development of Beast the authors have studied other software
packages and in particular the comments left during the Boost Review process
of other packages offering similar functionality. In this section we attempt
to address those issues.
of other packages offering similar functionality. In this section and the
FAQs that follow we attempt to answer those questions that are also applicable
to Beast.
[variablelist
[[
"I would also like to see instances of this library being used
in production. That would give some evidence that the design
works in practice.""
works in practice."
][
Beast.HTTP and Beast.WebSocket are production ready and currently
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`].
The HTTP interface is further driven by the needs of the WebSocket module,
as a WebSocket session requires a HTTP Upgrade handshake exchange at the
start. Other design goals:
* Don't try to invent a complete web server or client
* 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
[[
"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 `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
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),
etc.
etc."
][
Beast.HTTP does not provide direct facilities for implementing TLS
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
which higher abstractions such as the aforementioned HTTP service or
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."
][
These behaviors are best left to the calling software. A future library
can build on Beast.HTTP to provide these behaviors.
The Beast interface needs to support this functionality (by allowing this
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
a HTTP library. The authors agree that HTTP/2 is important but also
@@ -145,13 +161,13 @@ start. Other design goals:
and 1.1.
The Beast.HTTP message model is suitable for HTTP/2 and can be re-used.
The IEFT HTTP Working Group adopted message compatiblity with HTTP/1.x
The IETF HTTP Working Group adopted message compatiblity with HTTP/1.x
as an explicit goal. A parser can simply emit full headers after
decoding the compressed HTTP/2 headers. The stream ID is not logicaly
decoding the compressed HTTP/2 headers. The stream ID is not logically
part of the message but rather message metadata and should be
communicated out-of-band (see below). HTTP/2 sessions begin with a
traditional HTTP/1.1 Upgrade similar in fashion to the WebSocket
upgrade. A HTTP/2 implementation can use existing Beast.HTTP primitives
upgrade. An HTTP/2 implementation can use existing Beast.HTTP primitives
to perform this handshake.
Free functions for HTTP/2 sessions are not possible because of the
@@ -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]
[section:websocket WebSocket]
[section:websocketpp Comparison to Zaphoyd Studios WebSocket++]
[variablelist
@@ -208,21 +269,19 @@ start. Other design goals:
To get an idea of the complexity involved with implementing a transport,
compare the asio transport to the
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59 `iostream` transport]
(a layer that allows websocket communication over a std iostream).
(a layer that allows websocket communication over a `std::iostream`).
In contrast, Beast abstracts the transport by defining just one [*`NextLayer`]
template argument The type requirements for [*`NextLayer`] are
already familiar to users as they are documented in Asio:
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html SyncReadStream],
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html SyncWriteStream],
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html AsyncReadStream], and
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html AsyncWriteStream].
__AsyncReadStream__, __AsyncWriteStream__, __SyncReadStream__, __SyncWriteStream__.
The type requirements for instantiating `beast::websocket::stream` versus
`websocketpp::connection` with user defined types are vastly reduced
(18 functions versus 2). Note that websocketpp connections are passed by
`shared_ptr`. Beast does not use `shared_ptr` anywhere in its public interface.
A `beast::websocket::stream` is constructible and movable in a manner identical
`to a boost::asio::ip::tcp::socket`. Callers can put such objects in a
to a `boost::asio::ip::tcp::socket`. Callers can put such objects in a
`shared_ptr` if they want to, but there is no requirement to do so.
[table
@@ -283,8 +342,7 @@ start. Other design goals:
implementation. Instead, it follows the Asio pattern. Calls to
asynchronous initiation functions use the same method to invoke
intermediate handlers as the method used to invoke the final handler,
through the
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/asio_handler_invoke.html asio_handler_invoke] mechanism.
through the __asio_handler_invoke__ mechanism.
The only requirement in Beast is that calls to asynchronous initiation
functions are made from the same implicit or explicit strand. For
@@ -314,7 +372,7 @@ start. Other design goals:
websocketpp requires a one-time call to set the handler for each event
in its interface (for example, upon message receipt). The handler is
represented by a `std::function equivalent`. Its important to recognize
represented by a `std::function` equivalent. Its important to recognize
that the websocketpp interface performs type-erasure on this handler.
In comparison, Beast handlers are specified in a manner identical to
@@ -378,29 +436,27 @@ start. Other design goals:
websocketpp defines a message buffer, passed in arguments by
`shared_ptr`, and an associated message manager which permits
aggregation and memory reuse of memory. The implementation of
aggregation and reuse of memory. The implementation of
`websocketpp::message` uses a `std::string` to hold the payload. If an
incoming message is broken up into multiple frames, the string may be
reallocated for each continuation frame. The std::string always uses
reallocated for each continuation frame. The `std::string` always uses
the standard allocator, it is not possible to customize the choice of
allocator.
Beast allows callers to specify the object for receiving the message
or frame data, which is of any type meeting the requirements of
[@http://vinniefalco.github.io/beast/beast/types/DynamicBuffer.html [*DynamicBuffer]]
(modeled after `boost::asio::streambuf`).
__DynamicBuffer__ (modeled after `boost::asio::streambuf`).
Beast comes with the class `beast::basic_streambuf`, an efficient
implementation of the [*DynamicBuffer] concept which makes use of multiple
Beast comes with the class __basic_streambuf__, an efficient
implementation of the __DynamicBuffer__ concept which makes use of multiple
allocated octet arrays. If an incoming message is broken up into
multiple pieces, no reallocation occurs. Instead, new allocations are
appended to the sequence when existing allocations are filled. Beast
does not impose any particular memory management model on callers. The
`basic_streambuf` provided by beast supports standard allocators through
a template argument. Use the [*DynamicBuffer] that comes with beast,
__basic_streambuf__ provided by beast supports standard allocators through
a template argument. Use the __DynamicBuffer__ that comes with beast,
customize the allocator if you desire, or provide your own type that
meets the
[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/basic_streambuf.hpp#L21 concept requirements].
meets the requirements.
[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]

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

Binary file not shown.

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)
-->
<section id="index">
<section id="beast.index">
<title>Index</title>
<index/>
</section>

2
src/beast/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)
#

View File

@@ -8,7 +8,7 @@
[library Beast
[quickbook 1.6]
[copyright 2013 - 2016 Vinnie Falco]
[purpose C++ Library]
[purpose Networking Protocol Library]
[license
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
@@ -22,174 +22,85 @@
[template mdash[] '''&mdash; ''']
[template indexterm1[term1] '''<indexterm><primary>'''[term1]'''</primary></indexterm>''']
[template indexterm2[term1 term2] '''<indexterm><primary>'''[term1]'''</primary><secondary>'''[term2]'''</secondary></indexterm>''']
[def __POSIX__ /POSIX/]
[def __Windows__ /Windows/]
[def __accept__ [@http://www.opengroup.org/onlinepubs/000095399/functions/accept.html `accept()`]]
[def __connect__ [@http://www.opengroup.org/onlinepubs/000095399/functions/connect.html `connect()`]]
[def __getpeername__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getpeername.html `getpeername()`]]
[def __getsockname__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getsockname.html `getsockname()`]]
[def __getsockopt__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getsockopt.html `getsockopt()`]]
[def __ioctl__ [@http://www.opengroup.org/onlinepubs/000095399/functions/ioctl.html `ioctl()`]]
[def __recvfrom__ [@http://www.opengroup.org/onlinepubs/000095399/functions/recvfrom.html `recvfrom()`]]
[def __sendto__ [@http://www.opengroup.org/onlinepubs/000095399/functions/sendto.html `sendto()`]]
[def __setsockopt__ [@http://www.opengroup.org/onlinepubs/000095399/functions/setsockopt.html `setsockopt()`]]
[def __socket__ [@http://www.opengroup.org/onlinepubs/000095399/functions/socket.html `socket()`]]
[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
Boost, containing two modules implementing widely used network protocols.
Beast.HTTP offers a universal model for describing, sending, and receiving
HTTP messages while Beast.WebSocket provides a complete implementation of
the WebSocket protocol. Their design achieves these goals:
[def __Body__ [link beast.ref.Body [*`Body`]]]
[def __DynamicBuffer__ [link beast.ref.DynamicBuffer [*DynamicBuffer]]]
[def __FieldSequence__ [link beast.ref.FieldSequence [*FieldSequence]]]
[def __Parser__ [link beast.ref.Parser [*`Parser`]]]
* [*Symmetry.] Interfaces are role-agnostic; the same interfaces can be
used to build clients, servers, or both.
* [*Ease of Use.] HTTP messages are modeled using simple, readily
accessible objects. Functions and classes used to send and receive HTTP
or WebSocket messages are designed to resemble Boost.Asio as closely as
possible. Users familiar with Boost.Asio will be immediately comfortable
using this library.
* [*Flexibility.] Interfaces do not mandate specific implementation
strategies; important decisions such as buffer or thread management are
left to users of the library.
* [*Performance.] The implementation performs competitively, making it a
realistic choice for building high performance network servers.
* [*Scalability.] Development of network applications that scale to thousands
of concurrent connections is possible with the implementation.
* [*Basis for further abstraction.] The interfaces facilitate the
development of other libraries that provide higher levels of abstraction.
[section:requirements Requirements]
Beast requires:
* [*C++11.] A minimum of C++11 is needed.
* [*Boost.] Beast is built on Boost, especially Boost.Asio.
* [*OpenSSL.] If using TLS/Secure sockets (optional).
[note Tested compilers: msvc-14+, gcc 5+, clang 3.6+]
The library is [*header-only]. It is not necessary to add any .cpp files,
or to edit your existing build script or project file except to provide
that the include/ directory for beast is searched for include files.
[endsect]
[section:example Examples]
These usage examples are intended to quickly impress upon readers the
flavor of the library. They are complete programs which may be built
and run. Source code and build scripts for these programs may be found
in the examples directory.
Use HTTP to request the root page from a website and print the response:
```
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
int main()
{
// Normal boost::asio setup
std::string const host = "boost.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
// Send HTTP request using beast
beast::http::request_v1<beast::http::empty_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast");
beast::http::prepare(req);
beast::http::write(sock, req);
// Receive and print HTTP response using beast
beast::streambuf sb;
beast::http::response_v1<beast::http::streambuf_body> resp;
beast::http::read(sock, sb, resp);
std::cout << resp;
}
```
Establish a WebSocket connection, send a message and receive the reply:
```
#include <beast/core/to_string.hpp>
#include <beast/websocket.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
int main()
{
// Normal boost::asio setup
std::string const host = "echo.websocket.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
// WebSocket connect and send message using beast
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
ws.handshake(host, "/");
ws.write(boost::asio::buffer("Hello, world!"));
// Receive WebSocket message, print and close using beast
beast::streambuf sb;
beast::websocket::opcode op;
ws.read(op, sb);
ws.close(beast::websocket::close_code::normal);
std::cout << to_string(sb.data()) << "\n";
}
```
[endsect]
[section:credits Credits]
Boost.Asio is the inspiration behind which all of the interfaces and
implementation strategies are built. Some parts of the documentation are
written to closely resemble the wording and presentation of Boost.Asio
documentation. Credit goes to Christopher Kohloff for the wonderful
Asio library and the ideas upon which Beast is built.
Beast would not be possible without the considerable time and patience
contributed by David Schwartz, Edward Hennis, Howard Hinnant, Miguel Portilla,
Nikolaos Bougalis, Scott Determan, Scott Schurr, and Ripple Labs for
supporting its development.
[endsect]
[endsect]
[def __basic_fields__ [link beast.ref.http__basic_fields `basic_fields`]]
[def __fields__ [link beast.ref.http__fields `fields`]]
[def __header__ [link beast.ref.http__header `header`]]
[def __message__ [link beast.ref.http__message `message`]]
[def __streambuf__ [link beast.ref.streambuf `streambuf`]]
[def __basic_streambuf__ [link beast.ref.basic_streambuf `basic_streambuf`]]
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 websocket.qbk]
[include examples.qbk]
[include design.qbk]
[section:types Type Requirements]
[section:ref Reference]
[xinclude quickref.xml]
[include types/Body.qbk]
[include types/BufferSequence.qbk]
[include types/DynamicBuffer.qbk]
@@ -199,13 +110,7 @@ supporting its development.
[include types/Reader.qbk]
[include types/Streams.qbk]
[include types/Writer.qbk]
[include reference.qbk]
[endsect]
[include design.qbk]
[section:quickref Quick Reference]
[xinclude quickref.xml]
[endsect]
[include reference.qbk]
[section:idx Index]
[xinclude index.xml]
[endsect]

114
src/beast/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"/>
<thead>
<row>
<entry valign="center" namest="a" nameend="b">
<entry valign="center" namest="a" nameend="c">
<bridgehead renderas="sect2">HTTP</bridgehead>
</entry>
<entry valign="center" namest="c" nameend="d">
<entry valign="center" namest="d" nameend="d">
<bridgehead renderas="sect2">WebSocket</bridgehead>
</entry>
</row>
@@ -30,53 +30,82 @@
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__basic_dynabuf_body">basic_dynabuf_body</link></member>
<member><link linkend="beast.ref.http__basic_headers">basic_headers</link></member>
<member><link linkend="beast.ref.http__basic_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__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__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__streambuf_body">streambuf_body</link></member>
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
</simplelist>
<bridgehead renderas="sect3">Options</bridgehead>
<bridgehead renderas="sect3">rfc7230</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__body_max_size">body_max_size</link></member>
<member><link linkend="beast.ref.http__headers_max_size">headers_max_size</link></member>
<member><link linkend="beast.ref.http__skip_body">skip_body</link></member>
</simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
<member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member>
<member><link linkend="beast.ref.http__is_ReadableBody">is_ReadableBody</link></member>
<member><link linkend="beast.ref.http__is_WritableBody">is_WritableBody</link></member>
<member><link linkend="beast.ref.http__ext_list">ext_list</link></member>
<member><link linkend="beast.ref.http__param_list">param_list</link></member>
<member><link linkend="beast.ref.http__token_list">token_list</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__async_parse">async_parse</link></member>
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
<member><link linkend="beast.ref.http__async_parse">async_parse</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__prepare">prepare</link></member>
<member><link linkend="beast.ref.http__read">read</link></member>
<member><link linkend="beast.ref.http__swap">swap</link></member>
<member><link linkend="beast.ref.http__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>
</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>
<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__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>
<bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.types.Body">Body</link></member>
<member><link linkend="beast.types.Field">Field</link></member>
<member><link linkend="beast.types.FieldSequence">FieldSequence</link></member>
<member><link linkend="beast.types.Parser">Parser</link></member>
<member><link linkend="beast.types.Reader">Reader</link></member>
<member><link linkend="beast.types.Writer">Writer</link></member>
<member><link linkend="beast.ref.Body">Body</link></member>
<member><link linkend="beast.ref.Field">Field</link></member>
<member><link linkend="beast.ref.FieldSequence">FieldSequence</link></member>
<member><link linkend="beast.ref.Parser">Parser</link></member>
<member><link linkend="beast.ref.Reader">Reader</link></member>
<member><link linkend="beast.ref.Writer">Writer</link></member>
</simplelist>
</entry>
<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__teardown_tag">teardown_tag</link></member>
</simplelist>
<bridgehead renderas="sect3">Options</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.websocket__auto_fragment_size">auto_fragment_size</link></member>
<member><link linkend="beast.ref.websocket__decorate">decorate</link></member>
<member><link linkend="beast.ref.websocket__keep_alive">keep_alive</link></member>
<member><link linkend="beast.ref.websocket__mask_buffer_size">mask_buffer_size</link></member>
<member><link linkend="beast.ref.websocket__message_type">message_type</link></member>
<member><link linkend="beast.ref.websocket__pong_callback">pong_callback</link></member>
<member><link linkend="beast.ref.websocket__read_buffer_size">read_buffer_size</link></member>
<member><link linkend="beast.ref.websocket__read_message_max">read_message_max</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.websocket__async_teardown">async_teardown</link></member>
<member><link linkend="beast.ref.websocket__teardown">teardown</link></member>
</simplelist>
<bridgehead renderas="sect3">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>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.websocket__close_code">close_code</link></member>
@@ -121,11 +148,15 @@
<colspec colname="b"/>
<colspec colname="c"/>
<colspec colname="d"/>
<colspec colname="e"/>
<thead>
<row>
<entry valign="center" namest="a" nameend="d">
<bridgehead renderas="sect2">Core</bridgehead>
</entry>
<entry valign="center" namest="e" nameend="e">
<bridgehead renderas="sect2">ZLib</bridgehead>
</entry>
</row>
</thead>
<tbody>
@@ -138,9 +169,12 @@
<member><link linkend="beast.ref.buffers_adapter">buffers_adapter</link></member>
<member><link linkend="beast.ref.consuming_buffers">consuming_buffers</link></member>
<member><link linkend="beast.ref.dynabuf_readstream">dynabuf_readstream</link></member>
<member><link linkend="beast.ref.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_condition">error_condition</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_n">static_streambuf_n</link></member>
<member><link linkend="beast.ref.static_string">static_string</link></member>
@@ -153,18 +187,15 @@
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.bind_handler">bind_handler</link></member>
<member><link linkend="beast.ref.buffer_cat">buffer_cat</link></member>
<member><link linkend="beast.ref.consumed_buffers">consumed_buffers</link></member>
<member><link linkend="beast.ref.prepare_buffer">prepare_buffer</link></member>
<member><link linkend="beast.ref.prepare_buffers">prepare_buffers</link></member>
<member><link linkend="beast.ref.to_string">to_string</link></member>
<member><link linkend="beast.ref.write">write</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.is_AsyncReadStream">is_AsyncReadStream</link></member>
<member><link linkend="beast.ref.is_AsyncWriteStream">is_AsyncWriteStream</link></member>
<member><link linkend="beast.ref.is_AsyncStream">is_AsyncStream</link></member>
@@ -181,11 +212,29 @@
<entry valign="top">
<bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.types.streams.AsyncStream">AsyncStream</link></member>
<member><link linkend="beast.types.BufferSequence">BufferSequence</link></member>
<member><link linkend="beast.types.DynamicBuffer">DynamicBuffer</link></member>
<member><link linkend="beast.types.streams.Stream">Stream</link></member>
<member><link linkend="beast.types.streams.SyncStream">SyncStream</link></member>
<member><link linkend="beast.ref.streams.AsyncStream">AsyncStream</link></member>
<member><link linkend="beast.ref.BufferSequence">BufferSequence</link></member>
<member><link linkend="beast.ref.DynamicBuffer">DynamicBuffer</link></member>
<member><link linkend="beast.ref.streams.Stream">Stream</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>
</entry>
</row>

View File

@@ -31,8 +31,6 @@
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:ref Reference]
</xsl:text>
<xsl:for-each select="
compounddef[@kind = 'class' or @kind = 'struct'] |
@@ -59,7 +57,6 @@
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:text>&#xd;[endsect]</xsl:text>
</xsl:template>
<!--========== Utilities ==========-->
@@ -165,7 +162,7 @@
<xsl:text>``['implementation-defined]``</xsl:text>
</xsl:when>
<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:otherwise>
<xsl:value-of select="$type"/>
@@ -198,6 +195,18 @@
select="concat(substring-before($name, '::'), '__', substring-after($name, '::'))"/>
</xsl:call-template>
</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:call-template name="make-id">
<xsl:with-param name="name"
@@ -270,10 +279,10 @@
select="concat(substring-before($name, '*'), '_star_', substring-after($name, '*'))"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($name, '~')">
<xsl:when test="starts-with($name, '~')">
<xsl:call-template name="make-id">
<xsl:with-param name="name"
select="concat(substring-before($name, '~'), '_', substring-after($name, '~'))"/>
select="concat(substring-after($name, '~'), '_dtor_')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($name, ' ')">
@@ -1055,10 +1064,26 @@
</xsl:for-each>
<xsl:text>]&#xd;</xsl:text>
</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>[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:text> [&#xd;</xsl:text>
<xsl:text> [[link beast.ref.</xsl:text>
@@ -1528,47 +1553,50 @@
<xsl:text> </xsl:text>
<xsl:choose>
<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 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 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 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 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 test="(type = 'class' or type = 'class...') and declname = 'BufferSequence'">
<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 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 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 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 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 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 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 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 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 test="declname = 'T'">
@@ -1682,6 +1710,14 @@
</xsl:choose>
<xsl:text>```&#xd;</xsl:text>
<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:call-template name="cleanup-type">
<xsl:with-param name="name" select="type"/>

View File

@@ -107,52 +107,8 @@ INPUT = \
../include/beast/core \
../include/beast/http \
../include/beast/websocket \
../include/beast/doc_debug.hpp \
../include/beast/async_completion.hpp \
../include/beast/basic_streambuf.hpp \
../include/beast/bind_handler.hpp \
../include/beast/buffer_cat.hpp \
../include/beast/buffers_adapter.hpp \
../include/beast/consuming_buffers.hpp \
../include/beast/handler_alloc.hpp \
../include/beast/http.hpp \
../include/beast/placeholders.hpp \
../include/beast/prepare_buffers.hpp \
../include/beast/static_streambuf.hpp \
../include/beast/streambuf.hpp \
../include/beast/streambuf_readstream.hpp \
../include/beast/to_string.hpp \
../include/beast/type_check.hpp \
../include/beast/websocket.hpp \
../include/beast/write_streambuf.hpp \
../include/beast/http/basic_headers.hpp \
../include/beast/http/basic_parser_v1.hpp \
../include/beast/http/body_writer.hpp \
../include/beast/http/chunk_encode.hpp \
../include/beast/http/empty_body.hpp \
../include/beast/http/error.hpp \
../include/beast/http/fields.hpp \
../include/beast/http/headers.hpp \
../include/beast/http/message.hpp \
../include/beast/http/message_v1.hpp \
../include/beast/http/method.hpp \
../include/beast/http/parse_error.hpp \
../include/beast/http/parser.hpp \
../include/beast/http/read.hpp \
../include/beast/http/resume_context.hpp \
../include/beast/http/rfc2616.hpp \
../include/beast/http/streambuf_body.hpp \
../include/beast/http/string_body.hpp \
../include/beast/http/type_check.hpp \
../include/beast/http/write.hpp \
../include/beast/websocket/error.hpp \
../include/beast/websocket/option.hpp \
../include/beast/websocket/rfc6455.hpp \
../include/beast/websocket/ssl.hpp \
../include/beast/websocket/static_string.hpp \
../include/beast/websocket/stream.hpp \
../include/beast/websocket/teardown.hpp \
../include/beast/zlib \
../extras/beast/doc_debug.hpp
INPUT_ENCODING = UTF-8
FILE_PATTERNS =

View File

@@ -5,7 +5,11 @@
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:
@@ -22,17 +26,12 @@ In this table:
will be not movable or not copyable.
]
]
[
[`X:value_type{}`]
[]
[`DefaultConstructible`]
]
[
[`Body::reader`]
[]
[
If present, a type meeting the requirements of
[link beast.types.Reader [*`Reader`]].
[link beast.ref.Reader [*`Reader`]].
Provides an implementation to parse the body.
]
]
@@ -41,7 +40,7 @@ In this table:
[]
[
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.
]
]

View File

@@ -5,7 +5,7 @@
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:

View File

@@ -5,7 +5,7 @@
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
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
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`].
In the table below:
@@ -88,7 +88,7 @@ In the table below:
]
[
[`a.prepare(n)`]
[`X:mutable_buffers_type`]
[`X::mutable_buffers_type`]
[
Returns a mutable buffer sequence u representing the output sequence,
and where `buffer_size(u) == n`. The dynamic buffer reallocates memory

View File

@@ -5,7 +5,7 @@
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.

View File

@@ -5,16 +5,17 @@
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
the requirements of [link beast.types.Field [*`Field`]].
A [*FieldSequence] is an iterable container whose value type meets
the requirements of [link beast.ref.Field [*Field]]. Objects that meet
these requirements become serializable by the implementation.
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
[[operation][type][semantics, pre/post-conditions]]
@@ -22,25 +23,33 @@ In this table:
[`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`]
[]
[
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`]
[
Returns an iterator to the beginning of the field sequence.
]
]
[
[`a.end()`]
[`c.end()`]
[`X::const_iterator`]
[
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)
]
[section:Parser Parser]
[section:Parser Parser requirements]
A [*`Parser`] is used to deserialize HTTP/1 messages from [link beast.types.streams streams].
Objects of this type are used with [link beast.ref.http__parse http::parse] and
[link beast.ref.http__async_parse http::async_parse].
A [*Parser] is used to deserialize objects from
[link beast.ref.streams streams]. Objects of this type are used with
[link beast.ref.http__parse http::parse] and
[link beast.ref.http__async_parse http::async_parse]. The definition of
an object, and the predicate defining when the parse is complete, are
determined by the implementation.
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`.
* `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&`].
@@ -27,18 +30,18 @@ In this table:
[`a.complete()`]
[`bool`]
[
Returns `true` when a complete HTTP/1 message has been parsed.
Returns `true` when parsing is complete.
]
]
[
[`a.write(b, ec)`]
[`std::size_t`]
[
Parses the octets in the specified input buffer sequentially until
an error occurs, the end of the buffer is reached, or a complete
HTTP/1 message has been parsed. If an error occurs, `ec` is set
to the error code and parsing stops. This function returns the
number of bytes consumed from the input buffer.
Sequentially parses the octets in the specified input buffer sequence
until an error occurs, the end of the buffer is reached, or parsing is
complete. Upon success, this function returns the number of bytes used
from the input. If an error occurs, `ec` is set to the error code and
parsing stops.
]
]
[
@@ -48,9 +51,9 @@ In this table:
Indicates to the parser that no more octets will be available.
Typically this function is called when the end of stream is reached.
For example, if a call to `boost::asio::ip::tcp::socket::read_some`
generates a `boost::asio::error::eof` error. Some HTTP/1 messages
determine the end of the message body by an end of file marker or
closing of the connection.
generates a `boost::asio::error::eof` error. Some objects, such as
certain HTTP/1 messages, determine the end of the message body by
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)
]
[section:Reader Reader]
[section:Reader Reader requirements]
Parser implementations will construct the corresponding `reader` object
during the parse. This customization point allows the Body to determine
the strategy for storing incoming message body data.
Parsers provided by the implementation will construct the corresponding
`reader` object during parsing. This customization point allows the
Body to determine the strategy for storing incoming message body data.
In this table:
@@ -17,15 +17,14 @@ In this table:
* `a` denotes a value of type `X`.
* `p` is any pointer.
* `n` is a value convertible to `std::size_t`.
* `ec` is a value of type `error_code&`.
* `p` is a `void const*` to valid memory of at least `n` bytes.
* `m` denotes a value of type `message const&` where
`std::is_same<decltype(m.body), Body::value_type>:value == true`
* `ec` is a value of type [link beast.ref.error_code `error_code&`].
* `m` denotes a value of type `message&` where
`std::is_same<decltype(m.body), Body::value_type>::value == true`.
[table Reader requirements
[[operation] [type] [semantics, pre/post-conditions]]
@@ -33,22 +32,38 @@ In this table:
[`X a(m);`]
[]
[
`a` is constructible from `m`. The lifetime of `m` is
guaranteed to end no earlier than after `a` is destroyed.
`a` is constructible from `m`. The lifetime of `m` is guaranteed
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)`]
[`void`]
[
Deserializes the input sequence into the body.
If `ec` is set, the deserialization is aborted and the error
is returned to the caller.
Deserializes the input sequence into the body. If `ec` is set,
the deserialization is aborted and the error is propagated to
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
inline so the generated code becomes part of the implementation. ]
[note
Definitions for required `Reader` member functions should be declared
inline so the generated code can become part of the implementation.
]
[endsect]

View File

@@ -5,7 +5,7 @@
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
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)
]
[section:Writer Writer]
[section:Writer Writer requirements]
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
@@ -28,13 +28,13 @@ In this table:
* `m` denotes a value of type `message const&` where
`std::is_same<decltype(m.body), Body::value_type>:value == true`.
* `rc` is an object of type [link beast.ref.http__resume_context resume_context].
* `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
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
[[operation] [type] [semantics, pre/post-conditions]]
@@ -42,17 +42,18 @@ In this table:
[`X a(m);`]
[]
[
`a` is constructible from `m`. The lifetime of `m` is
guaranteed to end no earlier than after `a` is destroyed.
`a` is constructible from `m`. The lifetime of `m` is guaranteed
to end no earlier than after `a` is destroyed. This function must
be `noexcept`.
]
]
[
[`a.init(ec)`]
[`void`]
[
Called immediately after construction.
If `ec` is set, the serialization is aborted and the error
is propagated to the caller.
Called immediately after construction. If the function sets an
error code in `ec`, the serialization is aborted and the error
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
error `boost::asio::error::eof` returned to the caller, to notify
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`]
[
Called repeatedly after `init` succeeds.
`wf` is a function object which takes as its single parameter,
any value meeting the requirements of `ConstBufferSequence`.
Buffers provided by the `writer` to this [*write function] must
remain valid until the next member function of `writer` is
Called repeatedly after `init` succeeds. `wf` is a function object
which takes as its single parameter any value meeting the requirements
of __ConstBufferSequence__. Buffers provided to this write function
must remain valid until the next member function of `writer` is
invoked (which may be the destructor). This function returns `true`
to indicate all message body data has been written, or `false`
if there is more body data. If the return value is
`boost::indeterminate`, the implementation will suspend the operation
until the writer invokes `rc`. It is the writers responsibility when
returning `boost::indeterminate`, to acquire ownership of the
`resume_context` via move construction and eventually call it or else
undefined behavior results.
to indicate all message body data has been written, or `false` if
there is more body data. If the return value is `boost::indeterminate`,
the implementation will suspend the operation until the writer invokes
`rc`. It is the writers responsibility when returning
`boost::indeterminate`, to acquire ownership of `rc` via move
construction and eventually call it or else undefined behavior
results. This function must be `noexcept`.
]
]
]
[note Definitions for required `Writer` member functions should be declared
inline so the generated code becomes part of the implementation. ]
[note
Definitions for required `Writer` member functions should be declared
inline so the generated code can become part of the implementation.
]
Exemplar:
```
@@ -109,7 +112,7 @@ public:
*/
template<bool isRequest, class Body, class Headers>
explicit
writer(message<isRequest, Body, Headers> const& msg);
writer(message<isRequest, Body, Headers> const& msg) noexcept;
/** Initialize the writer.
@@ -119,7 +122,7 @@ public:
@param ec Contains the error code if any errors occur.
*/
void
init(error_code& ec);
init(error_code& ec) noexcept;
/** Returns the content length.
@@ -128,8 +131,8 @@ public:
use chunk-encoding or terminate the connection to indicate the end
of the message.
*/
std::size_t
content_length() const;
std::uint64_t
content_length() noexcept;
/** Write zero or one buffer representing the message body.
@@ -172,7 +175,10 @@ public:
*/
template<class WriteFunction>
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]
[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
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
@@ -22,48 +37,12 @@ C++ approach.
The WebSocket protocol is described fully in
[@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]
@@ -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
class [link beast.ref.websocket__stream `beast::websocket::stream`] which
wraps a "next layer" object. The next layer object must meet the requirements
of [link beast.types.streams.SyncStream [*`SyncReadStream`]] if synchronous
of [link beast.ref.streams.SyncStream [*`SyncReadStream`]] if synchronous
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
passed to next layer's constructor. Here we declare a websocket stream over
a TCP/IP socket with ownership of the socket:
```
boost::asio::io_service ios;
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
```
[heading Using SSL]
@@ -92,8 +71,8 @@ argument when constructing the stream.
#include <boost/asio/ssl.hpp>
boost::asio::io_service ios;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ws(ios, ctx);
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ws{ios, ctx};
```
[note
@@ -108,7 +87,7 @@ to wrap an object that already exists. This socket can be moved in:
```
boost::asio::ip::tcp::socket&& sock;
...
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(std::move(sock));
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{std::move(sock)};
```
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;
...
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",
permitting callers to interact directly with its interface.
```
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws(ios, ctx);
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws{ios, ctx};
...
ws.next_layer().shutdown(); // ssl::stream shutdown
```
[important Initiating read and write operations on the next layer while
websocket operations are being performed can break invariants, and
result in undefined behavior. ]
[warning
Initiating read and write operations on the next layer while
stream operations are being performed can break invariants, and
result in undefined behavior.
]
[endsect]
[section:connecting Making connections]
[section:connections Making connections]
Connections are established by using the interfaces which already exist
for the next layer. For example, making an outgoing connection:
```
std::string const host = "mywebapp.com";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r(ios);
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
boost::asio::ip::tcp::resolver r{ios};
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
boost::asio::connect(ws.next_layer(),
r.resolve(boost::asio::ip::tcp::resolver::query{host, "ws"}));
```
@@ -154,12 +135,14 @@ Accepting an incoming connection:
```
void do_accept(boost::asio::ip::tcp::acceptor& acceptor)
{
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(acceptor.get_io_service());
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{acceptor.get_io_service()};
acceptor.accept(ws.next_layer());
}
```
[note Examples use synchronous interfaces for clarity of exposition. ]
[note
Examples use synchronous interfaces for clarity of exposition.
]
[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
indicating that the request was accepted and that the connection has
been upgraded. The HTTP Upgrade request must include the Host HTTP field,
and the URI of the resource to request. `handshake` is used to send the
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.
```
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.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.
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
@@ -190,7 +174,7 @@ open for a subsequent handshake attempt
Performing a handshake for an incoming websocket upgrade request operates
similarly. If the handshake fails, an error is returned or exception thrown:
```
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
...
ws.accept();
```
@@ -205,7 +189,7 @@ void do_accept(boost::asio::ip::tcp::socket& sock)
boost::asio::streambuf sb;
boost::asio::read_until(sock, sb, "\r\n\r\n");
...
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
ws.accept(sb.data());
...
}
@@ -218,10 +202,10 @@ void do_accept(boost::asio::ip::tcp::socket& sock)
{
boost::asio::streambuf sb;
beast::http::request<http::empty_body> request;
beast::http::read(sock, request);
beast::http::read(sock, sb, request);
if(beast::http::is_upgrade(request))
{
websocket::stream<ip::tcp::socket&> ws(sock);
websocket::stream<ip::tcp::socket&> ws{sock};
ws.accept(request);
...
}
@@ -242,17 +226,19 @@ void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
{
beast::streambuf sb;
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());
sb.consume(sb.size());
}
```
[important Calls to [link beast.ref.websocket__stream.set_option `set_option`]
must be made from the same implicit or explicit strand as that used to perform
other operations. ]
[important
Calls to [link beast.ref.websocket__stream.set_option `set_option`]
must be made from the same implicit or explicit strand as that used
to perform other operations.
]
[endsect]
@@ -280,9 +266,9 @@ void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
if(fi.fin)
break;
}
ws.set_option(beast::websocket::message_type(fi.op));
ws.set_option(beast::websocket::message_type{fi.op});
beast::consuming_buffers<
beast::streambuf::const_buffers_type> cb(sb.data());
beast::streambuf::const_buffers_type> cb{sb.data()};
for(;;)
{
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
WebSocket control frames such as ping, pong, and close. Pings are replied
to as soon as possible, pongs are delivered to the pong callback. The receipt
of a close frame initiates the WebSocket close procedure, eventually resulting
in the error code [link beast.ref.websocket__error `error::closed`] being
delivered to the caller in a subsequent read operation, assuming no other error
Control frames are small (less than 128 bytes) messages entirely contained
in an individual WebSocket frame. They may be sent at any time by either
peer on an established connection, and can appear in between continuation
frames for a message. There are three types of control frames: ping, pong,
and close.
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.
To ensure timely delivery of control frames, large messages are broken up
into smaller sized frames. The implementation chooses the size and number
of the frames making up the message. The automatic fragment size option
gives callers control over the size of these frames:
```
...
ws.set_option(beast::websocket::auto_fragment_size(8192));
```
A consequence of this automatic behavior is that caller-initiated read
operations can cause socket writes. However, these writes will not
compete with caller-initiated write operations. For the purposes of
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
the implementation also automatically handles control 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
[link beast.ref.websocket__stream.close `close`]:
```
ws.close();
```
[heading Ping and Pong Frames]
[note To receive the [link beast.ref.websocket__error `error::closed`]
error, a read operation is required. ]
[endsect]
[section:pongs Pong messages]
Ping and pong messages are control frames which may be sent at any time
by either peer on an established WebSocket connection. They are sent
using the functions
[link beast.ref.websocket__stream.ping `ping`] and
[link beast.ref.websocket__stream.pong `pong`].
To receive pong control frames, callers may register a "pong callback" using
[link beast.ref.websocket__stream.set_option `set_option`]:
the following signature:
[link beast.ref.websocket__stream.set_option `set_option`]. The object provided
with this option should be callable with the following signature:
```
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
pongs, a synchronous or asynchronous stream read function must be active.
[note When an asynchronous read function receives a pong, the the pong callback
is invoked in the same manner as that used to invoke the final completion
handler of the corresponding read function.]
[note
When an asynchronous read function receives a pong, the the pong
callback is invoked in the same manner as that used to invoke the
final completion handler of the corresponding read function.
]
[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]
@@ -373,7 +400,7 @@ handler of the corresponding read function.]
Because calls to read data may return a variable amount of bytes, the
interface to calls that read data require an object that meets the requirements
of [link beast.types.DynamicBuffer [*`DynamicBuffer`]]. This concept is modeled on
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`].
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
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)
#
import os ;
exe http-crawl :
http_crawl.cpp
urls_large_data.cpp
@@ -23,4 +21,3 @@ exe http-example :
exe websocket-example :
websocket_example.cpp
;

View File

@@ -8,9 +8,12 @@
#ifndef 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/filesystem.hpp>
#include <boost/logic/tribool.hpp>
#include <cstdio>
#include <cstdint>
@@ -34,8 +37,8 @@ struct file_body
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
writer(message<isRequest, file_body, Headers> const& m) noexcept
template<bool isRequest, class Fields>
writer(message<isRequest, file_body, Fields> const& m) noexcept
: path_(m.body)
{
}
@@ -58,14 +61,15 @@ struct file_body
}
std::uint64_t
content_length() const
content_length() const noexcept
{
return size_;
}
template<class Write>
template<class WriteFunction>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
write(resume_context&&, error_code&,
WriteFunction&& wf) noexcept
{
if(size_ - offset_ < sizeof(buf_))
buf_len_ = static_cast<std::size_t>(
@@ -75,7 +79,7 @@ struct file_body
auto const nread = fread(buf_, 1, sizeof(buf_), file_);
(void)nread;
offset_ += buf_len_;
write(boost::asio::buffer(buf_, buf_len_));
wf(boost::asio::buffer(buf_, buf_len_));
return offset_ >= size_;
}
};

View File

@@ -12,6 +12,8 @@
#include "mime_type.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/streambuf.hpp>
#include <boost/asio.hpp>
@@ -32,8 +34,8 @@ class http_async_server
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
using req_type = request_v1<string_body>;
using resp_type = response_v1<file_body>;
using req_type = request<string_body>;
using resp_type = response<file_body>;
std::mutex m_;
bool log_ = true;
@@ -85,32 +87,26 @@ public:
private:
template<class Stream, class Handler,
bool isRequest, class Body, class Headers>
bool isRequest, class Body, class Fields>
class write_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
Stream& s;
message_v1<isRequest, Body, Headers> m;
Handler h;
bool cont;
Stream& s;
message<isRequest, Body, Fields> m;
template<class DeducedHandler>
data(DeducedHandler&& h_, Stream& s_,
message_v1<isRequest, Body, Headers>&& m_)
: s(s_)
data(Handler& handler, Stream& s_,
message<isRequest, Body, Fields>&& m_)
: cont(beast_asio_helpers::
is_continuation(handler))
, s(s_)
, m(std::move(m_))
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
handler_ptr<data, Handler> d_;
public:
write_op(write_op&&) = default;
@@ -118,7 +114,7 @@ private:
template<class DeducedHandler, class... 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<Args>(args)...))
{
@@ -135,23 +131,23 @@ private:
beast::http::async_write(d.s, d.m, std::move(*this));
return;
}
d.h(ec);
d_.invoke(ec);
}
friend
void* asio_handler_allocate(
std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
return beast_asio_helpers::
allocate(size, op->d_.handler());
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
return beast_asio_helpers::
deallocate(p, size, op->d_.handler());
}
friend
@@ -164,22 +160,22 @@ private:
friend
void asio_handler_invoke(Function&& f, write_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
return beast_asio_helpers::
invoke(f, op->d_.handler());
}
};
template<class Stream,
bool isRequest, class Body, class Headers,
bool isRequest, class Body, class Fields,
class DeducedHandler>
static
void
async_write(Stream& stream, message_v1<
isRequest, Body, Headers>&& msg,
async_write(Stream& stream, message<
isRequest, Body, Fields>&& msg,
DeducedHandler&& handler)
{
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)};
}
@@ -236,12 +232,12 @@ private:
path = server_.root_ + path;
if(! boost::filesystem::exists(path))
{
response_v1<string_body> res;
response<string_body> res;
res.status = 404;
res.reason = "Not Found";
res.version = req_.version;
res.headers.insert("Server", "http_async_server");
res.headers.insert("Content-Type", "text/html");
res.fields.insert("Server", "http_async_server");
res.fields.insert("Content-Type", "text/html");
res.body = "The file '" + path + "' was not found";
prepare(res);
async_write(sock_, std::move(res),
@@ -249,32 +245,35 @@ private:
asio::placeholders::error));
return;
}
resp_type res;
res.status = 200;
res.reason = "OK";
res.version = req_.version;
res.headers.insert("Server", "http_async_server");
res.headers.insert("Content-Type", mime_type(path));
res.body = path;
try
{
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);
async_write(sock_, std::move(res),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
}
catch(std::exception const& e)
{
res = {};
response<string_body> res;
res.status = 500;
res.reason = "Internal Error";
res.version = req_.version;
res.headers.insert("Server", "http_async_server");
res.headers.insert("Content-Type", "text/html");
res.fields.insert("Server", "http_async_server");
res.fields.insert("Content-Type", "text/html");
res.body =
std::string{"An internal error occurred"} + e.what();
prepare(res);
async_write(sock_, std::move(res),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
}
async_write(sock_, std::move(res),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
}
void on_write(error_code ec)

View File

@@ -10,6 +10,7 @@
#include <beast/core/streambuf.hpp>
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>
using namespace beast::http;
@@ -35,21 +36,21 @@ int main(int, char const*[])
ip::tcp::socket sock(ios);
connect(sock, it);
auto ep = sock.remote_endpoint();
request_v1<empty_body> req;
request<empty_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;
req.headers.insert("Host", host +
std::string(":") + std::to_string(ep.port()));
req.headers.insert("User-Agent", "beast/http");
req.fields.insert("Host", host + std::string(":") +
boost::lexical_cast<std::string>(ep.port()));
req.fields.insert("User-Agent", "beast/http");
prepare(req);
write(sock, req);
response_v1<string_body> res;
response<string_body> res;
streambuf sb;
beast::http::read(sock, sb, res);
std::cout << res;
}
catch(boost::system::system_error const& ec)
catch(beast::system_error const& ec)
{
std::cerr << host << ": " << ec.what();
}

View File

@@ -7,6 +7,7 @@
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <string>
@@ -21,18 +22,19 @@ int main()
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
// 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.url = "/";
req.version = 11;
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast");
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_v1<beast::http::streambuf_body> resp;
beast::http::response<beast::http::streambuf_body> resp;
beast::http::read(sock, sb, resp);
std::cout << resp;
}

View File

@@ -20,34 +20,26 @@ int main(int ac, char const* av[])
po::options_description desc("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")
("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")
("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")
("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")
("sync,s", "Launch a synchronous server")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
std::string root = ".";
if(vm.count("root"))
root = vm["root"].as<std::string>();
std::string root = vm["root"].as<std::string>();
std::uint16_t port = 8080;
if(vm.count("port"))
port = vm["port"].as<std::uint16_t>();
std::uint16_t port = vm["port"].as<std::uint16_t>();
std::string ip = "0.0.0.0";
if(vm.count("ip"))
ip = vm["ip"].as<std::string>();
std::string ip = vm["ip"].as<std::string>();
std::size_t threads = 4;
if(vm.count("threads"))
threads = vm["threads"].as<std::size_t>();
std::size_t threads = vm["threads"].as<std::size_t>();
bool sync = vm.count("sync") > 0;

View File

@@ -11,6 +11,8 @@
#include "file_body.hpp"
#include "mime_type.hpp"
#include <beast/http.hpp>
#include <beast/core/placeholders.hpp>
#include <beast/core/streambuf.hpp>
#include <boost/asio.hpp>
#include <cstdint>
@@ -19,6 +21,7 @@
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
#include <iostream>
@@ -32,8 +35,8 @@ class http_sync_server
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
using req_type = request_v1<string_body>;
using resp_type = response_v1<file_body>;
using req_type = request<string_body>;
using resp_type = response<file_body>;
bool log_ = true;
std::mutex m_;
@@ -161,44 +164,48 @@ private:
path = root_ + path;
if(! boost::filesystem::exists(path))
{
response_v1<string_body> res;
response<string_body> res;
res.status = 404;
res.reason = "Not Found";
res.version = req.version;
res.headers.insert("Server", "http_sync_server");
res.headers.insert("Content-Type", "text/html");
res.fields.insert("Server", "http_sync_server");
res.fields.insert("Content-Type", "text/html");
res.body = "The file '" + path + "' was not found";
prepare(res);
write(sock, res, ec);
if(ec)
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
{
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);
write(sock, res, ec);
if(ec)
break;
}
catch(std::exception const& e)
{
res = {};
response<string_body> res;
res.status = 500;
res.reason = "Internal Error";
res.version = req.version;
res.headers.insert("Server", "http_sync_server");
res.headers.insert("Content-Type", "text/html");
res.fields.insert("Server", "http_sync_server");
res.fields.insert("Content-Type", "text/html");
res.body =
std::string{"An internal error occurred"} + e.what();
std::string{"An internal error occurred: "} + e.what();
prepare(res);
write(sock, res, ec);
if(ec)
break;
}
write(sock, res, ec);
if(ec)
break;
}
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()

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
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
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
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";
std::cout << beast::to_string(sb.data()) << "\n";
}

View File

@@ -13,11 +13,9 @@
namespace beast {
namespace test {
enum error
enum class error
{
success = 0,
fail_error
fail_error = 1
};
namespace detail {
@@ -79,14 +77,15 @@ inline
error_code
make_error_code(error ev)
{
return error_code{static_cast<int>(ev),
detail::get_error_category()};
return error_code{
static_cast<std::underlying_type<error>::type>(ev),
detail::get_error_category()};
}
/** A countdown to simulated failure.
On the Nth operation, the class will fail with the specified
error code, or the default error code of @ref fail_error.
error code, or the default error code of @ref error::fail_error.
*/
class fail_counter
{
@@ -102,7 +101,7 @@ public:
*/
explicit
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)
, ec_(ev)
{

View File

@@ -8,6 +8,7 @@
#ifndef 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/error.hpp>
#include <boost/asio/buffer.hpp>

View File

@@ -10,6 +10,7 @@
#include <beast/unit_test/amount.hpp>
#include <beast/unit_test/recorder.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/optional.hpp>
#include <algorithm>
#include <chrono>
@@ -214,7 +215,8 @@ reporter<_>::fmtdur(typename clock_type::duration const& d)
using namespace std::chrono;
auto const ms = duration_cast<milliseconds>(d);
if(ms < seconds{1})
return std::to_string(ms.count()) + "ms";
return boost::lexical_cast<std::string>(
ms.count()) + "ms";
std::stringstream ss;
ss << std::fixed << std::setprecision(1) <<
(ms.count()/1000.) << "s";

View File

@@ -9,7 +9,7 @@
#define BEAST_UNIT_TEST_RUNNER_H_INCLUDED
#include <beast/unit_test/suite_info.hpp>
#include <cassert>
#include <boost/assert.hpp>
#include <mutex>
#include <ostream>
#include <string>
@@ -185,7 +185,7 @@ runner::run(suite_info const& s)
on_suite_begin(s);
s.run(*this);
// Forgot to call pass or fail.
assert(cond_);
BOOST_ASSERT(cond_);
on_case_end();
on_suite_end();
return failed_;
@@ -239,9 +239,9 @@ runner::testcase(std::string const& name)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
// Name may not be empty
assert(default_ || ! name.empty());
BOOST_ASSERT(default_ || ! name.empty());
// Forgot to call pass or fail
assert(default_ || cond_);
BOOST_ASSERT(default_ || cond_);
if(! default_)
on_case_end();
default_ = false;

View File

@@ -10,6 +10,7 @@
#include <beast/unit_test/runner.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <ostream>
#include <sstream>
#include <string>
@@ -17,6 +18,27 @@
namespace beast {
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;
enum abort_t
@@ -178,11 +200,21 @@ public:
/** 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>
void
fail(std::string const& reason = "");
/** @} */
/** Evaluate a test condition.
@@ -206,25 +238,25 @@ public:
bool
expect(Condition const& shouldBeTrue)
{
return expect(shouldBeTrue, {});
return expect(shouldBeTrue, "");
}
template<class Condition>
template<class Condition, class String>
bool
expect(Condition const& shouldBeTrue, std::string const& reason);
expect(Condition const& shouldBeTrue, String const& reason);
template<class Condition>
bool
expect(Condition const& shouldBeTrue,
char const* file, int line)
{
return expect(shouldBeTrue, {}, file, line);
return expect(shouldBeTrue, "", file, line);
}
template<class Condition>
template<class Condition, class String>
bool
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
suite::
expect(
Condition const& shouldBeTrue, std::string const& reason)
Condition const& shouldBeTrue, String const& reason)
{
if(shouldBeTrue)
{
@@ -417,26 +449,18 @@ expect(
return false;
}
template<class Condition>
template<class Condition, class String>
bool
suite::
expect(Condition const& shouldBeTrue,
std::string const& reason, char const* file, int line)
String const& reason, char const* file, int line)
{
if(shouldBeTrue)
{
pass();
return true;
}
std::string s;
if(! reason.empty())
{
s += reason;
s += " ";
}
s += boost::filesystem::path{file}.filename().string() +
"(" + std::to_string(line) + ")";
fail(s);
fail(detail::make_reason(reason, file, line));
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
void
suite::
@@ -583,7 +615,8 @@ run(runner& r)
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
} // unit_test

View File

@@ -10,7 +10,7 @@
#include <beast/unit_test/suite_info.hpp>
#include <beast/unit_test/detail/const_container.hpp>
#include <cassert>
#include <boost/assert.hpp>
#include <typeindex>
#include <set>
#include <unordered_set>
@@ -57,13 +57,13 @@ suite_list::insert(
std::string s;
s = std::string(library) + "." + module + "." + name;
auto const result(names_.insert(s));
assert(result.second); // Duplicate name
BOOST_ASSERT(result.second); // Duplicate name
}
{
auto const result(classes_.insert(
std::type_index(typeid(Suite))));
assert(result.second); // Duplicate type
BOOST_ASSERT(result.second); // Duplicate type
}
#endif
cont().emplace(make_suite_info<Suite>(

View File

@@ -18,6 +18,8 @@
#include <beast/core/error.hpp>
#include <beast/core/handler_alloc.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/prepare_buffers.hpp>
#include <beast/core/static_streambuf.hpp>

View File

@@ -34,11 +34,11 @@ namespace beast {
...
template<class CompletionHandler>
typename async_completion<CompletionHandler,
void(boost::system::error_code)>::result_type
void(error_code)>::result_type
async_initfn(..., CompletionHandler&& handler)
{
async_completion<CompletionHandler,
void(boost::system::error_code)> completion(handler);
void(error_code)> completion(handler);
...
return completion.result.get();
}

View File

@@ -25,7 +25,7 @@ namespace beast {
the sequence to accommodate changes in the size of the character
sequence.
@note Meets the requirements of @b `DynamicBuffer`.
@note Meets the requirements of @b DynamicBuffer.
@tparam Allocator The allocator to use for managing memory.
*/
@@ -209,6 +209,36 @@ public:
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.
size_type
size() const
@@ -220,7 +250,7 @@ public:
size_type
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.

View File

@@ -19,18 +19,21 @@
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,
efficiently concatenates the input buffer sequences. Copies of the
arguments passed will be made; however, the returned object does
not take ownership of the underlying memory. The application is still
responsible for managing the lifetime of the referenced memory.
This function returns a constant or mutable buffer sequence which,
when iterated, efficiently concatenates the input buffer sequences.
Copies of the arguments passed will be made; however, the returned
object does not take ownership of the underlying memory. The application
is still responsible for managing the lifetime of the referenced memory.
@param buffers The list of buffer sequences to concatenate.
@return A new @b `ConstBufferSequence` that represents the
concatenation of the input buffer sequences.
@return A new buffer sequence that represents the concatenation of
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
template<class... BufferSequence>
@@ -38,14 +41,15 @@ implementation_defined
buffer_cat(BufferSequence const&... buffers)
#else
template<class B1, class B2, class... Bn>
detail::buffer_cat_helper<
boost::asio::const_buffer, B1, B2, Bn...>
detail::buffer_cat_helper<B1, B2, Bn...>
buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
#endif
{
static_assert(
detail::is_all_ConstBufferSequence<B1, B2, Bn...>::value,
"BufferSequence requirements not met");
return detail::buffer_cat_helper<
boost::asio::const_buffer,
B1, B2, Bn...>(b1, b2, bn...);
B1, B2, Bn...>{b1, b2, bn...};
}
} // beast

View File

@@ -37,20 +37,12 @@ namespace beast {
consumable `ConstBufferSequence`. Violations of buffer const safety
are not permitted, and will result in a compile error.
*/
template<class BufferSequence,
class ValueType = typename BufferSequence::value_type>
template<class BufferSequence>
class consuming_buffers
{
using iter_type =
typename BufferSequence::const_iterator;
static_assert(is_BufferSequence<BufferSequence, ValueType>::value,
"BufferSequence requirements not met");
static_assert(std::is_constructible<ValueType,
typename std::iterator_traits<iter_type>::value_type>::value,
"ValueType requirements not met");
BufferSequence bs_;
iter_type begin_;
std::size_t skip_ = 0;
@@ -65,7 +57,12 @@ class consuming_buffers
public:
/// 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
/// A bidirectional iterator type that may be used to read elements.
@@ -100,7 +97,7 @@ public:
const_iterator
begin() const;
/// Get a bidirectional iterator for one past the last element.
/// Get a bidirectional iterator to one past the last element.
const_iterator
end() const;
@@ -114,25 +111,6 @@ public:
consume(std::size_t n);
};
/** Returns a new, consumed buffer sequence.
This function returns a new buffer sequence which when iterated,
efficiently represents the portion of the original buffer sequence
with `n` bytes removed from the beginning.
Copies will be made of the buffer sequence passed, but ownership
of the underlying memory is not transferred.
@param buffers The buffer sequence to consume.
@param n The number of bytes to remove from the front. If this is
larger than the size of the buffer sequence, an empty buffer sequence
is returned.
*/
template<class BufferSequence>
consuming_buffers<BufferSequence, typename BufferSequence::value_type>
consumed_buffers(BufferSequence const& buffers, std::size_t n);
} // beast
#include <beast/core/impl/consuming_buffers.ipp>

View File

@@ -8,10 +8,8 @@
#ifndef 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 <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/detail/handler_cont_helpers.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <utility>
namespace beast {
@@ -69,7 +67,7 @@ public:
asio_handler_allocate(
std::size_t size, bound_handler* h)
{
return boost_asio_handler_alloc_helpers::
return beast_asio_helpers::
allocate(size, h->h_);
}
@@ -78,7 +76,7 @@ public:
asio_handler_deallocate(
void* p, std::size_t size, bound_handler* h)
{
boost_asio_handler_alloc_helpers::
beast_asio_helpers::
deallocate(p, size, h->h_);
}
@@ -86,7 +84,7 @@ public:
bool
asio_handler_is_continuation(bound_handler* h)
{
return boost_asio_handler_cont_helpers::
return beast_asio_helpers::
is_continuation (h->h_);
}
@@ -95,7 +93,7 @@ public:
void
asio_handler_invoke(F&& f, bound_handler* h)
{
boost_asio_handler_invoke_helpers::
beast_asio_helpers::
invoke(f, h->h_);
}
};

View File

@@ -8,6 +8,8 @@
#ifndef 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 <cstdint>
#include <iterator>
@@ -19,24 +21,36 @@
namespace beast {
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
{
std::tuple<Bs...> bs_;
std::tuple<Bn...> bn_;
public:
using value_type = ValueType;
using value_type = typename
common_buffers_type<Bn...>::type;
class const_iterator;
buffer_cat_helper(buffer_cat_helper&&) = default;
buffer_cat_helper(buffer_cat_helper const&) = default;
buffer_cat_helper& operator=(buffer_cat_helper&&) = default;
buffer_cat_helper& operator=(buffer_cat_helper const&) = default;
buffer_cat_helper& operator=(buffer_cat_helper&&) = delete;
buffer_cat_helper& operator=(buffer_cat_helper const&) = delete;
explicit
buffer_cat_helper(Bs const&... bs)
: bs_(bs...)
buffer_cat_helper(Bn const&... bn)
: bn_(bn...)
{
}
@@ -47,39 +61,22 @@ public:
end() const;
};
template<class U>
std::size_t constexpr
max_sizeof()
{
return sizeof(U);
}
template<class U0, class U1, class... Us>
std::size_t constexpr
max_sizeof()
{
return
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
max_sizeof<U0>() : max_sizeof<U1, Us...>();
}
template<class ValueType, class... Bs>
class buffer_cat_helper<
ValueType, Bs...>::const_iterator
template<class... Bn>
class buffer_cat_helper<Bn...>::const_iterator
{
std::size_t n_;
std::tuple<Bs...> const* bs_;
std::tuple<Bn...> const* bn_;
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>
using C = std::integral_constant<std::size_t, I>;
template<std::size_t I>
using iter_t = typename std::tuple_element<
I, std::tuple<Bs...>>::type::const_iterator;
I, std::tuple<Bn...>>::type::const_iterator;
template<std::size_t I>
iter_t<I>&
@@ -98,7 +95,8 @@ class buffer_cat_helper<
}
public:
using value_type = ValueType;
using value_type = typename
common_buffers_type<Bn...>::type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
@@ -151,39 +149,39 @@ public:
private:
const_iterator(
std::tuple<Bs...> const& bs, bool at_end);
std::tuple<Bn...> const& bn, bool at_end);
void
construct(C<sizeof...(Bs)>)
construct(C<sizeof...(Bn)> const&)
{
auto constexpr I = sizeof...(Bs);
auto constexpr I = sizeof...(Bn);
n_ = I;
}
template<std::size_t I>
void
construct(C<I>)
construct(C<I> const&)
{
if(std::get<I>(*bs_).begin() !=
std::get<I>(*bs_).end())
if(std::get<I>(*bn_).begin() !=
std::get<I>(*bn_).end())
{
n_ = I;
new(buf_.data()) iter_t<I>{
std::get<I>(*bs_).begin()};
std::get<I>(*bn_).begin()};
return;
}
construct(C<I+1>{});
}
void
destroy(C<sizeof...(Bs)>)
destroy(C<sizeof...(Bn)> const&)
{
return;
}
template<std::size_t I>
void
destroy(C<I>)
destroy(C<I> const&)
{
if(n_ == I)
{
@@ -195,13 +193,15 @@ private:
}
void
move(C<sizeof...(Bs)>, const_iterator&&)
move(const_iterator&&,
C<sizeof...(Bn)> const&)
{
}
template<std::size_t I>
void
move(C<I>, const_iterator&& other)
move(const_iterator&& other,
C<I> const&)
{
if(n_ == I)
{
@@ -209,17 +209,19 @@ private:
std::move(other.iter<I>())};
return;
}
move(C<I+1>{}, std::move(other));
move(std::move(other), C<I+1>{});
}
void
copy(C<sizeof...(Bs)>, const_iterator const&)
copy(const_iterator const&,
C<sizeof...(Bn)> const&)
{
}
template<std::size_t I>
void
copy(C<I>, const_iterator const& other)
copy(const_iterator const& other,
C<I> const&)
{
if(n_ == I)
{
@@ -227,35 +229,36 @@ private:
other.iter<I>()};
return;
}
copy(C<I+1>{}, other);
copy(other, C<I+1>{});
}
bool
equal(C<sizeof...(Bs)>,
const_iterator const&) const
equal(const_iterator const&,
C<sizeof...(Bn)> const&) const
{
return true;
}
template<std::size_t I>
bool
equal(C<I>, const_iterator const& other) const
equal(const_iterator const& other,
C<I> const&) const
{
if(n_ == I)
return iter<I>() == other.iter<I>();
return equal(C<I+1>{}, other);
return equal(other, C<I+1>{});
}
[[noreturn]]
reference
dereference(C<sizeof...(Bs)>) const
dereference(C<sizeof...(Bn)> const&) const
{
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
reference
dereference(C<I>) const
dereference(C<I> const&) const
{
if(n_ == I)
return *iter<I>();
@@ -264,19 +267,19 @@ private:
[[noreturn]]
void
increment(C<sizeof...(Bs)>)
increment(C<sizeof...(Bn)> const&)
{
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
void
increment(C<I>)
increment(C<I> const&)
{
if(n_ == I)
{
if(++iter<I>() !=
std::get<I>(*bs_).end())
std::get<I>(*bn_).end())
return;
using Iter = iter_t<I>;
iter<I>().~Iter();
@@ -286,23 +289,23 @@ private:
}
void
decrement(C<sizeof...(Bs)>)
decrement(C<sizeof...(Bn)> const&)
{
auto constexpr I = sizeof...(Bs);
auto constexpr I = sizeof...(Bn);
if(n_ == I)
{
--n_;
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()};
std::get<I-1>(*bn_).end()};
}
decrement(C<I-1>{});
}
void
decrement(C<0>)
decrement(C<0> const&)
{
auto constexpr I = 0;
if(iter<I>() != std::get<I>(*bs_).begin())
if(iter<I>() != std::get<I>(*bn_).begin())
{
--iter<I>();
return;
@@ -312,11 +315,11 @@ private:
template<std::size_t I>
void
decrement(C<I>)
decrement(C<I> const&)
{
if(n_ == I)
{
if(iter<I>() != std::get<I>(*bs_).begin())
if(iter<I>() != std::get<I>(*bn_).begin())
{
--iter<I>();
return;
@@ -325,7 +328,7 @@ private:
using Iter = iter_t<I>;
iter<I>().~Iter();
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()};
std::get<I-1>(*bn_).end()};
}
decrement(C<I-1>{});
}
@@ -333,54 +336,54 @@ private:
//------------------------------------------------------------------------------
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
template<class... Bn>
buffer_cat_helper<Bn...>::
const_iterator::~const_iterator()
{
destroy(C<0>{});
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
template<class... Bn>
buffer_cat_helper<Bn...>::
const_iterator::const_iterator()
: n_(sizeof...(Bs))
, bs_(nullptr)
: n_(sizeof...(Bn))
, bn_(nullptr)
{
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
template<class... Bn>
buffer_cat_helper<Bn...>::
const_iterator::const_iterator(
std::tuple<Bs...> const& bs, bool at_end)
: bs_(&bs)
std::tuple<Bn...> const& bn, bool at_end)
: bn_(&bn)
{
if(at_end)
n_ = sizeof...(Bs);
n_ = sizeof...(Bn);
else
construct(C<0>{});
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
template<class... Bn>
buffer_cat_helper<Bn...>::
const_iterator::const_iterator(const_iterator&& other)
: 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>
buffer_cat_helper<ValueType, Bs...>::
template<class... Bn>
buffer_cat_helper<Bn...>::
const_iterator::const_iterator(const_iterator const& other)
: 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
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator=(const_iterator&& other) ->
const_iterator&
{
@@ -388,14 +391,14 @@ const_iterator::operator=(const_iterator&& other) ->
return *this;
destroy(C<0>{});
n_ = other.n_;
bs_ = other.bs_;
move(C<0>{}, std::move(other));
bn_ = other.bn_;
move(std::move(other), C<0>{});
return *this;
}
template<class ValueType, class... Bs>
template<class... Bn>
auto
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator=(const_iterator const& other) ->
const_iterator&
{
@@ -403,35 +406,35 @@ const_iterator&
return *this;
destroy(C<0>{});
n_ = other.n_;
bs_ = other.bs_;
copy(C<0>{}, other);
bn_ = other.bn_;
copy(other, C<0>{});
return *this;
}
template<class ValueType, class... Bs>
template<class... Bn>
bool
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator==(const_iterator const& other) const
{
if(bs_ != other.bs_)
if(bn_ != other.bn_)
return false;
if(n_ != other.n_)
return false;
return equal(C<0>{}, other);
return equal(other, C<0>{});
}
template<class ValueType, class... Bs>
template<class... Bn>
auto
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator*() const ->
reference
{
return dereference(C<0>{});
}
template<class ValueType, class... Bs>
template<class... Bn>
auto
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator++() ->
const_iterator&
{
@@ -439,30 +442,32 @@ const_iterator::operator++() ->
return *this;
}
template<class ValueType, class... Bs>
template<class... Bn>
auto
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator--() ->
const_iterator&
{
decrement(C<sizeof...(Bs)>{});
decrement(C<sizeof...(Bn)>{});
return *this;
}
template<class ValueType, class... Bs>
template<class... Bn>
inline
auto
buffer_cat_helper<ValueType, Bs...>::begin() const ->
buffer_cat_helper<Bn...>::begin() const ->
const_iterator
{
return const_iterator(bs_, false);
return const_iterator{bn_, false};
}
template<class ValueType, class... Bs>
template<class... Bn>
inline
auto
buffer_cat_helper<ValueType, Bs...>::end() const ->
buffer_cat_helper<Bn...>::end() const ->
const_iterator
{
return const_iterator(bs_, true);
return const_iterator{bn_, true};
}
} // detail

View File

@@ -85,53 +85,93 @@ public:
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>
class is_DynamicBuffer
{
template<class U, class R = std::integral_constant<
bool, is_BufferSequence<decltype(
std::declval<U>().prepare(1)),
boost::asio::mutable_buffer>::type::value>>
// size()
template<class U, class R = std::is_convertible<decltype(
std::declval<U const>().size()), std::size_t>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::integral_constant<
bool, is_BufferSequence<decltype(
std::declval<U>().data()),
boost::asio::const_buffer>::type::value>>
// max_size()
template<class U, class R = std::is_convertible<decltype(
std::declval<U const>().max_size()), std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R = decltype(
std::declval<U>().commit(1), std::true_type{})>
// capacity()
template<class U, class R = std::is_convertible<decltype(
std::declval<U const>().capacity()), std::size_t>>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
template<class U, class R = decltype(
std::declval<U>().consume(1), std::true_type{})>
// data()
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);
template<class>
static std::false_type check4(...);
using type4 = decltype(check4<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().size()), std::size_t>>
// prepare()
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);
template<class>
static std::false_type check5(...);
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:
using type = std::integral_constant<bool,
type1::value && type2::value &&
type3::value && type4::value &&
type5::value>;
type1::value
&& type2::value
//&& type3::value // Networking TS
&& type4::value
&& type5::value
&& type6::value
&& type7::value
>;
};
} // 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);
};
#ifndef __INTELLISENSE__
// VFALCO Fails to compile with Intellisense
static_assert(is_call_possible<
is_call_possible_udt1, void(int)>::value, "");
@@ -81,6 +83,7 @@ static_assert(is_call_possible<
static_assert(! is_call_possible<
is_call_possible_udt3 const, int(int)>::value, "");
#endif
} // test

View File

@@ -5,38 +5,104 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_PREPARE_BUFFERS_IPP
#define BEAST_IMPL_PREPARE_BUFFERS_IPP
#ifndef BEAST_DETAIL_PREPARED_BUFFERS_HPP
#define BEAST_DETAIL_PREPARED_BUFFERS_HPP
#include <beast/core/prepare_buffer.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <stdexcept>
#include <type_traits>
#include <utility>
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>
void
prepared_buffers<BufferSequence>::
setup(std::size_t n)
class prepared_buffers
{
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>
class prepared_buffers<BufferSequence>::const_iterator
@@ -50,8 +116,12 @@ class prepared_buffers<BufferSequence>::const_iterator
typename BufferSequence::const_iterator it_;
public:
using value_type =
typename std::iterator_traits<iter_type>::value_type;
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;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
@@ -59,10 +129,10 @@ public:
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
const_iterator(const_iterator&& other);
const_iterator(const_iterator const& other);
const_iterator& operator=(const_iterator&& other);
const_iterator& operator=(const_iterator const& other);
bool
operator==(const_iterator const& other) const
@@ -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>
prepared_buffers<BufferSequence>::
prepared_buffers(prepared_buffers&& other)
@@ -187,6 +318,7 @@ prepared_buffers(std::size_t n, BufferSequence const& bs)
}
template<class BufferSequence>
inline
auto
prepared_buffers<BufferSequence>::begin() const ->
const_iterator
@@ -195,6 +327,7 @@ prepared_buffers<BufferSequence>::begin() const ->
}
template<class BufferSequence>
inline
auto
prepared_buffers<BufferSequence>::end() const ->
const_iterator
@@ -202,14 +335,7 @@ prepared_buffers<BufferSequence>::end() const ->
return const_iterator{*this, true};
}
template<class BufferSequence>
inline
prepared_buffers<BufferSequence>
prepare_buffers(std::size_t n, BufferSequence const& buffers)
{
return prepared_buffers<BufferSequence>(n, buffers);
}
} // detail
} // beast
#endif

View File

@@ -249,7 +249,7 @@ update(sha1_context& ctx,
std::uint8_t const*>(message);
for(;;)
{
auto const n = std::min(
auto const n = (std::min)(
size, sizeof(ctx.buf) - ctx.buflen);
std::memcpy(ctx.buf + ctx.buflen, p, n);
ctx.buflen += n;

View File

@@ -9,8 +9,8 @@
#define BEAST_DETAIL_STREAM_CONCEPTS_HPP
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/error.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/system/error_code.hpp>
#include <type_traits>
#include <utility>
@@ -22,7 +22,7 @@ namespace detail {
struct StreamHandler
{
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 WriteHandler = StreamHandler;
@@ -79,9 +79,6 @@ public:
template<class T>
class is_SyncReadStream
{
using error_code =
boost::system::error_code;
template<class U, class R = std::is_same<decltype(
std::declval<U>().read_some(
std::declval<MutableBufferSequence>())),
@@ -108,9 +105,6 @@ public:
template<class T>
class is_SyncWriteStream
{
using error_code =
boost::system::error_code;
template<class U, class R = std::is_same<decltype(
std::declval<U>().write_some(
std::declval<ConstBufferSequence>())),

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 <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/system/error_code.hpp>
#include <cstdint>
#include <utility>
@@ -47,7 +46,7 @@ namespace beast {
Example:
@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.
//
template<class DynamicBuffer>
@@ -55,7 +54,7 @@ namespace beast {
dynabuf_readstream<DynamicBuffer>& stream)
{
// 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
// headers; the return value will include only the
// 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
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
#endif

View File

@@ -8,7 +8,7 @@
#ifndef 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 <memory>
#include <type_traits>
@@ -23,15 +23,15 @@ namespace beast {
This allocator uses the handler customizations `asio_handler_allocate`
and `asio_handler_deallocate` to manage memory. It meets the requirements
of `Allocator` and can be used anywhere a `std::allocator` is
of @b Allocator and can be used anywhere a `std::allocator` is
accepted.
@tparam T The type of objects allocated by the allocator.
@tparam CompletionHandler The type of handler.
@note Allocated memory is only valid until the handler is called. The
caller is still responsible for freeing memory.
@note Memory allocated by this allocator must be freed before
the handler is invoked or undefined behavior results.
*/
#if GENERATING_DOCS
template<class T, class CompletionHandler>
@@ -49,12 +49,18 @@ private:
template<class U, class H>
friend class handler_alloc;
CompletionHandler h_;
CompletionHandler& h_;
public:
using value_type = T;
using is_always_equal = std::true_type;
template<class U>
struct rebind
{
using other = handler_alloc<U, CompletionHandler>;
};
handler_alloc() = delete;
handler_alloc(handler_alloc&&) = default;
handler_alloc(handler_alloc const&) = default;
@@ -63,31 +69,16 @@ public:
/** 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
handler_alloc(CompletionHandler&& h)
: h_(std::move(h))
{
}
/** Construct the allocator.
A copy of the handler is made.
*/
explicit
handler_alloc(CompletionHandler const& h)
handler_alloc(CompletionHandler& h)
: h_(h)
{
}
template<class U>
handler_alloc(
handler_alloc<U, CompletionHandler>&& other)
: h_(std::move(other.h_))
{
}
/// Copy constructor
template<class U>
handler_alloc(
handler_alloc<U, CompletionHandler> const& other)
@@ -100,7 +91,7 @@ public:
{
auto const size = n * sizeof(T);
return static_cast<value_type*>(
boost_asio_handler_alloc_helpers::allocate(
beast_asio_helpers::allocate(
size, h_));
}
@@ -108,7 +99,7 @@ public:
deallocate(value_type* p, std::ptrdiff_t n)
{
auto const size = n * sizeof(T);
boost_asio_handler_alloc_helpers::deallocate(
beast_asio_helpers::deallocate(
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
#define BEAST_IMPL_BASIC_STREAMBUF_IPP
#include <beast/core/detail/type_traits.hpp>
#include <beast/core/detail/write_dynabuf.hpp>
#include <boost/assert.hpp>
#include <algorithm>
#include <cassert>
#include <exception>
#include <sstream>
#include <string>
@@ -533,7 +534,7 @@ basic_streambuf<Allocator>::capacity() const
{
auto pos = out_;
if(pos == list_.end())
return 0;
return in_size_;
auto n = pos->size() - out_pos_;
while(++pos != list_.end())
n += pos->size();
@@ -656,7 +657,7 @@ basic_streambuf<Allocator>::commit(size_type n)
debug_check();
}
n = std::min(n, out_end_ - out_pos_);
n = (std::min)(n, out_end_ - out_pos_);
out_pos_ += n;
in_size_ += n;
if(out_pos_ == out_->size())
@@ -786,6 +787,7 @@ void
basic_streambuf<Allocator>::
copy_assign(basic_streambuf const& other, std::false_type)
{
beast::detail::ignore_unused(other);
}
template<class Allocator>
@@ -816,36 +818,36 @@ basic_streambuf<Allocator>::debug_check() const
{
#ifndef NDEBUG
using boost::asio::buffer_size;
assert(buffer_size(data()) == in_size_);
BOOST_ASSERT(buffer_size(data()) == in_size_);
if(list_.empty())
{
assert(in_pos_ == 0);
assert(in_size_ == 0);
assert(out_pos_ == 0);
assert(out_end_ == 0);
assert(out_ == list_.end());
BOOST_ASSERT(in_pos_ == 0);
BOOST_ASSERT(in_size_ == 0);
BOOST_ASSERT(out_pos_ == 0);
BOOST_ASSERT(out_end_ == 0);
BOOST_ASSERT(out_ == list_.end());
return;
}
auto const& front = list_.front();
assert(in_pos_ < front.size());
BOOST_ASSERT(in_pos_ < front.size());
if(out_ == list_.end())
{
assert(out_pos_ == 0);
assert(out_end_ == 0);
BOOST_ASSERT(out_pos_ == 0);
BOOST_ASSERT(out_end_ == 0);
}
else
{
auto const& out = *out_;
auto const& back = list_.back();
assert(out_end_ <= back.size());
assert(out_pos_ < out.size());
assert(&out != &front || out_pos_ >= in_pos_);
assert(&out != &front || out_pos_ - in_pos_ == in_size_);
assert(&out != &back || out_pos_ <= out_end_);
BOOST_ASSERT(out_end_ <= back.size());
BOOST_ASSERT(out_pos_ < out.size());
BOOST_ASSERT(&out != &front || out_pos_ >= in_pos_);
BOOST_ASSERT(&out != &front || out_pos_ - in_pos_ == in_size_);
BOOST_ASSERT(&out != &back || out_pos_ <= out_end_);
}
#endif
}
@@ -855,11 +857,18 @@ std::size_t
read_size_helper(basic_streambuf<
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();
if(avail == 0)
return std::min(max_size,
std::max<std::size_t>(512, streambuf.alloc_size_));
return std::min(max_size, avail);
if (avail > 0)
return (std::min)(avail, max_size);
// Try to have just one new block allocated
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>

View File

@@ -444,7 +444,7 @@ buffers_adapter<MutableBufferSequence>::commit(std::size_t n)
max_size_ -= avail;
}
n = std::min(n, out_end_ - out_pos_);
n = (std::min)(n, out_end_ - out_pos_);
out_pos_ += n;
in_size_ += n;
max_size_ -= n;

View File

@@ -18,10 +18,10 @@
namespace beast {
template<class BufferSequence, class ValueType>
class consuming_buffers<BufferSequence, ValueType>::const_iterator
template<class BufferSequence>
class consuming_buffers<BufferSequence>::const_iterator
{
friend class consuming_buffers<BufferSequence, ValueType>;
friend class consuming_buffers<BufferSequence>;
using iter_type =
typename BufferSequence::const_iterator;
@@ -30,8 +30,12 @@ class consuming_buffers<BufferSequence, ValueType>::const_iterator
consuming_buffers const* b_ = nullptr;
public:
using value_type =
typename std::iterator_traits<iter_type>::value_type;
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;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
@@ -59,8 +63,9 @@ public:
reference
operator*() const
{
return it_ == b_->begin_ ?
*it_ + b_->skip_ : *it_;
return it_ == b_->begin_
? value_type{*it_} + b_->skip_
: *it_;
}
pointer
@@ -105,8 +110,8 @@ private:
}
};
template<class BufferSequence, class ValueType>
consuming_buffers<BufferSequence, ValueType>::
template<class BufferSequence>
consuming_buffers<BufferSequence>::
consuming_buffers(consuming_buffers&& other)
: consuming_buffers(std::move(other),
std::distance<iter_type>(
@@ -114,8 +119,8 @@ consuming_buffers(consuming_buffers&& other)
{
}
template<class BufferSequence, class ValueType>
consuming_buffers<BufferSequence, ValueType>::
template<class BufferSequence>
consuming_buffers<BufferSequence>::
consuming_buffers(consuming_buffers const& other)
: consuming_buffers(other,
std::distance<iter_type>(
@@ -123,9 +128,9 @@ consuming_buffers(consuming_buffers const& other)
{
}
template<class BufferSequence, class ValueType>
template<class BufferSequence>
auto
consuming_buffers<BufferSequence, ValueType>::
consuming_buffers<BufferSequence>::
operator=(consuming_buffers&& other) ->
consuming_buffers&
{
@@ -137,9 +142,9 @@ operator=(consuming_buffers&& other) ->
return *this;
}
template<class BufferSequence, class ValueType>
template<class BufferSequence>
auto
consuming_buffers<BufferSequence, ValueType>::
consuming_buffers<BufferSequence>::
operator=(consuming_buffers const& other) ->
consuming_buffers&
{
@@ -151,35 +156,41 @@ operator=(consuming_buffers const& other) ->
return *this;
}
template<class BufferSequence, class ValueType>
consuming_buffers<BufferSequence, ValueType>::
template<class BufferSequence>
consuming_buffers<BufferSequence>::
consuming_buffers(BufferSequence const& bs)
: bs_(bs)
, begin_(bs_.begin())
{
static_assert(is_BufferSequence<BufferSequence, ValueType>::value,
"BufferSequence requirements not met");
static_assert(
is_BufferSequence<BufferSequence, value_type>::value,
"BufferSequence requirements not met");
}
template<class BufferSequence, class ValueType>
template<class BufferSequence>
inline
auto
consuming_buffers<BufferSequence, ValueType>::begin() const ->
consuming_buffers<BufferSequence>::
begin() const ->
const_iterator
{
return const_iterator{*this, begin_};
}
template<class BufferSequence, class ValueType>
template<class BufferSequence>
inline
auto
consuming_buffers<BufferSequence, ValueType>::end() const ->
consuming_buffers<BufferSequence>::
end() const ->
const_iterator
{
return const_iterator{*this, bs_.end()};
}
template<class BufferSequence, class ValueType>
template<class BufferSequence>
void
consuming_buffers<BufferSequence, ValueType>::consume(std::size_t n)
consuming_buffers<BufferSequence>::
consume(std::size_t n)
{
using boost::asio::buffer_size;
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
#endif

View File

@@ -9,10 +9,10 @@
#define BEAST_IMPL_DYNABUF_READSTREAM_HPP
#include <beast/core/bind_handler.hpp>
#include <beast/core/error.hpp>
#include <beast/core/handler_concepts.hpp>
#include <beast/core/handler_alloc.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
#include <beast/core/handler_helpers.hpp>
#include <beast/core/handler_ptr.hpp>
namespace beast {
@@ -21,28 +21,22 @@ template<class MutableBufferSequence, class Handler>
class dynabuf_readstream<
Stream, DynamicBuffer>::read_some_op
{
using alloc_type =
handler_alloc<char, Handler>;
// VFALCO What about bool cont for is_continuation?
struct data
{
dynabuf_readstream& srs;
MutableBufferSequence bs;
Handler h;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_,
dynabuf_readstream& srs_,
data(Handler&, dynabuf_readstream& srs_,
MutableBufferSequence const& bs_)
: srs(srs_)
, bs(bs_)
, h(std::forward<DeducedHandler>(h_))
{
}
};
std::shared_ptr<data> d_;
handler_ptr<data, Handler> d_;
public:
read_some_op(read_some_op&&) = default;
@@ -51,7 +45,7 @@ public:
template<class DeducedHandler, class... Args>
read_some_op(DeducedHandler&& h,
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<Args>(args)...))
{
@@ -66,31 +60,31 @@ public:
void* asio_handler_allocate(
std::size_t size, read_some_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
return beast_asio_helpers::
allocate(size, op->d_.handler());
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, read_some_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
return beast_asio_helpers::
deallocate(p, size, op->d_.handler());
}
friend
bool asio_handler_is_continuation(read_some_op* op)
{
return boost_asio_handler_cont_helpers::
is_continuation(op->d_->h);
return beast_asio_helpers::
is_continuation(op->d_.handler());
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_some_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
return beast_asio_helpers::
invoke(f, op->d_.handler());
}
};
@@ -150,7 +144,7 @@ read_some_op<MutableBufferSequence, Handler>::operator()(
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
auto
static_streambuf::prepare(std::size_t n) ->
@@ -290,15 +300,6 @@ static_streambuf::prepare(std::size_t n) ->
return mutable_buffers_type{out_, n};
}
inline
auto
static_streambuf::data() const ->
const_buffers_type
{
return const_buffers_type{in_,
static_cast<std::size_t>(out_ - in_)};
}
} // beast
#endif

View File

@@ -0,0 +1,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
#define BEAST_PREPARE_BUFFERS_HPP
#include <beast/core/detail/prepare_buffers.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace beast {
/** Get a trimmed const buffer.
/** Return a shortened buffer sequence.
The new buffer starts at the beginning of the passed
buffer. Ownership of the underlying memory is not
transferred.
*/
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)) };
}
This function returns a new buffer sequence which adapts the
passed buffer sequence and efficiently presents a shorter subset
of the original list of buffers starting with the first byte of
the original sequence.
/** 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
buffer. Ownership of the underlying 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.
@param buffers The buffer sequence to adapt. A copy of
the sequence will be made, but ownership of the underlying
memory is not transferred.
*/
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
/// A bidirectional iterator type that may be used to read elements.
using const_iterator = implementation_defined;
implementation_defined
#else
class const_iterator;
inline
detail::prepared_buffers<BufferSequence>
#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 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);
prepare_buffers(std::size_t n, BufferSequence const& buffers)
{
return detail::prepared_buffers<BufferSequence>(n, buffers);
}
} // beast
#include <beast/core/impl/prepare_buffers.ipp>
#endif

View File

@@ -32,6 +32,7 @@ private:
#else
protected:
#endif
std::uint8_t* begin_;
std::uint8_t* in_;
std::uint8_t* out_;
std::uint8_t* last_;
@@ -57,21 +58,35 @@ public:
#endif
/// Returns the largest size output sequence possible.
std::size_t
max_size() const
{
return end_ - in_;
}
/// Get the size of the input sequence.
/// Return the size of the input sequence.
std::size_t
size() const
{
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
imposed by the underlying mutable buffer sequence.
@@ -93,13 +108,6 @@ public:
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.
void
consume(std::size_t n)
@@ -120,6 +128,7 @@ protected:
void
reset(std::uint8_t* p, std::size_t n)
{
begin_ = p;
in_ = p;
out_ = p;
last_ = p;

View File

@@ -8,13 +8,13 @@
#ifndef 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/body_type.hpp>
#include <beast/http/chunk_encode.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_v1.hpp>
#include <beast/http/parse.hpp>
#include <beast/http/parse_error.hpp>
#include <beast/http/parser_v1.hpp>
#include <beast/http/read.hpp>

View File

@@ -8,8 +8,12 @@
#ifndef 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/logic/tribool.hpp>
namespace beast {
namespace http {
@@ -33,14 +37,19 @@ private:
value_type& sb_;
public:
template<bool isRequest, class Headers>
template<bool isRequest, class Fields>
explicit
reader(message<isRequest,
basic_dynabuf_body, Headers>& m) noexcept
basic_dynabuf_body, Fields>& m) noexcept
: sb_(m.body)
{
}
void
init(error_code&) noexcept
{
}
void
write(void const* data,
std::size_t size, error_code&) noexcept
@@ -57,33 +66,32 @@ private:
DynamicBuffer const& body_;
public:
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
template<bool isRequest, class Fields>
explicit
writer(message<
isRequest, basic_dynabuf_body, Headers> const& m)
isRequest, basic_dynabuf_body, Fields> const& m) noexcept
: body_(m.body)
{
}
void
init(error_code& ec)
init(error_code& ec) noexcept
{
beast::detail::ignore_unused(ec);
}
std::uint64_t
content_length() const
content_length() const noexcept
{
return body_.size();
}
template<class Write>
template<class WriteFunction>
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;
}
};

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/detail/basic_parser_v1.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/assert.hpp>
#include <array>
#include <cassert>
#include <climits>
#include <cstdint>
#include <type_traits>
@@ -22,8 +22,11 @@
namespace beast {
namespace http {
namespace parse_flag {
enum values
/** Parse flags
The set of parser bit flags are returned by @ref basic_parser_v1::flags.
*/
enum parse_flag
{
chunked = 1,
connection_keep_alive = 2,
@@ -34,28 +37,6 @@ enum values
skipbody = 64,
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.
@@ -67,7 +48,7 @@ struct headers_max_size
The default body maximum size for requests is 4MB (four
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
{
@@ -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.
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.
@@ -97,86 +142,95 @@ static std::uint64_t constexpr no_content_length =
more calls to derived class members functions (referred to as
"callbacks" from here on) matching a specific signature.
Callbacks are detected through SFINAE. The derived class may
implement as few or as many of the members as needed.
These are the signatures of the callbacks:<br>
Every callback must be provided by the derived class, or else
a compilation error will be generated. This exemplar shows
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&)`
Called for each piece of the current header field.
@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.
The return value of `on_body_what` is special, it controls
whether or not the parser should expect a body. See @ref body_what
for choices of the return value.
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
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>
class basic_parser_v1 : public detail::parser_base
{
private:
template<bool, class>
friend class basic_parser_v1;
using self = basic_parser_v1;
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
public:
/// Copy constructor.
basic_parser_v1(basic_parser_v1 const&) = default;
/// Copy assignment.
basic_parser_v1& operator=(basic_parser_v1 const&) = default;
/// Default constructor
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.
@param args One or more parser options to set.
@@ -263,9 +321,9 @@ public:
std::forward<An>(an)...);
}
/// Set the headers maximum size option
/// Set the header maximum size option
void
set_option(headers_max_size const& o)
set_option(header_max_size const& o)
{
h_max_ = o.value;
h_left_ = h_max_;
@@ -373,7 +431,10 @@ public:
bool
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.
@@ -418,6 +479,15 @@ public:
void
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:
Derived&
impl()
@@ -437,18 +507,10 @@ private:
s_ = s_res_start;
}
void
reset()
{
h_left_ = h_max_;
b_left_ = b_max_;
reset(std::integral_constant<bool, isRequest>{});
}
void
init(std::true_type)
{
// 16KB max headers, 4MB max body
// Request: 16KB max header, 4MB max body
h_max_ = 16 * 1024;
b_max_ = 4 * 1024 * 1024;
}
@@ -456,7 +518,7 @@ private:
void
init(std::false_type)
{
// 16KB max headers, unlimited body
// Response: 16KB max header, unlimited body
h_max_ = 16 * 1024;
b_max_ = 0;
}
@@ -474,13 +536,102 @@ private:
bool
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>
class has_on_start_t
class check_on_body_what_t
{
template<class T, class R =
decltype(std::declval<T>().on_start(
std::declval<error_code&>()),
std::true_type{})>
template<class T, class R = std::is_convertible<decltype(
std::declval<T>().on_body_what(
std::declval<std::uint64_t>(),
std::declval<error_code&>())),
body_what>>
static R check(int);
template<class>
static std::false_type check(...);
@@ -489,211 +640,46 @@ private:
static bool const value = type::value;
};
template<class C>
using has_on_start =
std::integral_constant<bool, has_on_start_t<C>::value>;
using check_on_body_what =
std::integral_constant<bool, check_on_body_what_t<C>::value>;
template<class C>
class has_on_method_t
{
template<class T, class R =
decltype(std::declval<T>().on_method(
std::declval<boost::string_ref const&>(),
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_method =
std::integral_constant<bool, has_on_method_t<C>::value>;
template<class T, class = beast::detail::void_t<>>
struct check_on_body : std::false_type {};
template<class C>
class has_on_uri_t
{
template<class T, class R =
decltype(std::declval<T>().on_uri(
std::declval<boost::string_ref const&>(),
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_uri =
std::integral_constant<bool, has_on_uri_t<C>::value>;
template<class T>
struct check_on_body<T, beast::detail::void_t<decltype(
std::declval<T>().on_body(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class C>
class has_on_reason_t
{
template<class T, class R =
decltype(std::declval<T>().on_reason(
std::declval<boost::string_ref const&>(),
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_reason =
std::integral_constant<bool, has_on_reason_t<C>::value>;
template<class T, class = beast::detail::void_t<>>
struct check_on_complete : std::false_type {};
template<class C>
class has_on_request_t
{
template<class T, class R =
decltype(std::declval<T>().on_request(
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_request =
std::integral_constant<bool, has_on_request_t<C>::value>;
template<class C>
class has_on_response_t
{
template<class T, class R =
decltype(std::declval<T>().on_response(
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_response =
std::integral_constant<bool, has_on_response_t<C>::value>;
template<class C>
class has_on_field_t
{
template<class T, class R =
decltype(std::declval<T>().on_uri(
std::declval<boost::string_ref const&>(),
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_field =
std::integral_constant<bool, has_on_field_t<C>::value>;
template<class C>
class has_on_value_t
{
template<class T, class R =
decltype(std::declval<T>().on_uri(
std::declval<boost::string_ref const&>(),
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_value =
std::integral_constant<bool, has_on_value_t<C>::value>;
template<class C>
class has_on_headers_t
{
template<class T, class R = std::is_same<int,
decltype(std::declval<T>().on_headers(
std::declval<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)
{
}
template<class T>
struct check_on_complete<T, beast::detail::void_t<decltype(
std::declval<T>().on_complete(
std::declval<error_code&>())
)>> : std::true_type {};
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,
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);
}
@@ -705,21 +691,21 @@ private:
void call_on_method(error_code& ec,
boost::string_ref const& s)
{
if(! h_max_ || s.size() <= h_left_)
{
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;
}
call_on_method(ec, s,
std::integral_constant<bool, isRequest>{});
}
void call_on_uri(error_code& ec,
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);
}
@@ -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_)
{
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;
}
call_on_uri(ec, s,
std::integral_constant<bool, isRequest>{});
}
void call_on_reason(error_code& ec,
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);
}
@@ -755,20 +742,14 @@ private:
void call_on_reason(error_code& ec, boost::string_ref const& s)
{
if(! h_max_ || s.size() <= h_left_)
{
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;
}
call_on_reason(ec, s,
std::integral_constant<bool, ! isRequest>{});
}
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);
}
@@ -778,12 +759,14 @@ private:
void call_on_request(error_code& ec)
{
call_on_request(ec, std::integral_constant<bool,
isRequest && has_on_request<Derived>::value>{});
call_on_request(ec,
std::integral_constant<bool, isRequest>{});
}
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);
}
@@ -793,111 +776,73 @@ private:
void call_on_response(error_code& ec)
{
call_on_response(ec, std::integral_constant<bool,
! isRequest && has_on_response<Derived>::value>{});
call_on_response(ec,
std::integral_constant<bool, ! isRequest>{});
}
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);
}
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,
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);
}
void call_on_value(error_code&,
boost::string_ref const&, std::false_type)
void
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_)
{
h_left_ -= s.size();
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>{});
static_assert(check_on_body_what<Derived>::value,
"on_body_what requirements not met");
return impl().on_body_what(content_length_, 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);
}
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
static_assert(check_on_body<Derived>::value,
"on_body requirements not met");
if(b_max_ && s.size() > b_left_)
{
ec = parse_error::body_too_big;
return;
}
}
void call_on_complete(error_code& ec, std::true_type)
{
impl().on_complete(ec);
}
void call_on_complete(error_code&, std::false_type)
{
b_left_ -= s.size();
impl().on_body(s, 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
#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/logic/tribool.hpp>
#include <type_traits>
#include <utility>
@@ -18,65 +21,107 @@ namespace http {
namespace detail {
template<class T>
class has_value_type
struct write_function
{
template<class U, class R =
typename U::value_type>
static std::true_type check(int);
template<class ConstBufferSequence>
void
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>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
static bool constexpr value = type::value;
};
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class T, bool B = has_value_type<T>::value>
struct extract_value_type
{
using type = void;
};
template<class T>
struct extract_value_type<T, true>
{
using type = typename T::value_type;
};
template<class T>
class has_reader
{
// VFALCO This is unfortunate, we have to provide the template
// argument type because this is not a deduced context?
//
template<class U, class R =
typename U::reader>
static std::true_type check(int);
std::is_convertible<decltype(
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>
static std::false_type check(...);
public:
using type = decltype(check<T>(0));
};
static std::false_type check2(...);
using type2 = decltype(check2<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:
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,
has_value_type<T>::value &&
std::is_default_constructible<
typename extract_value_type<T>::type>::value
std::is_nothrow_constructible<T, M const&>::value
&& type1::value
&& type2::value
>;
};
#endif
template<class T>
class is_Parser
{
@@ -92,7 +137,7 @@ class is_Parser
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().write(
std::declval<boost::asio::const_buffer const&>(),
std::declval<boost::asio::const_buffers_1 const&>(),
std::declval<error_code&>())),
std::size_t>>
static R check2(int);
@@ -118,31 +163,97 @@ public:
} // detail
/// Determine if `T` meets the requirements of `Body`.
/// Determine if `T` meets the requirements of @b Body.
template<class T>
#if GENERATING_DOCS
struct is_Body : std::integral_constant<bool, ...>{};
#else
using is_Body = typename detail::is_Body<T>::type;
using is_Body = detail::has_value_type<T>;
#endif
/// Determine if `T` meets the requirements of `ReadableBody`.
template<class T>
/** Determine if a @b Body has a nested type `reader`.
@tparam T The type to check, which must meet the
requirements of @b Body.
*/
#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>
#if GENERATING_DOCS
struct is_WritableBody : std::integral_constant<bool, ...>{};
struct has_reader : std::integral_constant<bool, ...>{};
#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
/// 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>
#if GENERATING_DOCS
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
#define BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
#include <boost/system/error_code.hpp>
#include <boost/utility/string_ref.hpp>
#include <array>
#include <cstdint>
namespace beast {
@@ -130,6 +127,7 @@ protected:
s_chunk_data_cr,
s_chunk_data_lf,
s_body_pause,
s_body_identity0,
s_body_identity,
s_body_identity_eof0,

View File

@@ -8,27 +8,30 @@
#ifndef 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/logic/tribool.hpp>
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <type_traits>
namespace beast {
namespace http {
namespace detail {
class chunk_encode_text
class chunk_encode_delim
{
boost::asio::const_buffer cb_;
// Storage for the longest hex string we might need, plus delimiters.
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>
static
OutIter
@@ -52,24 +55,15 @@ public:
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 =
boost::asio::buffer_size(other.cb_);
buf_ = other.buf_;
cb_ = boost::asio::const_buffer(
&buf_[buf_.size() - n], n);
copy(other);
}
explicit
chunk_encode_text(std::size_t n)
chunk_encode_delim(std::size_t n)
{
buf_[buf_.size() - 2] = '\r';
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()))};
setup(n);
}
const_iterator
@@ -85,44 +79,29 @@ public:
}
};
/** Returns a chunk-encoded ConstBufferSequence.
This returns a buffer sequence representing the
first chunk of a chunked transfer coded body.
@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)
template<class>
void
chunk_encode_delim::
copy(chunk_encode_delim const& other)
{
using boost::asio::buffer_size;
return buffer_cat(
chunk_encode_text{buffer_size(buffers)},
buffers,
boost::asio::const_buffers_1{"\r\n", 2});
auto const n =
boost::asio::buffer_size(other.cb_);
buf_ = other.buf_;
cb_ = boost::asio::const_buffer(
&buf_[buf_.size() - n], n);
}
/// Returns a chunked encoding final chunk.
inline
#if GENERATING_DOCS
implementation_defined
#else
boost::asio::const_buffers_1
#endif
chunk_encode_final()
template<class>
void
chunk_encode_delim::
setup(std::size_t n)
{
return boost::asio::const_buffers_1(
"0\r\n\r\n", 5);
buf_[buf_.size() - 2] = '\r';
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

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
boost::string_ref
trim(boost::string_ref const& s)
@@ -258,14 +266,14 @@ struct param_iter
using iter_type = boost::string_ref::const_iterator;
iter_type it;
iter_type begin;
iter_type end;
iter_type first;
iter_type last;
std::pair<boost::string_ref, boost::string_ref> v;
bool
empty() const
{
return begin == it;
return first == it;
}
template<class = void>
@@ -279,59 +287,48 @@ param_iter::
increment()
{
/*
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
ext = token param-list
param-list = *( OWS ";" OWS param )
param = token OWS "=" OWS ( token / quoted-string )
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
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
param-list = *( OWS ";" OWS param )
param = token OWS [ "=" OWS ( token / quoted-string ) ]
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
obs-text = %x80-FF
*/
auto const err =
[&]
{
it = begin;
it = first;
};
v.first.clear();
v.second.clear();
detail::skip_ows(it, end);
begin = it;
if(it == end)
detail::skip_ows(it, last);
first = it;
if(it == last)
return err();
if(*it != ';')
return err();
++it;
detail::skip_ows(it, end);
if(it == end)
detail::skip_ows(it, last);
if(it == last)
return err();
// param
if(! detail::is_tchar(*it))
return err();
auto const p0 = it;
for(;;)
{
++it;
if(it == end)
return err();
if(! detail::is_tchar(*it))
break;
}
skip_token(++it, last);
auto const p1 = it;
detail::skip_ows(it, end);
if(it == end)
return err();
v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
detail::skip_ows(it, last);
if(it == last)
return;
if(*it == ';')
return;
if(*it != '=')
return err();
++it;
detail::skip_ows(it, end);
if(it == end)
return err();
detail::skip_ows(it, last);
if(it == last)
return;
if(*it == '"')
{
// quoted-string
@@ -339,7 +336,7 @@ increment()
++it;
for(;;)
{
if(it == end)
if(it == last)
return err();
auto c = *it++;
if(c == '"')
@@ -348,13 +345,12 @@ increment()
continue;
if(c != '\\')
return err();
if(it == end)
if(it == last)
return err();
c = *it++;
if(! detail::is_qpchar(c))
return err();
}
v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
}
else
@@ -363,15 +359,7 @@ increment()
if(! detail::is_tchar(*it))
return err();
auto const p2 = it;
for(;;)
{
it++;
if(it == end)
break;
if(! detail::is_tchar(*it))
break;
}
v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
skip_token(++it, last);
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
}
}

View File

@@ -8,8 +8,12 @@
#ifndef 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/logic/tribool.hpp>
#include <memory>
#include <string>
@@ -35,31 +39,31 @@ private:
struct writer
{
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
template<bool isRequest, class Fields>
explicit
writer(message<isRequest, empty_body, Headers> const& m)
writer(message<isRequest, empty_body, Fields> const& m) noexcept
{
beast::detail::ignore_unused(m);
}
void
init(error_code& ec)
init(error_code& ec) noexcept
{
beast::detail::ignore_unused(ec);
}
std::uint64_t
content_length() const
content_length() const noexcept
{
return 0;
}
template<class Write>
template<class WriteFunction>
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;
}
};

View File

@@ -5,17 +5,18 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_HEADERS_HPP
#define BEAST_HTTP_HEADERS_HPP
#ifndef BEAST_HTTP_FIELDS_HPP
#define BEAST_HTTP_FIELDS_HPP
#include <beast/http/basic_headers.hpp>
#include <beast/http/basic_fields.hpp>
#include <memory>
namespace beast {
namespace http {
using headers =
basic_headers<std::allocator<char>>;
/// A typical HTTP header fields container
using fields =
basic_fields<std::allocator<char>>;
} // http
} // 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)
//
#ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#ifndef BEAST_HTTP_IMPL_BASIC_FIELDS_IPP
#define BEAST_HTTP_IMPL_BASIC_FIELDS_IPP
#include <beast/http/detail/rfc7230.hpp>
#include <algorithm>
@@ -14,47 +14,9 @@
namespace beast {
namespace http {
namespace detail {
inline
auto
basic_headers_base::begin() const ->
const_iterator
{
return list_.cbegin();
}
inline
auto
basic_headers_base::end() const ->
const_iterator
{
return list_.cend();
}
inline
auto
basic_headers_base::cbegin() const ->
const_iterator
{
return list_.cbegin();
}
inline
auto
basic_headers_base::cend() const ->
const_iterator
{
return list_.cend();
}
} // detail
//------------------------------------------------------------------------------
template<class Allocator>
void
basic_headers<Allocator>::
basic_fields<Allocator>::
delete_all()
{
for(auto it = list_.begin(); it != list_.end();)
@@ -69,8 +31,8 @@ delete_all()
template<class Allocator>
inline
void
basic_headers<Allocator>::
move_assign(basic_headers& other, std::false_type)
basic_fields<Allocator>::
move_assign(basic_fields& other, std::false_type)
{
if(this->member() != other.member())
{
@@ -87,8 +49,8 @@ move_assign(basic_headers& other, std::false_type)
template<class Allocator>
inline
void
basic_headers<Allocator>::
move_assign(basic_headers& other, std::true_type)
basic_fields<Allocator>::
move_assign(basic_fields& other, std::true_type)
{
this->member() = std::move(other.member());
set_ = std::move(other.set_);
@@ -98,8 +60,8 @@ move_assign(basic_headers& other, std::true_type)
template<class Allocator>
inline
void
basic_headers<Allocator>::
copy_assign(basic_headers const& other, std::false_type)
basic_fields<Allocator>::
copy_assign(basic_fields const& other, std::false_type)
{
copy_from(other);
}
@@ -107,8 +69,8 @@ copy_assign(basic_headers const& other, std::false_type)
template<class Allocator>
inline
void
basic_headers<Allocator>::
copy_assign(basic_headers const& other, std::true_type)
basic_fields<Allocator>::
copy_assign(basic_fields const& other, std::true_type)
{
this->member() = other.member();
copy_from(other);
@@ -117,35 +79,35 @@ copy_assign(basic_headers const& other, std::true_type)
//------------------------------------------------------------------------------
template<class Allocator>
basic_headers<Allocator>::
~basic_headers()
basic_fields<Allocator>::
~basic_fields()
{
delete_all();
}
template<class Allocator>
basic_headers<Allocator>::
basic_headers(Allocator const& alloc)
basic_fields<Allocator>::
basic_fields(Allocator const& alloc)
: beast::detail::empty_base_optimization<
alloc_type>(alloc)
{
}
template<class Allocator>
basic_headers<Allocator>::
basic_headers(basic_headers&& other)
basic_fields<Allocator>::
basic_fields(basic_fields&& other)
: beast::detail::empty_base_optimization<alloc_type>(
std::move(other.member()))
, detail::basic_headers_base(
, detail::basic_fields_base(
std::move(other.set_), std::move(other.list_))
{
}
template<class Allocator>
auto
basic_headers<Allocator>::
operator=(basic_headers&& other) ->
basic_headers&
basic_fields<Allocator>::
operator=(basic_fields&& other) ->
basic_fields&
{
if(this == &other)
return *this;
@@ -156,9 +118,9 @@ operator=(basic_headers&& other) ->
}
template<class Allocator>
basic_headers<Allocator>::
basic_headers(basic_headers const& other)
: basic_headers(alloc_traits::
basic_fields<Allocator>::
basic_fields(basic_fields const& other)
: basic_fields(alloc_traits::
select_on_container_copy_construction(other.member()))
{
copy_from(other);
@@ -166,9 +128,9 @@ basic_headers(basic_headers const& other)
template<class Allocator>
auto
basic_headers<Allocator>::
operator=(basic_headers const& other) ->
basic_headers&
basic_fields<Allocator>::
operator=(basic_fields const& other) ->
basic_fields&
{
clear();
copy_assign(other, std::integral_constant<bool,
@@ -178,8 +140,8 @@ operator=(basic_headers const& other) ->
template<class Allocator>
template<class OtherAlloc>
basic_headers<Allocator>::
basic_headers(basic_headers<OtherAlloc> const& other)
basic_fields<Allocator>::
basic_fields(basic_fields<OtherAlloc> const& other)
{
copy_from(other);
}
@@ -187,9 +149,9 @@ basic_headers(basic_headers<OtherAlloc> const& other)
template<class Allocator>
template<class OtherAlloc>
auto
basic_headers<Allocator>::
operator=(basic_headers<OtherAlloc> const& other) ->
basic_headers&
basic_fields<Allocator>::
operator=(basic_fields<OtherAlloc> const& other) ->
basic_fields&
{
clear();
copy_from(other);
@@ -198,8 +160,8 @@ operator=(basic_headers<OtherAlloc> const& other) ->
template<class Allocator>
template<class FwdIt>
basic_headers<Allocator>::
basic_headers(FwdIt first, FwdIt last)
basic_fields<Allocator>::
basic_fields(FwdIt first, FwdIt last)
{
for(;first != last; ++first)
insert(first->name(), first->value());
@@ -207,7 +169,7 @@ basic_headers(FwdIt first, FwdIt last)
template<class Allocator>
std::size_t
basic_headers<Allocator>::
basic_fields<Allocator>::
count(boost::string_ref const& name) const
{
auto const it = set_.find(name, less{});
@@ -219,7 +181,7 @@ count(boost::string_ref const& name) const
template<class Allocator>
auto
basic_headers<Allocator>::
basic_fields<Allocator>::
find(boost::string_ref const& name) const ->
iterator
{
@@ -231,7 +193,7 @@ find(boost::string_ref const& name) const ->
template<class Allocator>
boost::string_ref
basic_headers<Allocator>::
basic_fields<Allocator>::
operator[](boost::string_ref const& name) const
{
auto const it = find(name);
@@ -242,7 +204,7 @@ operator[](boost::string_ref const& name) const
template<class Allocator>
void
basic_headers<Allocator>::
basic_fields<Allocator>::
clear() noexcept
{
delete_all();
@@ -252,7 +214,7 @@ clear() noexcept
template<class Allocator>
std::size_t
basic_headers<Allocator>::
basic_fields<Allocator>::
erase(boost::string_ref const& name)
{
auto it = set_.find(name, less{});
@@ -276,7 +238,7 @@ erase(boost::string_ref const& name)
template<class Allocator>
void
basic_headers<Allocator>::
basic_fields<Allocator>::
insert(boost::string_ref const& name,
boost::string_ref value)
{
@@ -289,7 +251,7 @@ insert(boost::string_ref const& name,
template<class Allocator>
void
basic_headers<Allocator>::
basic_fields<Allocator>::
replace(boost::string_ref const& name,
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)
//
#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
*
* 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
* IN THE SOFTWARE.
*/
/*
This code is a modified version of nodejs/http-parser, copyright above:
https://github.com/nodejs/http-parser
/* This code is a modified version of nodejs/http-parser, copyright above:
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>
basic_parser_v1<isRequest, Derived>::
basic_parser_v1()
@@ -50,6 +49,55 @@ basic_parser_v1()
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>
bool
basic_parser_v1<isRequest, Derived>::
@@ -170,7 +218,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
call_on_start(ec);
if(ec)
return errc();
assert(! cb_);
BOOST_ASSERT(! cb_);
cb(&self::call_on_method);
s_ = s_req_method;
break;
@@ -194,7 +242,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
// VFALCO TODO Better checking for valid URL characters
if(! is_text(ch))
return err(parse_error::bad_uri);
assert(! cb_);
BOOST_ASSERT(! cb_);
cb(&self::call_on_uri);
s_ = s_req_url;
break;
@@ -377,7 +425,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
}
if(! is_text(ch))
return err(parse_error::bad_reason);
assert(! cb_);
BOOST_ASSERT(! cb_);
cb(&self::call_on_reason);
s_ = s_res_reason;
break;
@@ -429,7 +477,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
fs_ = h_general;
break;
}
assert(! cb_);
BOOST_ASSERT(! cb_);
cb(&self::call_on_field);
s_ = s_header_name;
break;
@@ -549,7 +597,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
content_length_ = 0;
flags_ |= parse_flag::contentlength;
}
assert(! cb_);
BOOST_ASSERT(! cb_);
cb(&self::call_on_value);
s_ = s_header_value;
// fall through
@@ -801,7 +849,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
return err(parse_error::bad_content_length);
if(fs_ == h_upgrade)
flags_ |= parse_flag::upgrade;
assert(! cb_);
BOOST_ASSERT(! cb_);
call_on_value(ec, boost::string_ref{"", 0});
if(ec)
return errc();
@@ -880,7 +928,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
goto redo;
case s_header_value_unfold:
assert(! cb_);
BOOST_ASSERT(! cb_);
cb(&self::call_on_value);
s_ = s_header_value;
goto redo;
@@ -899,30 +947,57 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
return err(parse_error::illegal_content_length);
upgrade_ = ((flags_ & (parse_flag::upgrade | parse_flag::connection_upgrade)) ==
(parse_flag::upgrade | parse_flag::connection_upgrade)) /*|| method == "connect"*/;
auto const maybe_skip = call_on_headers(ec);
call_on_headers(ec);
if(ec)
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;
case 2:
case body_what::upgrade:
upgrade_ = true;
// fall through
case 1:
case body_what::skip:
flags_ |= parse_flag::skipbody;
break;
default:
return err(parse_error::bad_on_headers_rv);
case body_what::pause:
++p;
s_ = s_body_pause;
return used();
}
s_ = s_headers_done;
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:
{
assert(! cb_);
call_on_headers(ec);
BOOST_ASSERT(! cb_);
if(ec)
return errc();
bool const hasBody =
@@ -959,7 +1034,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
}
case s_body_identity0:
assert(! cb_);
BOOST_ASSERT(! cb_);
cb(&self::call_on_body);
s_ = s_body_identity;
// fall through
@@ -971,7 +1046,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
n = end - p;
else
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;
if(content_length_ == 0)
{
@@ -984,7 +1059,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
}
case s_body_identity_eof0:
assert(! cb_);
BOOST_ASSERT(! cb_);
cb(&self::call_on_body);
s_ = s_body_identity_eof;
// fall through
@@ -1073,7 +1148,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
break;
case s_chunk_data0:
assert(! cb_);
BOOST_ASSERT(! cb_);
cb(&self::call_on_body);
s_ = s_chunk_data;
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>
bool
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