mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-23 12:35:50 +00:00
Squashed 'src/beast/' changes from 2f9a844..1ab7a2f
1ab7a2f Set version to 1.0.0-b22
2eb4b0c Fix code sample in websocket.qbk
58802f4 Fix typos in design.qbk
19dc4bb Update documentation examples
10dbc5b Disable Boost.Coroutine deprecation warning
01c76c7 Fix websocket stream read documentation
d152c96 Update README.md example programs
995d86f Avoid copies in handler_alloc
851cb62 Add handler helpers
114175c Implement asio dealloc-before-invoke guarantee:
681db2e Add missing include
7db3c6e Fix broken Intellisense (MSVC)
09c183d Set version to 1.0.0-b21
1cb01fe Remove extraneous includes
62e65ed Set version to 1.0.0-b20
45eaa8c Increase utf8 checker code coverage
9ff1a27 Add zlib module:
a0a3359 Refactor HTTP identifier names (API Change):
79be7f8 Set version to 1.0.0-b19
eda1120 Tidy up internal name
4130ad4 Better buffer_cat:
f94f21d Fix consuming_buffers value_type (API Change):
2c524b4 prepared_buffers is private (API Change)
df2a108 Fix prepare_buffers value_type:
a4af9d6 Use boost::lexical_cast instead of std::to_string
62d670b Fix with_body example:
a63bd84 Increase code coverage
84a6775 Boost library min/max guidance:
02feea5 Add read, async_read for message_headers:
f224585 Add write, async_write, operator<< for message_headers:
ea48bcf Make chunk_encode public:
f6dd744 Refactor message and message_headers declarations:
9fd8aed Move sync_ostream to core/detail
c98b2d3 Optimize mask operations
d4dfc1a Optimize utf8 validation
7b4de4b Set version to 1.0.0-b18
feb5204 Add websocket::stream pong and async_pong
d4ffde5 Close connection during async_read on close frame:
644d518 Move clamp to core
427ba38 Fix write_frame masking and auto-fragment handling
54a51b1 Write buffer option does not change capacity
591dbc0 Meet DynamicBuffer requirements for static_streambuf
46d5e72 Reorganize source files and definitions
efa4b8f Override incremental link flags:
eef6e86 Higher optimization settings for MSVC builds
b6f3a36 Check invariants in parse_op:
47b0fa6 Remove unused field in test
8b8e57e unit_test improvements:
e907252 Clean up message docs
1e3543f Set version to 1.0.0-b17
de97a69 Trim unused code
796b484 Doc fixes
95c37e2 Fix unused parameter warnings and missing includes:
8b0d285 Refactor read_size_helper
97a9dcb Improve websocket example in README.md
236caef Engaged invokable is destructible:
d107ba1 Add headers_parser:
2f90627 Fix handling of body_what::pause in basic_parser_v1
9353d04 Add basic_parser_v1::reset
658e03c Add on_body_what parser callback (API Change):
50bd446 Fix parser traits detection (API Change):
df8d306 Tidy up documentation:
47105f8 Tidy up basic_headers for documentation
ada1f60 Refine message class hierarchy:
cf43f51 Rework HTTP concepts (API Change):
8a261ca HTTP Reader (API Change):
183055a Parser callbacks may not throw (API Change)
ebebe52 Add basic_streambuf::alloc_size
c9cd171 Fix basic_streambuf::capacity
0eb0e48 Tidying:
c5c436d Change implicit_value to default_value
01f939d Set version to 1.0.0-b16
206d0a9 Fix websocket failure tests
6b4fb28 Fix Writer exemplar in docs
4224a3a Relax ForwardIterator requirements in FieldSequence
14d7f8d Refactor base_parser_v1 callback traits:
d812344 Add pause option to on_headers interface:
c59bd53 Improve first line serialization
78ff20b Constrain parser_v1 constructor
2765a67 Refine Parser concept:
c329d33 Fix on_headers called twice from basic_parser_v1
55c4c93 Put back missing Design section in docs
90cec54 Make auto_fragment a boolean option
03642fb Rename to write_buffer_size
0ca8964 Frame processing routines are member functions
d99dfb3 Make value optional in param-list
325f579 Set version to 1.0.0-b15
c54762a Fix handling empty HTTP headers in parser_v1.hpp
c39cc06 Regression test for empty headers
60e637b Tidy up error types:
d54d597 Tidy up DynamicBuffer requirements
707fb5e Fix doc reference section
38af0f7 Fix message_v1 constructor
027c4e8 Add Secure WebSocket example
5baaa49 Add HTTPS example
076456b rfc7230 section 3.3.2 compliance
a09a044 Use bin/sh
1ff192d Update README.md for CppCon 2016 presentation
70b8555 Set version to 1.0.0-b14
b4a8342 Update and tidy documentation
8607af5 Update README.md
4abb43e Use BOOST_ASSERT
b5bffee Don't rely on undefined behavior
8ee7a21 Better WebSocket decorator:
38f0d95 Update build scripts for MSVC, MinGW
2a5b116 Fix error handling in server examples
4c7065a Add missing rebind to handler_alloc
git-subtree-dir: src/beast
git-subtree-split: 1ab7a2f04ca9a0b35f2032877cab78d94e96ebad
This commit is contained in:
@@ -10,8 +10,8 @@ env:
|
||||
# to boost's .tar.gz.
|
||||
- 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
|
||||
|
||||
180
CHANGELOG.md
180
CHANGELOG.md
@@ -1,3 +1,183 @@
|
||||
1.0.0-b22
|
||||
|
||||
* Fix broken Intellisense
|
||||
* Implement the Asio deallocation-before-invocation guarantee
|
||||
* Add handler helpers
|
||||
* Avoid copies in handler_alloc
|
||||
* Update README.md example programs
|
||||
* Fix websocket stream read documentation
|
||||
* Disable Boost.Coroutine deprecation warning
|
||||
* Update documentation examples
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b21
|
||||
|
||||
* Remove extraneous includes
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b20
|
||||
|
||||
ZLib
|
||||
|
||||
* Add ZLib module
|
||||
|
||||
API Changes:
|
||||
|
||||
* Rename HTTP identifiers
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b19
|
||||
|
||||
* Boost library min/max guidance
|
||||
* Improvements to code coverage
|
||||
* Use boost::lexical_cast instead of std::to_string
|
||||
* Fix prepare_buffers value_type
|
||||
* Fix consuming_buffers value_type
|
||||
* Better buffer_cat
|
||||
|
||||
HTTP
|
||||
|
||||
* Make chunk_encode public
|
||||
* Add write, async_write, operator<< for message_headers
|
||||
* Add read, async_read for message_headers
|
||||
* Fix with_body example
|
||||
|
||||
WebSocket
|
||||
|
||||
* Optimize utf8 validation
|
||||
* Optimize mask operations
|
||||
|
||||
API Changes:
|
||||
|
||||
* Refactor message and message_headers declarations
|
||||
* prepared_buffers is private
|
||||
* consume_buffers is removed
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b18
|
||||
|
||||
* Increase optimization settings for MSVC builds
|
||||
|
||||
HTTP
|
||||
|
||||
* Check invariants in parse_op:
|
||||
* Clean up message docs
|
||||
|
||||
WebSocket
|
||||
|
||||
* Write buffer option does not change capacity
|
||||
* Close connection during async_read on close frame
|
||||
* Add pong, async pong to stream
|
||||
|
||||
Core
|
||||
|
||||
* Meet DynamicBuffer requirements for static_streambuf
|
||||
* Fix write_frame masking and auto-fragment handling
|
||||
|
||||
Extras
|
||||
|
||||
* unit_test::suite fixes:
|
||||
- New overload of fail() specifies file and line
|
||||
- BEAST_EXPECTS only evaluates the reason string on a failure
|
||||
* Add zlib module
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b17
|
||||
|
||||
* Change implicit to default value in example
|
||||
* Tidy up some declarations
|
||||
* Fix basic_streambuf::capacity
|
||||
* Add basic_streambuf::alloc_size
|
||||
* Parser callbacks may not throw
|
||||
* Fix Reader concept doc typo
|
||||
* Add is_Reader trait
|
||||
* Tidy up basic_headers for documentation
|
||||
* Tidy up documentation
|
||||
* Add basic_parser_v1::reset
|
||||
* Fix handling of body_what::pause in basic_parser_v1
|
||||
* Add headers_parser
|
||||
* Engaged invokable is destructible
|
||||
* Improve websocket example in README.md
|
||||
* Refactor read_size_helper
|
||||
|
||||
API Changes:
|
||||
|
||||
* Added init() to Reader requirements
|
||||
* Reader must be nothrow constructible
|
||||
* Reader is now constructed right before reading the body
|
||||
- The message passed on construction is filled in
|
||||
* Rework HTTP concepts:
|
||||
- Writer uses write instead of operator()
|
||||
- Refactor traits to use void_t
|
||||
- Remove is_ReadableBody, is_WritableBody
|
||||
- Add has_reader, has_writer, is_Reader, is_Writer
|
||||
- More friendly compile errors on failed concept checks
|
||||
* basic_parser_v1 requires all callbacks present
|
||||
* on_headers parser callback now returns void
|
||||
* on_body_what is a new required parser callback returning body_what
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b16
|
||||
|
||||
* Make value optional in param-list
|
||||
* Frame processing routines are member functions
|
||||
* Fix on_headers called twice from basic_parser_v1
|
||||
* Constrain parser_v1 constructor
|
||||
* Improve first line serialization
|
||||
* Add pause option to on_headers interface
|
||||
* Refactor base_parser_v1 callback traits:
|
||||
* Refine Parser concept
|
||||
* Relax ForwardIterator requirements in FieldSequence
|
||||
* Fix websocket failure testing
|
||||
* Refine Writer concept and fix exemplar in documentation
|
||||
|
||||
API Changes:
|
||||
|
||||
* Rename mask_buffer_size to write_buffer_size
|
||||
* Make auto_fragment a boolean option
|
||||
|
||||
The message class hierarchy is refactored (breaking change):
|
||||
|
||||
* One message class now models both HTTP/1 and HTTP/2 messages
|
||||
* message_v1, request_v1, response_v1 removed
|
||||
* New classes basic_request and basic_response model
|
||||
messages without the body.
|
||||
|
||||
Error resolution: Callers should use message, request,
|
||||
and response instead of message_v1, request_v1, and
|
||||
response_v1 respectively.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b15
|
||||
|
||||
* rfc7230 section 3.3.2 compliance
|
||||
* Add HTTPS example
|
||||
* Add Secure WebSocket example
|
||||
* Fix message_v1 constructor
|
||||
* Tidy up DynamicBuffer requirements
|
||||
* Tidy up error types and headers
|
||||
* Fix handling empty HTTP headers in parser_v1
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b14
|
||||
|
||||
* Add missing rebind to handler_alloc
|
||||
* Fix error handling in http server examples
|
||||
* Fix CMake scripts for MinGW
|
||||
* Use BOOST_ASSERT
|
||||
* Better WebSocket decorator
|
||||
* Update and tidy documentation
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1.0.0-b13
|
||||
|
||||
* dstream improvements
|
||||
|
||||
@@ -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)
|
||||
|
||||
17
Jamroot
17
Jamroot
@@ -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
|
||||
;
|
||||
|
||||
32
README.md
32
README.md
@@ -12,16 +12,15 @@
|
||||
|
||||
---
|
||||
|
||||
## CppCon 2016
|
||||
## Beast at CppCon 2016
|
||||
|
||||
I will be giving a lightning talk on Beast at CppCon 2016 in Bellevue,
|
||||
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;
|
||||
}
|
||||
|
||||
2
TODO.txt
2
TODO.txt
@@ -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
|
||||
|
||||
@@ -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]
|
||||
198
doc/design.qbk
198
doc/design.qbk
@@ -5,44 +5,52 @@
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
]
|
||||
|
||||
[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
doc/examples.qbk
Normal file
127
doc/examples.qbk
Normal file
@@ -0,0 +1,127 @@
|
||||
[/
|
||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
]
|
||||
|
||||
[section:example Examples]
|
||||
|
||||
These usage examples are intended to quickly impress upon readers the
|
||||
flavor of the library. They are complete programs which may be built
|
||||
and run. Source code and build scripts for these programs may be found
|
||||
in the examples directory.
|
||||
|
||||
[heading HTTP GET]
|
||||
|
||||
Use HTTP to request the root page from a website and print the response:
|
||||
|
||||
```
|
||||
#include <beast/http.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main()
|
||||
{
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "boost.org";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
boost::asio::connect(sock,
|
||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
|
||||
|
||||
// Send HTTP request using beast
|
||||
beast::http::request<beast::http::empty_body> req;
|
||||
req.method = "GET";
|
||||
req.url = "/";
|
||||
req.version = 11;
|
||||
req.fields.replace("Host", host + ":" +
|
||||
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
||||
req.fields.replace("User-Agent", "Beast");
|
||||
beast::http::prepare(req);
|
||||
beast::http::write(sock, req);
|
||||
|
||||
// Receive and print HTTP response using beast
|
||||
beast::streambuf sb;
|
||||
beast::http::response<beast::http::streambuf_body> resp;
|
||||
beast::http::read(sock, sb, resp);
|
||||
std::cout << resp;
|
||||
}
|
||||
```
|
||||
[heading WebSocket]
|
||||
|
||||
Establish a WebSocket connection, send a message and receive the reply:
|
||||
```
|
||||
#include <beast/core/to_string.hpp>
|
||||
#include <beast/websocket.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main()
|
||||
{
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "echo.websocket.org";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
boost::asio::connect(sock,
|
||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
|
||||
|
||||
// WebSocket connect and send message using beast
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
||||
ws.handshake(host, "/");
|
||||
ws.write(boost::asio::buffer(std::string("Hello, world!")));
|
||||
|
||||
// Receive WebSocket message, print and close using beast
|
||||
beast::streambuf sb;
|
||||
beast::websocket::opcode op;
|
||||
ws.read(op, sb);
|
||||
ws.close(beast::websocket::close_code::normal);
|
||||
std::cout << beast::to_string(sb.data()) << "\n";
|
||||
}
|
||||
```
|
||||
|
||||
[heading Secure WebSocket]
|
||||
|
||||
Establish a WebSocket connection over an encrypted TLS connection,
|
||||
send a message and receive the reply. Requires OpenSSL to build.
|
||||
|
||||
* [@examples/websocket_ssl_example.cpp]
|
||||
|
||||
[heading HTTPS GET]
|
||||
|
||||
This example demonstrates sending and receiving HTTP messages
|
||||
over a TLS connection. Requires OpenSSL to build.
|
||||
|
||||
* [@examples/http_ssl_example.cpp]
|
||||
|
||||
[heading HTTP Crawl]
|
||||
|
||||
This example retrieves the page at each of the most popular domains
|
||||
as measured by Alexa.
|
||||
|
||||
* [@examples/http_crawl.cpp]
|
||||
|
||||
[heading HTTP Server]
|
||||
|
||||
This example demonstrates both synchronous and asynchronous server
|
||||
implementations. It also provides an example of implementing a [*Body]
|
||||
type, in `file_body`.
|
||||
|
||||
* [@examples/file_body.hpp]
|
||||
* [@examples/http_async_server.hpp]
|
||||
* [@examples/http_sync_server.hpp]
|
||||
* [@examples/http_server.cpp]
|
||||
|
||||
[heading Listings]
|
||||
|
||||
These are stand-alone listings of the HTTP and WebSocket examples.
|
||||
|
||||
* [@examples/http_example.cpp]
|
||||
* [@examples/websocket_example.cpp]
|
||||
|
||||
[endsect]
|
||||
481
doc/http.qbk
481
doc/http.qbk
@@ -5,138 +5,203 @@
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
]
|
||||
|
||||
[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;
|
||||
req.method = "GET";
|
||||
req.url = "/index.html";
|
||||
[table Create Message
|
||||
[[HTTP Request] [HTTP Response]]
|
||||
[[
|
||||
```
|
||||
request<empty_body> req;
|
||||
req.version = 11; // HTTP/1.1
|
||||
req.headers.insert("User-Agent", "hello_world");
|
||||
req.body = "";
|
||||
req.method = "GET";
|
||||
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]
|
||||
|
||||
|
||||
BIN
doc/images/CppCon2016.pdf
Normal file
BIN
doc/images/CppCon2016.pdf
Normal file
Binary file not shown.
BIN
doc/images/CppCon2016.png
Normal file
BIN
doc/images/CppCon2016.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 241 KiB After Width: | Height: | Size: 145 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 30 KiB |
Binary file not shown.
@@ -8,6 +8,7 @@
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
-->
|
||||
|
||||
<section id="index">
|
||||
<section id="beast.index">
|
||||
<title>Index</title>
|
||||
<index/>
|
||||
</section>
|
||||
|
||||
2
doc/makeqbk.sh
Normal file → Executable file
2
doc/makeqbk.sh
Normal file → Executable file
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
#
|
||||
|
||||
235
doc/master.qbk
235
doc/master.qbk
@@ -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[] '''— ''']
|
||||
[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
doc/overview.qbk
Normal file
114
doc/overview.qbk
Normal file
@@ -0,0 +1,114 @@
|
||||
[/
|
||||
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
]
|
||||
|
||||
[section:overview Introduction]
|
||||
|
||||
Beast is a header-only, cross-platform C++ library built on Boost.Asio and
|
||||
parts of Boost, containing two modules implementing widely used network
|
||||
protocols. Beast offers a universal HTTP message model, plus algorithms for
|
||||
parsing and serializing HTTP/1 messages. Beast.WebSocket provides a complete
|
||||
implementation of the WebSocket protocol. Their design achieves these goals:
|
||||
|
||||
* [*Symmetry.] Interfaces are role-agnostic; the same interfaces can be
|
||||
used to build clients, servers, or both.
|
||||
|
||||
* [*Ease of Use.] HTTP messages are modeled using simple, readily
|
||||
accessible objects. Functions and classes used to send and receive HTTP
|
||||
or WebSocket messages are designed to resemble Boost.Asio as closely as
|
||||
possible. Users familiar with Boost.Asio will be immediately comfortable
|
||||
using this library.
|
||||
|
||||
* [*Flexibility.] Interfaces do not mandate specific implementation
|
||||
strategies; important decisions such as buffer or thread management are
|
||||
left to users of the library.
|
||||
|
||||
* [*Performance.] The implementation performs competitively, making it a
|
||||
realistic choice for building high performance network servers.
|
||||
|
||||
* [*Scalability.] Development of network applications that scale to thousands
|
||||
of concurrent connections is possible with the implementation.
|
||||
|
||||
* [*Basis for further abstraction.] The interfaces facilitate the
|
||||
development of other libraries that provide higher levels of abstraction.
|
||||
|
||||
The HTTP portion of Beast is designed to be a low-level building block for
|
||||
creating higher level libraries. It implements only the HTTP protocol, and
|
||||
does not handle domain specific features (for example: cookies, redirects, or
|
||||
deflate content encodings).
|
||||
|
||||
[heading Requirements]
|
||||
|
||||
Beast requires:
|
||||
|
||||
* [*C++11.] A minimum of C++11 is needed.
|
||||
* [*Boost.] Beast is built on Boost, especially Boost.Asio.
|
||||
* [*OpenSSL.] If using TLS/Secure sockets (optional).
|
||||
|
||||
[note Tested compilers: msvc-14+, gcc 5+, clang 3.6+]
|
||||
|
||||
The library is [*header-only]. It is not necessary to add any .cpp files,
|
||||
or to add commands to your build script for building Beast. To link your
|
||||
program successfully, you'll need to add the Boost.System library to link
|
||||
with. If you use coroutines you'll also need the Boost.Coroutine library.
|
||||
Please visit the Boost documentation for instructions on how to do this for
|
||||
your particular build system.
|
||||
|
||||
[heading Motivation]
|
||||
|
||||
Beast is built on Boost.Asio A proposal to add networking functionality to the
|
||||
C++ standard library, based on Boost.Asio, is under consideration by the
|
||||
committee and on track for standardization. Since the final approved networking
|
||||
interface for the C++ standard library will likely closely resemble the current
|
||||
interface of Boost.Asio, the choice of Boost.Asio as the network transport
|
||||
layer is prudent.
|
||||
|
||||
The HTTP protocol is pervasive in network applications. As C++ is a logical
|
||||
choice for high performance network servers, there is great utility in solid
|
||||
building blocks for manipulating, sending, and receiving HTTP messages
|
||||
compliant with the Hypertext Transfer Protocol and the supplements that
|
||||
follow. Unfortunately reliable implementations or industry standards do not
|
||||
exist in C++. The development of higher level libraries is stymied by the
|
||||
lack of a common set of low-level algorithms and types for interacting with
|
||||
the HTTP protocol.
|
||||
|
||||
Today's web applications increasingly rely on alternatives to standard HTTP
|
||||
to achieve performance and/or responsiveness. While WebSocket implementations
|
||||
are widely available in common web development languages such as Javascript,
|
||||
good implementations in C++ are scarce. A survey of existing C++ WebSocket
|
||||
solutions reveals interfaces which lack symmetry, impose performance penalties,
|
||||
and needlessly restrict implementation strategies.
|
||||
|
||||
Beast.WebSocket takes advantage of Boost.Asio's extensible asynchronous
|
||||
model, handler allocation, and handler invocation hooks. Calls to
|
||||
Beast.WebSocket asynchronous initiation functions allow callers the choice
|
||||
of using a completion handler, stackful or stackless coroutines, futures,
|
||||
or user defined customizations (for example, Boost.Fiber). The
|
||||
implementation uses handler invocation hooks (__asio_handler_invoke__),
|
||||
providing execution guarantees on composed operations in a manner identical
|
||||
to Boost.Asio. The implementation also uses handler allocation hooks
|
||||
(__asio_handler_allocate__) when allocating memory internally for composed
|
||||
operations.
|
||||
|
||||
There is no need for inheritance or virtual members in a
|
||||
[link beast.ref.websocket__stream `websocket::stream`].
|
||||
All operations are templated and transparent to the compiler, allowing for
|
||||
maximum inlining and optimization.
|
||||
|
||||
[heading Credits]
|
||||
|
||||
Boost.Asio is the inspiration behind which all of the interfaces and
|
||||
implementation strategies are built. Some parts of the documentation are
|
||||
written to closely resemble the wording and presentation of Boost.Asio
|
||||
documentation. Credit goes to Christopher Kohlhoff for the wonderful
|
||||
Asio library and the ideas upon which Beast is built.
|
||||
|
||||
Beast would not be possible without the considerable time and patience
|
||||
contributed by David Schwartz, Edward Hennis, Howard Hinnant, Miguel Portilla,
|
||||
Nikolaos Bougalis, Scott Determan, Scott Schurr, and Ripple Labs for
|
||||
supporting its development.
|
||||
|
||||
[endsect]
|
||||
139
doc/quickref.xml
139
doc/quickref.xml
@@ -16,10 +16,10 @@
|
||||
<colspec colname="d"/>
|
||||
<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<<</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>
|
||||
|
||||
@@ -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>
[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) = '<<'">
|
||||
<xsl:call-template name="make-id">
|
||||
<xsl:with-param name="name"
|
||||
select="concat(substring-before($name, '<<'), '_ls_')"/>
|
||||
</xsl:call-template>
|
||||
</xsl:when>
|
||||
<xsl:when test="substring($name, string-length($name) - 1) = '>>'">
|
||||
<xsl:call-template name="make-id">
|
||||
<xsl:with-param name="name"
|
||||
select="concat(substring-before($name, '>>'), '_rs_')"/>
|
||||
</xsl:call-template>
|
||||
</xsl:when>
|
||||
<xsl:when test="contains($name, '=')">
|
||||
<xsl: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>]
</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']) > 0">
|
||||
<xsl:text>[heading Static Data Members]
</xsl:text>
|
||||
<xsl:text>[table
 [[Name][Description]]
</xsl:text>
|
||||
<xsl:for-each select="sectiondef[@kind='public-static-attrib']/memberdef" mode="class-table">
|
||||
<xsl:sort select="name"/>
|
||||
<xsl:text> [
</xsl:text>
|
||||
<xsl:text> [[link beast.ref.</xsl:text>
|
||||
<xsl:value-of select="$class-id"/>.<xsl:value-of select="name"/>
|
||||
<xsl:text> [*</xsl:text>
|
||||
<xsl:value-of select="name"/>
|
||||
<xsl:text>]]]
 [
 </xsl:text>
|
||||
<xsl:value-of select="briefdescription"/>
|
||||
<xsl:text>
 ]
 ]
</xsl:text>
|
||||
</xsl:for-each>
|
||||
<xsl:text>]
</xsl:text>
|
||||
</xsl:if>
|
||||
<xsl:if test="count(sectiondef[@kind='public-attrib']) > 0">
|
||||
<xsl:text>[heading Data Members]
</xsl:text>
|
||||
<xsl:text>[table
 [[Name][Description]]
</xsl:text>
|
||||
<xsl:for-each select="sectiondef[@kind='public-attrib' or @kind='public-static-attrib']/memberdef" mode="class-table">
|
||||
<xsl:for-each select="sectiondef[@kind='public-attrib']/memberdef" mode="class-table">
|
||||
<xsl:sort select="name"/>
|
||||
<xsl:text> [
</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>```
</xsl:text>
|
||||
<xsl:for-each select="../memberdef[name = $unqualified-name]">
|
||||
<xsl:if test="position() > 1">
|
||||
<xsl:text>
</xsl:text>
|
||||
<xsl:if test=" not(briefdescription = preceding-sibling::*/briefdescription)">
|
||||
<xsl:text>```
</xsl:text>
|
||||
<xsl:apply-templates select="briefdescription" mode="markup"/>
|
||||
<xsl:text>```
</xsl:text>
|
||||
</xsl:if>
|
||||
</xsl:if>
|
||||
<xsl:variable name="stripped-type">
|
||||
<xsl:call-template name="cleanup-type">
|
||||
<xsl:with-param name="name" select="type"/>
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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.
|
||||
]
|
||||
]
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
;
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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,33 +245,36 @@ private:
|
||||
asio::placeholders::error));
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
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.fields.insert("Server", "http_async_server");
|
||||
res.fields.insert("Content-Type", mime_type(path));
|
||||
res.body = path;
|
||||
try
|
||||
{
|
||||
prepare(res);
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
res = {};
|
||||
res.status = 500;
|
||||
res.reason = "Internal Error";
|
||||
res.version = req_.version;
|
||||
res.headers.insert("Server", "http_async_server");
|
||||
res.headers.insert("Content-Type", "text/html");
|
||||
res.body =
|
||||
std::string{"An internal error occurred"} + e.what();
|
||||
prepare(res);
|
||||
}
|
||||
async_write(sock_, std::move(res),
|
||||
std::bind(&peer::on_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
response<string_body> res;
|
||||
res.status = 500;
|
||||
res.reason = "Internal Error";
|
||||
res.version = req_.version;
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
void on_write(error_code ec)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,45 +164,49 @@ 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;
|
||||
}
|
||||
try
|
||||
{
|
||||
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.fields.insert("Server", "http_sync_server");
|
||||
res.fields.insert("Content-Type", mime_type(path));
|
||||
res.body = path;
|
||||
try
|
||||
{
|
||||
prepare(res);
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
res = {};
|
||||
res.status = 500;
|
||||
res.reason = "Internal Error";
|
||||
res.version = req.version;
|
||||
res.headers.insert("Server", "http_sync_server");
|
||||
res.headers.insert("Content-Type", "text/html");
|
||||
res.body =
|
||||
std::string{"An internal error occurred"} + e.what();
|
||||
prepare(res);
|
||||
}
|
||||
write(sock, res, ec);
|
||||
if(ec)
|
||||
break;
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
response<string_body> res;
|
||||
res.status = 500;
|
||||
res.reason = "Internal Error";
|
||||
res.version = req.version;
|
||||
res.fields.insert("Server", "http_sync_server");
|
||||
res.fields.insert("Content-Type", "text/html");
|
||||
res.body =
|
||||
std::string{"An internal error occurred: "} + e.what();
|
||||
prepare(res);
|
||||
write(sock, res, ec);
|
||||
if(ec)
|
||||
break;
|
||||
}
|
||||
}
|
||||
fail(id, ec);
|
||||
}
|
||||
};
|
||||
|
||||
32
examples/ssl/CMakeLists.txt
Normal file
32
examples/ssl/CMakeLists.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
# Part of Beast
|
||||
|
||||
GroupSources(extras/beast extras)
|
||||
GroupSources(include/beast beast)
|
||||
|
||||
GroupSources(examples/ssl "/")
|
||||
|
||||
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||
|
||||
add_executable (http-ssl-example
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
http_ssl_example.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(http-ssl-example ${OPENSSL_LIBRARIES})
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(http-ssl-example ${Boost_LIBRARIES} Threads::Threads)
|
||||
endif()
|
||||
|
||||
add_executable (websocket-ssl-example
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
websocket_ssl_example.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(websocket-ssl-example ${OPENSSL_LIBRARIES})
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(websocket-ssl-example ${Boost_LIBRARIES} Threads::Threads)
|
||||
endif()
|
||||
54
examples/ssl/Jamfile.v2
Normal file
54
examples/ssl/Jamfile.v2
Normal file
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
|
||||
import os ;
|
||||
|
||||
if [ os.name ] = SOLARIS
|
||||
{
|
||||
lib socket ;
|
||||
lib nsl ;
|
||||
}
|
||||
else if [ os.name ] = NT
|
||||
{
|
||||
lib ws2_32 ;
|
||||
lib mswsock ;
|
||||
}
|
||||
else if [ os.name ] = HPUX
|
||||
{
|
||||
lib ipv6 ;
|
||||
}
|
||||
else if [ os.name ] = HAIKU
|
||||
{
|
||||
lib network ;
|
||||
}
|
||||
|
||||
if [ os.name ] = NT
|
||||
{
|
||||
lib ssl : : <name>ssleay32 ;
|
||||
lib crypto : : <name>libeay32 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
lib ssl ;
|
||||
lib crypto ;
|
||||
}
|
||||
|
||||
project
|
||||
: requirements
|
||||
<library>ssl
|
||||
<library>crypto
|
||||
;
|
||||
|
||||
exe http-ssl-example
|
||||
:
|
||||
http_ssl_example.cpp
|
||||
;
|
||||
|
||||
exe websocket-ssl-example
|
||||
:
|
||||
websocket_ssl_example.cpp
|
||||
;
|
||||
58
examples/ssl/http_ssl_example.cpp
Normal file
58
examples/ssl/http_ssl_example.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <beast/http.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::asio::connect;
|
||||
using socket = boost::asio::ip::tcp::socket;
|
||||
using resolver = boost::asio::ip::tcp::resolver;
|
||||
using io_service = boost::asio::io_service;
|
||||
namespace ssl = boost::asio::ssl;
|
||||
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "github.com";
|
||||
io_service ios;
|
||||
resolver r{ios};
|
||||
socket sock{ios};
|
||||
connect(sock, r.resolve(resolver::query{host, "https"}));
|
||||
|
||||
// Perform SSL handshaking
|
||||
ssl::context ctx{ssl::context::sslv23};
|
||||
ssl::stream<socket&> stream{sock, ctx};
|
||||
stream.set_verify_mode(ssl::verify_none);
|
||||
stream.handshake(ssl::stream_base::client);
|
||||
|
||||
// Send HTTP request over SSL using Beast
|
||||
beast::http::request<beast::http::empty_body> req;
|
||||
req.method = "GET";
|
||||
req.url = "/";
|
||||
req.version = 11;
|
||||
req.fields.insert("Host", host + ":" +
|
||||
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
||||
req.fields.insert("User-Agent", "Beast");
|
||||
beast::http::prepare(req);
|
||||
beast::http::write(stream, req);
|
||||
|
||||
// Receive and print HTTP response using Beast
|
||||
beast::streambuf sb;
|
||||
beast::http::response<beast::http::streambuf_body> resp;
|
||||
beast::http::read(stream, sb, resp);
|
||||
std::cout << resp;
|
||||
|
||||
// Shut down SSL on the stream
|
||||
boost::system::error_code ec;
|
||||
stream.shutdown(ec);
|
||||
if(ec && ec != boost::asio::error::eof)
|
||||
std::cout << "error: " << ec.message();
|
||||
}
|
||||
49
examples/ssl/websocket_ssl_example.cpp
Normal file
49
examples/ssl/websocket_ssl_example.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <beast/core/to_string.hpp>
|
||||
#include <beast/websocket.hpp>
|
||||
#include <beast/websocket/ssl.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::asio::connect;
|
||||
using socket = boost::asio::ip::tcp::socket;
|
||||
using resolver = boost::asio::ip::tcp::resolver;
|
||||
using io_service = boost::asio::io_service;
|
||||
namespace ssl = boost::asio::ssl;
|
||||
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "echo.websocket.org";
|
||||
io_service ios;
|
||||
resolver r{ios};
|
||||
socket sock{ios};
|
||||
connect(sock, r.resolve(resolver::query{host, "https"}));
|
||||
|
||||
// Perform SSL handshaking
|
||||
using stream_type = ssl::stream<socket&>;
|
||||
ssl::context ctx{ssl::context::sslv23};
|
||||
stream_type stream{sock, ctx};
|
||||
stream.set_verify_mode(ssl::verify_none);
|
||||
stream.handshake(ssl::stream_base::client);
|
||||
|
||||
// Secure WebSocket connect and send message using Beast
|
||||
beast::websocket::stream<stream_type&> ws{stream};
|
||||
ws.handshake(host, "/");
|
||||
ws.write(boost::asio::buffer("Hello, world!"));
|
||||
|
||||
// Receive Secure WebSocket message, print and close using Beast
|
||||
beast::streambuf sb;
|
||||
beast::websocket::opcode op;
|
||||
ws.read(op, sb);
|
||||
ws.close(beast::websocket::close_code::normal);
|
||||
std::cout << to_string(sb.data()) << "\n";
|
||||
}
|
||||
@@ -24,12 +24,12 @@ int main()
|
||||
// WebSocket connect and send message using beast
|
||||
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";
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
40
include/beast/core/detail/clamp.hpp
Normal file
40
include/beast/core/detail/clamp.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_DETAIL_CLAMP_HPP
|
||||
#define BEAST_CORE_DETAIL_CLAMP_HPP
|
||||
|
||||
#include <limits>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class UInt>
|
||||
static
|
||||
std::size_t
|
||||
clamp(UInt x)
|
||||
{
|
||||
if(x >= (std::numeric_limits<std::size_t>::max)())
|
||||
return (std::numeric_limits<std::size_t>::max)();
|
||||
return static_cast<std::size_t>(x);
|
||||
}
|
||||
|
||||
template<class UInt>
|
||||
static
|
||||
std::size_t
|
||||
clamp(UInt x, std::size_t limit)
|
||||
{
|
||||
if(x >= limit)
|
||||
return limit;
|
||||
return static_cast<std::size_t>(x);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -61,6 +61,8 @@ struct is_call_possible_udt3
|
||||
int operator()(int);
|
||||
};
|
||||
|
||||
#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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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>())),
|
||||
|
||||
93
include/beast/core/detail/sync_ostream.hpp
Normal file
93
include/beast/core/detail/sync_ostream.hpp
Normal file
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_SYNC_OSTREAM_HPP
|
||||
#define BEAST_DETAIL_SYNC_OSTREAM_HPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <ostream>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/** A SyncWriteStream which outputs to a `std::ostream`
|
||||
|
||||
Objects of this type meet the requirements of @b SyncWriteStream.
|
||||
*/
|
||||
class sync_ostream
|
||||
{
|
||||
std::ostream& os_;
|
||||
|
||||
public:
|
||||
/** Construct the stream.
|
||||
|
||||
@param os The associated `std::ostream`. All buffers
|
||||
written will be passed to the associated output stream.
|
||||
*/
|
||||
sync_ostream(std::ostream& os)
|
||||
: os_(os)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers);
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers,
|
||||
error_code& ec);
|
||||
};
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
sync_ostream::
|
||||
write_some(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(
|
||||
is_ConstBufferSequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
auto const n = write_some(buffers, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
sync_ostream::
|
||||
write_some(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(
|
||||
is_ConstBufferSequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
std::size_t n = 0;
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
for(auto const& buffer : buffers)
|
||||
{
|
||||
os_.write(buffer_cast<char const*>(buffer),
|
||||
buffer_size(buffer));
|
||||
if(os_.fail())
|
||||
{
|
||||
ec = errc::make_error_code(
|
||||
errc::no_stream_resources);
|
||||
break;
|
||||
}
|
||||
n += buffer_size(buffer);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
84
include/beast/core/detail/type_traits.hpp
Normal file
84
include/beast/core/detail/type_traits.hpp
Normal file
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_TYPE_TRAITS_HPP
|
||||
#define BEAST_DETAIL_TYPE_TRAITS_HPP
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class... Ts>
|
||||
struct make_void
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template<class... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
template<class... Ts>
|
||||
inline
|
||||
void
|
||||
ignore_unused(Ts const& ...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
inline
|
||||
void
|
||||
ignore_unused()
|
||||
{}
|
||||
|
||||
template<class U>
|
||||
std::size_t constexpr
|
||||
max_sizeof()
|
||||
{
|
||||
return sizeof(U);
|
||||
}
|
||||
|
||||
template<class U0, class U1, class... Us>
|
||||
std::size_t constexpr
|
||||
max_sizeof()
|
||||
{
|
||||
return
|
||||
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
|
||||
max_sizeof<U0>() : max_sizeof<U1, Us...>();
|
||||
}
|
||||
|
||||
template<unsigned N, class T, class... Tn>
|
||||
struct repeat_tuple_impl
|
||||
{
|
||||
using type = typename repeat_tuple_impl<
|
||||
N - 1, T, T, Tn...>::type;
|
||||
};
|
||||
|
||||
template<class T, class... Tn>
|
||||
struct repeat_tuple_impl<0, T, Tn...>
|
||||
{
|
||||
using type = std::tuple<T, Tn...>;
|
||||
};
|
||||
|
||||
template<unsigned N, class T>
|
||||
struct repeat_tuple
|
||||
{
|
||||
using type =
|
||||
typename repeat_tuple_impl<N-1, T>::type;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct repeat_tuple<0, T>
|
||||
{
|
||||
using type = std::tuple<>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <beast/core/detail/get_lowest_layer.hpp>
|
||||
#include <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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
|
||||
104
include/beast/core/handler_helpers.hpp
Normal file
104
include/beast/core/handler_helpers.hpp
Normal file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HANDLER_HELPERS_HPP
|
||||
#define BEAST_HANDLER_HELPERS_HPP
|
||||
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <memory>
|
||||
|
||||
/* Calls to:
|
||||
|
||||
* asio_handler_allocate
|
||||
* asio_handler_deallocate
|
||||
* asio_handler_invoke
|
||||
* asio_handler_is_continuation
|
||||
|
||||
must be made from a namespace that does not
|
||||
contain overloads of this function. The beast_asio_helpers
|
||||
namespace is defined here for that purpose.
|
||||
*/
|
||||
|
||||
namespace beast_asio_helpers {
|
||||
|
||||
/// Allocation function for handlers.
|
||||
template <class Handler>
|
||||
inline
|
||||
void*
|
||||
allocate(std::size_t s, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
return ::operator new(s);
|
||||
#else
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(s, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Deallocation function for handlers.
|
||||
template<class Handler>
|
||||
inline
|
||||
void
|
||||
deallocate(void* p, std::size_t s, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
::operator delete(p);
|
||||
#else
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(p, s, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Invoke function for handlers.
|
||||
template<class Function, class Handler>
|
||||
inline
|
||||
void
|
||||
invoke(Function& function, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
Function tmp(function);
|
||||
tmp();
|
||||
#else
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(function, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Invoke function for handlers.
|
||||
template<class Function, class Handler>
|
||||
inline
|
||||
void
|
||||
invoke(Function const& function, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
Function tmp(function);
|
||||
tmp();
|
||||
#else
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(function, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Returns true if handler represents a continuation of the asynchronous operation
|
||||
template<class Handler>
|
||||
inline
|
||||
bool
|
||||
is_continuation(Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
return false;
|
||||
#else
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // beast_asio_helpers
|
||||
|
||||
#endif
|
||||
173
include/beast/core/handler_ptr.hpp
Normal file
173
include/beast/core/handler_ptr.hpp
Normal file
@@ -0,0 +1,173 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HANDLER_PTR_HPP
|
||||
#define BEAST_HANDLER_PTR_HPP
|
||||
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A smart pointer container.
|
||||
|
||||
This is a smart pointer that retains shared ownership of an
|
||||
object through a pointer. Memory is managed using the allocation
|
||||
and deallocation functions associated with a completion handler,
|
||||
which is also stored in the object. The object is destroyed and
|
||||
its memory deallocated when one of the following happens:
|
||||
|
||||
@li The function @ref invoke is called.
|
||||
|
||||
@li The function @ref release_handler is called
|
||||
|
||||
@li The last remaining container owning the object is destroyed
|
||||
|
||||
Objects of this type are used in the implementation of
|
||||
composed operations. Typically the composed operation's shared
|
||||
state is managed by the @ref handler_ptr and an allocator
|
||||
associated with the final handler is used to create the managed
|
||||
object.
|
||||
|
||||
@note The reference count is stored using a 16 bit unsigned
|
||||
integer. Making more than 2^16 copies of one object results
|
||||
in undefined behavior.
|
||||
*/
|
||||
template<class T, class Handler>
|
||||
class handler_ptr
|
||||
{
|
||||
struct P
|
||||
{
|
||||
T* t;
|
||||
std::atomic<std::uint16_t> n;
|
||||
|
||||
// There's no way to put the handler anywhere else
|
||||
// without exposing ourselves to race conditions
|
||||
// and all sorts of ugliness.
|
||||
// See:
|
||||
// https://github.com/vinniefalco/Beast/issues/215
|
||||
Handler handler;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
P(DeducedHandler&& handler, Args&&... args);
|
||||
};
|
||||
|
||||
P* p_;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
handler_ptr(int, DeducedHandler&& handler, Args&&... args);
|
||||
|
||||
public:
|
||||
/// The type of handler this object stores
|
||||
using handler_type = Handler;
|
||||
|
||||
/// Copy assignment (disallowed).
|
||||
handler_ptr& operator=(handler_ptr const&) = delete;
|
||||
|
||||
/** Destructs the owned object if no more @ref handler_ptr link to it.
|
||||
|
||||
If `*this` owns an object and it is the last @ref handler_ptr
|
||||
owning it, the object is destroyed and the memory deallocated
|
||||
using the associated deallocator.
|
||||
*/
|
||||
~handler_ptr();
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
When this call returns, the moved-from container
|
||||
will have no owned object.
|
||||
*/
|
||||
handler_ptr(handler_ptr&& other);
|
||||
|
||||
/// Copy constructor
|
||||
handler_ptr(handler_ptr const& other);
|
||||
|
||||
/// Returns a reference to the handler
|
||||
handler_type&
|
||||
handler() const
|
||||
{
|
||||
return p_->handler;
|
||||
}
|
||||
|
||||
/// Returns a pointer to the owned object
|
||||
T*
|
||||
get() const
|
||||
{
|
||||
return p_->t;
|
||||
}
|
||||
|
||||
/// Return a reference to the owned object.
|
||||
T&
|
||||
operator*() const
|
||||
{
|
||||
return *get();
|
||||
}
|
||||
|
||||
/// Return a pointer to the owned object.
|
||||
T*
|
||||
operator->() const
|
||||
{
|
||||
return get();
|
||||
}
|
||||
|
||||
/** Release ownership of the handler
|
||||
|
||||
If `*this` owns an object, it is first destroyed.
|
||||
|
||||
@return The released handler.
|
||||
*/
|
||||
handler_type
|
||||
release_handler();
|
||||
|
||||
/** Invoke the handler in the owned object.
|
||||
|
||||
This function invokes the handler in the owned object
|
||||
with a forwarded argument list. Before the invocation,
|
||||
the owned object is destroyed, satisfying the
|
||||
deallocation-before-invocation Asio guarantee. All
|
||||
instances of @ref handler_ptr which refer to the
|
||||
same owned object will be reset, including this instance.
|
||||
*/
|
||||
template<class... Args>
|
||||
void
|
||||
invoke(Args&&... args);
|
||||
|
||||
// VFALCO The free function interface works around
|
||||
// a horrible Visual Studio 15 Update 3 bug
|
||||
|
||||
/** Construct a new `handler_ptr`.
|
||||
|
||||
@param handler The handler. The allocator associated with
|
||||
the handler will be used to allocate memory for the owned
|
||||
object. This argument will be forwarded to the owned object's
|
||||
constructor.
|
||||
|
||||
@param args Optional arguments forwarded to
|
||||
the owned object's constructor.
|
||||
*/
|
||||
/** @{ */
|
||||
template<class U, class CompletionHandler, class... Args>
|
||||
friend
|
||||
handler_ptr<U, CompletionHandler>
|
||||
make_handler_ptr(
|
||||
CompletionHandler&& handler, Args&&... args);
|
||||
|
||||
template<class U, class CompletionHandler, class... Args>
|
||||
friend
|
||||
handler_ptr<U, CompletionHandler>
|
||||
make_handler_ptr(
|
||||
CompletionHandler const& handler, Args&&... args);
|
||||
/** @} */
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/handler_ptr.ipp>
|
||||
|
||||
#endif
|
||||
@@ -8,9 +8,10 @@
|
||||
#ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP
|
||||
#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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
138
include/beast/core/impl/handler_ptr.ipp
Normal file
138
include/beast/core/impl/handler_ptr.ipp
Normal file
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_HANDLER_PTR_HPP
|
||||
#define BEAST_IMPL_HANDLER_PTR_HPP
|
||||
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <boost/asio/detail/handler_alloc_helpers.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
|
||||
template<class T, class Handler>
|
||||
template<class DeducedHandler, class... Args>
|
||||
inline
|
||||
handler_ptr<T, Handler>::P::
|
||||
P(DeducedHandler&& h, Args&&... args)
|
||||
: n(1)
|
||||
, handler(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
t = reinterpret_cast<T*>(
|
||||
beast_asio_helpers::
|
||||
allocate(sizeof(T), handler));
|
||||
try
|
||||
{
|
||||
t = new(t) T{handler,
|
||||
std::forward<Args>(args)...};
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
beast_asio_helpers::
|
||||
deallocate(t, sizeof(T), handler);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, class Handler>
|
||||
template<class DeducedHandler, class... Args>
|
||||
handler_ptr<T, Handler>::
|
||||
handler_ptr(int, DeducedHandler&& handler, Args&&... args)
|
||||
: p_(new P(std::forward<DeducedHandler>(handler),
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
}
|
||||
|
||||
template<class T, class Handler>
|
||||
handler_ptr<T, Handler>::
|
||||
~handler_ptr()
|
||||
{
|
||||
if(! p_)
|
||||
return;
|
||||
if(--p_->n)
|
||||
return;
|
||||
if(p_->t)
|
||||
{
|
||||
p_->t->~T();
|
||||
beast_asio_helpers::
|
||||
deallocate(p_->t, sizeof(T), p_->handler);
|
||||
}
|
||||
delete p_;
|
||||
}
|
||||
|
||||
template<class T, class Handler>
|
||||
handler_ptr<T, Handler>::
|
||||
handler_ptr(handler_ptr&& other)
|
||||
: p_(other.p_)
|
||||
{
|
||||
other.p_ = nullptr;
|
||||
}
|
||||
|
||||
template<class T, class Handler>
|
||||
handler_ptr<T, Handler>::
|
||||
handler_ptr(handler_ptr const& other)
|
||||
: p_(other.p_)
|
||||
{
|
||||
if(p_)
|
||||
++p_->n;
|
||||
}
|
||||
|
||||
template<class T, class Handler>
|
||||
auto
|
||||
handler_ptr<T, Handler>::
|
||||
release_handler() ->
|
||||
handler_type
|
||||
{
|
||||
BOOST_ASSERT(p_);
|
||||
BOOST_ASSERT(p_->t);
|
||||
p_->t->~T();
|
||||
beast_asio_helpers::
|
||||
deallocate(p_->t, sizeof(T), p_->handler);
|
||||
p_->t = nullptr;
|
||||
return std::move(p_->handler);
|
||||
}
|
||||
|
||||
template<class T, class Handler>
|
||||
template<class... Args>
|
||||
void
|
||||
handler_ptr<T, Handler>::
|
||||
invoke(Args&&... args)
|
||||
{
|
||||
BOOST_ASSERT(p_);
|
||||
BOOST_ASSERT(p_->t);
|
||||
p_->t->~T();
|
||||
beast_asio_helpers::
|
||||
deallocate(p_->t, sizeof(T), p_->handler);
|
||||
p_->t = nullptr;
|
||||
p_->handler(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<
|
||||
class T, class CompletionHandler, class... Args>
|
||||
handler_ptr<T, CompletionHandler>
|
||||
make_handler_ptr(
|
||||
CompletionHandler&& handler, Args&&... args)
|
||||
{
|
||||
return handler_ptr<T, CompletionHandler>{0,
|
||||
std::move(handler),
|
||||
std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
template<
|
||||
class T, class CompletionHandler, class... Args>
|
||||
handler_ptr<T, CompletionHandler>
|
||||
make_handler_ptr(
|
||||
CompletionHandler const& handler, Args&&... args)
|
||||
{
|
||||
return handler_ptr<T, CompletionHandler>{0,
|
||||
handler, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -279,6 +279,16 @@ static_streambuf::mutable_buffers_type::end() const ->
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
return const_buffers_type{in_,
|
||||
static_cast<std::size_t>(out_ - in_)};
|
||||
}
|
||||
|
||||
inline
|
||||
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
|
||||
|
||||
68
include/beast/core/prepare_buffer.hpp
Normal file
68
include/beast/core/prepare_buffer.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_PREPARE_BUFFER_HPP
|
||||
#define BEAST_PREPARE_BUFFER_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Return a shortened buffer.
|
||||
|
||||
The returned buffer points to the same memory as the
|
||||
passed buffer, but with a size that is equal to or less
|
||||
than the size of the original buffer.
|
||||
|
||||
@param n The size of the returned buffer.
|
||||
|
||||
@param buffer The buffer to shorten. Ownership of the
|
||||
underlying memory is not transferred.
|
||||
|
||||
@return A new buffer that points to the first `n` bytes
|
||||
of the original buffer.
|
||||
*/
|
||||
inline
|
||||
boost::asio::const_buffer
|
||||
prepare_buffer(std::size_t n,
|
||||
boost::asio::const_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void const*>(buffer),
|
||||
(std::min)(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
/** Return a shortened buffer.
|
||||
|
||||
The returned buffer points to the same memory as the
|
||||
passed buffer, but with a size that is equal to or less
|
||||
than the size of the original buffer.
|
||||
|
||||
@param n The size of the returned buffer.
|
||||
|
||||
@param buffer The buffer to shorten. Ownership of the
|
||||
underlying memory is not transferred.
|
||||
|
||||
@return A new buffer that points to the first `n` bytes
|
||||
of the original buffer.
|
||||
*/
|
||||
inline
|
||||
boost::asio::mutable_buffer
|
||||
prepare_buffer(std::size_t n,
|
||||
boost::asio::mutable_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void*>(buffer),
|
||||
(std::min)(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -8,150 +8,45 @@
|
||||
#ifndef BEAST_PREPARE_BUFFERS_HPP
|
||||
#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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
306
include/beast/http/basic_fields.hpp
Normal file
306
include/beast/http/basic_fields.hpp
Normal file
@@ -0,0 +1,306 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BASIC_FIELDS_HPP
|
||||
#define BEAST_HTTP_BASIC_FIELDS_HPP
|
||||
|
||||
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <beast/http/detail/basic_fields.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A container for storing HTTP header fields.
|
||||
|
||||
This container is designed to store the field value pairs that make
|
||||
up the fields and trailers in a HTTP message. Objects of this type
|
||||
are iterable, with each element holding the field name and field
|
||||
value.
|
||||
|
||||
Field names are stored as-is, but comparisons are case-insensitive.
|
||||
When the container is iterated, the fields are presented in the order
|
||||
of insertion. For fields with the same name, the container behaves
|
||||
as a `std::multiset`; there will be a separate value for each occurrence
|
||||
of the field name.
|
||||
|
||||
@note Meets the requirements of @b FieldSequence.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_fields :
|
||||
#if ! GENERATING_DOCS
|
||||
private beast::detail::empty_base_optimization<
|
||||
typename std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<
|
||||
detail::basic_fields_base::element>>,
|
||||
#endif
|
||||
public detail::basic_fields_base
|
||||
{
|
||||
using alloc_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<
|
||||
detail::basic_fields_base::element>;
|
||||
|
||||
using alloc_traits =
|
||||
std::allocator_traits<alloc_type>;
|
||||
|
||||
using size_type =
|
||||
typename std::allocator_traits<Allocator>::size_type;
|
||||
|
||||
void
|
||||
delete_all();
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::false_type);
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::true_type);
|
||||
|
||||
template<class FieldSequence>
|
||||
void
|
||||
copy_from(FieldSequence const& fs)
|
||||
{
|
||||
for(auto const& e : fs)
|
||||
insert(e.first, e.second);
|
||||
}
|
||||
|
||||
public:
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
|
||||
/** The value type of the field sequence.
|
||||
|
||||
Meets the requirements of @b Field.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using value_type = implementation_defined;
|
||||
#endif
|
||||
|
||||
/// A const iterator to the field sequence
|
||||
#if GENERATING_DOCS
|
||||
using iterator = implementation_defined;
|
||||
#endif
|
||||
|
||||
/// A const iterator to the field sequence
|
||||
#if GENERATING_DOCS
|
||||
using const_iterator = implementation_defined;
|
||||
#endif
|
||||
|
||||
/// Default constructor.
|
||||
basic_fields() = default;
|
||||
|
||||
/// Destructor
|
||||
~basic_fields();
|
||||
|
||||
/** Construct the fields.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
explicit
|
||||
basic_fields(Allocator const& alloc);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The moved-from object becomes an empty field sequence.
|
||||
|
||||
@param other The object to move from.
|
||||
*/
|
||||
basic_fields(basic_fields&& other);
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
The moved-from object becomes an empty field sequence.
|
||||
|
||||
@param other The object to move from.
|
||||
*/
|
||||
basic_fields& operator=(basic_fields&& other);
|
||||
|
||||
/// Copy constructor.
|
||||
basic_fields(basic_fields const&);
|
||||
|
||||
/// Copy assignment.
|
||||
basic_fields& operator=(basic_fields const&);
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherAlloc>
|
||||
basic_fields(basic_fields<OtherAlloc> const&);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherAlloc>
|
||||
basic_fields& operator=(basic_fields<OtherAlloc> const&);
|
||||
|
||||
/// Construct from a field sequence.
|
||||
template<class FwdIt>
|
||||
basic_fields(FwdIt first, FwdIt last);
|
||||
|
||||
/// Returns `true` if the field sequence contains no elements.
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return set_.empty();
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the field sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return set_.size();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
cbegin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
cend() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
/// Returns `true` if the specified field exists.
|
||||
bool
|
||||
exists(boost::string_ref const& name) const
|
||||
{
|
||||
return set_.find(name, less{}) != set_.end();
|
||||
}
|
||||
|
||||
/// Returns the number of values for the specified field.
|
||||
std::size_t
|
||||
count(boost::string_ref const& name) const;
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching field name.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
*/
|
||||
iterator
|
||||
find(boost::string_ref const& name) const;
|
||||
|
||||
/** Returns the value for a case-insensitive matching header, or `""`.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
*/
|
||||
boost::string_ref
|
||||
operator[](boost::string_ref const& name) const;
|
||||
|
||||
/// Clear the contents of the basic_fields.
|
||||
void
|
||||
clear() noexcept;
|
||||
|
||||
/** Remove a field.
|
||||
|
||||
If more than one field with the specified name exists, all
|
||||
matching fields will be removed.
|
||||
|
||||
@param name The name of the field(s) to remove.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
erase(boost::string_ref const& name);
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field with the same name already exists, the
|
||||
existing field is untouched and a new field value pair
|
||||
is inserted into the container.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@param value A string holding the value of the field.
|
||||
*/
|
||||
void
|
||||
insert(boost::string_ref const& name, boost::string_ref value);
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field with the same name already exists, the
|
||||
existing field is untouched and a new field value pair
|
||||
is inserted into the container.
|
||||
|
||||
@param name The name of the field
|
||||
|
||||
@param value The value of the field. The object will be
|
||||
converted to a string using `boost::lexical_cast`.
|
||||
*/
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_constructible<boost::string_ref, T>::value>::type
|
||||
insert(boost::string_ref name, T const& value)
|
||||
{
|
||||
insert(name, boost::lexical_cast<std::string>(value));
|
||||
}
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@param value A string holding the value of the field.
|
||||
*/
|
||||
void
|
||||
replace(boost::string_ref const& name, boost::string_ref value);
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The name of the field
|
||||
|
||||
@param value The value of the field. The object will be
|
||||
converted to a string using `boost::lexical_cast`.
|
||||
*/
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_constructible<boost::string_ref, T>::value>::type
|
||||
replace(boost::string_ref const& name, T const& value)
|
||||
{
|
||||
replace(name,
|
||||
boost::lexical_cast<std::string>(value));
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/basic_fields.ipp>
|
||||
|
||||
#endif
|
||||
@@ -1,474 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BASIC_HEADERS_HPP
|
||||
#define BEAST_HTTP_BASIC_HEADERS_HPP
|
||||
|
||||
#include <beast/core/detail/ci_char_traits.hpp>
|
||||
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Allocator>
|
||||
class basic_headers;
|
||||
|
||||
namespace detail {
|
||||
|
||||
class basic_headers_base
|
||||
{
|
||||
public:
|
||||
struct value_type
|
||||
{
|
||||
std::string first;
|
||||
std::string second;
|
||||
|
||||
value_type(boost::string_ref const& name_,
|
||||
boost::string_ref const& value_)
|
||||
: first(name_)
|
||||
, second(value_)
|
||||
{
|
||||
}
|
||||
|
||||
boost::string_ref
|
||||
name() const
|
||||
{
|
||||
return first;
|
||||
}
|
||||
|
||||
boost::string_ref
|
||||
value() const
|
||||
{
|
||||
return second;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
template<class Allocator>
|
||||
friend class beast::http::basic_headers;
|
||||
|
||||
struct element
|
||||
: boost::intrusive::set_base_hook <
|
||||
boost::intrusive::link_mode <
|
||||
boost::intrusive::normal_link>>
|
||||
, boost::intrusive::list_base_hook <
|
||||
boost::intrusive::link_mode <
|
||||
boost::intrusive::normal_link>>
|
||||
{
|
||||
value_type data;
|
||||
|
||||
element(boost::string_ref const& name,
|
||||
boost::string_ref const& value)
|
||||
: data(name, value)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct less : private beast::detail::ci_less
|
||||
{
|
||||
template<class String>
|
||||
bool
|
||||
operator()(String const& lhs, element const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(lhs, rhs.data.first);
|
||||
}
|
||||
|
||||
template<class String>
|
||||
bool
|
||||
operator()(element const& lhs, String const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(lhs.data.first, rhs);
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& lhs, element const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(
|
||||
lhs.data.first, rhs.data.first);
|
||||
}
|
||||
};
|
||||
|
||||
using list_t = typename boost::intrusive::make_list<
|
||||
element, boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using set_t = typename boost::intrusive::make_multiset<
|
||||
element, boost::intrusive::constant_time_size<true>,
|
||||
boost::intrusive::compare<less>>::type;
|
||||
|
||||
// data
|
||||
set_t set_;
|
||||
list_t list_;
|
||||
|
||||
basic_headers_base(set_t&& set, list_t&& list)
|
||||
: set_(std::move(set))
|
||||
, list_(std::move(list))
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
class const_iterator;
|
||||
|
||||
using iterator = const_iterator;
|
||||
|
||||
basic_headers_base() = default;
|
||||
|
||||
/// Returns an iterator to the beginning of the field sequence.
|
||||
iterator
|
||||
begin() const;
|
||||
|
||||
/// Returns an iterator to the end of the field sequence.
|
||||
iterator
|
||||
end() const;
|
||||
|
||||
/// Returns an iterator to the beginning of the field sequence.
|
||||
iterator
|
||||
cbegin() const;
|
||||
|
||||
/// Returns an iterator to the end of the field sequence.
|
||||
iterator
|
||||
cend() const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class basic_headers_base::const_iterator
|
||||
{
|
||||
using iter_type = list_t::const_iterator;
|
||||
|
||||
iter_type it_;
|
||||
|
||||
template<class Allocator>
|
||||
friend class beast::http::basic_headers;
|
||||
|
||||
friend class basic_headers_base;
|
||||
|
||||
const_iterator(iter_type it)
|
||||
: it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename basic_headers_base::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return it_->data;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
--it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A container for storing HTTP headers.
|
||||
|
||||
This container is designed to store the field value pairs that make
|
||||
up the headers and trailers in a HTTP message. Objects of this type
|
||||
are iterable, which each element holding the field name and field
|
||||
value.
|
||||
|
||||
Field names are stored as-is, but comparison are case-insensitive.
|
||||
When the container is iterated, the fields are presented in the order
|
||||
of insertion. For fields with the same name, the container behaves
|
||||
as a std::multiset; there will be a separate value for each occurrence
|
||||
of the field name.
|
||||
|
||||
@note Meets the requirements of @b `FieldSequence`.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_headers
|
||||
#if ! GENERATING_DOCS
|
||||
: private beast::detail::empty_base_optimization<
|
||||
typename std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<
|
||||
detail::basic_headers_base::element>>
|
||||
, public detail::basic_headers_base
|
||||
#endif
|
||||
{
|
||||
using alloc_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<
|
||||
detail::basic_headers_base::element>;
|
||||
|
||||
using alloc_traits =
|
||||
std::allocator_traits<alloc_type>;
|
||||
|
||||
using size_type =
|
||||
typename std::allocator_traits<Allocator>::size_type;
|
||||
|
||||
void
|
||||
delete_all();
|
||||
|
||||
void
|
||||
move_assign(basic_headers&, std::false_type);
|
||||
|
||||
void
|
||||
move_assign(basic_headers&, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_headers const&, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_headers const&, std::true_type);
|
||||
|
||||
template<class FieldSequence>
|
||||
void
|
||||
copy_from(FieldSequence const& fs)
|
||||
{
|
||||
for(auto const& e : fs)
|
||||
insert(e.first, e.second);
|
||||
}
|
||||
|
||||
public:
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
|
||||
/// Default constructor.
|
||||
basic_headers() = default;
|
||||
|
||||
/// Destructor
|
||||
~basic_headers();
|
||||
|
||||
/** Construct the headers.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
explicit
|
||||
basic_headers(Allocator const& alloc);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The moved-from object becomes an empty field sequence.
|
||||
|
||||
@param other The object to move from.
|
||||
*/
|
||||
basic_headers(basic_headers&& other);
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
The moved-from object becomes an empty field sequence.
|
||||
|
||||
@param other The object to move from.
|
||||
*/
|
||||
basic_headers& operator=(basic_headers&& other);
|
||||
|
||||
/// Copy constructor.
|
||||
basic_headers(basic_headers const&);
|
||||
|
||||
/// Copy assignment.
|
||||
basic_headers& operator=(basic_headers const&);
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherAlloc>
|
||||
basic_headers(basic_headers<OtherAlloc> const&);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherAlloc>
|
||||
basic_headers& operator=(basic_headers<OtherAlloc> const&);
|
||||
|
||||
/// Construct from a field sequence.
|
||||
template<class FwdIt>
|
||||
basic_headers(FwdIt first, FwdIt last);
|
||||
|
||||
/// Returns `true` if the field sequence contains no elements.
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return set_.empty();
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the field sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return set_.size();
|
||||
}
|
||||
|
||||
/// Returns `true` if the specified field exists.
|
||||
bool
|
||||
exists(boost::string_ref const& name) const
|
||||
{
|
||||
return set_.find(name, less{}) != set_.end();
|
||||
}
|
||||
|
||||
/// Returns the number of values for the specified field.
|
||||
std::size_t
|
||||
count(boost::string_ref const& name) const;
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching field name.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
*/
|
||||
iterator
|
||||
find(boost::string_ref const& name) const;
|
||||
|
||||
/** Returns the value for a case-insensitive matching header, or `""`.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
*/
|
||||
boost::string_ref
|
||||
operator[](boost::string_ref const& name) const;
|
||||
|
||||
/// Clear the contents of the basic_headers.
|
||||
void
|
||||
clear() noexcept;
|
||||
|
||||
/** Remove a field.
|
||||
|
||||
If more than one field with the specified name exists, all
|
||||
matching fields will be removed.
|
||||
|
||||
@param name The name of the field(s) to remove.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
erase(boost::string_ref const& name);
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field with the same name already exists, the
|
||||
existing field is untouched and a new field value pair
|
||||
is inserted into the container.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@param value A string holding the value of the field.
|
||||
*/
|
||||
void
|
||||
insert(boost::string_ref const& name, boost::string_ref value);
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field with the same name already exists, the
|
||||
existing field is untouched and a new field value pair
|
||||
is inserted into the container.
|
||||
|
||||
@param name The name of the field
|
||||
|
||||
@param value The value of the field. The object will be
|
||||
converted to a string using `boost::lexical_cast`.
|
||||
*/
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_constructible<boost::string_ref, T>::value>::type
|
||||
insert(boost::string_ref name, T const& value)
|
||||
{
|
||||
insert(name, boost::lexical_cast<std::string>(value));
|
||||
}
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@param value A string holding the value of the field.
|
||||
*/
|
||||
void
|
||||
replace(boost::string_ref const& name, boost::string_ref value);
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The name of the field
|
||||
|
||||
@param value The value of the field. The object will be
|
||||
converted to a string using `boost::lexical_cast`.
|
||||
*/
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_constructible<boost::string_ref, T>::value>::type
|
||||
replace(boost::string_ref const& name, T const& value)
|
||||
{
|
||||
replace(name,
|
||||
boost::lexical_cast<std::string>(value));
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/basic_headers.ipp>
|
||||
|
||||
#endif
|
||||
@@ -13,8 +13,8 @@
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/http/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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BODY_TYPE_HPP
|
||||
#define BEAST_HTTP_BODY_TYPE_HPP
|
||||
|
||||
// Convenience header to include everything
|
||||
// needed when declarating a user defined Body type.
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/resume_context.hpp>
|
||||
#include <boost/logic/tribool.hpp>
|
||||
|
||||
#endif
|
||||
74
include/beast/http/chunk_encode.hpp
Normal file
74
include/beast/http/chunk_encode.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
#define BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/http/detail/chunk_encode.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Returns a chunk-encoded ConstBufferSequence.
|
||||
|
||||
This returns a buffer sequence representing the
|
||||
first chunk of a chunked transfer coded body.
|
||||
|
||||
@param fin `true` if this is the last chunk.
|
||||
|
||||
@param buffers The input buffer sequence.
|
||||
|
||||
@return A chunk-encoded ConstBufferSequence representing the input.
|
||||
|
||||
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
beast::detail::buffer_cat_helper<
|
||||
detail::chunk_encode_delim,
|
||||
ConstBufferSequence,
|
||||
boost::asio::const_buffers_1>
|
||||
#endif
|
||||
chunk_encode(bool fin, ConstBufferSequence const& buffers)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
return buffer_cat(
|
||||
detail::chunk_encode_delim{buffer_size(buffers)},
|
||||
buffers,
|
||||
fin ? boost::asio::const_buffers_1{"\r\n0\r\n\r\n", 7}
|
||||
: boost::asio::const_buffers_1{"\r\n", 2});
|
||||
}
|
||||
|
||||
/** Returns a chunked encoding final chunk.
|
||||
|
||||
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
|
||||
*/
|
||||
inline
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
boost::asio::const_buffers_1
|
||||
#endif
|
||||
chunk_encode_final()
|
||||
{
|
||||
return boost::asio::const_buffers_1{"0\r\n\r\n", 5};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -9,7 +9,10 @@
|
||||
#define BEAST_HTTP_TYPE_CHECK_HPP
|
||||
|
||||
#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, ...>{};
|
||||
|
||||
214
include/beast/http/detail/basic_fields.hpp
Normal file
214
include/beast/http/detail/basic_fields.hpp
Normal file
@@ -0,0 +1,214 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_BASIC_FIELDS_HPP
|
||||
#define BEAST_HTTP_DETAIL_BASIC_FIELDS_HPP
|
||||
|
||||
#include <beast/core/detail/ci_char_traits.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Allocator>
|
||||
class basic_fields;
|
||||
|
||||
namespace detail {
|
||||
|
||||
class basic_fields_base
|
||||
{
|
||||
public:
|
||||
struct value_type
|
||||
{
|
||||
std::string first;
|
||||
std::string second;
|
||||
|
||||
value_type(boost::string_ref const& name_,
|
||||
boost::string_ref const& value_)
|
||||
: first(name_)
|
||||
, second(value_)
|
||||
{
|
||||
}
|
||||
|
||||
boost::string_ref
|
||||
name() const
|
||||
{
|
||||
return first;
|
||||
}
|
||||
|
||||
boost::string_ref
|
||||
value() const
|
||||
{
|
||||
return second;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
template<class Allocator>
|
||||
friend class beast::http::basic_fields;
|
||||
|
||||
struct element
|
||||
: boost::intrusive::set_base_hook <
|
||||
boost::intrusive::link_mode <
|
||||
boost::intrusive::normal_link>>
|
||||
, boost::intrusive::list_base_hook <
|
||||
boost::intrusive::link_mode <
|
||||
boost::intrusive::normal_link>>
|
||||
{
|
||||
value_type data;
|
||||
|
||||
element(boost::string_ref const& name,
|
||||
boost::string_ref const& value)
|
||||
: data(name, value)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct less : private beast::detail::ci_less
|
||||
{
|
||||
template<class String>
|
||||
bool
|
||||
operator()(String const& lhs, element const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(lhs, rhs.data.first);
|
||||
}
|
||||
|
||||
template<class String>
|
||||
bool
|
||||
operator()(element const& lhs, String const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(lhs.data.first, rhs);
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& lhs, element const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(
|
||||
lhs.data.first, rhs.data.first);
|
||||
}
|
||||
};
|
||||
|
||||
using list_t = typename boost::intrusive::make_list<
|
||||
element, boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using set_t = typename boost::intrusive::make_multiset<
|
||||
element, boost::intrusive::constant_time_size<true>,
|
||||
boost::intrusive::compare<less>>::type;
|
||||
|
||||
// data
|
||||
set_t set_;
|
||||
list_t list_;
|
||||
|
||||
basic_fields_base(set_t&& set, list_t&& list)
|
||||
: set_(std::move(set))
|
||||
, list_(std::move(list))
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
class const_iterator;
|
||||
|
||||
using iterator = const_iterator;
|
||||
|
||||
basic_fields_base() = default;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class basic_fields_base::const_iterator
|
||||
{
|
||||
using iter_type = list_t::const_iterator;
|
||||
|
||||
iter_type it_;
|
||||
|
||||
template<class Allocator>
|
||||
friend class beast::http::basic_fields;
|
||||
|
||||
friend class basic_fields_base;
|
||||
|
||||
const_iterator(iter_type it)
|
||||
: it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename basic_fields_base::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return it_->data;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
--it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -8,9 +8,6 @@
|
||||
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
|
||||
#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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_HAS_CONTENT_LENGTH_HPP
|
||||
#define BEAST_HTTP_DETAIL_HAS_CONTENT_LENGTH_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<class T>
|
||||
class has_content_length_value
|
||||
{
|
||||
template<class U, class R = typename std::is_convertible<
|
||||
decltype(std::declval<U>().content_length()),
|
||||
std::uint64_t>>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
public:
|
||||
// `true` if `T` meets the requirements.
|
||||
static bool const value = type::value;
|
||||
};
|
||||
|
||||
// Determines if the writer can provide the content length
|
||||
template<class T>
|
||||
using has_content_length =
|
||||
std::integral_constant<bool,
|
||||
has_content_length_value<T>::value>;
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -233,6 +233,14 @@ skip_ows(FwdIt& it, FwdIt const& end)
|
||||
}
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
void
|
||||
skip_token(FwdIt& it, FwdIt const& last)
|
||||
{
|
||||
while(it != last && is_tchar(*it))
|
||||
++it;
|
||||
}
|
||||
|
||||
inline
|
||||
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 )
|
||||
|
||||
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
|
||||
*/
|
||||
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) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
232
include/beast/http/header_parser_v1.hpp
Normal file
232
include/beast/http/header_parser_v1.hpp
Normal file
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_HEADERS_PARSER_V1_HPP
|
||||
#define BEAST_HTTP_HEADERS_PARSER_V1_HPP
|
||||
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct request_parser_base
|
||||
{
|
||||
std::string method_;
|
||||
std::string uri_;
|
||||
};
|
||||
|
||||
struct response_parser_base
|
||||
{
|
||||
std::string reason_;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/** A parser for a HTTP/1 request or response header.
|
||||
|
||||
This class uses the HTTP/1 wire format parser to
|
||||
convert a series of octets into a request or
|
||||
response @ref header.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
class header_parser_v1
|
||||
: public basic_parser_v1<isRequest,
|
||||
header_parser_v1<isRequest, Fields>>
|
||||
, private std::conditional<isRequest,
|
||||
detail::request_parser_base,
|
||||
detail::response_parser_base>::type
|
||||
{
|
||||
public:
|
||||
/// The type of the header this parser produces.
|
||||
using header_type = header<isRequest, Fields>;
|
||||
|
||||
private:
|
||||
// VFALCO Check Fields requirements?
|
||||
|
||||
std::string field_;
|
||||
std::string value_;
|
||||
header_type h_;
|
||||
bool flush_ = false;
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
header_parser_v1() = default;
|
||||
|
||||
/// Move constructor
|
||||
header_parser_v1(header_parser_v1&&) = default;
|
||||
|
||||
/// Copy constructor (disallowed)
|
||||
header_parser_v1(header_parser_v1 const&) = delete;
|
||||
|
||||
/// Move assignment (disallowed)
|
||||
header_parser_v1& operator=(header_parser_v1&&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
header_parser_v1& operator=(header_parser_v1 const&) = delete;
|
||||
|
||||
/** Construct the parser.
|
||||
|
||||
@param args Forwarded to the header constructor.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... Args>
|
||||
explicit
|
||||
header_parser_v1(Args&&... args);
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<! std::is_same<
|
||||
typename std::decay<Arg1>::type, header_parser_v1>::value>>
|
||||
explicit
|
||||
header_parser_v1(Arg1&& arg1, ArgN&&... argn)
|
||||
: h_(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Returns the parsed header
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
header_type const&
|
||||
get() const
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns the parsed header.
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
header_type&
|
||||
get()
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns ownership of the parsed header.
|
||||
|
||||
Ownership is transferred to the caller. Only
|
||||
valid if @ref complete would return `true`.
|
||||
|
||||
Requires:
|
||||
@ref header_type is @b MoveConstructible
|
||||
*/
|
||||
header_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(h_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(h_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser_v1<isRequest, header_parser_v1>;
|
||||
|
||||
void flush()
|
||||
{
|
||||
if(! flush_)
|
||||
return;
|
||||
flush_ = false;
|
||||
BOOST_ASSERT(! field_.empty());
|
||||
h_.fields.insert(field_, value_);
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
|
||||
void on_start(error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_method(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->method_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_uri(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->uri_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_reason(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->reason_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_request_or_response(std::true_type)
|
||||
{
|
||||
h_.method = std::move(this->method_);
|
||||
h_.url = std::move(this->uri_);
|
||||
}
|
||||
|
||||
void on_request_or_response(std::false_type)
|
||||
{
|
||||
h_.status = this->status_code();
|
||||
h_.reason = std::move(this->reason_);
|
||||
}
|
||||
|
||||
void on_request(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_response(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_field(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
flush();
|
||||
field_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_value(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
value_.append(s.data(), s.size());
|
||||
flush_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
on_header(std::uint64_t, error_code&)
|
||||
{
|
||||
flush();
|
||||
h_.version = 10 * this->http_major() + this->http_minor();
|
||||
}
|
||||
|
||||
body_what
|
||||
on_body_what(std::uint64_t, error_code&)
|
||||
{
|
||||
return body_what::pause;
|
||||
}
|
||||
|
||||
void on_body(boost::string_ref const&, error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_complete(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -5,8 +5,8 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#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)
|
||||
{
|
||||
@@ -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:
|
||||
/* 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>::
|
||||
|
||||
264
include/beast/http/impl/message.ipp
Normal file
264
include/beast/http/impl/message.ipp
Normal file
@@ -0,0 +1,264 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||
#define BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/core/detail/ci_char_traits.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
swap(
|
||||
header<true, Fields>& m1,
|
||||
header<true, Fields>& m2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(m1.version, m2.version);
|
||||
swap(m1.method, m2.method);
|
||||
swap(m1.url, m2.url);
|
||||
swap(m1.fields, m2.fields);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
swap(
|
||||
header<false, Fields>& a,
|
||||
header<false, Fields>& b)
|
||||
{
|
||||
using std::swap;
|
||||
swap(a.version, b.version);
|
||||
swap(a.status, b.status);
|
||||
swap(a.reason, b.reason);
|
||||
swap(a.fields, b.fields);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
swap(
|
||||
message<isRequest, Body, Fields>& m1,
|
||||
message<isRequest, Body, Fields>& m2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(m1.base(), m2.base());
|
||||
swap(m1.body, m2.body);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
bool
|
||||
is_keep_alive(header<isRequest, Fields> const& msg)
|
||||
{
|
||||
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||
if(msg.version == 11)
|
||||
{
|
||||
if(token_list{msg.fields["Connection"]}.exists("close"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
if(token_list{msg.fields["Connection"]}.exists("keep-alive"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
bool
|
||||
is_upgrade(header<isRequest, Fields> const& msg)
|
||||
{
|
||||
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||
if(msg.version == 10)
|
||||
return false;
|
||||
if(token_list{msg.fields["Connection"]}.exists("upgrade"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct prepare_info
|
||||
{
|
||||
boost::optional<connection> connection_value;
|
||||
boost::optional<std::uint64_t> content_length;
|
||||
};
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
inline
|
||||
void
|
||||
prepare_options(prepare_info& pi,
|
||||
message<isRequest, Body, Fields>& msg)
|
||||
{
|
||||
beast::detail::ignore_unused(pi, msg);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
prepare_option(prepare_info& pi,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
connection value)
|
||||
{
|
||||
beast::detail::ignore_unused(msg);
|
||||
pi.connection_value = value;
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Opt, class... Opts>
|
||||
void
|
||||
prepare_options(prepare_info& pi,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
Opt&& opt, Opts&&... opts)
|
||||
{
|
||||
prepare_option(pi, msg, opt);
|
||||
prepare_options(pi, msg,
|
||||
std::forward<Opts>(opts)...);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
prepare_content_length(prepare_info& pi,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
std::true_type)
|
||||
{
|
||||
typename Body::writer w(msg);
|
||||
// VFALCO This is a design problem!
|
||||
error_code ec;
|
||||
w.init(ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
pi.content_length = w.content_length();
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
prepare_content_length(prepare_info& pi,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
std::false_type)
|
||||
{
|
||||
beast::detail::ignore_unused(msg);
|
||||
pi.content_length = boost::none;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields,
|
||||
class... Options>
|
||||
void
|
||||
prepare(message<isRequest, Body, Fields>& msg,
|
||||
Options&&... options)
|
||||
{
|
||||
// VFALCO TODO
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_writer<Body>::value,
|
||||
"Body has no writer");
|
||||
static_assert(is_Writer<typename Body::writer,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Writer requirements not met");
|
||||
detail::prepare_info pi;
|
||||
detail::prepare_content_length(pi, msg,
|
||||
detail::has_content_length<typename Body::writer>{});
|
||||
detail::prepare_options(pi, msg,
|
||||
std::forward<Options>(options)...);
|
||||
|
||||
if(msg.fields.exists("Connection"))
|
||||
throw std::invalid_argument(
|
||||
"prepare called with Connection field set");
|
||||
|
||||
if(msg.fields.exists("Content-Length"))
|
||||
throw std::invalid_argument(
|
||||
"prepare called with Content-Length field set");
|
||||
|
||||
if(token_list{msg.fields["Transfer-Encoding"]}.exists("chunked"))
|
||||
throw std::invalid_argument(
|
||||
"prepare called with Transfer-Encoding: chunked set");
|
||||
|
||||
if(pi.connection_value != connection::upgrade)
|
||||
{
|
||||
if(pi.content_length)
|
||||
{
|
||||
struct set_field
|
||||
{
|
||||
void
|
||||
operator()(message<true, Body, Fields>& msg,
|
||||
detail::prepare_info const& pi) const
|
||||
{
|
||||
using beast::detail::ci_equal;
|
||||
if(*pi.content_length > 0 ||
|
||||
ci_equal(msg.method, "POST"))
|
||||
{
|
||||
msg.fields.insert(
|
||||
"Content-Length", *pi.content_length);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
operator()(message<false, Body, Fields>& msg,
|
||||
detail::prepare_info const& pi) const
|
||||
{
|
||||
if((msg.status / 100 ) != 1 &&
|
||||
msg.status != 204 &&
|
||||
msg.status != 304)
|
||||
{
|
||||
msg.fields.insert(
|
||||
"Content-Length", *pi.content_length);
|
||||
}
|
||||
}
|
||||
};
|
||||
set_field{}(msg, pi);
|
||||
}
|
||||
else if(msg.version >= 11)
|
||||
{
|
||||
msg.fields.insert("Transfer-Encoding", "chunked");
|
||||
}
|
||||
}
|
||||
|
||||
auto const content_length =
|
||||
msg.fields.exists("Content-Length");
|
||||
|
||||
if(pi.connection_value)
|
||||
{
|
||||
switch(*pi.connection_value)
|
||||
{
|
||||
case connection::upgrade:
|
||||
msg.fields.insert("Connection", "upgrade");
|
||||
break;
|
||||
|
||||
case connection::keep_alive:
|
||||
if(msg.version < 11)
|
||||
{
|
||||
if(content_length)
|
||||
msg.fields.insert("Connection", "keep-alive");
|
||||
}
|
||||
break;
|
||||
|
||||
case connection::close:
|
||||
if(msg.version >= 11)
|
||||
msg.fields.insert("Connection", "close");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rfc7230 6.7.
|
||||
if(msg.version < 11 && token_list{
|
||||
msg.fields["Connection"]}.exists("upgrade"))
|
||||
throw std::invalid_argument(
|
||||
"invalid version for Connection: upgrade");
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user