Merge subtree Beast 1.0.0-b6:

Merge commit '999e2fa0318b5982736d3ea01a418770ea802671'
This commit is contained in:
Vinnie Falco
2016-06-03 17:03:35 -04:00
84 changed files with 4584 additions and 2805 deletions

View File

@@ -296,7 +296,9 @@
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\detail\stream_concepts.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\detail\stream_concepts.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\detail\write_streambuf.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\detail\write_dynabuf.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\dynabuf_readstream.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\error.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\error.hpp">
</ClInclude> </ClInclude>
@@ -308,12 +310,12 @@
</None> </None>
<None Include="..\..\src\beast\include\beast\core\impl\consuming_buffers.ipp"> <None Include="..\..\src\beast\include\beast\core\impl\consuming_buffers.ipp">
</None> </None>
<None Include="..\..\src\beast\include\beast\core\impl\dynabuf_readstream.ipp">
</None>
<None Include="..\..\src\beast\include\beast\core\impl\prepare_buffers.ipp"> <None Include="..\..\src\beast\include\beast\core\impl\prepare_buffers.ipp">
</None> </None>
<None Include="..\..\src\beast\include\beast\core\impl\static_streambuf.ipp"> <None Include="..\..\src\beast\include\beast\core\impl\static_streambuf.ipp">
</None> </None>
<None Include="..\..\src\beast\include\beast\core\impl\streambuf_readstream.ipp">
</None>
<ClInclude Include="..\..\src\beast\include\beast\core\placeholders.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\placeholders.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\prepare_buffers.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\prepare_buffers.hpp">
@@ -324,16 +326,16 @@
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\streambuf.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\streambuf.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\streambuf_readstream.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\stream_concepts.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\stream_concepts.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\to_string.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\to_string.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\write_streambuf.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\write_dynabuf.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\basic_dynabuf_body.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\basic_headers.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\basic_headers.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\basic_parser_v1.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\basic_parser_v1.hpp">
@@ -348,6 +350,8 @@
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\detail\has_content_length.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\detail\has_content_length.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\detail\rfc7230.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\empty_body.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\empty_body.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\headers.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\headers.hpp">
@@ -360,6 +364,8 @@
</None> </None>
<None Include="..\..\src\beast\include\beast\http\impl\read.ipp"> <None Include="..\..\src\beast\include\beast\http\impl\read.ipp">
</None> </None>
<None Include="..\..\src\beast\include\beast\http\impl\rfc7230.ipp">
</None>
<None Include="..\..\src\beast\include\beast\http\impl\write.ipp"> <None Include="..\..\src\beast\include\beast\http\impl\write.ipp">
</None> </None>
<ClInclude Include="..\..\src\beast\include\beast\http\message.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\message.hpp">
@@ -376,8 +382,6 @@
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\resume_context.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\resume_context.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\rfc2616.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\rfc7230.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\rfc7230.hpp">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\streambuf_body.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\streambuf_body.hpp">

View File

@@ -627,9 +627,12 @@
<ClInclude Include="..\..\src\beast\include\beast\core\detail\stream_concepts.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\detail\stream_concepts.hpp">
<Filter>include\beast\core\detail</Filter> <Filter>include\beast\core\detail</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\detail\write_streambuf.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\detail\write_dynabuf.hpp">
<Filter>include\beast\core\detail</Filter> <Filter>include\beast\core\detail</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\dynabuf_readstream.hpp">
<Filter>include\beast\core</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\error.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\error.hpp">
<Filter>include\beast\core</Filter> <Filter>include\beast\core</Filter>
</ClInclude> </ClInclude>
@@ -645,15 +648,15 @@
<None Include="..\..\src\beast\include\beast\core\impl\consuming_buffers.ipp"> <None Include="..\..\src\beast\include\beast\core\impl\consuming_buffers.ipp">
<Filter>include\beast\core\impl</Filter> <Filter>include\beast\core\impl</Filter>
</None> </None>
<None Include="..\..\src\beast\include\beast\core\impl\dynabuf_readstream.ipp">
<Filter>include\beast\core\impl</Filter>
</None>
<None Include="..\..\src\beast\include\beast\core\impl\prepare_buffers.ipp"> <None Include="..\..\src\beast\include\beast\core\impl\prepare_buffers.ipp">
<Filter>include\beast\core\impl</Filter> <Filter>include\beast\core\impl</Filter>
</None> </None>
<None Include="..\..\src\beast\include\beast\core\impl\static_streambuf.ipp"> <None Include="..\..\src\beast\include\beast\core\impl\static_streambuf.ipp">
<Filter>include\beast\core\impl</Filter> <Filter>include\beast\core\impl</Filter>
</None> </None>
<None Include="..\..\src\beast\include\beast\core\impl\streambuf_readstream.ipp">
<Filter>include\beast\core\impl</Filter>
</None>
<ClInclude Include="..\..\src\beast\include\beast\core\placeholders.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\placeholders.hpp">
<Filter>include\beast\core</Filter> <Filter>include\beast\core</Filter>
</ClInclude> </ClInclude>
@@ -669,21 +672,21 @@
<ClInclude Include="..\..\src\beast\include\beast\core\streambuf.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\streambuf.hpp">
<Filter>include\beast\core</Filter> <Filter>include\beast\core</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\streambuf_readstream.hpp">
<Filter>include\beast\core</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\stream_concepts.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\stream_concepts.hpp">
<Filter>include\beast\core</Filter> <Filter>include\beast\core</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\to_string.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\to_string.hpp">
<Filter>include\beast\core</Filter> <Filter>include\beast\core</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\write_streambuf.hpp"> <ClInclude Include="..\..\src\beast\include\beast\core\write_dynabuf.hpp">
<Filter>include\beast\core</Filter> <Filter>include\beast\core</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http.hpp">
<Filter>include\beast</Filter> <Filter>include\beast</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\basic_dynabuf_body.hpp">
<Filter>include\beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\basic_headers.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\basic_headers.hpp">
<Filter>include\beast\http</Filter> <Filter>include\beast\http</Filter>
</ClInclude> </ClInclude>
@@ -705,6 +708,9 @@
<ClInclude Include="..\..\src\beast\include\beast\http\detail\has_content_length.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\detail\has_content_length.hpp">
<Filter>include\beast\http\detail</Filter> <Filter>include\beast\http\detail</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\detail\rfc7230.hpp">
<Filter>include\beast\http\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\empty_body.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\empty_body.hpp">
<Filter>include\beast\http</Filter> <Filter>include\beast\http</Filter>
</ClInclude> </ClInclude>
@@ -723,6 +729,9 @@
<None Include="..\..\src\beast\include\beast\http\impl\read.ipp"> <None Include="..\..\src\beast\include\beast\http\impl\read.ipp">
<Filter>include\beast\http\impl</Filter> <Filter>include\beast\http\impl</Filter>
</None> </None>
<None Include="..\..\src\beast\include\beast\http\impl\rfc7230.ipp">
<Filter>include\beast\http\impl</Filter>
</None>
<None Include="..\..\src\beast\include\beast\http\impl\write.ipp"> <None Include="..\..\src\beast\include\beast\http\impl\write.ipp">
<Filter>include\beast\http\impl</Filter> <Filter>include\beast\http\impl</Filter>
</None> </None>
@@ -747,9 +756,6 @@
<ClInclude Include="..\..\src\beast\include\beast\http\resume_context.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\resume_context.hpp">
<Filter>include\beast\http</Filter> <Filter>include\beast\http</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\rfc2616.hpp">
<Filter>include\beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\rfc7230.hpp"> <ClInclude Include="..\..\src\beast\include\beast\http\rfc7230.hpp">
<Filter>include\beast\http</Filter> <Filter>include\beast\http</Filter>
</ClInclude> </ClInclude>

View File

@@ -24,3 +24,7 @@ contents.xcworkspacedata
.svn .svn
profile profile
bin/ bin/
node_modules/
cov-int/
nohup.out
venv/

View File

@@ -14,13 +14,6 @@ env:
packages: &gcc5_pkgs packages: &gcc5_pkgs
- gcc-5 - gcc-5
- g++-5 - g++-5
# - gcc-5-multilib
# - g++-5-multilib
# - gcc-multilib
# - g++-multilib
# - libgd2-xpm
# - ia32-libs
# - ia32-libs-multiarch
- python-software-properties - python-software-properties
- libssl-dev - libssl-dev
- libffi-dev - libffi-dev
@@ -52,20 +45,7 @@ packages: &clang38_pkgs
matrix: matrix:
include: include:
# GCC/Debug # GCC/Coverage
# - compiler: gcc
# env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=64
# addons: &ao_gcc5
# apt:
# sources: ['ubuntu-toolchain-r-test']
# packages: *gcc5_pkgs
# # GCC/Release
# - compiler: gcc
# env: GCC_VER=5 VARIANT=release ADDRESS_MODEL=64
# addons: *ao_gcc5
# Coverage
- compiler: gcc - compiler: gcc
env: GCC_VER=5 VARIANT=coverage ADDRESS_MODEL=64 env: GCC_VER=5 VARIANT=coverage ADDRESS_MODEL=64
addons: &ao_gcc5 addons: &ao_gcc5
@@ -73,36 +53,25 @@ matrix:
sources: ['ubuntu-toolchain-r-test'] sources: ['ubuntu-toolchain-r-test']
packages: *gcc5_pkgs packages: *gcc5_pkgs
# # Clang/Debug # # GCC/Debug
# - compiler: clang # - compiler: gcc
# env: GCC_VER=5 VARIANT=debug CLANG_VER=3.8 ADDRESS_MODEL=64 # env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=64
# addons: &ao_clang38 # addons: *ao_gcc5
# apt: # branches: # NOTE: this does NOT work, though it SHOULD
# sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8'] # - master
# packages: *clang38_pkgs # - develop
# # Clang/Release # Clang/UndefinedBehaviourSanitizer
# - compiler: clang
# env: GCC_VER=5 VARIANT=release CLANG_VER=3.8 ADDRESS_MODEL=64
# addons: *ao_clang38
# Clang/AddressSanitizer
- compiler: clang - compiler: clang
env: GCC_VER=5 VARIANT=asan CLANG_VER=3.8 ADDRESS_MODEL=64 env: GCC_VER=5 VARIANT=usan CLANG_VER=3.8 ADDRESS_MODEL=64 UBSAN_OPTIONS='print_stacktrace=1'
addons: &ao_clang38 addons: &ao_clang38
apt: apt:
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8'] sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8']
packages: *clang38_pkgs packages: *clang38_pkgs
# Clang/MemorySanitizer # Clang/AddressSanitizer
# VFALCO Generates false positives unless libc++ is compiled with msan turned on
#- compiler: clang
# env: GCC_VER=5 VARIANT=msan CLANG_VER=3.8 ADDRESS_MODEL=64 MSAN_OPTIONS=poison_in_dtor=1,sanitize-memory-track-origins=2
# addons: *ao_clang38
# Clang/UndefinedBehaviourSanitizer
- compiler: clang - compiler: clang
env: GCC_VER=5 VARIANT=usan CLANG_VER=3.8 ADDRESS_MODEL=64 env: GCC_VER=5 VARIANT=asan CLANG_VER=3.8 ADDRESS_MODEL=64
addons: *ao_clang38 addons: *ao_clang38
cache: cache:

21
src/beast/CHANGELOG Normal file
View File

@@ -0,0 +1,21 @@
1.0.0-b6
* Use SFINAE on return values
* Use beast::error_code instead of nested types
* Tidy up use of GENERATING_DOCS
* Remove obsolete RFC2616 functions
* Add message swap members and free functions
* Add HTTP field value parser containers: ext_list, param_list, token_list
* Fixes for some corner cases in basic_parser_v1
* Configurable limits on headers and body sizes in basic_parser_v1
API Changes:
* ci_equal is moved to beast::http namespace, in rfc7230.hpp
* "DynamicBuffer","dynabuf" renamed from "Streambuf", "streambuf". See:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4478.html#requirements.dynamic_buffers
* basic_parser_v1 adheres to rfc7230 as strictly as possible
--------------------------------------------------------------------------------

View File

@@ -1,7 +1,9 @@
# Beast # Beast
[![Build Status](https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) [![codecov] [![Join the chat at https://gitter.im/vinniefalco/Beast](https://badges.gitter.im/vinniefalco/Beast.svg)](https://gitter.im/vinniefalco/Beast?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status]
(https://codecov.io/gh/vinniefalco/Beast/branch/master/graph/badge.svg)](https://codecov.io/gh/vinniefalco/Beast) [![Documentation] (https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) [![codecov]
(https://codecov.io/gh/vinniefalco/Beast/branch/master/graph/badge.svg)](https://codecov.io/gh/vinniefalco/Beast) [![coveralls]
(https://coveralls.io/repos/github/vinniefalco/Beast/badge.svg?branch=master)](https://coveralls.io/github/vinniefalco/Beast?branch=master) [![Documentation]
(https://img.shields.io/badge/documentation-master-brightgreen.svg)](http://vinniefalco.github.io/beast/) [![License] (https://img.shields.io/badge/documentation-master-brightgreen.svg)](http://vinniefalco.github.io/beast/) [![License]
(https://img.shields.io/badge/license-boost-brightgreen.svg)](LICENSE_1_0.txt) (https://img.shields.io/badge/license-boost-brightgreen.svg)](LICENSE_1_0.txt)
@@ -14,6 +16,11 @@ Requirements:
* C++11 or greater * C++11 or greater
* OpenSSL (optional) * OpenSSL (optional)
This software is currently in beta: interfaces are subject to change. For
recent changes see [CHANGELOG](CHANGELOG).
The library has been submitted to the
[Boost Library Incubator](http://rrsd.com/blincubator.com/bi_library/beast-2/?gform_post_id=1579)
Example WebSocket program: Example WebSocket program:
```C++ ```C++
#include <beast/to_string.hpp> #include <beast/to_string.hpp>

View File

@@ -1,21 +1,12 @@
* Add writer::prepare(msg&) interface to set Content-Type * Add writer::prepare(msg&) interface to set Content-Type
General:
* Use SFINAE on return values (search for "class =")
* Remove http,websocket error_code types and use the one in <beast/error.hpp>
Boost.Http Boost.Http
* Use enum instead of bool in isRequest * Use enum instead of bool in isRequest
* move version to a subclass of message
Docs: Docs:
* Include Example program listings in the docs * Include Example program listings in the docs
* Fix index in docs * Fix index in docs
* Figure out why namespace rfc2616 is included in docs
(currently disabled via GENERATING_DOCS macro)
* melpon sandbox? * melpon sandbox?
* Check DOXYGEN, GENERATIC_DOCS directives in source
- See if we can include them now that xsl is fixed
* Implement cleanup-param to remove spaces around template arguments * Implement cleanup-param to remove spaces around template arguments
e.g. in basic_streambuf move constructor members e.g. in basic_streambuf move constructor members
* Don't put using namespace at file scope in examples, * Don't put using namespace at file scope in examples,
@@ -27,15 +18,12 @@ Core:
* Complete allocator testing in basic_streambuf * Complete allocator testing in basic_streambuf
WebSocket: WebSocket:
* more invokable unit test coverage
* More control over the HTTP request and response during handshakes
* optimized versions of key/masking, choose prepared_key size * optimized versions of key/masking, choose prepared_key size
* invokable unit test
* Don't try to read requests into empty_body
* Give callers control over the http request/response used during handshake * Give callers control over the http request/response used during handshake
* Investigate poor autobahn results in Debug builds * Investigate poor autobahn results in Debug builds
* Fall through composed operation switch cases * Fall through composed operation switch cases
* Replace stream::error_ with stream::state_, example states: ok, error, abort_io
Need a cancel state so waking up a ping stored in invokable knows to call the
final handler with operation_aborted
* Use close_code::no_code instead of close_code::none * Use close_code::no_code instead of close_code::none
* Make request_type, response_type public APIs, * Make request_type, response_type public APIs,
use in stream member function signatures use in stream member function signatures
@@ -44,24 +32,25 @@ HTTP:
* Define Parser concept in HTTP * Define Parser concept in HTTP
- Need parse version of read() so caller can set parser options - Need parse version of read() so caller can set parser options
like maximum size of headers, maximum body size, etc like maximum size of headers, maximum body size, etc
* trim public interface of rfc2616.h to essentials only
* add bool should_close(message_v1 const&) to replace the use * add bool should_close(message_v1 const&) to replace the use
of eof return value from write and async_write of eof return value from write and async_write
* http type_check, e.g. is_WritableBody
* More fine grained parser errors * More fine grained parser errors
* HTTP parser size limit with test (configurable?) * HTTP parser size limit with test (configurable?)
* HTTP parser trailers with test * HTTP parser trailers with test
* Decode chunk encoding parameters * Decode chunk encoding parameters
* URL parser, strong URL character checking in HTTP parser * URL parser, strong URL character checking in HTTP parser
* Update for rfc7230
* Consider rename to MessageBody concept
* Fix prepare() calling content_length() without init() * Fix prepare() calling content_length() without init()
* Use construct,destroy allocator routines in basic_headers
* Complete allocator testing in basic_streambuf, basic_headers * Complete allocator testing in basic_streambuf, basic_headers
* Add tests for writer using the resume function / coros
* Custom HTTP error codes for various situations * Custom HTTP error codes for various situations
* Make empty_body write-only, remove reader nested type
* Add concepts WritableBody ReadableBody with type checks,
check them in read and write functions
* Branch prediction hints in parser * Branch prediction hints in parser
* Check basic_parser_v1 against rfc7230 for leading message whitespace * Check basic_parser_v1 against rfc7230 for leading message whitespace
* Fix the order of message constructor parameters:
body first then headers (since body is constructed with arguments more often)
* Unit tests for char tables
* Remove status_code() from API when isRequest==true, et. al.
* Permit sending trailers and parameters in chunk-encoding chunks
Future:
* SOCKS proxy client and server implementations

View File

@@ -192,11 +192,11 @@ supporting its development.
[section:types Type Requirements] [section:types Type Requirements]
[include types/Body.qbk] [include types/Body.qbk]
[include types/BufferSequence.qbk] [include types/BufferSequence.qbk]
[include types/DynamicBuffer.qbk]
[include types/Field.qbk] [include types/Field.qbk]
[include types/FieldSequence.qbk] [include types/FieldSequence.qbk]
[include types/Parser.qbk] [include types/Parser.qbk]
[include types/Reader.qbk] [include types/Reader.qbk]
[include types/Streambuf.qbk]
[include types/Streams.qbk] [include types/Streams.qbk]
[include types/Writer.qbk] [include types/Writer.qbk]
[endsect] [endsect]

View File

@@ -25,13 +25,13 @@ libraries:
* Let library users make the important decisions such as how to * Let library users make the important decisions such as how to
allocate memory or how to leverage flow control. allocate memory or how to leverage flow control.
Beast formalizes the [link beast.types.Streambuf [*`Streambuf`]] concept, Beast uses the [link beast.types.DynamicBuffer [*`DynamicBuffer`]] concept
and relies heavily on the Boost.Asio [*`ConstBufferSequence`] and presented in the Netwoking TS, and relies heavily on the Boost.Asio
[*`MutableBufferSequence`] concepts for passing buffers to functions. [*`ConstBufferSequence`] and [*`MutableBufferSequence`] concepts for passing
The authors have found the `Streambuf` and buffer sequence interfaces buffers to functions. The authors have found the dynamic buffer and buffer
to be optimal for interacting with Asio, and for other tasks such as sequence interfaces to be optimal for interacting with Asio, and for other
incremental parsing of data in buffers (for example, parsing websocket tasks such as incremental parsing of data in buffers (for example, parsing
frames stored in a [link beast.ref.static_streambuf `static_streambuf`]). websocket frames stored in a [link beast.ref.static_streambuf `static_streambuf`]).
During the development of Beast the authors have studied other software During the development of Beast the authors have studied other software
packages and in particular the comments left during the Boost Review process packages and in particular the comments left during the Boost Review process

View File

@@ -182,21 +182,9 @@ used in the examples:
resp.body = "Here is the data you requested"; resp.body = "Here is the data you requested";
``` ```
* [link beast.ref.http__basic_streambuf_body [*`basic_streambuf_body`:]] A body with a * [link beast.ref.http__streambuf_body [*`streambuf_body`:]] A body with a
`value_type` of `streambuf`. A streambuf is an efficient storage object which `value_type` of [link beast.ref.streambuf `streambuf`]: an efficient storage
uses multiple octet arrays of varying lengths to represent data. Here is object which uses multiple octet arrays of varying lengths to represent data.
a body that uses a `boost::asio::streambuf` as its container:
```
template<class ConstBufferSequence>
http::response<http::basic_streambuf_body<boost::asio::streambuf>>
make_response(ConstBufferSequence const& buffers)
{
http::response<http::streambuf_body<boost::asio::streambuf>> resp;
resp.body.commit(boost::asio::buffer_copy(resp.body.prepare(
boost::asio::buffer_size(buffers)), buffers));
return resp;
}
```
[heading Sockets] [heading Sockets]
@@ -234,7 +222,7 @@ When the implementation reads messages from a socket, it can read bytes lying
after the end of the message if they are present (the alternative is to read after the end of the message if they are present (the alternative is to read
a single byte at a time which is unsuitable for performance reasons). To a single byte at a time which is unsuitable for performance reasons). To
store and re-use these extra bytes on subsequent messages, the read interface store and re-use these extra bytes on subsequent messages, the read interface
requires an additional paramter: a [link beast.types.Streambuf [*`Streambuf`]] requires an additional paramter: a [link beast.types.DynamicBuffer [*`DynamicBuffer`]]
object. This example reads a message from the socket, with the extra bytes object. This example reads a message from the socket, with the extra bytes
stored in the streambuf parameter for use in a subsequent call to read: stored in the streambuf parameter for use in a subsequent call to read:
``` ```
@@ -264,7 +252,7 @@ called:
An alternative to using a `boost::asio::streambuf` is to use a An alternative to using a `boost::asio::streambuf` is to use a
[link beast.ref.streambuf `beast::streambuf`], which meets the requirements of [link beast.ref.streambuf `beast::streambuf`], which meets the requirements of
[*`Streambuf`] and is optimized for performance: [*`DynamicBuffer`] and is optimized for performance:
``` ```
void handle_read(boost::system::error_code); void handle_read(boost::system::error_code);
... ...
@@ -274,7 +262,7 @@ An alternative to using a `boost::asio::streambuf` is to use a
``` ```
The `read` implementation can use any object meeting the requirements of The `read` implementation can use any object meeting the requirements of
[link beast.types.Streambuf [*`Streambuf`]], allowing callers to define custom [link beast.types.DynamicBuffer [*`DynamicBuffer`]], allowing callers to define custom
memory management strategies used by the implementation. memory management strategies used by the implementation.
[endsect] [endsect]

View File

@@ -29,9 +29,9 @@
<entry valign="top"> <entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead> <bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__basic_dynabuf_body">basic_dynabuf_body</link></member>
<member><link linkend="beast.ref.http__basic_headers">basic_headers</link></member> <member><link linkend="beast.ref.http__basic_headers">basic_headers</link></member>
<member><link linkend="beast.ref.http__basic_parser_v1">basic_parser_v1</link></member> <member><link linkend="beast.ref.http__basic_parser_v1">basic_parser_v1</link></member>
<member><link linkend="beast.ref.http__basic_streambuf_body">basic_streambuf_body</link></member>
<member><link linkend="beast.ref.http__empty_body">empty_body</link></member> <member><link linkend="beast.ref.http__empty_body">empty_body</link></member>
<member><link linkend="beast.ref.http__headers">headers</link></member> <member><link linkend="beast.ref.http__headers">headers</link></member>
<member><link linkend="beast.ref.http__message">message</link></member> <member><link linkend="beast.ref.http__message">message</link></member>
@@ -39,6 +39,11 @@
<member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member> <member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member>
<member><link linkend="beast.ref.http__string_body">string_body</link></member> <member><link linkend="beast.ref.http__string_body">string_body</link></member>
</simplelist> </simplelist>
<bridgehead renderas="sect3">Options</bridgehead>
<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>
</simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead> <bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member> <member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
@@ -56,6 +61,7 @@
<member><link linkend="beast.ref.http__parse">parse</link></member> <member><link linkend="beast.ref.http__parse">parse</link></member>
<member><link linkend="beast.ref.http__prepare">prepare</link></member> <member><link linkend="beast.ref.http__prepare">prepare</link></member>
<member><link linkend="beast.ref.http__read">read</link></member> <member><link linkend="beast.ref.http__read">read</link></member>
<member><link linkend="beast.ref.http__swap">swap</link></member>
<member><link linkend="beast.ref.http__write">write</link></member> <member><link linkend="beast.ref.http__write">write</link></member>
</simplelist> </simplelist>
<bridgehead renderas="sect3">Constants</bridgehead> <bridgehead renderas="sect3">Constants</bridgehead>
@@ -129,6 +135,7 @@
<member><link linkend="beast.ref.basic_streambuf">basic_streambuf</link></member> <member><link linkend="beast.ref.basic_streambuf">basic_streambuf</link></member>
<member><link linkend="beast.ref.buffers_adapter">buffers_adapter</link></member> <member><link linkend="beast.ref.buffers_adapter">buffers_adapter</link></member>
<member><link linkend="beast.ref.consuming_buffers">consuming_buffers</link></member> <member><link linkend="beast.ref.consuming_buffers">consuming_buffers</link></member>
<member><link linkend="beast.ref.dynabuf_readstream">dynabuf_readstream</link></member>
<member><link linkend="beast.ref.error_code">error_code</link></member> <member><link linkend="beast.ref.error_code">error_code</link></member>
<member><link linkend="beast.ref.handler_alloc">handler_alloc</link></member> <member><link linkend="beast.ref.handler_alloc">handler_alloc</link></member>
<member><link linkend="beast.ref.prepared_buffers">prepared_buffers</link></member> <member><link linkend="beast.ref.prepared_buffers">prepared_buffers</link></member>
@@ -136,7 +143,6 @@
<member><link linkend="beast.ref.static_streambuf_n">static_streambuf_n</link></member> <member><link linkend="beast.ref.static_streambuf_n">static_streambuf_n</link></member>
<member><link linkend="beast.ref.static_string">static_string</link></member> <member><link linkend="beast.ref.static_string">static_string</link></member>
<member><link linkend="beast.ref.streambuf">streambuf</link></member> <member><link linkend="beast.ref.streambuf">streambuf</link></member>
<member><link linkend="beast.ref.streambuf_readstream">streambuf_readstream</link></member>
<member><link linkend="beast.ref.system_error">system_error</link></member> <member><link linkend="beast.ref.system_error">system_error</link></member>
</simplelist> </simplelist>
</entry> </entry>
@@ -163,8 +169,8 @@
<member><link linkend="beast.ref.is_BufferSequence">is_BufferSequence</link></member> <member><link linkend="beast.ref.is_BufferSequence">is_BufferSequence</link></member>
<member><link linkend="beast.ref.is_CompletionHandler">is_CompletionHandler</link></member> <member><link linkend="beast.ref.is_CompletionHandler">is_CompletionHandler</link></member>
<member><link linkend="beast.ref.is_ConstBufferSequence">is_ConstBufferSequence</link></member> <member><link linkend="beast.ref.is_ConstBufferSequence">is_ConstBufferSequence</link></member>
<member><link linkend="beast.ref.is_DynamicBuffer">is_DynamicBuffer</link></member>
<member><link linkend="beast.ref.is_MutableBufferSequence">is_MutableBufferSequence</link></member> <member><link linkend="beast.ref.is_MutableBufferSequence">is_MutableBufferSequence</link></member>
<member><link linkend="beast.ref.is_Streambuf">is_Streambuf</link></member>
<member><link linkend="beast.ref.is_SyncReadStream">is_SyncReadStream</link></member> <member><link linkend="beast.ref.is_SyncReadStream">is_SyncReadStream</link></member>
<member><link linkend="beast.ref.is_SyncStream">is_SyncStream</link></member> <member><link linkend="beast.ref.is_SyncStream">is_SyncStream</link></member>
<member><link linkend="beast.ref.is_SyncWriteStream">is_SyncWriteStream</link></member> <member><link linkend="beast.ref.is_SyncWriteStream">is_SyncWriteStream</link></member>
@@ -173,10 +179,10 @@
<entry valign="top"> <entry valign="top">
<bridgehead renderas="sect3">Concepts</bridgehead> <bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.types.BufferSequence">BufferSequence</link></member>
<member><link linkend="beast.types.streams.AsyncStream">AsyncStream</link></member> <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.Stream">Stream</link></member>
<member><link linkend="beast.types.Streambuf">Streambuf</link></member>
<member><link linkend="beast.types.streams.SyncStream">SyncStream</link></member> <member><link linkend="beast.types.streams.SyncStream">SyncStream</link></member>
</simplelist> </simplelist>
</entry> </entry>

View File

@@ -1552,15 +1552,15 @@
<xsl:when test="declname = 'ConstBufferSequence' or type = 'class ConstBufferSequence'"> <xsl:when test="declname = 'ConstBufferSequence' or type = 'class ConstBufferSequence'">
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*ConstBufferSequence]]``</xsl:text> <xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*ConstBufferSequence]]``</xsl:text>
</xsl:when> </xsl:when>
<xsl:when test="declname = 'DynamicBuffer' or type = 'class DynamicBuffer'">
<xsl:text>class ``[link beast.types.DynamicBuffer [*DynamicBuffer]]``</xsl:text>
</xsl:when>
<xsl:when test="declname = 'MutableBufferSequence' or type = 'class MutableBufferSequence'"> <xsl:when test="declname = 'MutableBufferSequence' or type = 'class MutableBufferSequence'">
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*MutableBufferSequence]]``</xsl:text> <xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*MutableBufferSequence]]``</xsl:text>
</xsl:when> </xsl:when>
<xsl:when test="declname = 'Stream' or type = 'class Stream'"> <xsl:when test="declname = 'Stream' or type = 'class Stream'">
<xsl:text>class ``[link beast.types.streams.Stream [*Stream]]``</xsl:text> <xsl:text>class ``[link beast.types.streams.Stream [*Stream]]``</xsl:text>
</xsl:when> </xsl:when>
<xsl:when test="declname = 'Streambuf' or type = 'class Streambuf'">
<xsl:text>class ``[link beast.types.Streambuf [*Streambuf]]``</xsl:text>
</xsl:when>
<xsl:when test="type = 'class SyncStream'"> <xsl:when test="type = 'class SyncStream'">
<xsl:text>class ``[link beast.types.streams.SyncStream [*SyncStream]]``</xsl:text> <xsl:text>class ``[link beast.types.streams.SyncStream [*SyncStream]]``</xsl:text>
</xsl:when> </xsl:when>

View File

@@ -0,0 +1,125 @@
[/
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:DynamicBuffer DynamicBuffer]
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
output sequence. These memory regions are internal to the dynamic buffer, but
direct access to the elements is provided to permit them to be efficiently used
with I/O operations, such as the send or receive operations of a socket. Data
written to the output sequence of a dynamic buffer object is appended to the
input sequence of the same object.
The interface to this concept is intended to permit the following
implementation strategies:
* A single contiguous octet array, which is reallocated as necessary to
accommodate changes in the size of the octet sequence.
* A sequence of one or more octet arrays, where each array is of the same
size. Additional octet array objects are appended to the sequence to
accommodate changes in the size of the octet sequence.
* 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
currently offered by [link beast.ref.basic_streambuf `basic_streambuf`].
In the table below:
* `X` denotes a dynamic buffer class.
* `a` denotes a value of type `X`.
* `c` denotes a (possibly const) value of type `X`.
* `n` denotes a value of type `std::size_t`.
* `T` denotes a type meeting the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html `ConstBufferSequence`].
* `U` denotes a type meeting the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html `MutableBufferSequence`].
[table DynamicBuffer requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X::const_buffers_type`]
[`T`]
[
This type represents the memory associated with the input sequence.
]
]
[
[`X::mutable_buffers_type`]
[`U`]
[
This type represents the memory associated with the output sequence.
]
]
[
[`c.size()`]
[`std::size_t`]
[
Returns the size, in bytes, of the input sequence.
]
]
[
[`c.max_size()`]
[`std::size_t`]
[
Returns the permitted maximum of the sum of the sizes of the input
sequence and output sequence.
]
]
[
[`c.capacity()`]
[`std::size_t`]
[
Returns the maximum sum of the sizes of the input sequence and output
sequence that the dynamic buffer can hold without requiring reallocation.
]
]
[
[`c.data()`]
[`X::const_buffers_type`]
[
Returns a constant buffer sequence u that represents the memory
associated with the input sequence, and where `buffer_size(u) == size()`.
]
]
[
[`a.prepare(n)`]
[`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
as required. All constant or mutable buffer sequences previously
obtained using `data()` or `prepare()` are invalidated.
Throws: `length_error` if `size() + n` exceeds `max_size()`.
]
]
[
[`a.commit(n)`]
[ ]
[
Appends `n` bytes from the start of the output sequence to the end of
the input sequence. The remainder of the output sequence is discarded.
If `n` is greater than the size of the output sequence, the entire
output sequence is appended to the input sequence. All constant or
mutable buffer sequences previously obtained using `data()` or
`prepare()` are invalidated.
]
]
[
[`a.consume(n)`]
[ ]
[
Removes `n` bytes from beginning of the input sequence. If `n` is
greater than the size of the input sequence, the entire input sequence
is removed. All constant or mutable buffer sequences previously
obtained using `data()` or `prepare()` are invalidated.
]
]
]
[endsect]

View File

@@ -1,96 +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)
]
[section:Streambuf Streambuf]
A [*`Streambuf`] represents a logical octet sequence divided in two sections,
the input sequence and the output sequence. Octets are written to the output
sequence, then moved to the input sequence where they are available for
reading. When some or all of the input sequence is no longer needed, it may
be consumed.
The interface to this concept is intended to permit the following
implementation strategies:
* A single contiguous octet array, which is reallocated as necessary to
accommodate changes in the size of the octet sequence.
* A sequence of one or more octet arrays, where each array is of the same
size. Additional octet array objects are appended to the sequence to
accommodate changes in the size of the octet sequence.
* 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
currently offered by [link beast.ref.basic_streambuf `basic_streambuf`].
In the table below:
* `X` denotes a class meeting the requirements of [*`Streambuf`]
* `a` denotes a value of type `X`
* `n` denotes a value convertible to `std::size_t`
* `U`, `T` denote unspecified types.
[table Streambuf requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X::const_buffers_type`]
[`T`]
[`T` meets the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html `ConstBufferSequence`].]
]
[
[`X::mutable_buffers_type`]
[`U`]
[`U` meets the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html `MutableBufferSequence`].]
]
[
[`a.commit(n)`]
[`void`]
[Moves bytes from the output sequence to the input sequence.]
]
[
[`a.consume(n)`]
[`void`]
[Removes bytes from the input sequence.]
]
[
[`a.data()`]
[`T`]
[Returns a list of buffers that represents the input sequence.]
]
[
[`a.prepare(n)`]
[`U`]
[Returns a list of buffers that represents the output sequence, with
the given size.]
]
[
[`a.size()`]
[`std::size_t`]
[Returns the size of the input sequence.]
]
[
[`a.max_size()`]
[`std::size_t`]
[Returns the maximum size of the stream buffer.]
]
[
[`read_size_helper(a, n)`]
[`std::size_t`]
[
Returns the suggested number of bytes to read into the output
sequence where `n` is an upper limit on this value. One possible
implementation is to return the number of bytes that may be prepared
without causing a dynamic allocation or `n`, whichever is smaller.
Calls to `read_size_helper` will be made without namespace
qualification, to allow the rules for argument dependent lookup to
take effect.
]
]
]
[endsect]

View File

@@ -358,7 +358,7 @@ handler of the corresponding read function.]
Because calls to read data may return a variable amount of bytes, the Because calls to read data may return a variable amount of bytes, the
interface to calls that read data require an object that meets the requirements interface to calls that read data require an object that meets the requirements
of [link beast.types.Streambuf [*`Streambuf`]]. This concept is modeled on of [link beast.types.DynamicBuffer [*`DynamicBuffer`]]. This concept is modeled on
[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/basic_streambuf.html `boost::asio::basic_streambuf`]. [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/basic_streambuf.html `boost::asio::basic_streambuf`].
The implementation does not perform queueing or buffering of messages. If The implementation does not perform queueing or buffering of messages. If

View File

@@ -321,12 +321,12 @@ public:
*/ */
template<bool isRequest, class Body, class Headers, template<bool isRequest, class Body, class Headers,
class ReadHandler> class ReadHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
#endif #endif
async_read(message_v1<isRequest, Body, Headers>& msg, async_read(message_v1<isRequest, Body, Headers>& msg,
ReadHandler&& handler); ReadHandler&& handler);
@@ -416,12 +416,12 @@ public:
*/ */
template<bool isRequest, class Body, class Headers, template<bool isRequest, class Body, class Headers,
class WriteHandler> class WriteHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
WriteHandler, void(error_code)>::result_type WriteHandler, void(error_code)>::result_type
#endif #endif
async_write(message_v1<isRequest, Body, Headers> const& msg, async_write(message_v1<isRequest, Body, Headers> const& msg,
WriteHandler&& handler); WriteHandler&& handler);

View File

@@ -13,10 +13,78 @@
namespace beast { namespace beast {
namespace test { namespace test {
enum error
{
fail_error = 1
};
namespace detail {
class fail_error_category : public boost::system::error_category
{
public:
const char*
name() const noexcept override
{
return "test";
}
std::string
message(int ev) const override
{
switch(static_cast<error>(ev))
{
default:
case error::fail_error:
return "test error";
}
}
boost::system::error_condition
default_error_condition(int ev) const noexcept override
{
return boost::system::error_condition{ev, *this};
}
bool
equivalent(int ev,
boost::system::error_condition const& condition
) const noexcept override
{
return condition.value() == ev &&
&condition.category() == this;
}
bool
equivalent(error_code const& error, int ev) const noexcept override
{
return error.value() == ev &&
&error.category() == this;
}
};
inline
boost::system::error_category const&
get_error_category()
{
static fail_error_category const cat{};
return cat;
}
} // detail
inline
error_code
make_error_code(error ev)
{
return error_code{static_cast<int>(ev),
detail::get_error_category()};
}
/** A countdown to simulated failure. /** A countdown to simulated failure.
On the Nth operation, the class will fail with the specified On the Nth operation, the class will fail with the specified
error code, or the default error code of invalid_argument. error code, or the default error code of @ref fail_error.
*/ */
class fail_counter class fail_counter
{ {
@@ -31,10 +99,10 @@ public:
@param The 0-based index of the operation to fail on or after. @param The 0-based index of the operation to fail on or after.
*/ */
explicit explicit
fail_counter(std::size_t n = 0) fail_counter(std::size_t n,
error_code ev = make_error_code(fail_error))
: n_(n) : n_(n)
, ec_(boost::system::errc::make_error_code( , ec_(ev)
boost::system::errc::errc_t::invalid_argument))
{ {
} }
@@ -66,4 +134,14 @@ public:
} // test } // test
} // beast } // beast
namespace boost {
namespace system {
template<>
struct is_error_code_enum<beast::test::error>
{
static bool const value = true;
};
} // system
} // boost
#endif #endif

View File

@@ -14,6 +14,7 @@
#include <beast/core/detail/get_lowest_layer.hpp> #include <beast/core/detail/get_lowest_layer.hpp>
#include <beast/websocket/teardown.hpp> #include <beast/websocket/teardown.hpp>
#include <beast/test/fail_counter.hpp> #include <beast/test/fail_counter.hpp>
#include <boost/optional.hpp>
namespace beast { namespace beast {
namespace test { namespace test {
@@ -26,8 +27,8 @@ namespace test {
template<class NextLayer> template<class NextLayer>
class fail_stream class fail_stream
{ {
boost::optional<fail_counter> fc_;
fail_counter* pfc_; fail_counter* pfc_;
fail_counter fc_;
NextLayer next_layer_; NextLayer next_layer_;
public: public:
@@ -46,8 +47,8 @@ public:
template<class... Args> template<class... Args>
explicit explicit
fail_stream(std::size_t n, Args&&... args) fail_stream(std::size_t n, Args&&... args)
: pfc_(&fc_) : fc_(n)
, fc_(n) , pfc_(&*fc_)
, next_layer_(std::forward<Args>(args)...) , next_layer_(std::forward<Args>(args)...)
{ {
} }

View File

@@ -24,8 +24,8 @@
#include <beast/core/static_string.hpp> #include <beast/core/static_string.hpp>
#include <beast/core/stream_concepts.hpp> #include <beast/core/stream_concepts.hpp>
#include <beast/core/streambuf.hpp> #include <beast/core/streambuf.hpp>
#include <beast/core/streambuf_readstream.hpp> #include <beast/core/dynabuf_readstream.hpp>
#include <beast/core/to_string.hpp> #include <beast/core/to_string.hpp>
#include <beast/core/write_streambuf.hpp> #include <beast/core/write_dynabuf.hpp>
#endif #endif

View File

@@ -18,14 +18,14 @@
namespace beast { namespace beast {
/** A @b `Streambuf` that uses multiple buffers internally. /** A @b `DynamicBuffer` that uses multiple buffers internally.
The implementation uses a sequence of one or more character arrays The implementation uses a sequence of one or more character arrays
of varying sizes. Additional character array objects are appended to of varying sizes. Additional character array objects are appended to
the sequence to accommodate changes in the size of the character the sequence to accommodate changes in the size of the character
sequence. sequence.
@note Meets the requirements of @b Streambuf. @note Meets the requirements of @b `DynamicBuffer`.
@tparam Allocator The allocator to use for managing memory. @tparam Allocator The allocator to use for managing memory.
*/ */
@@ -202,26 +202,37 @@ public:
basic_streambuf(std::size_t alloc_size = 1024, basic_streambuf(std::size_t alloc_size = 1024,
Allocator const& alloc = allocator_type{}); Allocator const& alloc = allocator_type{});
/// Get the associated allocator /// Returns a copy of the associated allocator.
allocator_type allocator_type
get_allocator() const get_allocator() const
{ {
return this->member(); return this->member();
} }
/// Get the maximum size of the basic_streambuf. /// Returns the size of the input sequence.
size_type
size() const
{
return in_size_;
}
/// Returns the permitted maximum sum of the sizes of the input and output sequence.
size_type size_type
max_size() const max_size() const
{ {
return std::numeric_limits<std::size_t>::max(); return std::numeric_limits<std::size_t>::max();
} }
/// Get the size of the input sequence. /// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
size_type std::size_t
size() const capacity() const;
{
return in_size_; /** 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;
/** Get a list of buffers that represents the output sequence, with the given size. /** Get a list of buffers that represents the output sequence, with the given size.
@@ -239,22 +250,11 @@ public:
void void
commit(size_type n); commit(size_type n);
/** Get a list of buffers that represents the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type
data() const;
/// Remove bytes from the input sequence. /// Remove bytes from the input sequence.
void void
consume(size_type n); consume(size_type n);
/// Clear everything. // Helper for boost::asio::read_until
void
clear();
// Helper for read_until
template<class OtherAllocator> template<class OtherAllocator>
friend friend
std::size_t std::size_t
@@ -262,6 +262,9 @@ public:
OtherAllocator> const& streambuf, std::size_t max_size); OtherAllocator> const& streambuf, std::size_t max_size);
private: private:
void
clear();
void void
move_assign(basic_streambuf& other, std::false_type); move_assign(basic_streambuf& other, std::false_type);
@@ -277,20 +280,17 @@ private:
void void
delete_list(); delete_list();
std::size_t
prepare_size() const;
void void
debug_check() const; debug_check() const;
}; };
/** Format output to a stream buffer. /** Format output to a @ref basic_streambuf.
@param streambuf The streambuf to write to. @param streambuf The @ref basic_streambuf to write to.
@param t The object to write. @param t The object to write.
@return The stream buffer. @return A reference to the @ref basic_streambuf.
*/ */
template<class Allocator, class T> template<class Allocator, class T>
basic_streambuf<Allocator>& basic_streambuf<Allocator>&

View File

@@ -35,6 +35,16 @@ struct is_ConstBufferSequence :
{ {
}; };
/// Determine if `T` meets the requirements of @b `DynamicBuffer`.
template<class T>
#if GENERATING_DOCS
struct is_DynamicBuffer : std::integral_constant<bool, ...>
#else
struct is_DynamicBuffer : detail::is_DynamicBuffer<T>::type
#endif
{
};
/// Determine if `T` meets the requirements of @b `MutableBufferSequence`. /// Determine if `T` meets the requirements of @b `MutableBufferSequence`.
template<class T> template<class T>
#if GENERATING_DOCS #if GENERATING_DOCS
@@ -46,16 +56,6 @@ struct is_MutableBufferSequence :
{ {
}; };
/// Determine if `T` meets the requirements of @b `Streambuf`.
template<class T>
#if GENERATING_DOCS
struct is_Streambuf : std::integral_constant<bool, ...>
#else
struct is_Streambuf : detail::is_Streambuf<T>::type
#endif
{
};
} // beast } // beast
#endif #endif

View File

@@ -86,7 +86,7 @@ public:
}; };
template<class T> template<class T>
class is_Streambuf class is_DynamicBuffer
{ {
template<class U, class R = std::integral_constant< template<class U, class R = std::integral_constant<
bool, is_BufferSequence<decltype( bool, is_BufferSequence<decltype(

View File

@@ -8,76 +8,96 @@
#ifndef BEAST_DETAIL_CI_CHAR_TRAITS_HPP #ifndef BEAST_DETAIL_CI_CHAR_TRAITS_HPP
#define BEAST_DETAIL_CI_CHAR_TRAITS_HPP #define BEAST_DETAIL_CI_CHAR_TRAITS_HPP
#include <boost/range/algorithm/equal.hpp>
#include <boost/utility/string_ref.hpp> #include <boost/utility/string_ref.hpp>
#include <algorithm> #include <array>
#include <type_traits> #include <cstdint>
#include <cctype>
#include <iterator>
#include <string>
#include <utility>
namespace beast { namespace beast {
namespace detail { namespace detail {
/** Case-insensitive function object for performing less than comparisons. */ inline
char
tolower(char c)
{
static std::array<std::uint8_t, 256> constexpr tab = {{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
}};
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
}
template<std::size_t N>
inline
boost::string_ref
string_helper(const char (&s)[N])
{
return boost::string_ref{s, N-1};
}
template<class T>
inline
T const&
string_helper(T const& t)
{
return t;
}
// Case-insensitive less
struct ci_less struct ci_less
{ {
static bool const is_transparent = true; static bool const is_transparent = true;
template<class S1, class S2>
bool bool
operator()(boost::string_ref const& lhs, operator()(S1 const& lhs, S2 const& rhs) const noexcept
boost::string_ref const& rhs) const noexcept
{ {
using std::begin; using std::begin;
using std::end; using std::end;
auto const s1 = string_helper(lhs);
auto const s2 = string_helper(rhs);
return std::lexicographical_compare( return std::lexicographical_compare(
begin(lhs), end(lhs), begin(rhs), end(rhs), begin(s1), end(s1), begin(s2), end(s2),
[](char lhs, char rhs) [](char lhs, char rhs)
{ {
return std::tolower(lhs) < std::tolower(rhs); return tolower(lhs) < tolower(rhs);
} }
); );
} }
}; };
inline // Case-insensitive equal
struct ci_equal_pred
{
bool
operator()(char c1, char c2) const noexcept
{
return tolower(c1) == tolower(c2);
}
};
// Case-insensitive equal
template<class S1, class S2>
bool bool
ci_equal(std::pair<const char*, std::size_t> lhs, ci_equal(S1 const& lhs, S2 const& rhs)
std::pair<const char*, std::size_t> rhs)
{ {
if(lhs.second != rhs.second) return boost::range::equal(
return false; string_helper(lhs), string_helper(rhs),
return std::equal (lhs.first, lhs.first + lhs.second, ci_equal_pred{});
rhs.first,
[] (char lhs, char rhs)
{
return std::tolower(lhs) == std::tolower(rhs);
}
);
}
template <size_t N>
inline
std::pair<const char*, std::size_t>
view(const char (&s)[N])
{
return {s, N-1};
}
inline
std::pair<const char*, std::size_t>
view(std::string const& s)
{
return {s.data(), s.size()};
}
/** Returns `true` if strings are case-insensitive equal. */
template <class String1, class String2>
inline
bool
ci_equal(String1 const& lhs, String2 const& rhs)
{
return ci_equal(view(lhs), view(rhs));
} }
} // detail } // detail

View File

@@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_DETAIL_WRITE_STREAMBUF_HPP #ifndef BEAST_DETAIL_WRITE_DYNABUF_HPP
#define BEAST_DETAIL_WRITE_STREAMBUF_HPP #define BEAST_DETAIL_WRITE_DYNABUF_HPP
#include <beast/core/buffer_concepts.hpp> #include <beast/core/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
@@ -42,73 +42,73 @@ public:
! is_string_literal<T>::value; ! is_string_literal<T>::value;
}; };
template<class Streambuf> template<class DynamicBuffer>
void void
write_streambuf(Streambuf& streambuf, write_dynabuf(DynamicBuffer& dynabuf,
boost::asio::const_buffer const& buffer) boost::asio::const_buffer const& buffer)
{ {
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
streambuf.commit(buffer_copy( dynabuf.commit(buffer_copy(
streambuf.prepare(buffer_size(buffer)), dynabuf.prepare(buffer_size(buffer)),
buffer)); buffer));
} }
template<class Streambuf> template<class DynamicBuffer>
void void
write_streambuf(Streambuf& streambuf, write_dynabuf(DynamicBuffer& dynabuf,
boost::asio::mutable_buffer const& buffer) boost::asio::mutable_buffer const& buffer)
{ {
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
streambuf.commit(buffer_copy( dynabuf.commit(buffer_copy(
streambuf.prepare(buffer_size(buffer)), dynabuf.prepare(buffer_size(buffer)),
buffer)); buffer));
} }
template<class Streambuf, class T> template<class DynamicBuffer, class T>
typename std::enable_if< typename std::enable_if<
is_BufferConvertible<T>::value && is_BufferConvertible<T>::value &&
! std::is_convertible<T, boost::asio::const_buffer>::value && ! std::is_convertible<T, boost::asio::const_buffer>::value &&
! std::is_convertible<T, boost::asio::mutable_buffer>::value ! std::is_convertible<T, boost::asio::mutable_buffer>::value
>::type >::type
write_streambuf(Streambuf& streambuf, T const& t) write_dynabuf(DynamicBuffer& dynabuf, T const& t)
{ {
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
auto const buffers = boost::asio::buffer(t); auto const buffers = boost::asio::buffer(t);
streambuf.commit(buffer_copy( dynabuf.commit(buffer_copy(
streambuf.prepare(buffer_size(buffers)), dynabuf.prepare(buffer_size(buffers)),
buffers)); buffers));
} }
template<class Streambuf, class Buffers> template<class DynamicBuffer, class Buffers>
typename std::enable_if< typename std::enable_if<
is_ConstBufferSequence<Buffers>::value && is_ConstBufferSequence<Buffers>::value &&
! is_BufferConvertible<Buffers>::value && ! is_BufferConvertible<Buffers>::value &&
! std::is_convertible<Buffers, boost::asio::const_buffer>::value && ! std::is_convertible<Buffers, boost::asio::const_buffer>::value &&
! std::is_convertible<Buffers, boost::asio::mutable_buffer>::value ! std::is_convertible<Buffers, boost::asio::mutable_buffer>::value
>::type >::type
write_streambuf(Streambuf& streambuf, Buffers const& buffers) write_dynabuf(DynamicBuffer& dynabuf, Buffers const& buffers)
{ {
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
streambuf.commit(buffer_copy( dynabuf.commit(buffer_copy(
streambuf.prepare(buffer_size(buffers)), dynabuf.prepare(buffer_size(buffers)),
buffers)); buffers));
} }
template<class Streambuf, std::size_t N> template<class DynamicBuffer, std::size_t N>
void void
write_streambuf(Streambuf& streambuf, const char (&s)[N]) write_dynabuf(DynamicBuffer& dynabuf, const char (&s)[N])
{ {
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
streambuf.commit(buffer_copy( dynabuf.commit(buffer_copy(
streambuf.prepare(N - 1), dynabuf.prepare(N - 1),
boost::asio::buffer(s, N - 1))); boost::asio::buffer(s, N - 1)));
} }
template<class Streambuf, class T> template<class DynamicBuffer, class T>
typename std::enable_if< typename std::enable_if<
! is_string_literal<T>::value && ! is_string_literal<T>::value &&
! is_ConstBufferSequence<T>::value && ! is_ConstBufferSequence<T>::value &&
@@ -116,22 +116,22 @@ typename std::enable_if<
! std::is_convertible<T, boost::asio::const_buffer>::value && ! std::is_convertible<T, boost::asio::const_buffer>::value &&
! std::is_convertible<T, boost::asio::mutable_buffer>::value ! std::is_convertible<T, boost::asio::mutable_buffer>::value
>::type >::type
write_streambuf(Streambuf& streambuf, T const& t) write_dynabuf(DynamicBuffer& dynabuf, T const& t)
{ {
using boost::asio::buffer; using boost::asio::buffer;
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
auto const s = boost::lexical_cast<std::string>(t); auto const s = boost::lexical_cast<std::string>(t);
streambuf.commit(buffer_copy( dynabuf.commit(buffer_copy(
streambuf.prepare(s.size()), buffer(s))); dynabuf.prepare(s.size()), buffer(s)));
} }
template<class Streambuf, class T0, class T1, class... TN> template<class DynamicBuffer, class T0, class T1, class... TN>
void void
write_streambuf(Streambuf& streambuf, write_dynabuf(DynamicBuffer& dynabuf,
T0 const& t0, T1 const& t1, TN const&... tn) T0 const& t0, T1 const& t1, TN const&... tn)
{ {
write_streambuf(streambuf, t0); write_dynabuf(dynabuf, t0);
write_streambuf(streambuf, t1, tn...); write_dynabuf(dynabuf, t1, tn...);
} }
} // detail } // detail

View File

@@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_STREAMBUF_READSTREAM_HPP #ifndef BEAST_DYNABUF_READSTREAM_HPP
#define BEAST_STREAMBUF_READSTREAM_HPP #define BEAST_DYNABUF_READSTREAM_HPP
#include <beast/core/async_completion.hpp> #include <beast/core/async_completion.hpp>
#include <beast/core/buffer_concepts.hpp> #include <beast/core/buffer_concepts.hpp>
@@ -22,11 +22,11 @@
namespace beast { namespace beast {
/** A @b `Stream` with attached @b `Streambuf` to buffer reads. /** A @b `Stream` with attached @b `DynamicBuffer` to buffer reads.
This wraps a @b `Stream` implementation so that calls to write are This wraps a @b `Stream` implementation so that calls to write are
passed through to the underlying stream, while calls to read will passed through to the underlying stream, while calls to read will
first consume the input sequence stored in a @b `Streambuf` which first consume the input sequence stored in a @b `DynamicBuffer` which
is part of the object. is part of the object.
The use-case for this class is different than that of the The use-case for this class is different than that of the
@@ -50,9 +50,9 @@ namespace beast {
// Process the next HTTP headers on the stream, // Process the next HTTP headers on the stream,
// leaving excess bytes behind for the next call. // leaving excess bytes behind for the next call.
// //
template<class Streambuf> template<class DynamicBuffer>
void process_http_message( void process_http_message(
streambuf_readstream<Streambuf>& stream) dynabuf_readstream<DynamicBuffer>& stream)
{ {
// Read up to and including the end of the HTTP // Read up to and including the end of the HTTP
// headers, leaving the sequence in the stream's // headers, leaving the sequence in the stream's
@@ -85,26 +85,24 @@ namespace beast {
@tparam Stream The type of stream to wrap. @tparam Stream The type of stream to wrap.
@tparam Streambuf The type of stream buffer to use. @tparam DynamicBuffer The type of stream buffer to use.
*/ */
template<class Stream, class Streambuf> template<class Stream, class DynamicBuffer>
class streambuf_readstream class dynabuf_readstream
{ {
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"Streambuf requirements not met"); "DynamicBuffer requirements not met");
using error_code = boost::system::error_code;
template<class Buffers, class Handler> template<class Buffers, class Handler>
class read_some_op; class read_some_op;
Streambuf sb_; DynamicBuffer sb_;
std::size_t capacity_ = 0; std::size_t capacity_ = 0;
Stream next_layer_; Stream next_layer_;
public: public:
/// The type of the internal buffer /// The type of the internal buffer
using streambuf_type = Streambuf; using dynabuf_type = DynamicBuffer;
/// The type of the next layer. /// The type of the next layer.
using next_layer_type = using next_layer_type =
@@ -124,14 +122,14 @@ public:
@note The behavior of move assignment on or from streams @note The behavior of move assignment on or from streams
with active or pending operations is undefined. with active or pending operations is undefined.
*/ */
streambuf_readstream(streambuf_readstream&&) = default; dynabuf_readstream(dynabuf_readstream&&) = default;
/** Move assignment. /** Move assignment.
@note The behavior of move assignment on or from streams @note The behavior of move assignment on or from streams
with active or pending operations is undefined. with active or pending operations is undefined.
*/ */
streambuf_readstream& operator=(streambuf_readstream&&) = default; dynabuf_readstream& operator=(dynabuf_readstream&&) = default;
/** Construct the wrapping stream. /** Construct the wrapping stream.
@@ -139,7 +137,7 @@ public:
*/ */
template<class... Args> template<class... Args>
explicit explicit
streambuf_readstream(Args&&... args); dynabuf_readstream(Args&&... args);
/// Get a reference to the next layer. /// Get a reference to the next layer.
next_layer_type& next_layer_type&
@@ -176,7 +174,7 @@ public:
by causing the internal buffer size to increase beyond by causing the internal buffer size to increase beyond
the caller defined maximum. the caller defined maximum.
*/ */
Streambuf& DynamicBuffer&
buffer() buffer()
{ {
return sb_; return sb_;
@@ -189,7 +187,7 @@ public:
by causing the internal buffer size to increase beyond by causing the internal buffer size to increase beyond
the caller defined maximum. the caller defined maximum.
*/ */
Streambuf const& DynamicBuffer const&
buffer() const buffer() const
{ {
return sb_; return sb_;
@@ -277,6 +275,6 @@ public:
} // beast } // beast
#include <beast/core/impl/streambuf_readstream.ipp> #include <beast/core/impl/dynabuf_readstream.ipp>
#endif #endif

View File

@@ -8,7 +8,7 @@
#ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP #ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP
#define BEAST_IMPL_BASIC_STREAMBUF_IPP #define BEAST_IMPL_BASIC_STREAMBUF_IPP
#include <beast/core/detail/write_streambuf.hpp> #include <beast/core/detail/write_dynabuf.hpp>
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <exception> #include <exception>
@@ -527,6 +527,28 @@ basic_streambuf<Allocator>::basic_streambuf(
"basic_streambuf: invalid alloc_size"); "basic_streambuf: invalid alloc_size");
} }
template<class Allocator>
std::size_t
basic_streambuf<Allocator>::capacity() const
{
auto pos = out_;
if(pos == list_.end())
return 0;
auto n = pos->size() - out_pos_;
while(++pos != list_.end())
n += pos->size();
return in_size_ + n;
}
template<class Allocator>
auto
basic_streambuf<Allocator>::
data() const ->
const_buffers_type
{
return const_buffers_type(*this);
}
template<class Allocator> template<class Allocator>
auto auto
basic_streambuf<Allocator>::prepare(size_type n) -> basic_streambuf<Allocator>::prepare(size_type n) ->
@@ -646,14 +668,6 @@ basic_streambuf<Allocator>::commit(size_type n)
debug_check(); debug_check();
} }
template<class Allocator>
auto
basic_streambuf<Allocator>::data() const ->
const_buffers_type
{
return const_buffers_type(*this);
}
template<class Allocator> template<class Allocator>
void void
basic_streambuf<Allocator>::consume(size_type n) basic_streambuf<Allocator>::consume(size_type n)
@@ -717,7 +731,8 @@ basic_streambuf<Allocator>::consume(size_type n)
template<class Allocator> template<class Allocator>
void void
basic_streambuf<Allocator>::clear() basic_streambuf<Allocator>::
clear()
{ {
delete_list(); delete_list();
list_.clear(); list_.clear();
@@ -795,21 +810,6 @@ basic_streambuf<Allocator>::delete_list()
} }
} }
// Returns the number of bytes which can be
// prepared without causing a memory allocation.
template<class Allocator>
std::size_t
basic_streambuf<Allocator>::prepare_size() const
{
auto pos = out_;
if(pos == list_.end())
return 0;
auto n = pos->size() - out_pos_;
while(++pos != list_.end())
n += pos->size();
return n;
}
template<class Allocator> template<class Allocator>
void void
basic_streambuf<Allocator>::debug_check() const basic_streambuf<Allocator>::debug_check() const
@@ -855,7 +855,7 @@ std::size_t
read_size_helper(basic_streambuf< read_size_helper(basic_streambuf<
Allocator> const& streambuf, std::size_t max_size) Allocator> const& streambuf, std::size_t max_size)
{ {
auto const avail = streambuf.prepare_size(); auto const avail = streambuf.capacity() - streambuf.size();
if(avail == 0) if(avail == 0)
return std::min(max_size, return std::min(max_size,
std::max<std::size_t>(512, streambuf.alloc_size_)); std::max<std::size_t>(512, streambuf.alloc_size_));
@@ -866,7 +866,7 @@ template<class Alloc, class T>
basic_streambuf<Alloc>& basic_streambuf<Alloc>&
operator<<(basic_streambuf<Alloc>& streambuf, T const& t) operator<<(basic_streambuf<Alloc>& streambuf, T const& t)
{ {
detail::write_streambuf(streambuf, t); detail::write_dynabuf(streambuf, t);
return streambuf; return streambuf;
} }

View File

@@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_IMPL_STREAMBUF_READSTREAM_IPP #ifndef BEAST_IMPL_DYNABUF_READSTREAM_HPP
#define BEAST_IMPL_STREAMBUF_READSTREAM_IPP #define BEAST_IMPL_DYNABUF_READSTREAM_HPP
#include <beast/core/bind_handler.hpp> #include <beast/core/bind_handler.hpp>
#include <beast/core/handler_concepts.hpp> #include <beast/core/handler_concepts.hpp>
@@ -16,24 +16,24 @@
namespace beast { namespace beast {
template<class Stream, class Streambuf> template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class Handler> template<class MutableBufferSequence, class Handler>
class streambuf_readstream< class dynabuf_readstream<
Stream, Streambuf>::read_some_op Stream, DynamicBuffer>::read_some_op
{ {
using alloc_type = using alloc_type =
handler_alloc<char, Handler>; handler_alloc<char, Handler>;
struct data struct data
{ {
streambuf_readstream& srs; dynabuf_readstream& srs;
MutableBufferSequence bs; MutableBufferSequence bs;
Handler h; Handler h;
int state = 0; int state = 0;
template<class DeducedHandler> template<class DeducedHandler>
data(DeducedHandler&& h_, data(DeducedHandler&& h_,
streambuf_readstream& srs_, dynabuf_readstream& srs_,
MutableBufferSequence const& bs_) MutableBufferSequence const& bs_)
: srs(srs_) : srs(srs_)
, bs(bs_) , bs(bs_)
@@ -50,7 +50,7 @@ public:
template<class DeducedHandler, class... Args> template<class DeducedHandler, class... Args>
read_some_op(DeducedHandler&& h, read_some_op(DeducedHandler&& h,
streambuf_readstream& srs, Args&&... args) dynabuf_readstream& srs, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h}, : d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), srs, std::forward<DeducedHandler>(h), srs,
std::forward<Args>(args)...)) std::forward<Args>(args)...))
@@ -94,10 +94,10 @@ public:
} }
}; };
template<class Stream, class Streambuf> template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class Handler> template<class MutableBufferSequence, class Handler>
void void
streambuf_readstream<Stream, Streambuf>:: dynabuf_readstream<Stream, DynamicBuffer>::
read_some_op<MutableBufferSequence, Handler>::operator()( read_some_op<MutableBufferSequence, Handler>::operator()(
error_code const& ec, std::size_t bytes_transferred) error_code const& ec, std::size_t bytes_transferred)
{ {
@@ -155,18 +155,18 @@ read_some_op<MutableBufferSequence, Handler>::operator()(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class Stream, class Streambuf> template<class Stream, class DynamicBuffer>
template<class... Args> template<class... Args>
streambuf_readstream<Stream, Streambuf>:: dynabuf_readstream<Stream, DynamicBuffer>::
streambuf_readstream(Args&&... args) dynabuf_readstream(Args&&... args)
: next_layer_(std::forward<Args>(args)...) : next_layer_(std::forward<Args>(args)...)
{ {
} }
template<class Stream, class Streambuf> template<class Stream, class DynamicBuffer>
template<class ConstBufferSequence, class WriteHandler> template<class ConstBufferSequence, class WriteHandler>
auto auto
streambuf_readstream<Stream, Streambuf>:: dynabuf_readstream<Stream, DynamicBuffer>::
async_write_some(ConstBufferSequence const& buffers, async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler) -> WriteHandler&& handler) ->
typename async_completion< typename async_completion<
@@ -184,10 +184,10 @@ async_write_some(ConstBufferSequence const& buffers,
std::forward<WriteHandler>(handler)); std::forward<WriteHandler>(handler));
} }
template<class Stream, class Streambuf> template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence> template<class MutableBufferSequence>
std::size_t std::size_t
streambuf_readstream<Stream, Streambuf>:: dynabuf_readstream<Stream, DynamicBuffer>::
read_some( read_some(
MutableBufferSequence const& buffers) MutableBufferSequence const& buffers)
{ {
@@ -203,10 +203,10 @@ read_some(
return n; return n;
} }
template<class Stream, class Streambuf> template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence> template<class MutableBufferSequence>
std::size_t std::size_t
streambuf_readstream<Stream, Streambuf>:: dynabuf_readstream<Stream, DynamicBuffer>::
read_some(MutableBufferSequence const& buffers, read_some(MutableBufferSequence const& buffers,
error_code& ec) error_code& ec)
{ {
@@ -232,10 +232,10 @@ read_some(MutableBufferSequence const& buffers,
return bytes_transferred; return bytes_transferred;
} }
template<class Stream, class Streambuf> template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class ReadHandler> template<class MutableBufferSequence, class ReadHandler>
auto auto
streambuf_readstream<Stream, Streambuf>:: dynabuf_readstream<Stream, DynamicBuffer>::
async_read_some( async_read_some(
MutableBufferSequence const& buffers, MutableBufferSequence const& buffers,
ReadHandler&& handler) -> ReadHandler&& handler) ->

View File

@@ -15,7 +15,7 @@
namespace beast { namespace beast {
/** A @b `Streambuf` with a fixed size internal buffer. /** A @b `DynamicBuffer` with a fixed size internal buffer.
Ownership of the underlying storage belongs to the derived class. Ownership of the underlying storage belongs to the derived class.

View File

@@ -506,8 +506,6 @@ compare(
} // detail } // detail
#if ! GENERATING_DOCS
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool bool
operator==( operator==(
@@ -672,8 +670,6 @@ operator>=(
return detail::compare(lhs, s) >= 0; return detail::compare(lhs, s) >= 0;
} }
#endif
} // beast } // beast
#endif #endif

View File

@@ -25,15 +25,16 @@ namespace beast {
@return A string representing the contents of the input area. @return A string representing the contents of the input area.
@note This function participates in overload resolution only if @note This function participates in overload resolution only if
the streambuf parameter meets the requirements of @b `Streambuf`. the buffers parameter meets the requirements of @b `ConstBufferSequence`.
*/ */
template<class ConstBufferSequence template<class ConstBufferSequence>
#if ! GENERATING_DOCS #if GENERATING_DOCS
,class = std::enable_if<is_ConstBufferSequence<
ConstBufferSequence>::value>
#endif
>
std::string std::string
#else
typename std::enable_if<
is_ConstBufferSequence<ConstBufferSequence>::value,
std::string>::type
#endif
to_string(ConstBufferSequence const& buffers) to_string(ConstBufferSequence const& buffers)
{ {
using boost::asio::buffer_cast; using boost::asio::buffer_cast;

View File

@@ -5,20 +5,20 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_WRITE_STREAMBUF_HPP #ifndef BEAST_WRITE_DYNABUF_HPP
#define BEAST_WRITE_STREAMBUF_HPP #define BEAST_WRITE_DYNABUF_HPP
#include <beast/core/buffer_concepts.hpp> #include <beast/core/buffer_concepts.hpp>
#include <beast/core/detail/write_streambuf.hpp> #include <beast/core/detail/write_dynabuf.hpp>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
namespace beast { namespace beast {
/** Write to a Streambuf. /** Write to a @b `DynamicBuffer`.
This function appends the serialized representation of each provided This function appends the serialized representation of each provided
argument into the stream buffer. It is capable of converting the argument into the dynamic buffer. It is capable of converting the
following types of arguments: following types of arguments:
@li `boost::asio::const_buffer` @li `boost::asio::const_buffer`
@@ -33,29 +33,29 @@ namespace beast {
For all types not listed above, the function will invoke For all types not listed above, the function will invoke
`boost::lexical_cast` on the argument in an attempt to convert to `boost::lexical_cast` on the argument in an attempt to convert to
a string, which is then appended to the stream buffer. a string, which is then appended to the dynamic buffer.
When this function serializes numbers, it converts them to When this function serializes numbers, it converts them to
their text representation as if by a call to `std::to_string`. their text representation as if by a call to `std::to_string`.
@param streambuf The stream buffer to write to. @param dynabuf The dynamic buffer to write to.
@param args A list of one or more arguments to write. @param args A list of one or more arguments to write.
@throws unspecified Any exceptions thrown by `boost::lexical_cast`. @throws unspecified Any exceptions thrown by `boost::lexical_cast`.
@note This function participates in overload resolution only if @note This function participates in overload resolution only if
the `streambuf` parameter meets the requirements of @b `Streambuf`. the `dynabuf` parameter meets the requirements of @b `DynamicBuffer`.
*/ */
template<class Streambuf, class... Args> template<class DynamicBuffer, class... Args>
#if GENERATING_DOCS #if GENERATING_DOCS
void void
#else #else
typename std::enable_if<is_Streambuf<Streambuf>::value>::type typename std::enable_if<is_DynamicBuffer<DynamicBuffer>::value>::type
#endif #endif
write(Streambuf& streambuf, Args const&... args) write(DynamicBuffer& dynabuf, Args const&... args)
{ {
detail::write_streambuf(streambuf, args...); detail::write_dynabuf(dynabuf, args...);
} }
} // beast } // beast

View File

@@ -20,7 +20,7 @@
#include <beast/http/read.hpp> #include <beast/http/read.hpp>
#include <beast/http/reason.hpp> #include <beast/http/reason.hpp>
#include <beast/http/resume_context.hpp> #include <beast/http/resume_context.hpp>
#include <beast/http/rfc2616.hpp> #include <beast/http/rfc7230.hpp>
#include <beast/http/streambuf_body.hpp> #include <beast/http/streambuf_body.hpp>
#include <beast/http/string_body.hpp> #include <beast/http/string_body.hpp>
#include <beast/http/write.hpp> #include <beast/http/write.hpp>

View File

@@ -0,0 +1,95 @@
//
// 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_DYNABUF_BODY_HPP
#define BEAST_HTTP_BASIC_DYNABUF_BODY_HPP
#include <beast/http/body_type.hpp>
#include <boost/asio/buffer.hpp>
namespace beast {
namespace http {
/** A message body represented by a @b `DynamicBuffer`
Meets the requirements of @b `Body`.
*/
template<class DynamicBuffer>
struct basic_dynabuf_body
{
/// The type of the `message::body` member
using value_type = DynamicBuffer;
#if GENERATING_DOCS
private:
#endif
class reader
{
value_type& sb_;
public:
template<bool isRequest, class Headers>
explicit
reader(message<isRequest,
basic_dynabuf_body, Headers>& m) noexcept
: sb_(m.body)
{
}
void
write(void const* data,
std::size_t size, error_code&) noexcept
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
sb_.commit(buffer_copy(
sb_.prepare(size), buffer(data, size)));
}
};
class writer
{
DynamicBuffer const& body_;
public:
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
explicit
writer(message<
isRequest, basic_dynabuf_body, Headers> const& m)
: body_(m.body)
{
}
void
init(error_code& ec)
{
}
std::uint64_t
content_length() const
{
return body_.size();
}
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
write(body_.data());
return true;
}
};
};
} // http
} // beast
#endif

View File

@@ -246,8 +246,7 @@ public:
Field names are stored as-is, but comparison are case-insensitive. Field names are stored as-is, but comparison are case-insensitive.
The container preserves the order of insertion of fields with The container preserves the order of insertion of fields with
different names. For fields with the same name, the implementation different names. For fields with the same name, the implementation
concatenates values inserted with duplicate names as per the concatenates values inserted with duplicate names as per rfc7230.
rules in rfc2616 section 4.2.
@note Meets the requirements of @b `FieldSequence`. @note Meets the requirements of @b `FieldSequence`.
*/ */
@@ -393,18 +392,16 @@ public:
*/ */
// VFALCO TODO Consider allowing rvalue references for std::move? // VFALCO TODO Consider allowing rvalue references for std::move?
void void
insert(boost::string_ref const& name, insert(boost::string_ref const& name, boost::string_ref value);
boost::string_ref const& value);
/** Insert a field value. /** Insert a field value.
If a field value already exists the new value will be If a field value already exists the new value will be
extended as per RFC2616 Section 4.2. extended as per RFC2616 Section 4.2.
*/ */
template<class T, template<class T>
class = typename std::enable_if< typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type> ! std::is_constructible<boost::string_ref, T>::value>::type
void
insert(boost::string_ref name, T const& value) insert(boost::string_ref name, T const& value)
{ {
insert(name, insert(name,
@@ -414,21 +411,19 @@ public:
/** Replace a field value. /** Replace a field value.
The current field value, if any, is removed. Then the The current field value, if any, is removed. Then the
specified value is inserted as if by insert(field, value). specified value is inserted as if by `insert(field, value)`.
*/ */
void void
replace(boost::string_ref const& name, replace(boost::string_ref const& name, boost::string_ref value);
boost::string_ref const& value);
/** Replace a field value. /** Replace a field value.
The current field value, if any, is removed. Then the The current field value, if any, is removed. Then the
specified value is inserted as if by insert(field, value). specified value is inserted as if by `insert(field, value)`.
*/ */
template<class T, template<class T>
class = typename std::enable_if< typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type> ! std::is_constructible<boost::string_ref, T>::value>::type
void
replace(boost::string_ref const& name, T const& value) replace(boost::string_ref const& name, T const& value)
{ {
replace(name, replace(name,

View File

@@ -25,17 +25,65 @@ namespace http {
namespace parse_flag { namespace parse_flag {
enum values enum values
{ {
chunked = 1 << 0, chunked = 1,
connection_keep_alive = 1 << 1, connection_keep_alive = 2,
connection_close = 1 << 2, connection_close = 4,
connection_upgrade = 1 << 3, connection_upgrade = 8,
trailing = 1 << 4, trailing = 16,
upgrade = 1 << 5, upgrade = 32,
skipbody = 1 << 6, skipbody = 64,
contentlength = 1 << 7 contentlength = 128
}; };
} // parse_flag } // 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.
Sets the maximum number of cumulative bytes allowed including
all body octets. Octets in chunk-encoded bodies are counted
after decoding. A value of zero indicates no limit on
the number of body octets.
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.
*/
struct body_max_size
{
std::size_t value;
explicit
body_max_size(std::size_t v)
: value(v)
{
}
};
/// 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();
/** A parser for decoding HTTP/1 wire format messages. /** A parser for decoding HTTP/1 wire format messages.
This parser is designed to efficiently parse messages in the This parser is designed to efficiently parse messages in the
@@ -85,7 +133,7 @@ enum values
Called for each piece of the current header value. Called for each piece of the current header value.
@li `int on_headers(error_code&)` @li `int on_headers(std::uint64_t content_length, error_code&)`
Called when all the headers have been parsed successfully. Called when all the headers have been parsed successfully.
@@ -126,90 +174,12 @@ enum values
presented with request or response message. presented with request or response message.
*/ */
template<bool isRequest, class Derived> template<bool isRequest, class Derived>
class basic_parser_v1 class basic_parser_v1 : public detail::parser_base
{ {
private: private:
using self = basic_parser_v1; using self = basic_parser_v1;
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&); typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
static std::uint64_t constexpr no_content_length =
std::numeric_limits<std::uint64_t>::max();
enum state : std::uint8_t
{
s_closed = 1,
s_req_start,
s_req_method_start,
s_req_method,
s_req_space_before_url,
s_req_url_start,
s_req_url,
s_req_http_start,
s_req_http_H,
s_req_http_HT,
s_req_http_HTT,
s_req_http_HTTP,
s_req_major_start,
s_req_major,
s_req_minor_start,
s_req_minor,
s_req_line_end,
s_res_start,
s_res_H,
s_res_HT,
s_res_HTT,
s_res_HTTP,
s_res_major_start,
s_res_major,
s_res_minor_start,
s_res_minor,
s_res_status_code_start,
s_res_status_code,
s_res_status_start,
s_res_status,
s_res_line_almost_done,
s_res_line_done,
s_header_field_start,
s_header_field,
s_header_value_start,
s_header_value_discard_lWs0,
s_header_value_discard_ws0,
s_header_value_almost_done0,
s_header_value_text_start,
s_header_value_discard_lWs,
s_header_value_discard_ws,
s_header_value_text,
s_header_value_almost_done,
s_headers_almost_done,
s_headers_done,
s_chunk_size_start,
s_chunk_size,
s_chunk_parameters,
s_chunk_size_almost_done,
// states below do not count towards
// the limit on the size of the message
s_body_identity0,
s_body_identity,
s_body_identity_eof0,
s_body_identity_eof,
s_chunk_data_start,
s_chunk_data,
s_chunk_data_almost_done,
s_chunk_data_done,
s_complete,
s_restart,
s_closed_complete
};
enum field_state : std::uint8_t enum field_state : std::uint8_t
{ {
h_general = 0, h_general = 0,
@@ -224,25 +194,36 @@ private:
h_matching_upgrade, h_matching_upgrade,
h_connection, h_connection,
h_content_length0,
h_content_length, h_content_length,
h_content_length_ows,
h_transfer_encoding, h_transfer_encoding,
h_upgrade, h_upgrade,
h_matching_transfer_encoding_chunked, h_matching_transfer_encoding_chunked,
h_matching_connection_token_start, h_matching_transfer_encoding_general,
h_matching_connection_keep_alive, h_matching_connection_keep_alive,
h_matching_connection_close, h_matching_connection_close,
h_matching_connection_upgrade, h_matching_connection_upgrade,
h_matching_connection_token,
h_transfer_encoding_chunked, h_transfer_encoding_chunked,
h_transfer_encoding_chunked_ows,
h_connection_keep_alive, h_connection_keep_alive,
h_connection_keep_alive_ows,
h_connection_close, h_connection_close,
h_connection_close_ows,
h_connection_upgrade, h_connection_upgrade,
h_connection_upgrade_ows,
h_connection_token,
h_connection_token_ows
}; };
std::size_t h_max_;
std::size_t h_left_;
std::size_t b_max_;
std::size_t b_left_;
std::uint64_t content_length_; std::uint64_t content_length_;
std::uint64_t nread_;
pmf_t cb_; pmf_t cb_;
state s_ : 8; state s_ : 8;
unsigned flags_ : 8; unsigned flags_ : 8;
@@ -260,10 +241,42 @@ public:
/// Copy assignment. /// Copy assignment.
basic_parser_v1& operator=(basic_parser_v1 const&) = default; basic_parser_v1& operator=(basic_parser_v1 const&) = default;
/// Constructor /// Default constructor
basic_parser_v1() basic_parser_v1();
/** Set options on the parser.
@param args One or more parser options to set.
*/
#if GENERATING_DOCS
template<class... Args>
void
set_option(Args&&... args)
#else
template<class A1, class A2, class... An>
void
set_option(A1&& a1, A2&& a2, An&&... an)
#endif
{ {
init(std::integral_constant<bool, isRequest>{}); set_option(std::forward<A1>(a1));
set_option(std::forward<A2>(a2),
std::forward<An>(an)...);
}
/// Set the headers maximum size option
void
set_option(headers_max_size const& o)
{
h_max_ = o.value;
h_left_ = h_max_;
}
/// Set the body maximum size option
void
set_option(body_max_size const& o)
{
b_max_ = o.value;
b_left_ = b_max_;
} }
/// Returns internal flags associated with the parser. /// Returns internal flags associated with the parser.
@@ -373,14 +386,14 @@ public:
@return The number of bytes consumed in the input sequence. @return The number of bytes consumed in the input sequence.
*/ */
template<class ConstBufferSequence> template<class ConstBufferSequence>
#if GENERATING_DOCS #if GENERATING_DOCS
std::size_t std::size_t
#else #else
typename std::enable_if< typename std::enable_if<
! std::is_convertible<ConstBufferSequence, ! std::is_convertible<ConstBufferSequence,
boost::asio::const_buffer>::value, boost::asio::const_buffer>::value,
std::size_t>::type std::size_t>::type
#endif #endif
write(ConstBufferSequence const& buffers, error_code& ec); write(ConstBufferSequence const& buffers, error_code& ec);
/** Write a single buffer of data to the parser. /** Write a single buffer of data to the parser.
@@ -413,17 +426,48 @@ private:
} }
void void
init(std::true_type) reset(std::true_type)
{ {
s_ = s_req_start; s_ = s_req_start;
} }
void void
init(std::false_type) reset(std::false_type)
{ {
s_ = s_res_start; s_ = s_res_start;
} }
void
reset()
{
h_left_ = h_max_;
b_left_ = b_max_;
reset(std::integral_constant<bool, isRequest>{});
}
void
init(std::true_type)
{
// 16KB max headers, 4MB max body
h_max_ = 16 * 1024;
b_max_ = 4 * 1024 * 1024;
}
void
init(std::false_type)
{
// 16KB max headers, unlimited body
h_max_ = 16 * 1024;
b_max_ = 0;
}
void
init()
{
init(std::integral_constant<bool, isRequest>{});
reset();
}
bool bool
needs_eof(std::true_type) const; needs_eof(std::true_type) const;
@@ -584,7 +628,7 @@ private:
{ {
template<class T, class R = std::is_same<int, template<class T, class R = std::is_same<int,
decltype(std::declval<T>().on_headers( decltype(std::declval<T>().on_headers(
std::declval<error_code&>()))>> std::declval<std::uint64_t>(), std::declval<error_code&>()))>>
static R check(int); static R check(int);
template <class> template <class>
static std::false_type check(...); static std::false_type check(...);
@@ -661,8 +705,16 @@ private:
void call_on_method(error_code& ec, void call_on_method(error_code& ec,
boost::string_ref const& s) boost::string_ref const& s)
{ {
call_on_method(ec, s, std::integral_constant<bool, if(! h_max_ || s.size() <= h_left_)
isRequest && has_on_method<Derived>::value>{}); {
h_left_ -= s.size();
call_on_method(ec, s, std::integral_constant<bool,
isRequest && has_on_method<Derived>::value>{});
}
else
{
ec = parse_error::headers_too_big;
}
} }
void call_on_uri(error_code& ec, void call_on_uri(error_code& ec,
@@ -678,8 +730,16 @@ 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)
{ {
call_on_uri(ec, s, std::integral_constant<bool, if(! h_max_ || s.size() <= h_left_)
isRequest && has_on_uri<Derived>::value>{}); {
h_left_ -= s.size();
call_on_uri(ec, s, std::integral_constant<bool,
isRequest && has_on_uri<Derived>::value>{});
}
else
{
ec = parse_error::headers_too_big;
}
} }
void call_on_reason(error_code& ec, void call_on_reason(error_code& ec,
@@ -695,8 +755,16 @@ private:
void call_on_reason(error_code& ec, boost::string_ref const& s) void call_on_reason(error_code& ec, boost::string_ref const& s)
{ {
call_on_reason(ec, s, std::integral_constant<bool, if(! h_max_ || s.size() <= h_left_)
! isRequest && has_on_reason<Derived>::value>{}); {
h_left_ -= s.size();
call_on_reason(ec, s, std::integral_constant<bool,
! isRequest && has_on_reason<Derived>::value>{});
}
else
{
ec = parse_error::headers_too_big;
}
} }
void call_on_request(error_code& ec, std::true_type) void call_on_request(error_code& ec, std::true_type)
@@ -742,7 +810,15 @@ private:
void call_on_field(error_code& ec, boost::string_ref const& s) void call_on_field(error_code& ec, boost::string_ref const& s)
{ {
call_on_field(ec, s, has_on_field<Derived>{}); if(! h_max_ || s.size() <= h_left_)
{
h_left_ -= s.size();
call_on_field(ec, s, has_on_field<Derived>{});
}
else
{
ec = parse_error::headers_too_big;
}
} }
void call_on_value(error_code& ec, void call_on_value(error_code& ec,
@@ -758,22 +834,32 @@ private:
void call_on_value(error_code& ec, boost::string_ref const& s) void call_on_value(error_code& ec, boost::string_ref const& s)
{ {
call_on_value(ec, s, has_on_value<Derived>{}); 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::true_type) int call_on_headers(error_code& ec,
std::uint64_t content_length, std::true_type)
{ {
return impl().on_headers(ec); return impl().on_headers(content_length, ec);
} }
int call_on_headers(error_code& ec, std::false_type) int call_on_headers(error_code& ec, std::uint64_t, std::false_type)
{ {
return 0; return 0;
} }
int call_on_headers(error_code& ec) int call_on_headers(error_code& ec)
{ {
return call_on_headers(ec, has_on_headers<Derived>{}); return call_on_headers(ec, content_length_,
has_on_headers<Derived>{});
} }
void call_on_body(error_code& ec, void call_on_body(error_code& ec,
@@ -789,7 +875,15 @@ private:
void call_on_body(error_code& ec, boost::string_ref const& s) void call_on_body(error_code& ec, boost::string_ref const& s)
{ {
call_on_body(ec, s, has_on_body<Derived>{}); if(! b_max_ || s.size() <= b_left_)
{
b_left_ -= s.size();
call_on_body(ec, s, has_on_body<Derived>{});
}
else
{
ec = parse_error::body_too_big;
}
} }
void call_on_complete(error_code& ec, std::true_type) void call_on_complete(error_code& ec, std::true_type)

View File

@@ -17,137 +17,6 @@ namespace beast {
namespace http { namespace http {
namespace detail { namespace detail {
// '0'...'9'
inline
bool
is_digit(char c)
{
return c >= '0' && c <= '9';
}
inline
bool
is_token(char c)
{
/* token = 1*<any CHAR except CTLs or separators>
CHAR = <any US-ASCII character (octets 0 - 127)>
sep = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
*/
static std::array<char, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
}};
return tab[static_cast<std::uint8_t>(c)] != 0;
}
inline
bool
is_text(char c)
{
// TEXT = <any OCTET except CTLs, but including LWS>
static std::array<char, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
}};
return tab[static_cast<std::uint8_t>(c)] != 0;
}
// converts to lower case,
// returns 0 if not a valid token char
//
inline
char
to_field_char(char c)
{
/* token = 1*<any CHAR except CTLs or separators>
CHAR = <any US-ASCII character (octets 0 - 127)>
sep = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
*/
static std::array<char, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, '!', 0, '#', '$', '%', '&', '\'', 0, 0, '*', '+', 0, '-', '.', 0,
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0
}};
return tab[static_cast<std::uint8_t>(c)];
}
// converts to lower case,
// returns 0 if not a valid text char
//
inline
char
to_value_char(char c)
{
// TEXT = <any OCTET except CTLs, but including LWS>
static std::array<std::uint8_t, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
}};
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
}
inline
std::int8_t
unhex(char c)
{
static std::array<std::int8_t, 256> constexpr tab = {{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 112
}};
return tab[static_cast<std::uint8_t>(c)];
}
template<class = void> template<class = void>
struct parser_str_t struct parser_str_t
{ {
@@ -196,6 +65,82 @@ parser_str_t<_>::transfer_encoding[18];
using parser_str = parser_str_t<>; using parser_str = parser_str_t<>;
class parser_base
{
protected:
enum state : std::uint8_t
{
s_dead = 1,
s_req_start,
s_req_method0,
s_req_method,
s_req_url0,
s_req_url,
s_req_http,
s_req_http_H,
s_req_http_HT,
s_req_http_HTT,
s_req_http_HTTP,
s_req_major,
s_req_dot,
s_req_minor,
s_req_cr,
s_req_lf,
s_res_start,
s_res_H,
s_res_HT,
s_res_HTT,
s_res_HTTP,
s_res_major,
s_res_dot,
s_res_minor,
s_res_space_1,
s_res_status0,
s_res_status1,
s_res_status2,
s_res_space_2,
s_res_reason0,
s_res_reason,
s_res_line_lf,
s_res_line_done,
s_header_name0,
s_header_name,
s_header_value0_lf,
s_header_value0_almost_done,
s_header_value0,
s_header_value,
s_header_value_lf,
s_header_value_almost_done,
s_header_value_unfold,
s_headers_almost_done,
s_headers_done,
s_chunk_size0,
s_chunk_size,
s_chunk_ext_name0,
s_chunk_ext_name,
s_chunk_ext_val,
s_chunk_size_lf,
s_chunk_data0,
s_chunk_data,
s_chunk_data_cr,
s_chunk_data_lf,
s_body_identity0,
s_body_identity,
s_body_identity_eof0,
s_body_identity_eof,
s_complete,
s_restart,
s_closed_complete
};
};
} // detail } // detail
} // http } // http
} // beast } // beast

View File

@@ -0,0 +1,384 @@
//
// 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_RFC7230_HPP
#define BEAST_HTTP_DETAIL_RFC7230_HPP
#include <boost/utility/string_ref.hpp>
#include <array>
#include <iterator>
#include <utility>
namespace beast {
namespace http {
namespace detail {
inline
bool
is_digit(char c)
{
return c >= '0' && c <= '9';
}
inline
bool
is_alpha(char c)
{
static std::array<char, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 80
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 112
}};
return tab[static_cast<std::uint8_t>(c)] != 0;
}
inline
bool
is_text(char c)
{
// TEXT = <any OCTET except CTLs, but including LWS>
static std::array<char, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
}};
return tab[static_cast<std::uint8_t>(c)] != 0;
}
inline
bool
is_tchar(char c)
{
/*
tchar = "!" | "#" | "$" | "%" | "&" |
"'" | "*" | "+" | "-" | "." |
"^" | "_" | "`" | "|" | "~" |
DIGIT | ALPHA
*/
static std::array<char, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
}};
return tab[static_cast<std::uint8_t>(c)] != 0;
}
inline
bool
is_qdchar(char c)
{
/*
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
*/
static std::array<bool, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
}};
return tab[static_cast<std::uint8_t>(c)];
}
inline
bool
is_qpchar(char c)
{
/*
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
obs-text = %x80-FF
*/
static std::array<bool, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
}};
return tab[static_cast<std::uint8_t>(c)];
}
// converts to lower case,
// returns 0 if not a valid token char
//
inline
char
to_field_char(char c)
{
/* token = 1*<any CHAR except CTLs or separators>
CHAR = <any US-ASCII character (octets 0 - 127)>
sep = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
*/
static std::array<char, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, '!', 0, '#', '$', '%', '&', '\'', 0, 0, '*', '+', 0, '-', '.', 0,
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0
}};
return tab[static_cast<std::uint8_t>(c)];
}
// converts to lower case,
// returns 0 if not a valid text char
//
inline
char
to_value_char(char c)
{
// TEXT = <any OCTET except CTLs, but including LWS>
static std::array<std::uint8_t, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
}};
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
}
inline
std::int8_t
unhex(char c)
{
static std::array<std::int8_t, 256> constexpr tab = {{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 112
}};
return tab[static_cast<std::uint8_t>(c)];
}
template<class FwdIt>
void
skip_ows(FwdIt& it, FwdIt const& end)
{
while(it != end)
{
auto const c = *it;
if(c != ' ' && c != '\t')
break;
++it;
}
}
inline
boost::string_ref
trim(boost::string_ref const& s)
{
auto first = s.begin();
auto last = s.end();
skip_ows(first, last);
while(first != last)
{
auto const c = *std::prev(last);
if(c != ' ' && c != '\t')
break;
--last;
}
if(first == last)
return {};
return {&*first,
static_cast<std::size_t>(last - first)};
}
struct param_iter
{
using iter_type = boost::string_ref::const_iterator;
iter_type it;
iter_type begin;
iter_type end;
std::pair<boost::string_ref, boost::string_ref> v;
bool
empty() const
{
return begin == it;
}
template<class = void>
void
increment();
};
template<class>
void
param_iter::
increment()
{
/*
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
ext = token param-list
param-list = *( OWS ";" OWS param )
param = token OWS "=" OWS ( token / quoted-string )
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
obs-text = %x80-FF
Example:
chunked;a=b;i=j,gzip;windowBits=12
x,y
*/
auto const err =
[&]
{
it = begin;
};
v.first.clear();
v.second.clear();
detail::skip_ows(it, end);
begin = it;
if(it == end)
return err();
if(*it != ';')
return err();
++it;
detail::skip_ows(it, end);
if(it == end)
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;
}
auto const p1 = it;
detail::skip_ows(it, end);
if(it == end)
return err();
if(*it != '=')
return err();
++it;
detail::skip_ows(it, end);
if(it == end)
return err();
if(*it == '"')
{
// quoted-string
auto const p2 = it;
++it;
for(;;)
{
if(it == end)
return err();
auto c = *it++;
if(c == '"')
break;
if(detail::is_qdchar(c))
continue;
if(c != '\\')
return err();
if(it == end)
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
{
// token
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) };
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
}
}
} // detail
} // http
} // beast
#endif

View File

@@ -9,7 +9,6 @@
#define BEAST_HTTP_EMPTY_BODY_HPP #define BEAST_HTTP_EMPTY_BODY_HPP
#include <beast/http/body_type.hpp> #include <beast/http/body_type.hpp>
#include <beast/core/streambuf.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <memory> #include <memory>
#include <string> #include <string>

View File

@@ -8,6 +8,8 @@
#ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP #ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP #define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#include <beast/http/detail/rfc7230.hpp>
namespace beast { namespace beast {
namespace http { namespace http {
@@ -257,12 +259,13 @@ template<class Allocator>
void void
basic_headers<Allocator>:: basic_headers<Allocator>::
insert(boost::string_ref const& name, insert(boost::string_ref const& name,
boost::string_ref const& value) boost::string_ref value)
{ {
value = detail::trim(value);
typename set_t::insert_commit_data d; typename set_t::insert_commit_data d;
auto const result = auto const result =
set_.insert_check(name, less{}, d); set_.insert_check(name, less{}, d);
if (result.second) if(result.second)
{ {
auto const p = alloc_traits::allocate( auto const p = alloc_traits::allocate(
this->member(), 1); this->member(), 1);
@@ -284,8 +287,9 @@ template<class Allocator>
void void
basic_headers<Allocator>:: basic_headers<Allocator>::
replace(boost::string_ref const& name, replace(boost::string_ref const& name,
boost::string_ref const& value) boost::string_ref value)
{ {
value = detail::trim(value);
erase(name); erase(name);
insert(name, value); insert(name, value);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
#ifndef BEAST_HTTP_IMPL_MESSAGE_V1_IPP #ifndef BEAST_HTTP_IMPL_MESSAGE_V1_IPP
#define BEAST_HTTP_IMPL_MESSAGE_V1_IPP #define BEAST_HTTP_IMPL_MESSAGE_V1_IPP
#include <beast/http/rfc2616.hpp> #include <beast/http/rfc7230.hpp>
#include <beast/http/detail/has_content_length.hpp> #include <beast/http/detail/has_content_length.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <stdexcept> #include <stdexcept>
@@ -22,13 +22,11 @@ is_keep_alive(message_v1<isRequest, Body, Headers> const& msg)
{ {
if(msg.version >= 11) if(msg.version >= 11)
{ {
if(rfc2616::token_in_list( if(token_list{msg.headers["Connection"]}.exists("close"))
msg.headers["Connection"], "close"))
return false; return false;
return true; return true;
} }
if(rfc2616::token_in_list( if(token_list{msg.headers["Connection"]}.exists("keep-alive"))
msg.headers["Connection"], "keep-alive"))
return true; return true;
return false; return false;
} }
@@ -39,8 +37,7 @@ is_upgrade(message_v1<isRequest, Body, Headers> const& msg)
{ {
if(msg.version < 11) if(msg.version < 11)
return false; return false;
if(rfc2616::token_in_list( if(token_list{msg.headers["Connection"]}.exists("upgrade"))
msg.headers["Connection"], "upgrade"))
return true; return true;
return false; return false;
} }
@@ -129,8 +126,7 @@ prepare(message_v1<isRequest, Body, Headers>& msg,
throw std::invalid_argument( throw std::invalid_argument(
"prepare called with Content-Length field set"); "prepare called with Content-Length field set");
if(rfc2616::token_in_list( if(token_list{msg.headers["Transfer-Encoding"]}.exists("chunked"))
msg.headers["Transfer-Encoding"], "chunked"))
throw std::invalid_argument( throw std::invalid_argument(
"prepare called with Transfer-Encoding: chunked set"); "prepare called with Transfer-Encoding: chunked set");
@@ -175,8 +171,8 @@ prepare(message_v1<isRequest, Body, Headers>& msg,
} }
// rfc7230 6.7. // rfc7230 6.7.
if(msg.version < 11 && rfc2616::token_in_list( if(msg.version < 11 && token_list{
msg.headers["Connection"], "upgrade")) msg.headers["Connection"]}.exists("upgrade"))
throw std::invalid_argument( throw std::invalid_argument(
"invalid version for Connection: upgrade"); "invalid version for Connection: upgrade");
} }

View File

@@ -21,7 +21,7 @@ namespace http {
namespace detail { namespace detail {
template<class Stream, template<class Stream,
class Streambuf, class Parser, class Handler> class DynamicBuffer, class Parser, class Handler>
class parse_op class parse_op
{ {
using alloc_type = using alloc_type =
@@ -30,7 +30,7 @@ class parse_op
struct data struct data
{ {
Stream& s; Stream& s;
Streambuf& sb; DynamicBuffer& db;
Parser& p; Parser& p;
Handler h; Handler h;
bool started = false; bool started = false;
@@ -39,9 +39,9 @@ class parse_op
template<class DeducedHandler> template<class DeducedHandler>
data(DeducedHandler&& h_, Stream& s_, data(DeducedHandler&& h_, Stream& s_,
Streambuf& sb_, Parser& p_) DynamicBuffer& sb_, Parser& p_)
: s(s_) : s(s_)
, sb(sb_) , db(sb_)
, p(p_) , p(p_)
, h(std::forward<DeducedHandler>(h_)) , h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers:: , cont(boost_asio_handler_cont_helpers::
@@ -101,9 +101,9 @@ public:
}; };
template<class Stream, template<class Stream,
class Streambuf, class Parser, class Handler> class DynamicBuffer, class Parser, class Handler>
void void
parse_op<Stream, Streambuf, Parser, Handler>:: parse_op<Stream, DynamicBuffer, Parser, Handler>::
operator()(error_code ec, std::size_t bytes_transferred, bool again) operator()(error_code ec, std::size_t bytes_transferred, bool again)
{ {
auto& d = *d_; auto& d = *d_;
@@ -115,7 +115,7 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
case 0: case 0:
{ {
auto const used = auto const used =
d.p.write(d.sb.data(), ec); d.p.write(d.db.data(), ec);
if(ec) if(ec)
{ {
// call handler // call handler
@@ -126,7 +126,7 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
} }
if(used > 0) if(used > 0)
d.started = true; d.started = true;
d.sb.consume(used); d.db.consume(used);
if(d.p.complete()) if(d.p.complete())
{ {
// call handler // call handler
@@ -142,8 +142,8 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
case 1: case 1:
// read // read
d.state = 2; d.state = 2;
d.s.async_read_some(d.sb.prepare( d.s.async_read_some(d.db.prepare(
read_size_helper(d.sb, 65536)), read_size_helper(d.db, 65536)),
std::move(*this)); std::move(*this));
return; return;
@@ -172,8 +172,8 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
d.state = 99; d.state = 99;
break; break;
} }
d.sb.commit(bytes_transferred); d.db.commit(bytes_transferred);
auto const used = d.p.write(d.sb.data(), ec); auto const used = d.p.write(d.db.data(), ec);
if(ec) if(ec)
{ {
// call handler // call handler
@@ -182,7 +182,7 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
} }
if(used > 0) if(used > 0)
d.started = true; d.started = true;
d.sb.consume(used); d.db.consume(used);
if(d.p.complete()) if(d.p.complete())
{ {
// call handler // call handler
@@ -199,7 +199,7 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class Stream, class Streambuf, template<class Stream, class DynamicBuffer,
bool isRequest, class Body, class Headers, bool isRequest, class Body, class Headers,
class Handler> class Handler>
class read_op class read_op
@@ -216,7 +216,7 @@ class read_op
struct data struct data
{ {
Stream& s; Stream& s;
Streambuf& sb; DynamicBuffer& db;
message_type& m; message_type& m;
parser_type p; parser_type p;
Handler h; Handler h;
@@ -226,9 +226,9 @@ class read_op
template<class DeducedHandler> template<class DeducedHandler>
data(DeducedHandler&& h_, Stream& s_, data(DeducedHandler&& h_, Stream& s_,
Streambuf& sb_, message_type& m_) DynamicBuffer& sb_, message_type& m_)
: s(s_) : s(s_)
, sb(sb_) , db(sb_)
, m(m_) , m(m_)
, h(std::forward<DeducedHandler>(h_)) , h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers:: , cont(boost_asio_handler_cont_helpers::
@@ -286,11 +286,11 @@ public:
} }
}; };
template<class Stream, class Streambuf, template<class Stream, class DynamicBuffer,
bool isRequest, class Body, class Headers, bool isRequest, class Body, class Headers,
class Handler> class Handler>
void void
read_op<Stream, Streambuf, isRequest, Body, Headers, Handler>:: read_op<Stream, DynamicBuffer, isRequest, Body, Headers, Handler>::
operator()(error_code ec, bool again) operator()(error_code ec, bool again)
{ {
auto& d = *d_; auto& d = *d_;
@@ -301,7 +301,7 @@ operator()(error_code ec, bool again)
{ {
case 0: case 0:
d.state = 1; d.state = 1;
async_parse(d.s, d.sb, d.p, std::move(*this)); async_parse(d.s, d.db, d.p, std::move(*this));
return; return;
case 1: case 1:
@@ -318,49 +318,49 @@ operator()(error_code ec, bool again)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class SyncReadStream, class Streambuf, class Parser> template<class SyncReadStream, class DynamicBuffer, class Parser>
void void
parse(SyncReadStream& stream, parse(SyncReadStream& stream,
Streambuf& streambuf, Parser& parser) DynamicBuffer& dynabuf, Parser& parser)
{ {
static_assert(is_SyncReadStream<SyncReadStream>::value, static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met"); "SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"Streambuf requirements not met"); "DynamicBuffer requirements not met");
static_assert(is_Parser<Parser>::value, static_assert(is_Parser<Parser>::value,
"Parser requirements not met"); "Parser requirements not met");
error_code ec; error_code ec;
parse(stream, streambuf, parser, ec); parse(stream, dynabuf, parser, ec);
if(ec) if(ec)
throw boost::system::system_error{ec}; throw boost::system::system_error{ec};
} }
template<class SyncReadStream, class Streambuf, class Parser> template<class SyncReadStream, class DynamicBuffer, class Parser>
void void
parse(SyncReadStream& stream, Streambuf& streambuf, parse(SyncReadStream& stream, DynamicBuffer& dynabuf,
Parser& parser, error_code& ec) Parser& parser, error_code& ec)
{ {
static_assert(is_SyncReadStream<SyncReadStream>::value, static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met"); "SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"Streambuf requirements not met"); "DynamicBuffer requirements not met");
static_assert(is_Parser<Parser>::value, static_assert(is_Parser<Parser>::value,
"Parser requirements not met"); "Parser requirements not met");
bool started = false; bool started = false;
for(;;) for(;;)
{ {
auto used = auto used =
parser.write(streambuf.data(), ec); parser.write(dynabuf.data(), ec);
if(ec) if(ec)
return; return;
streambuf.consume(used); dynabuf.consume(used);
if(used > 0) if(used > 0)
started = true; started = true;
if(parser.complete()) if(parser.complete())
break; break;
streambuf.commit(stream.read_some( dynabuf.commit(stream.read_some(
streambuf.prepare(read_size_helper( dynabuf.prepare(read_size_helper(
streambuf, 65536)), ec)); dynabuf, 65536)), ec));
if(ec && ec != boost::asio::error::eof) if(ec && ec != boost::asio::error::eof)
return; return;
if(ec == boost::asio::error::eof) if(ec == boost::asio::error::eof)
@@ -379,86 +379,86 @@ parse(SyncReadStream& stream, Streambuf& streambuf,
} }
template<class AsyncReadStream, template<class AsyncReadStream,
class Streambuf, class Parser, class ReadHandler> class DynamicBuffer, class Parser, class ReadHandler>
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
async_parse(AsyncReadStream& stream, async_parse(AsyncReadStream& stream,
Streambuf& streambuf, Parser& parser, ReadHandler&& handler) DynamicBuffer& dynabuf, Parser& parser, ReadHandler&& handler)
{ {
static_assert(is_AsyncReadStream<AsyncReadStream>::value, static_assert(is_AsyncReadStream<AsyncReadStream>::value,
"AsyncReadStream requirements not met"); "AsyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"Streambuf requirements not met"); "DynamicBuffer requirements not met");
static_assert(is_Parser<Parser>::value, static_assert(is_Parser<Parser>::value,
"Parser requirements not met"); "Parser requirements not met");
beast::async_completion<ReadHandler, beast::async_completion<ReadHandler,
void(error_code)> completion(handler); void(error_code)> completion(handler);
detail::parse_op<AsyncReadStream, Streambuf, detail::parse_op<AsyncReadStream, DynamicBuffer,
Parser, decltype(completion.handler)>{ Parser, decltype(completion.handler)>{
completion.handler, stream, streambuf, parser}; completion.handler, stream, dynabuf, parser};
return completion.result.get(); return completion.result.get();
} }
template<class SyncReadStream, class Streambuf, template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void
read(SyncReadStream& stream, Streambuf& streambuf, read(SyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& msg) message_v1<isRequest, Body, Headers>& msg)
{ {
static_assert(is_SyncReadStream<SyncReadStream>::value, static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met"); "SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"Streambuf requirements not met"); "DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value, static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met"); "ReadableBody requirements not met");
error_code ec; error_code ec;
read(stream, streambuf, msg, ec); read(stream, dynabuf, msg, ec);
if(ec) if(ec)
throw system_error{ec}; throw system_error{ec};
} }
template<class SyncReadStream, class Streambuf, template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void
read(SyncReadStream& stream, Streambuf& streambuf, read(SyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& m, message_v1<isRequest, Body, Headers>& m,
error_code& ec) error_code& ec)
{ {
static_assert(is_SyncReadStream<SyncReadStream>::value, static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met"); "SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"Streambuf requirements not met"); "DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value, static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met"); "ReadableBody requirements not met");
parser_v1<isRequest, Body, Headers> p; parser_v1<isRequest, Body, Headers> p;
parse(stream, streambuf, p, ec); parse(stream, dynabuf, p, ec);
if(ec) if(ec)
return; return;
assert(p.complete()); assert(p.complete());
m = p.release(); m = p.release();
} }
template<class AsyncReadStream, class Streambuf, template<class AsyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Headers, bool isRequest, class Body, class Headers,
class ReadHandler> class ReadHandler>
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
async_read(AsyncReadStream& stream, Streambuf& streambuf, async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& m, message_v1<isRequest, Body, Headers>& m,
ReadHandler&& handler) ReadHandler&& handler)
{ {
static_assert(is_AsyncReadStream<AsyncReadStream>::value, static_assert(is_AsyncReadStream<AsyncReadStream>::value,
"AsyncReadStream requirements not met"); "AsyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"Streambuf requirements not met"); "DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value, static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met"); "ReadableBody requirements not met");
beast::async_completion<ReadHandler, beast::async_completion<ReadHandler,
void(error_code)> completion(handler); void(error_code)> completion(handler);
detail::read_op<AsyncReadStream, Streambuf, detail::read_op<AsyncReadStream, DynamicBuffer,
isRequest, Body, Headers, decltype( isRequest, Body, Headers, decltype(
completion.handler)>{completion.handler, completion.handler)>{completion.handler,
stream, streambuf, m}; stream, dynabuf, m};
return completion.result.get(); return completion.result.get();
} }

View File

@@ -0,0 +1,548 @@
//
// 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_RFC7230_IPP
#define BEAST_HTTP_IMPL_RFC7230_IPP
#include <beast/core/detail/ci_char_traits.hpp>
#include <beast/http/detail/rfc7230.hpp>
#include <iterator>
namespace beast {
namespace http {
class param_list::const_iterator
{
using iter_type = boost::string_ref::const_iterator;
std::string s_;
detail::param_iter pi_;
public:
using value_type = param_list::value_type;
using pointer = value_type const*;
using reference = value_type const&;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
const_iterator() = default;
bool
operator==(const_iterator const& other) const
{
return
other.pi_.it == pi_.it &&
other.pi_.end == pi_.end &&
other.pi_.begin == pi_.begin;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return pi_.v;
}
pointer
operator->() const
{
return &*(*this);
}
const_iterator&
operator++()
{
increment();
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
private:
friend class param_list;
const_iterator(iter_type begin, iter_type end)
{
pi_.it = begin;
pi_.begin = begin;
pi_.end = end;
increment();
}
template<class = void>
static
std::string
unquote(boost::string_ref const& sr);
template<class = void>
void
increment();
};
inline
auto
param_list::
begin() const ->
const_iterator
{
return const_iterator{s_.begin(), s_.end()};
}
inline
auto
param_list::
end() const ->
const_iterator
{
return const_iterator{s_.end(), s_.end()};
}
inline
auto
param_list::
cbegin() const ->
const_iterator
{
return const_iterator{s_.begin(), s_.end()};
}
inline
auto
param_list::
cend() const ->
const_iterator
{
return const_iterator{s_.end(), s_.end()};
}
template<class>
std::string
param_list::const_iterator::
unquote(boost::string_ref const& sr)
{
std::string s;
s.reserve(sr.size());
auto it = sr.begin() + 1;
auto end = sr.end() - 1;
while(it != end)
{
if(*it == '\\')
++it;
s.push_back(*it);
++it;
}
return s;
}
template<class>
void
param_list::const_iterator::
increment()
{
s_.clear();
pi_.increment();
if(pi_.empty())
{
pi_.it = pi_.end;
pi_.begin = pi_.end;
}
else if(pi_.v.second.front() == '"')
{
s_ = unquote(pi_.v.second);
pi_.v.second = boost::string_ref{
s_.data(), s_.size()};
}
}
//------------------------------------------------------------------------------
class ext_list::const_iterator
{
ext_list::value_type v_;
iter_type it_;
iter_type begin_;
iter_type end_;
public:
using value_type = ext_list::value_type;
using pointer = value_type const*;
using reference = value_type const&;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
const_iterator() = default;
bool
operator==(const_iterator const& other) const
{
return
other.it_ == it_ &&
other.begin_ == begin_ &&
other.end_ == end_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return v_;
}
pointer
operator->() const
{
return &*(*this);
}
const_iterator&
operator++()
{
increment();
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
private:
friend class ext_list;
const_iterator(iter_type begin, iter_type end)
{
it_ = begin;
begin_ = begin;
end_ = end;
increment();
}
template<class = void>
void
increment();
};
inline
auto
ext_list::
begin() const ->
const_iterator
{
return const_iterator{s_.begin(), s_.end()};
}
inline
auto
ext_list::
end() const ->
const_iterator
{
return const_iterator{s_.end(), s_.end()};
}
inline
auto
ext_list::
cbegin() const ->
const_iterator
{
return const_iterator{s_.begin(), s_.end()};
}
inline
auto
ext_list::
cend() const ->
const_iterator
{
return const_iterator{s_.end(), s_.end()};
}
template<class T>
auto
ext_list::
find(T const& s) ->
const_iterator
{
return std::find_if(begin(), end(),
[&s](value_type const& v)
{
return beast::detail::ci_equal(s, v.first);
});
}
template<class T>
bool
ext_list::
exists(T const& s)
{
return find(s) != end();
}
template<class>
void
ext_list::const_iterator::
increment()
{
/*
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
ext = token param-list
param-list = *( OWS ";" OWS param )
param = token OWS "=" OWS ( token / quoted-string )
chunked;a=b;i=j,gzip;windowBits=12
x,y
,,,,,chameleon
*/
auto const err =
[&]
{
it_ = end_;
begin_ = end_;
};
auto need_comma = it_ != begin_;
v_.first = {};
begin_ = it_;
for(;;)
{
detail::skip_ows(it_, end_);
if(it_ == end_)
return err();
auto const c = *it_;
if(detail::is_tchar(c))
{
if(need_comma)
return err();
auto const p0 = it_;
for(;;)
{
++it_;
if(it_ == end_)
break;
if(! detail::is_tchar(*it_))
break;
}
v_.first = boost::string_ref{&*p0,
static_cast<std::size_t>(it_ - p0)};
detail::param_iter pi;
pi.it = it_;
pi.begin = it_;
pi.end = end_;
for(;;)
{
pi.increment();
if(pi.empty())
break;
}
v_.second = param_list{boost::string_ref{&*it_,
static_cast<std::size_t>(pi.it - it_)}};
it_ = pi.it;
return;
}
if(c != ',')
return err();
need_comma = false;
++it_;
}
}
//------------------------------------------------------------------------------
class token_list::const_iterator
{
token_list::value_type v_;
iter_type it_;
iter_type begin_;
iter_type end_;
public:
using value_type = token_list::value_type;
using pointer = value_type const*;
using reference = value_type const&;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
const_iterator() = default;
bool
operator==(const_iterator const& other) const
{
return
other.it_ == it_ &&
other.begin_ == begin_ &&
other.end_ == end_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return v_;
}
pointer
operator->() const
{
return &*(*this);
}
const_iterator&
operator++()
{
increment();
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
private:
friend class token_list;
const_iterator(iter_type begin, iter_type end)
{
it_ = begin;
begin_ = begin;
end_ = end;
increment();
}
template<class = void>
void
increment();
};
inline
auto
token_list::
begin() const ->
const_iterator
{
return const_iterator{s_.begin(), s_.end()};
}
inline
auto
token_list::
end() const ->
const_iterator
{
return const_iterator{s_.end(), s_.end()};
}
inline
auto
token_list::
cbegin() const ->
const_iterator
{
return const_iterator{s_.begin(), s_.end()};
}
inline
auto
token_list::
cend() const ->
const_iterator
{
return const_iterator{s_.end(), s_.end()};
}
template<class>
void
token_list::const_iterator::
increment()
{
/*
token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] )
*/
auto const err =
[&]
{
it_ = end_;
begin_ = end_;
};
auto need_comma = it_ != begin_;
v_ = {};
begin_ = it_;
for(;;)
{
detail::skip_ows(it_, end_);
if(it_ == end_)
return err();
auto const c = *it_;
if(detail::is_tchar(c))
{
if(need_comma)
return err();
auto const p0 = it_;
for(;;)
{
++it_;
if(it_ == end_)
break;
if(! detail::is_tchar(*it_))
break;
}
v_ = boost::string_ref{&*p0,
static_cast<std::size_t>(it_ - p0)};
return;
}
if(c != ',')
return err();
need_comma = false;
++it_;
}
}
template<class T>
bool
token_list::
exists(T const& s)
{
return std::find_if(begin(), end(),
[&s](value_type const& v)
{
return beast::detail::ci_equal(s, v);
}
) != end();
}
} // http
} // beast
#endif

View File

@@ -18,7 +18,7 @@
#include <beast/core/handler_alloc.hpp> #include <beast/core/handler_alloc.hpp>
#include <beast/core/stream_concepts.hpp> #include <beast/core/stream_concepts.hpp>
#include <beast/core/streambuf.hpp> #include <beast/core/streambuf.hpp>
#include <beast/core/write_streambuf.hpp> #include <beast/core/write_dynabuf.hpp>
#include <boost/asio/write.hpp> #include <boost/asio/write.hpp>
#include <boost/logic/tribool.hpp> #include <boost/logic/tribool.hpp>
#include <condition_variable> #include <condition_variable>
@@ -32,51 +32,51 @@ namespace http {
namespace detail { namespace detail {
template<class Streambuf, class Body, class Headers> template<class DynamicBuffer, class Body, class Headers>
void void
write_firstline(Streambuf& streambuf, write_firstline(DynamicBuffer& dynabuf,
message_v1<true, Body, Headers> const& msg) message_v1<true, Body, Headers> const& msg)
{ {
write(streambuf, msg.method); write(dynabuf, msg.method);
write(streambuf, " "); write(dynabuf, " ");
write(streambuf, msg.url); write(dynabuf, msg.url);
write(streambuf, " HTTP/"); write(dynabuf, " HTTP/");
write(streambuf, msg.version / 10); write(dynabuf, msg.version / 10);
write(streambuf, "."); write(dynabuf, ".");
write(streambuf, msg.version % 10); write(dynabuf, msg.version % 10);
write(streambuf, "\r\n"); write(dynabuf, "\r\n");
} }
template<class Streambuf, class Body, class Headers> template<class DynamicBuffer, class Body, class Headers>
void void
write_firstline(Streambuf& streambuf, write_firstline(DynamicBuffer& dynabuf,
message_v1<false, Body, Headers> const& msg) message_v1<false, Body, Headers> const& msg)
{ {
write(streambuf, "HTTP/"); write(dynabuf, "HTTP/");
write(streambuf, msg.version / 10); write(dynabuf, msg.version / 10);
write(streambuf, "."); write(dynabuf, ".");
write(streambuf, msg.version % 10); write(dynabuf, msg.version % 10);
write(streambuf, " "); write(dynabuf, " ");
write(streambuf, msg.status); write(dynabuf, msg.status);
write(streambuf, " "); write(dynabuf, " ");
write(streambuf, msg.reason); write(dynabuf, msg.reason);
write(streambuf, "\r\n"); write(dynabuf, "\r\n");
} }
template<class Streambuf, class FieldSequence> template<class DynamicBuffer, class FieldSequence>
void void
write_fields(Streambuf& streambuf, FieldSequence const& fields) write_fields(DynamicBuffer& dynabuf, FieldSequence const& fields)
{ {
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"Streambuf requirements not met"); "DynamicBuffer requirements not met");
//static_assert(is_FieldSequence<FieldSequence>::value, //static_assert(is_FieldSequence<FieldSequence>::value,
// "FieldSequence requirements not met"); // "FieldSequence requirements not met");
for(auto const& field : fields) for(auto const& field : fields)
{ {
write(streambuf, field.name()); write(dynabuf, field.name());
write(streambuf, ": "); write(dynabuf, ": ");
write(streambuf, field.value()); write(dynabuf, field.value());
write(streambuf, "\r\n"); write(dynabuf, "\r\n");
} }
} }
@@ -97,10 +97,10 @@ struct write_preparation
message_v1<isRequest, Body, Headers> const& msg_) message_v1<isRequest, Body, Headers> const& msg_)
: msg(msg_) : msg(msg_)
, w(msg) , w(msg)
, chunked(rfc2616::token_in_list( , chunked(token_list{
msg.headers["Transfer-Encoding"], "chunked")) msg.headers["Transfer-Encoding"]}.exists("chunked"))
, close(rfc2616::token_in_list( , close(token_list{
msg.headers["Connection"], "close") || msg.headers["Connection"]}.exists("close") ||
(msg.version < 11 && ! msg.headers.exists( (msg.version < 11 && ! msg.headers.exists(
"Content-Length"))) "Content-Length")))
{ {
@@ -378,17 +378,17 @@ operator()(error_code ec, std::size_t, bool again)
d.copy = {}; d.copy = {};
} }
template<class SyncWriteStream, class Streambuf> template<class SyncWriteStream, class DynamicBuffer>
class writef0_lambda class writef0_lambda
{ {
Streambuf const& sb_; DynamicBuffer const& sb_;
SyncWriteStream& stream_; SyncWriteStream& stream_;
bool chunked_; bool chunked_;
error_code& ec_; error_code& ec_;
public: public:
writef0_lambda(SyncWriteStream& stream, writef0_lambda(SyncWriteStream& stream,
Streambuf const& sb, bool chunked, error_code& ec) DynamicBuffer const& sb, bool chunked, error_code& ec)
: sb_(sb) : sb_(sb)
, stream_(stream) , stream_(stream)
, chunked_(chunked) , chunked_(chunked)
@@ -548,9 +548,8 @@ async_write(AsyncWriteStream& stream,
message_v1<isRequest, Body, Headers> const& msg, message_v1<isRequest, Body, Headers> const& msg,
WriteHandler&& handler) WriteHandler&& handler)
{ {
static_assert( static_assert(is_AsyncWriteStream<AsyncWriteStream>::value,
is_AsyncWriteStream<AsyncWriteStream>::value, "AsyncWriteStream requirements not met");
"AsyncWriteStream requirements not met");
static_assert(is_WritableBody<Body>::value, static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met"); "WritableBody requirements not met");
beast::async_completion<WriteHandler, beast::async_completion<WriteHandler,

View File

@@ -24,12 +24,30 @@ struct request_fields
{ {
std::string method; std::string method;
std::string url; std::string url;
protected:
void
swap(request_fields& other)
{
using std::swap;
swap(method, other.method);
swap(url, other.url);
}
}; };
struct response_fields struct response_fields
{ {
int status; int status;
std::string reason; std::string reason;
protected:
void
swap(response_fields& other)
{
using std::swap;
swap(status, other.status);
swap(reason, other.reason);
}
}; };
} // detail } // detail
@@ -125,7 +143,14 @@ struct message
{ {
} }
/// Swap this message for another message.
void
swap(message& other);
private: private:
using base = typename std::conditional<isRequest,
detail::request_fields, detail::response_fields>::type;
template<class... Un, size_t... IUn> template<class... Un, size_t... IUn>
message(std::piecewise_construct_t, message(std::piecewise_construct_t,
std::tuple<Un...>& tu, beast::detail::index_sequence<IUn...>) std::tuple<Un...>& tu, beast::detail::index_sequence<IUn...>)
@@ -145,7 +170,26 @@ private:
} }
}; };
#if ! GENERATING_DOCS template<bool isRequest, class Body, class Headers>
void
message<isRequest, Body, Headers>::
swap(message& other)
{
using std::swap;
base::swap(other);
swap(headers, other.headers);
swap(body, other.body);
}
/// Swap one message for another message.
template<bool isRequest, class Body, class Headers>
inline
void
swap(message<isRequest, Body, Headers>& lhs,
message<isRequest, Body, Headers>& rhs)
{
lhs.swap(rhs);
}
/// A typical HTTP request /// A typical HTTP request
template<class Body, template<class Body,
@@ -157,8 +201,6 @@ template<class Body,
class Headers = basic_headers<std::allocator<char>>> class Headers = basic_headers<std::allocator<char>>>
using response = message<false, Body, Headers>; using response = message<false, Body, Headers>;
#endif
} // http } // http
} // beast } // beast

View File

@@ -48,9 +48,31 @@ struct message_v1 : message<isRequest, Body, Headers>
std::forward<Argn>(argn)...) std::forward<Argn>(argn)...)
{ {
} }
/// Swap this message for another message.
void
swap(message_v1& other);
}; };
#if ! GENERATING_DOCS template<bool isRequest, class Body, class Headers>
void
message_v1<isRequest, Body, Headers>::
swap(message_v1& other)
{
using std::swap;
message<isRequest, Body, Headers>::swap(other);
swap(version, other.version);
}
/// Swap one message for another message.
template<bool isRequest, class Body, class Headers>
inline
void
swap(message_v1<isRequest, Body, Headers>& lhs,
message_v1<isRequest, Body, Headers>& rhs)
{
lhs.swap(rhs);
}
/// A typical HTTP/1 request /// A typical HTTP/1 request
template<class Body, template<class Body,
@@ -62,8 +84,6 @@ template<class Body,
class Headers = basic_headers<std::allocator<char>>> class Headers = basic_headers<std::allocator<char>>>
using response_v1 = message_v1<false, Body, Headers>; using response_v1 = message_v1<false, Body, Headers>;
#endif
/// Returns `true` if a HTTP/1 message indicates a keep alive /// Returns `true` if a HTTP/1 message indicates a keep alive
template<bool isRequest, class Body, class Headers> template<bool isRequest, class Body, class Headers>
bool bool

View File

@@ -23,8 +23,8 @@ enum class parse_error
bad_crlf, bad_crlf,
bad_request, bad_request,
bad_status_code,
bad_status, bad_status,
bad_reason,
bad_field, bad_field,
bad_value, bad_value,
@@ -33,7 +33,11 @@ enum class parse_error
bad_on_headers_rv, bad_on_headers_rv,
invalid_chunk_size, invalid_chunk_size,
invalid_ext_name,
invalid_ext_val,
headers_too_big,
body_too_big,
short_read, short_read,
general general
@@ -60,7 +64,7 @@ public:
return "bad method"; return "bad method";
case parse_error::bad_uri: case parse_error::bad_uri:
return "bad Request-URI"; return "bad request-target";
case parse_error::bad_version: case parse_error::bad_version:
return "bad HTTP-Version"; return "bad HTTP-Version";
@@ -69,13 +73,13 @@ public:
return "missing CRLF"; return "missing CRLF";
case parse_error::bad_request: case parse_error::bad_request:
return "bad Request-Line"; return "bad reason-phrase";
case parse_error::bad_status_code:
return "bad Status-Code";
case parse_error::bad_status: case parse_error::bad_status:
return "bad Status-Line"; return "bad status-code";
case parse_error::bad_reason:
return "bad reason-phrase";
case parse_error::bad_field: case parse_error::bad_field:
return "bad field token"; return "bad field token";
@@ -95,6 +99,18 @@ public:
case parse_error::invalid_chunk_size: case parse_error::invalid_chunk_size:
return "invalid chunk size"; return "invalid chunk size";
case parse_error::invalid_ext_name:
return "invalid ext name";
case parse_error::invalid_ext_val:
return "invalid ext val";
case parse_error::headers_too_big:
return "headers size limit exceeded";
case parse_error::body_too_big:
return "body size limit exceeded";
case parse_error::short_read: case parse_error::short_read:
return "unexpected end of data"; return "unexpected end of data";

View File

@@ -124,8 +124,6 @@ private:
{ {
if(! value_.empty()) if(! value_.empty())
{ {
rfc2616::trim_right_in_place(value_);
// VFALCO could std::move
m_.headers.insert(field_, value_); m_.headers.insert(field_, value_);
field_.clear(); field_.clear();
value_.clear(); value_.clear();
@@ -174,7 +172,7 @@ private:
m_.reason = std::move(this->reason_); m_.reason = std::move(this->reason_);
} }
int on_headers(error_code&) int on_headers(std::uint64_t, error_code&)
{ {
flush(); flush();
m_.version = 10 * this->http_major() + this->http_minor(); m_.version = 10 * this->http_major() + this->http_minor();

View File

@@ -12,7 +12,6 @@
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <beast/core/async_completion.hpp> #include <beast/core/async_completion.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/system/error_code.hpp>
namespace beast { namespace beast {
namespace http { namespace http {
@@ -36,7 +35,7 @@ namespace http {
@param stream The stream from which the data is to be read. @param stream The stream from which the data is to be read.
The type must support the @b `SyncReadStream` concept. The type must support the @b `SyncReadStream` concept.
@param streambuf A `Streambuf` holding additional bytes @param dynabuf A @b `DynamicBuffer` holding additional bytes
read by the implementation from the stream. This is both read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser stream buffer's input sequence will be given to the parser
@@ -47,10 +46,10 @@ namespace http {
@throws boost::system::system_error on failure. @throws boost::system::system_error on failure.
*/ */
template<class SyncReadStream, class Streambuf, class Parser> template<class SyncReadStream, class DynamicBuffer, class Parser>
void void
parse(SyncReadStream& stream, parse(SyncReadStream& stream,
Streambuf& streambuf, Parser& parser); DynamicBuffer& dynabuf, Parser& parser);
/** Parse a HTTP/1 message from a stream. /** Parse a HTTP/1 message from a stream.
@@ -71,7 +70,7 @@ parse(SyncReadStream& stream,
@param stream The stream from which the data is to be read. @param stream The stream from which the data is to be read.
The type must support the @b `SyncReadStream` concept. The type must support the @b `SyncReadStream` concept.
@param streambuf A `Streambuf` holding additional bytes @param dynabuf A @b `DynamicBuffer` holding additional bytes
read by the implementation from the stream. This is both read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser stream buffer's input sequence will be given to the parser
@@ -82,10 +81,10 @@ parse(SyncReadStream& stream,
@param ec Set to the error, if any occurred. @param ec Set to the error, if any occurred.
*/ */
template<class SyncReadStream, class Streambuf, class Parser> template<class SyncReadStream, class DynamicBuffer, class Parser>
void void
parse(SyncReadStream& stream, parse(SyncReadStream& stream,
Streambuf& streambuf, Parser& parser, error_code& ec); DynamicBuffer& dynabuf, Parser& parser, error_code& ec);
/** Start an asynchronous operation to parse a HTTP/1 message from a stream. /** Start an asynchronous operation to parse a HTTP/1 message from a stream.
@@ -106,7 +105,7 @@ parse(SyncReadStream& stream,
@param stream The stream from which the data is to be read. @param stream The stream from which the data is to be read.
The type must support the @b `AsyncReadStream` concept. The type must support the @b `AsyncReadStream` concept.
@param streambuf A `Streambuf` holding additional bytes @param dynabuf A @b `DynamicBuffer` holding additional bytes
read by the implementation from the stream. This is both read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser stream buffer's input sequence will be given to the parser
@@ -128,14 +127,14 @@ parse(SyncReadStream& stream,
manner equivalent to using `boost::asio::io_service::post`. manner equivalent to using `boost::asio::io_service::post`.
*/ */
template<class AsyncReadStream, template<class AsyncReadStream,
class Streambuf, class Parser, class ReadHandler> class DynamicBuffer, class Parser, class ReadHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
#endif #endif
async_parse(AsyncReadStream& stream, Streambuf& streambuf, async_parse(AsyncReadStream& stream, DynamicBuffer& dynabuf,
Parser& parser, ReadHandler&& handler); Parser& parser, ReadHandler&& handler);
/** Read a HTTP/1 message from a stream. /** Read a HTTP/1 message from a stream.
@@ -157,7 +156,7 @@ async_parse(AsyncReadStream& stream, Streambuf& streambuf,
@param stream The stream from which the data is to be read. @param stream The stream from which the data is to be read.
The type must support the @b `SyncReadStream` concept. The type must support the @b `SyncReadStream` concept.
@param streambuf A `Streambuf` holding additional bytes @param dynabuf A @b `DynamicBuffer` holding additional bytes
read by the implementation from the stream. This is both read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser stream buffer's input sequence will be given to the parser
@@ -168,10 +167,10 @@ async_parse(AsyncReadStream& stream, Streambuf& streambuf,
@throws boost::system::system_error Thrown on failure. @throws boost::system::system_error Thrown on failure.
*/ */
template<class SyncReadStream, class Streambuf, template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void
read(SyncReadStream& stream, Streambuf& streambuf, read(SyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& msg); message_v1<isRequest, Body, Headers>& msg);
/** Read a HTTP/1 message from a stream. /** Read a HTTP/1 message from a stream.
@@ -193,7 +192,7 @@ read(SyncReadStream& stream, Streambuf& streambuf,
@param stream The stream from which the data is to be read. @param stream The stream from which the data is to be read.
The type must support the @b `SyncReadStream` concept. The type must support the @b `SyncReadStream` concept.
@param streambuf A `Streambuf` holding additional bytes @param dynabuf A @b `DynamicBuffer` holding additional bytes
read by the implementation from the stream. This is both read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser stream buffer's input sequence will be given to the parser
@@ -204,10 +203,10 @@ read(SyncReadStream& stream, Streambuf& streambuf,
@param ec Set to the error, if any occurred. @param ec Set to the error, if any occurred.
*/ */
template<class SyncReadStream, class Streambuf, template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void
read(SyncReadStream& stream, Streambuf& streambuf, read(SyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& msg, message_v1<isRequest, Body, Headers>& msg,
error_code& ec); error_code& ec);
@@ -229,7 +228,7 @@ read(SyncReadStream& stream, Streambuf& streambuf,
@param stream The stream to read the message from. @param stream The stream to read the message from.
The type must support the @b `AsyncReadStream` concept. The type must support the @b `AsyncReadStream` concept.
@param streambuf A `Streambuf` holding additional bytes @param dynabuf A @b `DynamicBuffer` holding additional bytes
read by the implementation from the stream. This is both read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser stream buffer's input sequence will be given to the parser
@@ -249,7 +248,7 @@ read(SyncReadStream& stream, Streambuf& streambuf,
this function. Invocation of the handler will be performed in a this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`. manner equivalent to using `boost::asio::io_service::post`.
*/ */
template<class AsyncReadStream, class Streambuf, template<class AsyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Headers, bool isRequest, class Body, class Headers,
class ReadHandler> class ReadHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
@@ -258,7 +257,7 @@ void_or_deduced
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
#endif #endif
async_read(AsyncReadStream& stream, Streambuf& streambuf, async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& msg, message_v1<isRequest, Body, Headers>& msg,
ReadHandler&& handler); ReadHandler&& handler);

View File

@@ -1,464 +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_RFC2616_HPP
#define BEAST_HTTP_RFC2616_HPP
#include <boost/range/algorithm/equal.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/utility/string_ref.hpp>
#include <algorithm>
#include <cctype>
#include <string>
#include <iterator>
#include <tuple> // for std::tie, remove ASAP
#include <utility>
#include <vector>
namespace beast {
#if ! GENERATING_DOCS
/** Routines for performing RFC2616 compliance.
RFC2616:
Hypertext Transfer Protocol -- HTTP/1.1
http://www.w3.org/Protocols/rfc2616/rfc2616
*/
namespace rfc2616 {
namespace detail {
struct ci_equal_pred
{
bool operator()(char c1, char c2)
{
// VFALCO TODO Use a table lookup here
return std::tolower(c1) == std::tolower(c2);
}
};
} // detail
/** Returns `true` if `c` is linear white space.
This excludes the CRLF sequence allowed for line continuations.
*/
inline
bool
is_lws(char c)
{
return c == ' ' || c == '\t';
}
/** Returns `true` if `c` is any whitespace character. */
inline
bool
is_white(char c)
{
switch (c)
{
case ' ': case '\f': case '\n':
case '\r': case '\t': case '\v':
return true;
};
return false;
}
/** Returns `true` if `c` is a control character. */
inline
bool
is_control(char c)
{
return c <= 31 || c >= 127;
}
/** Returns `true` if `c` is a separator. */
inline
bool
is_separator(char c)
{
// VFALCO Could use a static table
switch (c)
{
case '(': case ')': case '<': case '>': case '@':
case ',': case ';': case ':': case '\\': case '"':
case '{': case '}': case ' ': case '\t':
return true;
};
return false;
}
/** Returns `true` if `c` is a character. */
inline
bool
is_char(char c)
{
return c >= 0 && c <= 127;
}
template <class FwdIter>
FwdIter
trim_left (FwdIter first, FwdIter last)
{
return std::find_if_not (first, last,
is_white);
}
template <class FwdIter>
FwdIter
trim_right (FwdIter first, FwdIter last)
{
if (first == last)
return last;
do
{
--last;
if (! is_white (*last))
return ++last;
}
while (last != first);
return first;
}
template <class CharT, class Traits, class Allocator>
void
trim_right_in_place (std::basic_string <
CharT, Traits, Allocator>& s)
{
s.resize (std::distance (s.begin(),
trim_right (s.begin(), s.end())));
}
template <class FwdIter>
std::pair <FwdIter, FwdIter>
trim (FwdIter first, FwdIter last)
{
first = trim_left (first, last);
last = trim_right (first, last);
return std::make_pair (first, last);
}
template <class String>
String
trim (String const& s)
{
using std::begin;
using std::end;
auto first = begin(s);
auto last = end(s);
std::tie (first, last) = trim (first, last);
return { first, last };
}
template <class String>
String
trim_right (String const& s)
{
using std::begin;
using std::end;
auto first (begin(s));
auto last (end(s));
last = trim_right (first, last);
return { first, last };
}
inline
std::string
trim (std::string const& s)
{
return trim <std::string> (s);
}
/** Parse a character sequence of values separated by commas.
Double quotes and escape sequences will be converted. Excess white
space, commas, double quotes, and empty elements are not copied.
Format:
#(token|quoted-string)
Reference:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2
*/
template <class FwdIt,
class Result = std::vector<
std::basic_string<typename
std::iterator_traits<FwdIt>::value_type>>,
class Char>
Result
split(FwdIt first, FwdIt last, Char delim)
{
Result result;
using string = typename Result::value_type;
FwdIt iter = first;
string e;
while (iter != last)
{
if (*iter == '"')
{
// quoted-string
++iter;
while (iter != last)
{
if (*iter == '"')
{
++iter;
break;
}
if (*iter == '\\')
{
// quoted-pair
++iter;
if (iter != last)
e.append (1, *iter++);
}
else
{
// qdtext
e.append (1, *iter++);
}
}
if (! e.empty())
{
result.emplace_back(std::move(e));
e.clear();
}
}
else if (*iter == delim)
{
e = trim_right (e);
if (! e.empty())
{
result.emplace_back(std::move(e));
e.clear();
}
++iter;
}
else if (is_lws (*iter))
{
++iter;
}
else
{
e.append (1, *iter++);
}
}
if (! e.empty())
{
e = trim_right (e);
if (! e.empty())
result.emplace_back(std::move(e));
}
return result;
}
template <class FwdIt,
class Result = std::vector<
std::basic_string<typename std::iterator_traits<
FwdIt>::value_type>>>
Result
split_commas(FwdIt first, FwdIt last)
{
return split(first, last, ',');
}
template <class Result = std::vector<std::string>>
Result
split_commas(boost::string_ref const& s)
{
return split_commas(s.begin(), s.end());
}
//------------------------------------------------------------------------------
/** Iterates through a comma separated list.
Meets the requirements of ForwardIterator.
List defined in rfc2616 2.1.
@note Values returned may contain backslash escapes.
*/
class list_iterator
{
using iter_type = boost::string_ref::const_iterator;
iter_type it_;
iter_type end_;
boost::string_ref value_;
public:
using value_type = boost::string_ref;
using pointer = value_type const*;
using reference = value_type const&;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::forward_iterator_tag;
list_iterator(iter_type begin, iter_type end)
: it_(begin)
, end_(end)
{
if(it_ != end_)
increment();
}
bool
operator==(list_iterator const& other) const
{
return other.it_ == it_ && other.end_ == end_
&& other.value_.size() == value_.size();
}
bool
operator!=(list_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return value_;
}
pointer
operator->() const
{
return &*(*this);
}
list_iterator&
operator++()
{
increment();
return *this;
}
list_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
private:
template<class = void>
void
increment();
};
template<class>
void
list_iterator::increment()
{
value_.clear();
while(it_ != end_)
{
if(*it_ == '"')
{
// quoted-string
++it_;
if(it_ == end_)
return;
if(*it_ != '"')
{
auto start = it_;
for(;;)
{
++it_;
if(it_ == end_)
{
value_ = boost::string_ref(
&*start, std::distance(start, it_));
return;
}
if(*it_ == '"')
{
value_ = boost::string_ref(
&*start, std::distance(start, it_));
++it_;
return;
}
}
}
++it_;
}
else if(*it_ == ',')
{
it_++;
continue;
}
else if(is_lws(*it_))
{
++it_;
continue;
}
else
{
auto start = it_;
for(;;)
{
++it_;
if(it_ == end_ ||
*it_ == ',' ||
is_lws(*it_))
{
value_ = boost::string_ref(
&*start, std::distance(start, it_));
return;
}
}
}
}
}
/** Returns true if two strings are equal.
A case-insensitive comparison is used.
*/
inline
bool
ci_equal(boost::string_ref s1, boost::string_ref s2)
{
return boost::range::equal(s1, s2,
detail::ci_equal_pred{});
}
/** Returns a range representing the list. */
inline
boost::iterator_range<list_iterator>
make_list(boost::string_ref const& field)
{
return boost::iterator_range<list_iterator>{
list_iterator{field.begin(), field.end()},
list_iterator{field.end(), field.end()}};
}
/** Returns true if the specified token exists in the list.
A case-insensitive comparison is used.
*/
template<class = void>
bool
token_in_list(boost::string_ref const& value,
boost::string_ref const& token)
{
for(auto const& item : make_list(value))
if(ci_equal(item, token))
return true;
return false;
}
} // rfc2616
#endif
} // beast
#endif

View File

@@ -8,16 +8,238 @@
#ifndef BEAST_HTTP_RFC7230_HPP #ifndef BEAST_HTTP_RFC7230_HPP
#define BEAST_HTTP_RFC7230_HPP #define BEAST_HTTP_RFC7230_HPP
#include <array> #include <beast/http/detail/rfc7230.hpp>
#include <cstdint>
namespace beast { namespace beast {
namespace rfc7230 { namespace http {
/** A list of parameters in a HTTP extension field value.
This container allows iteration of the parameter list
in a HTTP extension. The parameter list is a series
of "name = value" pairs with each pair starting with
a semicolon.
} // rfc7230 BNF:
@code
param-list = *( OWS ";" OWS param )
param = token OWS "=" OWS ( token / quoted-string )
@endcode
If a parsing error is encountered while iterating the string,
the behavior of the container will be as if a string containing
only characters up to but excluding the first invalid character
was used to construct the list.
*/
class param_list
{
boost::string_ref s_;
public:
/** The type of each element in the list.
The first string in the pair is the name of the
parameter, and the second string in the pair is its value.
*/
using value_type =
std::pair<boost::string_ref, boost::string_ref>;
/// A constant iterator to the list
#if GENERATING_DOCS
using const_iterator = implementation_defined;
#else
class const_iterator;
#endif
/// Default constructor.
param_list() = default;
/** Construct a list.
@param s A string containing the list contents. The string
must remain valid for the lifetime of the container.
*/
explicit
param_list(boost::string_ref const& s)
: s_(s)
{
}
/// Return a const iterator to the beginning of the list
const_iterator begin() const;
/// Return a const iterator to the end of the list
const_iterator end() const;
/// Return a const iterator to the beginning of the list
const_iterator cbegin() const;
/// Return a const iterator to the end of the list
const_iterator cend() const;
};
//------------------------------------------------------------------------------
/** A list of extensions in a comma separated HTTP field value.
This container allows iteration of the extensions in a HTTP
field value. The extension list is a comma separated list of
token parameter list pairs.
BNF:
@code
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
ext = token param-list
param-list = *( OWS ";" OWS param )
param = token OWS "=" OWS ( token / quoted-string )
@endcode
If a parsing error is encountered while iterating the string,
the behavior of the container will be as if a string containing
only characters up to but excluding the first invalid character
was used to construct the list.
*/
class ext_list
{
using iter_type = boost::string_ref::const_iterator;
boost::string_ref s_;
public:
/** The type of each element in the list.
The first element of the pair is the extension token, and the
second element of the pair is an iterable container holding the
extension's name/value parameters.
*/
using value_type = std::pair<boost::string_ref, param_list>;
/// A constant iterator to the list
#if GENERATING_DOCS
using const_iterator = implementation_defined;
#else
class const_iterator;
#endif
/** Construct a list.
@param s A string containing the list contents. The string
must remain valid for the lifetime of the container.
*/
explicit
ext_list(boost::string_ref const& s)
: s_(s)
{
}
/// Return a const iterator to the beginning of the list
const_iterator begin() const;
/// Return a const iterator to the end of the list
const_iterator end() const;
/// Return a const iterator to the beginning of the list
const_iterator cbegin() const;
/// Return a const iterator to the end of the list
const_iterator cend() const;
/** Find a token in the list.
@param s The token to find. A case-insensitive comparison is used.
@return An iterator to the matching token, or `end()` if no
token exists.
*/
template<class T>
const_iterator
find(T const& s);
/** Return `true` if a token is present in the list.
@param s The token to find. A case-insensitive comparison is used.
*/
template<class T>
bool
exists(T const& s);
};
//------------------------------------------------------------------------------
/** A list of tokens in a comma separated HTTP field value.
This container allows iteration of the extensions in a HTTP
field value. The extension list is a comma separated list of
token parameter list pairs.
BNF:
@code
token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] )
@endcode
If a parsing error is encountered while iterating the string,
the behavior of the container will be as if a string containing
only characters up to but excluding the first invalid character
was used to construct the list.
*/
class token_list
{
using iter_type = boost::string_ref::const_iterator;
boost::string_ref s_;
public:
/** The type of each element in the token list.
The first element of the pair is the extension token, and the
second element of the pair is an iterable container holding the
extension's name/value parameters.
*/
using value_type = boost::string_ref;
/// A constant iterator to the list
#if GENERATING_DOCS
using const_iterator = implementation_defined;
#else
class const_iterator;
#endif
/** Construct a list.
@param s A string containing the list contents. The string
must remain valid for the lifetime of the container.
*/
explicit
token_list(boost::string_ref const& s)
: s_(s)
{
}
/// Return a const iterator to the beginning of the list
const_iterator begin() const;
/// Return a const iterator to the end of the list
const_iterator end() const;
/// Return a const iterator to the beginning of the list
const_iterator cbegin() const;
/// Return a const iterator to the end of the list
const_iterator cend() const;
/** Return `true` if a token is present in the list.
@param s The token to find. A case-insensitive comparison is used.
*/
template<class T>
bool
exists(T const& s);
};
} // http
} // beast } // beast
#include <beast/http/impl/rfc7230.ipp>
#endif #endif

View File

@@ -8,91 +8,17 @@
#ifndef BEAST_HTTP_STREAMBUF_BODY_HPP #ifndef BEAST_HTTP_STREAMBUF_BODY_HPP
#define BEAST_HTTP_STREAMBUF_BODY_HPP #define BEAST_HTTP_STREAMBUF_BODY_HPP
#include <beast/http/body_type.hpp> #include <beast/http/basic_dynabuf_body.hpp>
#include <beast/core/buffer_cat.hpp>
#include <beast/core/streambuf.hpp> #include <beast/core/streambuf.hpp>
#include <memory>
#include <string>
namespace beast { namespace beast {
namespace http { namespace http {
/** A message body represented by a Streambuf /** A message body represented by a @ref streambuf
Meets the requirements of @b `Body`. Meets the requirements of @b `Body`.
*/ */
template<class Streambuf> using streambuf_body = basic_dynabuf_body<streambuf>;
struct basic_streambuf_body
{
/// The type of the `message::body` member
using value_type = Streambuf;
#if GENERATING_DOCS
private:
#endif
class reader
{
value_type& sb_;
public:
template<bool isRequest, class Headers>
explicit
reader(message<isRequest,
basic_streambuf_body, Headers>& m) noexcept
: sb_(m.body)
{
}
void
write(void const* data,
std::size_t size, error_code&) noexcept
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
sb_.commit(buffer_copy(
sb_.prepare(size), buffer(data, size)));
}
};
class writer
{
Streambuf const& body_;
public:
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
explicit
writer(message<
isRequest, basic_streambuf_body, Headers> const& m)
: body_(m.body)
{
}
void
init(error_code& ec)
{
}
std::uint64_t
content_length() const
{
return body_.size();
}
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
write(body_.data());
return true;
}
};
};
using streambuf_body = basic_streambuf_body<streambuf>;
} // http } // http
} // beast } // beast

View File

@@ -9,8 +9,7 @@
#define BEAST_HTTP_STRING_BODY_HPP #define BEAST_HTTP_STRING_BODY_HPP
#include <beast/http/body_type.hpp> #include <beast/http/body_type.hpp>
#include <beast/core/buffer_cat.hpp> #include <boost/asio/buffer.hpp>
#include <beast/core/streambuf.hpp>
#include <memory> #include <memory>
#include <string> #include <string>

View File

@@ -14,8 +14,8 @@
// BEAST_VERSION / 100 % 1000 is the minor version // BEAST_VERSION / 100 % 1000 is the minor version
// BEAST_VERSION / 100000 is the major version // BEAST_VERSION / 100000 is the major version
// //
#define BEAST_VERSION 100000 #define BEAST_VERSION 100006
#define BEAST_VERSION_STRING "1.0.0-b5" #define BEAST_VERSION_STRING "1.0.0-b6"
#endif #endif

View File

@@ -117,11 +117,11 @@ is_valid(close_code::value code)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Write frame header to streambuf // Write frame header to dynamic buffer
// //
template<class Streambuf> template<class DynamicBuffer>
void void
write(Streambuf& sb, frame_header const& fh) write(DynamicBuffer& db, frame_header const& fh)
{ {
using boost::asio::buffer; using boost::asio::buffer;
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
@@ -159,24 +159,24 @@ write(Streambuf& sb, frame_header const& fh)
native_to_little_uint32(fh.key, &b[n]); native_to_little_uint32(fh.key, &b[n]);
n += 4; n += 4;
} }
sb.commit(buffer_copy( db.commit(buffer_copy(
sb.prepare(n), buffer(b))); db.prepare(n), buffer(b)));
} }
// Read fixed frame header // Read fixed frame header
// Requires at least 2 bytes // Requires at least 2 bytes
// //
template<class Streambuf> template<class DynamicBuffer>
std::size_t std::size_t
read_fh1(frame_header& fh, Streambuf& sb, read_fh1(frame_header& fh, DynamicBuffer& db,
role_type role, close_code::value& code) role_type role, close_code::value& code)
{ {
using boost::asio::buffer; using boost::asio::buffer;
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
std::uint8_t b[2]; std::uint8_t b[2];
assert(buffer_size(sb.data()) >= sizeof(b)); assert(buffer_size(db.data()) >= sizeof(b));
sb.consume(buffer_copy(buffer(b), sb.data())); db.consume(buffer_copy(buffer(b), db.data()));
std::size_t need; std::size_t need;
fh.len = b[1] & 0x7f; fh.len = b[1] & 0x7f;
switch(fh.len) switch(fh.len)
@@ -236,9 +236,9 @@ read_fh1(frame_header& fh, Streambuf& sb,
// Decode variable frame header from stream // Decode variable frame header from stream
// //
template<class Streambuf> template<class DynamicBuffer>
void void
read_fh2(frame_header& fh, Streambuf& sb, read_fh2(frame_header& fh, DynamicBuffer& db,
role_type role, close_code::value& code) role_type role, close_code::value& code)
{ {
using boost::asio::buffer; using boost::asio::buffer;
@@ -250,8 +250,8 @@ read_fh2(frame_header& fh, Streambuf& sb,
case 126: case 126:
{ {
std::uint8_t b[2]; std::uint8_t b[2];
assert(buffer_size(sb.data()) >= sizeof(b)); assert(buffer_size(db.data()) >= sizeof(b));
sb.consume(buffer_copy(buffer(b), sb.data())); db.consume(buffer_copy(buffer(b), db.data()));
fh.len = big_uint16_to_native(&b[0]); fh.len = big_uint16_to_native(&b[0]);
// length not canonical // length not canonical
if(fh.len < 126) if(fh.len < 126)
@@ -264,8 +264,8 @@ read_fh2(frame_header& fh, Streambuf& sb,
case 127: case 127:
{ {
std::uint8_t b[8]; std::uint8_t b[8];
assert(buffer_size(sb.data()) >= sizeof(b)); assert(buffer_size(db.data()) >= sizeof(b));
sb.consume(buffer_copy(buffer(b), sb.data())); db.consume(buffer_copy(buffer(b), db.data()));
fh.len = big_uint64_to_native(&b[0]); fh.len = big_uint64_to_native(&b[0]);
// length not canonical // length not canonical
if(fh.len < 65536) if(fh.len < 65536)
@@ -279,8 +279,8 @@ read_fh2(frame_header& fh, Streambuf& sb,
if(fh.mask) if(fh.mask)
{ {
std::uint8_t b[4]; std::uint8_t b[4];
assert(buffer_size(sb.data()) >= sizeof(b)); assert(buffer_size(db.data()) >= sizeof(b));
sb.consume(buffer_copy(buffer(b), sb.data())); db.consume(buffer_copy(buffer(b), db.data()));
fh.key = little_uint32_to_native(&b[0]); fh.key = little_uint32_to_native(&b[0]);
} }
else else

View File

@@ -18,7 +18,6 @@
#include <beast/http/empty_body.hpp> #include <beast/http/empty_body.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/string_body.hpp> #include <beast/http/string_body.hpp>
#include <beast/core/streambuf.hpp>
#include <boost/asio/error.hpp> #include <boost/asio/error.hpp>
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
@@ -107,15 +106,13 @@ protected:
void void
prepare_fh(close_code::value& code); prepare_fh(close_code::value& code);
template<class Streambuf> template<class DynamicBuffer>
void void
write_close(Streambuf& sb, write_close(DynamicBuffer& db, close_reason const& rc);
close_reason const& rc);
template<class Streambuf> template<class DynamicBuffer>
void void
write_ping(Streambuf& sb, opcode op, write_ping(DynamicBuffer& db, opcode op, ping_data const& data);
ping_data const& data);
}; };
} // detail } // detail

View File

@@ -23,7 +23,7 @@ namespace websocket {
// processes any received control frames. // processes any received control frames.
// //
template<class NextLayer> template<class NextLayer>
template<class Streambuf, class Handler> template<class DynamicBuffer, class Handler>
class stream<NextLayer>::read_frame_op class stream<NextLayer>::read_frame_op
{ {
using alloc_type = using alloc_type =
@@ -35,27 +35,27 @@ class stream<NextLayer>::read_frame_op
using fmb_type = using fmb_type =
typename fb_type::mutable_buffers_type; typename fb_type::mutable_buffers_type;
using smb_type = using dmb_type =
typename Streambuf::mutable_buffers_type; typename DynamicBuffer::mutable_buffers_type;
struct data : op struct data : op
{ {
stream<NextLayer>& ws; stream<NextLayer>& ws;
frame_info& fi; frame_info& fi;
Streambuf& sb; DynamicBuffer& db;
Handler h; Handler h;
fb_type fb; fb_type fb;
boost::optional<smb_type> smb; boost::optional<dmb_type> dmb;
boost::optional<fmb_type> fmb; boost::optional<fmb_type> fmb;
bool cont; bool cont;
int state = 0; int state = 0;
template<class DeducedHandler> template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& ws_, data(DeducedHandler&& h_, stream<NextLayer>& ws_,
frame_info& fi_, Streambuf& sb_) frame_info& fi_, DynamicBuffer& sb_)
: ws(ws_) : ws(ws_)
, fi(fi_) , fi(fi_)
, sb(sb_) , db(sb_)
, h(std::forward<DeducedHandler>(h_)) , h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers:: , cont(boost_asio_handler_cont_helpers::
is_continuation(h)) is_continuation(h))
@@ -127,9 +127,9 @@ public:
}; };
template<class NextLayer> template<class NextLayer>
template<class Buffers, class Handler> template<class DynamicBuffer, class Handler>
void void
stream<NextLayer>::read_frame_op<Buffers, Handler>:: stream<NextLayer>::read_frame_op<DynamicBuffer, Handler>::
operator()(error_code ec, std::size_t bytes_transferred) operator()(error_code ec, std::size_t bytes_transferred)
{ {
auto& d = *d_; auto& d = *d_;
@@ -139,9 +139,9 @@ operator()(error_code ec, std::size_t bytes_transferred)
} }
template<class NextLayer> template<class NextLayer>
template<class Buffers, class Handler> template<class DynamicBuffer, class Handler>
void void
stream<NextLayer>::read_frame_op<Buffers, Handler>:: stream<NextLayer>::read_frame_op<DynamicBuffer, Handler>::
operator()(error_code ec,std::size_t bytes_transferred, bool again) operator()(error_code ec,std::size_t bytes_transferred, bool again)
{ {
enum enum
@@ -187,18 +187,18 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
case do_read_payload: case do_read_payload:
d.state = do_read_payload + 1; d.state = do_read_payload + 1;
d.smb = d.sb.prepare( d.dmb = d.db.prepare(
detail::clamp(d.ws.rd_need_)); detail::clamp(d.ws.rd_need_));
// receive payload data // receive payload data
d.ws.stream_.async_read_some( d.ws.stream_.async_read_some(
*d.smb, std::move(*this)); *d.dmb, std::move(*this));
return; return;
case do_read_payload + 1: case do_read_payload + 1:
{ {
d.ws.rd_need_ -= bytes_transferred; d.ws.rd_need_ -= bytes_transferred;
auto const pb = prepare_buffers( auto const pb = prepare_buffers(
bytes_transferred, *d.smb); bytes_transferred, *d.dmb);
if(d.ws.rd_fh_.mask) if(d.ws.rd_fh_.mask)
detail::mask_inplace(pb, d.ws.rd_key_); detail::mask_inplace(pb, d.ws.rd_key_);
if(d.ws.rd_opcode_ == opcode::text) if(d.ws.rd_opcode_ == opcode::text)
@@ -213,7 +213,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
break; break;
} }
} }
d.sb.commit(bytes_transferred); d.db.commit(bytes_transferred);
if(d.ws.rd_need_ > 0) if(d.ws.rd_need_ > 0)
{ {
d.state = do_read_payload; d.state = do_read_payload;

View File

@@ -17,7 +17,7 @@ namespace websocket {
// read an entire message // read an entire message
// //
template<class NextLayer> template<class NextLayer>
template<class Streambuf, class Handler> template<class DynamicBuffer, class Handler>
class stream<NextLayer>::read_op class stream<NextLayer>::read_op
{ {
using alloc_type = using alloc_type =
@@ -27,7 +27,7 @@ class stream<NextLayer>::read_op
{ {
stream<NextLayer>& ws; stream<NextLayer>& ws;
opcode& op; opcode& op;
Streambuf& sb; DynamicBuffer& db;
Handler h; Handler h;
frame_info fi; frame_info fi;
bool cont; bool cont;
@@ -36,10 +36,10 @@ class stream<NextLayer>::read_op
template<class DeducedHandler> template<class DeducedHandler>
data(DeducedHandler&& h_, data(DeducedHandler&& h_,
stream<NextLayer>& ws_, opcode& op_, stream<NextLayer>& ws_, opcode& op_,
Streambuf& sb_) DynamicBuffer& sb_)
: ws(ws_) : ws(ws_)
, op(op_) , op(op_)
, sb(sb_) , db(sb_)
, h(std::forward<DeducedHandler>(h_)) , h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers:: , cont(boost_asio_handler_cont_helpers::
is_continuation(h)) is_continuation(h))
@@ -98,9 +98,9 @@ public:
}; };
template<class NextLayer> template<class NextLayer>
template<class Streambuf, class Handler> template<class DynamicBuffer, class Handler>
void void
stream<NextLayer>::read_op<Streambuf, Handler>:: stream<NextLayer>::read_op<DynamicBuffer, Handler>::
operator()(error_code const& ec, bool again) operator()(error_code const& ec, bool again)
{ {
auto& d = *d_; auto& d = *d_;
@@ -117,9 +117,9 @@ operator()(error_code const& ec, bool again)
// the handler is moved from the data block // the handler is moved from the data block
// before asio_handler_deallocate is called. // before asio_handler_deallocate is called.
d.ws.async_read_frame( d.ws.async_read_frame(
d.fi, d.sb, std::move(*this)); d.fi, d.db, std::move(*this));
#else #else
d.ws.async_read_frame(d.fi, d.sb, *this); d.ws.async_read_frame(d.fi, d.db, *this);
#endif #endif
return; return;

View File

@@ -22,14 +22,13 @@
#include <beast/http/read.hpp> #include <beast/http/read.hpp>
#include <beast/http/write.hpp> #include <beast/http/write.hpp>
#include <beast/http/reason.hpp> #include <beast/http/reason.hpp>
#include <beast/http/rfc2616.hpp> #include <beast/http/rfc7230.hpp>
#include <beast/core/buffer_cat.hpp> #include <beast/core/buffer_cat.hpp>
#include <beast/core/buffer_concepts.hpp> #include <beast/core/buffer_concepts.hpp>
#include <beast/core/consuming_buffers.hpp> #include <beast/core/consuming_buffers.hpp>
#include <beast/core/prepare_buffers.hpp> #include <beast/core/prepare_buffers.hpp>
#include <beast/core/static_streambuf.hpp> #include <beast/core/static_streambuf.hpp>
#include <beast/core/stream_concepts.hpp> #include <beast/core/stream_concepts.hpp>
#include <beast/core/streambuf.hpp>
#include <boost/endian/buffers.hpp> #include <boost/endian/buffers.hpp>
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
@@ -94,10 +93,10 @@ stream_base::prepare_fh(close_code::value& code)
} }
} }
template<class Streambuf> template<class DynamicBuffer>
void void
stream_base::write_close( stream_base::write_close(
Streambuf& sb, close_reason const& cr) DynamicBuffer& db, close_reason const& cr)
{ {
using namespace boost::endian; using namespace boost::endian;
frame_header fh; frame_header fh;
@@ -111,7 +110,7 @@ stream_base::write_close(
fh.mask = role_ == detail::role_type::client; fh.mask = role_ == detail::role_type::client;
if(fh.mask) if(fh.mask)
fh.key = maskgen_(); fh.key = maskgen_();
detail::write(sb, fh); detail::write(db, fh);
if(cr.code != close_code::none) if(cr.code != close_code::none)
{ {
detail::prepared_key_type key; detail::prepared_key_type key;
@@ -121,29 +120,29 @@ stream_base::write_close(
std::uint8_t b[2]; std::uint8_t b[2];
::new(&b[0]) big_uint16_buf_t{ ::new(&b[0]) big_uint16_buf_t{
(std::uint16_t)cr.code}; (std::uint16_t)cr.code};
auto d = sb.prepare(2); auto d = db.prepare(2);
boost::asio::buffer_copy(d, boost::asio::buffer_copy(d,
boost::asio::buffer(b)); boost::asio::buffer(b));
if(fh.mask) if(fh.mask)
detail::mask_inplace(d, key); detail::mask_inplace(d, key);
sb.commit(2); db.commit(2);
} }
if(! cr.reason.empty()) if(! cr.reason.empty())
{ {
auto d = sb.prepare(cr.reason.size()); auto d = db.prepare(cr.reason.size());
boost::asio::buffer_copy(d, boost::asio::buffer_copy(d,
boost::asio::const_buffer( boost::asio::const_buffer(
cr.reason.data(), cr.reason.size())); cr.reason.data(), cr.reason.size()));
if(fh.mask) if(fh.mask)
detail::mask_inplace(d, key); detail::mask_inplace(d, key);
sb.commit(cr.reason.size()); db.commit(cr.reason.size());
} }
} }
} }
template<class Streambuf> template<class DynamicBuffer>
void void
stream_base::write_ping(Streambuf& sb, stream_base::write_ping(DynamicBuffer& db,
opcode op, ping_data const& data) opcode op, ping_data const& data)
{ {
frame_header fh; frame_header fh;
@@ -156,19 +155,19 @@ stream_base::write_ping(Streambuf& sb,
fh.mask = role_ == role_type::client; fh.mask = role_ == role_type::client;
if(fh.mask) if(fh.mask)
fh.key = maskgen_(); fh.key = maskgen_();
detail::write(sb, fh); detail::write(db, fh);
if(data.empty()) if(data.empty())
return; return;
detail::prepared_key_type key; detail::prepared_key_type key;
if(fh.mask) if(fh.mask)
detail::prepare_key(key, fh.key); detail::prepare_key(key, fh.key);
auto d = sb.prepare(data.size()); auto d = db.prepare(data.size());
boost::asio::buffer_copy(d, boost::asio::buffer_copy(d,
boost::asio::const_buffers_1( boost::asio::const_buffers_1(
data.data(), data.size())); data.data(), data.size()));
if(fh.mask) if(fh.mask)
detail::mask_inplace(d, key); detail::mask_inplace(d, key);
sb.commit(data.size()); db.commit(data.size());
} }
} // detail } // detail
@@ -453,10 +452,10 @@ void
stream<NextLayer>:: stream<NextLayer>::
ping(ping_data const& payload, error_code& ec) ping(ping_data const& payload, error_code& ec)
{ {
detail::frame_streambuf sb; detail::frame_streambuf db;
write_ping<static_streambuf>( write_ping<static_streambuf>(
sb, opcode::ping, payload); db, opcode::ping, payload);
boost::asio::write(stream_, sb.data(), ec); boost::asio::write(stream_, db.data(), ec);
} }
template<class NextLayer> template<class NextLayer>
@@ -477,31 +476,35 @@ async_ping(ping_data const& payload, PingHandler&& handler)
} }
template<class NextLayer> template<class NextLayer>
template<class Streambuf> template<class DynamicBuffer>
void void
stream<NextLayer>:: stream<NextLayer>::
read(opcode& op, Streambuf& streambuf) read(opcode& op, DynamicBuffer& dynabuf)
{ {
static_assert(is_SyncStream<next_layer_type>::value, static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
error_code ec; error_code ec;
read(op, streambuf, ec); read(op, dynabuf, ec);
if(ec) if(ec)
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer> template<class NextLayer>
template<class Streambuf> template<class DynamicBuffer>
void void
stream<NextLayer>:: stream<NextLayer>::
read(opcode& op, Streambuf& streambuf, error_code& ec) read(opcode& op, DynamicBuffer& dynabuf, error_code& ec)
{ {
static_assert(is_SyncStream<next_layer_type>::value, static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
frame_info fi; frame_info fi;
for(;;) for(;;)
{ {
read_frame(fi, streambuf, ec); read_frame(fi, dynabuf, ec);
if(ec) if(ec)
break; break;
op = fi.op; op = fi.op;
@@ -511,47 +514,51 @@ read(opcode& op, Streambuf& streambuf, error_code& ec)
} }
template<class NextLayer> template<class NextLayer>
template<class Streambuf, class ReadHandler> template<class DynamicBuffer, class ReadHandler>
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
stream<NextLayer>:: stream<NextLayer>::
async_read(opcode& op, async_read(opcode& op,
Streambuf& streambuf, ReadHandler&& handler) DynamicBuffer& dynabuf, ReadHandler&& handler)
{ {
static_assert(is_AsyncStream<next_layer_type>::value, static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met"); "AsyncStream requirements requirements not met");
static_assert(beast::is_Streambuf<Streambuf>::value, static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
"Streambuf requirements not met"); "DynamicBuffer requirements not met");
beast::async_completion< beast::async_completion<
ReadHandler, void(error_code) ReadHandler, void(error_code)
> completion(handler); > completion(handler);
read_op<Streambuf, decltype(completion.handler)>{ read_op<DynamicBuffer, decltype(completion.handler)>{
completion.handler, *this, op, streambuf}; completion.handler, *this, op, dynabuf};
return completion.result.get(); return completion.result.get();
} }
template<class NextLayer> template<class NextLayer>
template<class Streambuf> template<class DynamicBuffer>
void void
stream<NextLayer>:: stream<NextLayer>::
read_frame(frame_info& fi, Streambuf& streambuf) read_frame(frame_info& fi, DynamicBuffer& dynabuf)
{ {
static_assert(is_SyncStream<next_layer_type>::value, static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
error_code ec; error_code ec;
read_frame(fi, streambuf, ec); read_frame(fi, dynabuf, ec);
if(ec) if(ec)
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer> template<class NextLayer>
template<class Streambuf> template<class DynamicBuffer>
void void
stream<NextLayer>:: stream<NextLayer>::
read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) read_frame(frame_info& fi, DynamicBuffer& dynabuf, error_code& ec)
{ {
static_assert(is_SyncStream<next_layer_type>::value, static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
close_code::value code{}; close_code::value code{};
for(;;) for(;;)
{ {
@@ -630,7 +637,7 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
} }
} }
// read payload // read payload
auto smb = streambuf.prepare( auto smb = dynabuf.prepare(
detail::clamp(rd_need_)); detail::clamp(rd_need_));
auto const bytes_transferred = auto const bytes_transferred =
stream_.read_some(smb, ec); stream_.read_some(smb, ec);
@@ -652,7 +659,7 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
break; break;
} }
} }
streambuf.commit(bytes_transferred); dynabuf.commit(bytes_transferred);
fi.op = rd_opcode_; fi.op = rd_opcode_;
fi.fin = rd_fh_.fin && rd_need_ == 0; fi.fin = rd_fh_.fin && rd_need_ == 0;
return; return;
@@ -686,21 +693,21 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
} }
template<class NextLayer> template<class NextLayer>
template<class Streambuf, class ReadHandler> template<class DynamicBuffer, class ReadHandler>
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
stream<NextLayer>:: stream<NextLayer>::
async_read_frame(frame_info& fi, async_read_frame(frame_info& fi,
Streambuf& streambuf, ReadHandler&& handler) DynamicBuffer& dynabuf, ReadHandler&& handler)
{ {
static_assert(is_AsyncStream<next_layer_type>::value, static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met"); "AsyncStream requirements requirements not met");
static_assert(beast::is_Streambuf<Streambuf>::value, static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
"Streambuf requirements not met"); "DynamicBuffer requirements not met");
beast::async_completion< beast::async_completion<
ReadHandler, void(error_code)> completion(handler); ReadHandler, void(error_code)> completion(handler);
read_frame_op<Streambuf, decltype(completion.handler)>{ read_frame_op<DynamicBuffer, decltype(completion.handler)>{
completion.handler, *this, fi, streambuf}; completion.handler, *this, fi, dynabuf};
return completion.result.get(); return completion.result.get();
} }
@@ -712,6 +719,9 @@ write(ConstBufferSequence const& buffers)
{ {
static_assert(is_SyncStream<next_layer_type>::value, static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
error_code ec; error_code ec;
write(buffers, ec); write(buffers, ec);
if(ec) if(ec)
@@ -774,6 +784,9 @@ write_frame(bool fin, ConstBufferSequence const& buffers)
{ {
static_assert(is_SyncStream<next_layer_type>::value, static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
error_code ec; error_code ec;
write_frame(fin, buffers, ec); write_frame(fin, buffers, ec);
if(ec) if(ec)
@@ -951,8 +964,7 @@ build_response(http::request_v1<Body, Headers> const& req)
return err("Missing Host"); return err("Missing Host");
if(! req.headers.exists("Sec-WebSocket-Key")) if(! req.headers.exists("Sec-WebSocket-Key"))
return err("Missing Sec-WebSocket-Key"); return err("Missing Sec-WebSocket-Key");
if(! rfc2616::token_in_list( if(! http::token_list{req.headers["Upgrade"]}.exists("websocket"))
req.headers["Upgrade"], "websocket"))
return err("Missing websocket Upgrade token"); return err("Missing websocket Upgrade token");
{ {
auto const version = auto const version =
@@ -1005,8 +1017,7 @@ do_response(http::response_v1<Body, Headers> const& res,
return fail(); return fail();
if(! is_upgrade(res)) if(! is_upgrade(res))
return fail(); return fail();
if(! rfc2616::ci_equal( if(! http::token_list{res.headers["Upgrade"]}.exists("websocket"))
res.headers["Upgrade"], "websocket"))
return fail(); return fail();
if(! res.headers.exists("Sec-WebSocket-Accept")) if(! res.headers.exists("Sec-WebSocket-Accept"))
return fail(); return fail();

View File

@@ -120,7 +120,7 @@ decorate(Decorator&& d)
This setting only affects the behavior of HTTP requests that This setting only affects the behavior of HTTP requests that
implicitly or explicitly ask for a keepalive. For HTTP requests implicitly or explicitly ask for a keepalive. For HTTP requests
that indicate the connection should be closed, the connection is that indicate the connection should be closed, the connection is
closed as per rfc2616. closed as per rfc7230.
The default setting is to close connections after a failed The default setting is to close connections after a failed
upgrade request. upgrade request.

View File

@@ -12,7 +12,7 @@
#include <beast/websocket/detail/stream_base.hpp> #include <beast/websocket/detail/stream_base.hpp>
#include <beast/http/message_v1.hpp> #include <beast/http/message_v1.hpp>
#include <beast/http/string_body.hpp> #include <beast/http/string_body.hpp>
#include <beast/core/streambuf_readstream.hpp> #include <beast/core/dynabuf_readstream.hpp>
#include <beast/core/async_completion.hpp> #include <beast/core/async_completion.hpp>
#include <beast/core/detail/get_lowest_layer.hpp> #include <beast/core/detail/get_lowest_layer.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@@ -78,7 +78,7 @@ struct frame_info
@par Concepts @par Concepts
@b `AsyncStream`, @b `AsyncStream`,
@b `Decorator`, @b `Decorator`,
@b `Streambuf`, @b `DynamicBuffer`,
@b `SyncStream` @b `SyncStream`
*/ */
template<class NextLayer> template<class NextLayer>
@@ -86,7 +86,7 @@ class stream : public detail::stream_base
{ {
friend class stream_test; friend class stream_test;
streambuf_readstream<NextLayer, streambuf> stream_; dynabuf_readstream<NextLayer, streambuf> stream_;
public: public:
/// The type of the next layer. /// The type of the next layer.
@@ -95,12 +95,12 @@ public:
/// The type of the lowest layer. /// The type of the lowest layer.
using lowest_layer_type = using lowest_layer_type =
#if GENERATING_DOCS #if GENERATING_DOCS
implementation_defined; implementation_defined;
#else #else
typename beast::detail::get_lowest_layer< typename beast::detail::get_lowest_layer<
next_layer_type>::type; next_layer_type>::type;
#endif #endif
/** Move-construct a stream. /** Move-construct a stream.
@@ -404,12 +404,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`. manner equivalent to using `boost::asio::io_service::post`.
*/ */
template<class AcceptHandler> template<class AcceptHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
AcceptHandler, void(error_code)>::result_type AcceptHandler, void(error_code)>::result_type
#endif #endif
async_accept(AcceptHandler&& handler); async_accept(AcceptHandler&& handler);
/** Read and respond to a WebSocket HTTP Upgrade request. /** Read and respond to a WebSocket HTTP Upgrade request.
@@ -530,12 +530,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`. manner equivalent to using `boost::asio::io_service::post`.
*/ */
template<class ConstBufferSequence, class AcceptHandler> template<class ConstBufferSequence, class AcceptHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
AcceptHandler, void(error_code)>::result_type AcceptHandler, void(error_code)>::result_type
#endif #endif
async_accept(ConstBufferSequence const& buffers, async_accept(ConstBufferSequence const& buffers,
AcceptHandler&& handler); AcceptHandler&& handler);
@@ -567,7 +567,7 @@ public:
@throws boost::system::system_error Thrown on failure. @throws boost::system::system_error Thrown on failure.
*/ */
// VFALCO TODO This should also take a streambuf with any leftover bytes. // VFALCO TODO This should also take a DynamicBuffer with any leftover bytes.
template<class Body, class Headers> template<class Body, class Headers>
void void
accept(http::request_v1<Body, Headers> const& request); accept(http::request_v1<Body, Headers> const& request);
@@ -647,12 +647,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`. manner equivalent to using `boost::asio::io_service::post`.
*/ */
template<class Body, class Headers, class AcceptHandler> template<class Body, class Headers, class AcceptHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
AcceptHandler, void(error_code)>::result_type AcceptHandler, void(error_code)>::result_type
#endif #endif
async_accept(http::request_v1<Body, Headers> const& request, async_accept(http::request_v1<Body, Headers> const& request,
AcceptHandler&& handler); AcceptHandler&& handler);
@@ -784,12 +784,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`. manner equivalent to using `boost::asio::io_service::post`.
*/ */
template<class HandshakeHandler> template<class HandshakeHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
HandshakeHandler, void(error_code)>::result_type HandshakeHandler, void(error_code)>::result_type
#endif #endif
async_handshake(boost::string_ref const& host, async_handshake(boost::string_ref const& host,
boost::string_ref const& resource, HandshakeHandler&& h); boost::string_ref const& resource, HandshakeHandler&& h);
@@ -895,12 +895,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`. manner equivalent to using `boost::asio::io_service::post`.
*/ */
template<class CloseHandler> template<class CloseHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
CloseHandler, void(error_code)>::result_type CloseHandler, void(error_code)>::result_type
#endif #endif
async_close(close_reason const& cr, CloseHandler&& handler); async_close(close_reason const& cr, CloseHandler&& handler);
/** Send a WebSocket ping frame. /** Send a WebSocket ping frame.
@@ -973,12 +973,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`. manner equivalent to using `boost::asio::io_service::post`.
*/ */
template<class PingHandler> template<class PingHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
PingHandler, void(error_code)>::result_type PingHandler, void(error_code)>::result_type
#endif #endif
async_ping(ping_data const& payload, PingHandler&& handler); async_ping(ping_data const& payload, PingHandler&& handler);
/** Read a message from the stream. /** Read a message from the stream.
@@ -1007,14 +1007,14 @@ public:
@param op A value to receive the message type. @param op A value to receive the message type.
This object must remain valid until the handler is called. This object must remain valid until the handler is called.
@param streambuf A stream buffer to hold the message data. @param dynabuf A dynamic buffer to hold the message data after
This object must remain valid until the handler is called. any masking or decompression has been applied.
@throws boost::system::system_error Thrown on failure. @throws boost::system::system_error Thrown on failure.
*/ */
template<class Streambuf> template<class DynamicBuffer>
void void
read(opcode& op, Streambuf& streambuf); read(opcode& op, DynamicBuffer& dynabuf);
/** Read a message from the stream. /** Read a message from the stream.
@@ -1042,15 +1042,14 @@ public:
@param op A value to receive the message type. @param op A value to receive the message type.
This object must remain valid until the handler is called. This object must remain valid until the handler is called.
@param streambuf A stream buffer to hold the message data. @param dynabuf A dynamic buffer to hold the message data after
This object must remain valid until the handler is called. any masking or decompression has been applied.
@param ec Set to indicate what error occurred, if any. @param ec Set to indicate what error occurred, if any.
*/ */
template<class Streambuf> template<class DynamicBuffer>
void void
read(opcode& op, read(opcode& op, DynamicBuffer& dynabuf, error_code& ec);
Streambuf& streambuf, error_code& ec);
/** Start an asynchronous operation to read a message from the stream. /** Start an asynchronous operation to read a message from the stream.
@@ -1086,8 +1085,9 @@ public:
@param op A value to receive the message type. @param op A value to receive the message type.
This object must remain valid until the handler is called. This object must remain valid until the handler is called.
@param streambuf A stream buffer to hold the message data. @param dynabuf A dynamic buffer to hold the message data after
This object must remain valid until the handler is called. any masking or decompression has been applied. This object must
remain valid until the handler is called.
@param handler The handler to be called when the read operation @param handler The handler to be called when the read operation
completes. Copies will be made of the handler as required. The completes. Copies will be made of the handler as required. The
@@ -1102,15 +1102,14 @@ public:
this function. Invocation of the handler will be performed in a this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`. manner equivalent to using `boost::asio::io_service::post`.
*/ */
template<class Streambuf, class ReadHandler> template<class DynamicBuffer, class ReadHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
#endif #endif
async_read(opcode& op, async_read(opcode& op, DynamicBuffer& dynabuf, ReadHandler&& handler);
Streambuf& streambuf, ReadHandler&& handler);
/** Read a message frame from the stream. /** Read a message frame from the stream.
@@ -1141,13 +1140,14 @@ public:
@param fi An object to store metadata about the message. @param fi An object to store metadata about the message.
@param streambuf A stream buffer to hold the message data. @param dynabuf A dynamic buffer to hold the message data after
any masking or decompression has been applied.
@throws boost::system::system_error Thrown on failure. @throws boost::system::system_error Thrown on failure.
*/ */
template<class Streambuf> template<class DynamicBuffer>
void void
read_frame(frame_info& fi, Streambuf& streambuf); read_frame(frame_info& fi, DynamicBuffer& dynabuf);
/** Read a message frame from the stream. /** Read a message frame from the stream.
@@ -1178,13 +1178,14 @@ public:
@param fi An object to store metadata about the message. @param fi An object to store metadata about the message.
@param streambuf A stream buffer to hold the message data. @param dynabuf A dynamic buffer to hold the message data after
any masking or decompression has been applied.
@param ec Set to indicate what error occurred, if any. @param ec Set to indicate what error occurred, if any.
*/ */
template<class Streambuf> template<class DynamicBuffer>
void void
read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec); read_frame(frame_info& fi, DynamicBuffer& dynabuf, error_code& ec);
/** Start an asynchronous operation to read a message frame from the stream. /** Start an asynchronous operation to read a message frame from the stream.
@@ -1225,7 +1226,7 @@ public:
@param fi An object to store metadata about the message. @param fi An object to store metadata about the message.
This object must remain valid until the handler is called. This object must remain valid until the handler is called.
@param streambuf A stream buffer to hold the message data after @param dynabuf A dynamic buffer to hold the message data after
any masking or decompression has been applied. This object must any masking or decompression has been applied. This object must
remain valid until the handler is called. remain valid until the handler is called.
@@ -1242,15 +1243,15 @@ public:
this function. Invocation of the handler will be performed in a this function. Invocation of the handler will be performed in a
manner equivalent to using boost::asio::io_service::post(). manner equivalent to using boost::asio::io_service::post().
*/ */
template<class Streambuf, class ReadHandler> template<class DynamicBuffer, class ReadHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
#endif #endif
async_read_frame(frame_info& fi, async_read_frame(frame_info& fi,
Streambuf& streambuf, ReadHandler&& handler); DynamicBuffer& dynabuf, ReadHandler&& handler);
/** Write a message to the stream. /** Write a message to the stream.
@@ -1369,12 +1370,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`. manner equivalent to using `boost::asio::io_service::post`.
*/ */
template<class ConstBufferSequence, class WriteHandler> template<class ConstBufferSequence, class WriteHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
WriteHandler, void(error_code)>::result_type WriteHandler, void(error_code)>::result_type
#endif #endif
async_write(ConstBufferSequence const& buffers, async_write(ConstBufferSequence const& buffers,
WriteHandler&& handler); WriteHandler&& handler);
@@ -1478,12 +1479,12 @@ public:
); @endcode ); @endcode
*/ */
template<class ConstBufferSequence, class WriteHandler> template<class ConstBufferSequence, class WriteHandler>
#if GENERATING_DOCS #if GENERATING_DOCS
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<
WriteHandler, void(error_code)>::result_type WriteHandler, void(error_code)>::result_type
#endif #endif
async_write_frame(bool fin, async_write_frame(bool fin,
ConstBufferSequence const& buffers, WriteHandler&& handler); ConstBufferSequence const& buffers, WriteHandler&& handler);
@@ -1495,8 +1496,8 @@ private:
template<class Handler> class response_op; template<class Handler> class response_op;
template<class Buffers, class Handler> class write_op; template<class Buffers, class Handler> class write_op;
template<class Buffers, class Handler> class write_frame_op; template<class Buffers, class Handler> class write_frame_op;
template<class Streambuf, class Handler> class read_op; template<class DynamicBuffer, class Handler> class read_op;
template<class Streambuf, class Handler> class read_frame_op; template<class DynamicBuffer, class Handler> class read_frame_op;
void void
reset(); reset();

View File

@@ -0,0 +1,39 @@
# Remember that this blacklist file is GLOBAL to all sanitizers
# Be therefore extremely careful when considering to add a sanitizer
# filter here instead of using a runtime suppression
#
# Remember also that filters here quite literally completely
# remove instrumentation altogether, so filtering here means
# that sanitizers such as tsan will false positive on problems
# introduced by code filtered here.
#
# The main use for this file is ubsan, as it's the only sanitizer
# without a runtime suppression facility.
#
# Be ESPECIALLY careful when filtering out entire source files!
# Try if at all possible to filter only functions using fun:regex
# Remember you must use mangled symbol names with fun:regex
#### Compile time filters for ubsan ####
## The well known ubsan failure in libstdc++ extant for years :)
# Line 96:24: runtime error: load of value 4294967221, which is not a valid value for type 'std::_Ios_Fmtflags'
fun:*_Ios_Fmtflags*
# boost/any.hpp:259:16: runtime error: downcast of address 0x000004392e70 which does not point to an object of type 'any::holder<int>'
fun:*any_cast*
# boost/lexical_cast.hpp:1625:43: runtime error: downcast of address 0x7fbb4fffbce8 which does not point to an object of type 'buffer_t' (aka 'parser_buf<std::basic_streambuf<char, char_traits<char> >, char>')
fun:*shl_input_streamable*
#### Compile time filters for asan ####
#### Compile time filters for msan ####
#### Compile time filters for tsan ####

View File

@@ -1,8 +1,11 @@
#!/bin/bash -u #!/usr/bin/env bash
# We use set -e and bash with -u to bail on first non zero exit code of any # We use set -e to bail on first non zero exit code of any processes launched
# processes launched or upon any unbound variable # and -x to exit upon any unbound variable. -x will output command lines used
# (with variable expansion)
set -eux
# brew install bash (4) to get this working on OSX!
shopt -s globstar shopt -s globstar
set -ex
################################## ENVIRONMENT ################################# ################################## ENVIRONMENT #################################
@@ -17,36 +20,90 @@ else
export PATH=$VALGRIND_ROOT/bin:$LCOV_ROOT/usr/bin:$PATH export PATH=$VALGRIND_ROOT/bin:$LCOV_ROOT/usr/bin:$PATH
fi fi
MAIN_BRANCH="0"
# For builds not triggered by a pull request TRAVIS_BRANCH is the name of the
# branch currently being built; whereas for builds triggered by a pull request
# it is the name of the branch targeted by the pull request (in many cases this
# will be master).
if [[ $TRAVIS_BRANCH == "master" || $TRAVIS_BRANCH == "develop" ]]; then
MAIN_BRANCH="1"
fi
num_jobs="1"
if [[ $(uname) == "Darwin" ]]; then
num_jobs=$(sysctl -n hw.physicalcpu)
elif [[ $(uname -s) == "Linux" ]]; then
# CircleCI returns 32 phys procs, but 2 virt proc
num_proc_units=$(nproc)
# Physical cores
num_jobs=$(lscpu -p | grep -v '^#' | sort -u -t, -k 2,4 | wc -l)
if (("$num_proc_units" < "$num_jobs")); then
num_jobs=$num_proc_units
fi
fi
echo "using toolset: $CC" echo "using toolset: $CC"
echo "using variant: $VARIANT" echo "using variant: $VARIANT"
echo "using address-model: $ADDRESS_MODEL" echo "using address-model: $ADDRESS_MODEL"
echo "using PATH: $PATH" echo "using PATH: $PATH"
echo "using MAIN_BRANCH: $MAIN_BRANCH"
echo "using BOOST_ROOT: $BOOST_ROOT"
#################################### HELPERS ################################### #################################### HELPERS ###################################
function run_tests_with_gdb { function run_tests_with_debugger {
for x in bin/**/*-tests; do scripts/run-with-gdb.sh "$x"; done for x in bin/**/$VARIANT/**/*-tests; do
scripts/run-with-debugger.sh "$x"
done
} }
function run_tests { function run_tests {
for x in bin/**/*-tests; do "$x"; done for x in bin/**/$VARIANT/**/*-tests; do
$x
done
} }
num_procs=1 function run_tests_with_valgrind {
if [[ $(uname) == "Darwin" ]]; then for x in bin/**/$VARIANT/**/*-tests; do
num_procs=$(sysctl -n hw.ncpu) if [[ $(basename $x) == "bench-tests" ]]; then
elif [[ $(uname -s) == "Linux" ]]; then $x
num_procs=$(lscpu -p | grep -v '^#' | sort -u -t, -k 2,4 | wc -l) # physical cores else
virt_num_procs=$(nproc) # CircleCI returns 32 phys procs, but 1 virt proc # TODO --max-stackframe=8388608
if (("$virt_num_procs" < "$num_procs")); then # see: https://travis-ci.org/vinniefalco/Beast/jobs/132486245
num_procs=$virt_num_procs valgrind --error-exitcode=1 "$x"
fi fi
fi done
}
function build_beast { function build_beast {
$BOOST_ROOT/bjam toolset=$CC \ $BOOST_ROOT/bjam toolset=$CC \
variant=$VARIANT \ variant=$VARIANT \
address-model=$ADDRESS_MODEL -j${num_procs} address-model=$ADDRESS_MODEL \
-j${num_jobs}
}
function run_autobahn_test_suite {
# Run autobahn tests
wsecho=$(find bin -name "websocket-echo" | grep /$VARIANT/)
nohup $wsecho&
# We need to wait a while so wstest can connect!
sleep 5
cd scripts && wstest -m fuzzingclient
cd ..
# Show the output
cat nohup.out
rm nohup.out
# Show what jobs are running
jobs
# Wait a while for things to wind down before issuing a kill
sleep 5
# Kill it gracefully
kill -INT %1
# Wait for all the jobs to finish
wait
# Parse the test results, with python>=2.5<3 script
python scripts/parseautobahn.py scripts/autoresults/index.json
} }
##################################### BUILD #################################### ##################################### BUILD ####################################
@@ -62,24 +119,12 @@ if [[ $VARIANT == "coverage" ]]; then
lcov --no-external -c -i -d . -o baseline.info > /dev/null lcov --no-external -c -i -d . -o baseline.info > /dev/null
# Perform test # Perform test
run_tests if [[ $MAIN_BRANCH == "1" ]]; then
run_tests_with_valgrind
# Run autobahn tests run_autobahn_test_suite
export SERVER=$(find . -name "websocket-echo") else
nohup $SERVER& run_tests
fi
# We need to wait a while so wstest can connect!
sleep 5
cd scripts && wstest -m fuzzingclient
cd ..
# Show the output
cat nohup.out
rm nohup.out
jobs
sleep 5
# Kill it gracefully
kill -INT %1
wait
# Create test coverage data file # Create test coverage data file
lcov --no-external -c -d . -o testrun.info > /dev/null lcov --no-external -c -d . -o testrun.info > /dev/null
@@ -88,20 +133,13 @@ if [[ $VARIANT == "coverage" ]]; then
lcov -a baseline.info -a testrun.info -o lcov-all.info > /dev/null lcov -a baseline.info -a testrun.info -o lcov-all.info > /dev/null
# Extract only include/beast, and don\'t report on examples/test # Extract only include/beast, and don\'t report on examples/test
lcov -e "lcov-all.info" "*/include/beast/*" -o lcov.info > /dev/null lcov -e "lcov-all.info" "$PWD/include/beast/*" -o lcov.info > /dev/null
~/.local/bin/codecov -X gcov ~/.local/bin/codecov -X gcov
else cat lcov.info | node_modules/.bin/coveralls
# TODO: make a function
run_tests_with_gdb
if [[ $VARIANT == "debug" ]]; then # Clean up these stragglers so BOOST_ROOT cache is clean
for x in bin/**/*-tests; do find $BOOST_ROOT/bin.v2 -name "*.gcda" | xargs rm -f
# if [[ $x != "bench-tests" ]]; then else
valgrind --error-exitcode=1 "$x" run_tests_with_debugger
## declare -i RESULT=$RESULT + $?
# fi
done
echo
fi
fi fi

View File

@@ -1,4 +1,4 @@
#!/bin/bash -u #!/usr/bin/env bash
# Assumptions: # Assumptions:
# 1) BOOST_ROOT and BOOST_URL are already defined, # 1) BOOST_ROOT and BOOST_URL are already defined,
# and contain valid values. # and contain valid values.
@@ -6,7 +6,7 @@
# folder name internal to boost's .tar.gz # folder name internal to boost's .tar.gz
# When testing you can force a boost build by clearing travis caches: # When testing you can force a boost build by clearing travis caches:
# https://travis-ci.org/ripple/rippled/caches # https://travis-ci.org/ripple/rippled/caches
set -e set -eu
if [ ! -d "$BOOST_ROOT/lib" ] if [ ! -d "$BOOST_ROOT/lib" ]
then then
wget $BOOST_URL -O /tmp/boost.tar.gz wget $BOOST_URL -O /tmp/boost.tar.gz

View File

@@ -1,6 +1,9 @@
#!/bin/bash -u #!/usr/bin/env bash
# Exit if anything fails. # Exit if anything fails.
set -e set -eux
HERE=$PWD
# Override gcc version to $GCC_VER. # Override gcc version to $GCC_VER.
# Put an appropriate symlink at the front of the path. # Put an appropriate symlink at the front of the path.
mkdir -v $HOME/bin mkdir -v $HOME/bin
@@ -44,3 +47,8 @@ tar xfvz lcov-1.12.tar.gz -C $HOME
# Set install path # Set install path
mkdir -p $LCOV_ROOT mkdir -p $LCOV_ROOT
cd $HOME/lcov-1.12 && make install PREFIX=$LCOV_ROOT cd $HOME/lcov-1.12 && make install PREFIX=$LCOV_ROOT
# Install coveralls reporter
cd $HERE
mkdir -p node_modules
npm install coveralls

4
src/beast/scripts/install-valgrind.sh Normal file → Executable file
View File

@@ -1,7 +1,7 @@
#!/bin/bash -u #!/usr/bin/env bash
# Assumptions: # Assumptions:
# 1) VALGRIND_ROOT is already defined, and contains a valid values # 1) VALGRIND_ROOT is already defined, and contains a valid values
set -e set -eu
if [ ! -d "$VALGRIND_ROOT/bin" ] if [ ! -d "$VALGRIND_ROOT/bin" ]
then then
# These are specified in the addons/apt section of .travis.yml # These are specified in the addons/apt section of .travis.yml

View File

@@ -0,0 +1,43 @@
import os
import json
import sys
VARIANT = os.environ.get('VARIANT', 'release')
EXPECTED_BEHAVIOR = ('OK', 'UNIMPLEMENTED', 'INFORMATIONAL')
EXPECTED_BEHAVIOR_CLOSE = ('OK', 'INFORMATIONAL')
WARNINGS = ("peer did not respond (in time) in closing handshake", )
args = sys.argv[1:]
fn = os.path.abspath(args[0])
indexPath = os.path.dirname(fn)
relativeToIndex = lambda f: os.path.join(indexPath, f)
print "index", fn
failures = []
warnings = []
with open(fn, 'r') as fh:
index = json.load(fh)
for servername, serverResults in index.items():
for test in serverResults:
result = serverResults[test]
if ((result['behavior'] not in EXPECTED_BEHAVIOR) or
result['behaviorClose'] not in EXPECTED_BEHAVIOR_CLOSE):
with open(relativeToIndex(result['reportfile'])) as rh:
report = json.load(rh)
if (report.get('wasNotCleanReason', '') in WARNINGS and
VARIANT != 'release'):
warnings.append(report)
else:
failures.append(report)
if warnings:
print >> sys.stderr, json.dumps(warnings, indent=2)
print >> sys.stderr, 'there was %s warnings' % len(warnings)
if failures:
print >> sys.stderr, json.dumps(failures, indent=2)
print >> sys.stderr, 'there was %s failures' % len(failures)
sys.exit(1)

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -eu
if [[ $(uname) == "Darwin" ]]; then
# -o runs after loading the binary
# -k runs after any crash
# We use a ghetto appromixation of --return-child-result, exiting with
# 1 on a crash
lldb --batch \
-o 'run' \
-k 'thread backtrace all' \
-k 'script import os; os._exit(1)' \
$@
else
gdb --silent \
--batch \
--return-child-result \
-ex="set print thread-events off" \
-ex=run \
-ex="thread apply all bt full" \
--args $@
fi

View File

@@ -21,6 +21,7 @@ unit-test core-tests :
core/buffer_concepts.cpp core/buffer_concepts.cpp
core/buffers_adapter.cpp core/buffers_adapter.cpp
core/consuming_buffers.cpp core/consuming_buffers.cpp
core/dynabuf_readstream.cpp
core/error.cpp core/error.cpp
core/handler_alloc.cpp core/handler_alloc.cpp
core/handler_concepts.cpp core/handler_concepts.cpp
@@ -30,9 +31,8 @@ unit-test core-tests :
core/static_string.cpp core/static_string.cpp
core/stream_concepts.cpp core/stream_concepts.cpp
core/streambuf.cpp core/streambuf.cpp
core/streambuf_readstream.cpp
core/to_string.cpp core/to_string.cpp
core/write_streambuf.cpp core/write_dynabuf.cpp
core/detail/base64.cpp core/detail/base64.cpp
core/detail/empty_base_optimization.cpp core/detail/empty_base_optimization.cpp
core/detail/get_lowest_layer.cpp core/detail/get_lowest_layer.cpp
@@ -41,6 +41,7 @@ unit-test core-tests :
unit-test http-tests : unit-test http-tests :
../extras/beast/unit_test/main.cpp ../extras/beast/unit_test/main.cpp
http/basic_dynabuf_body.cpp
http/basic_headers.cpp http/basic_headers.cpp
http/basic_parser_v1.cpp http/basic_parser_v1.cpp
http/body_type.cpp http/body_type.cpp
@@ -54,7 +55,6 @@ unit-test http-tests :
http/read.cpp http/read.cpp
http/reason.cpp http/reason.cpp
http/resume_context.cpp http/resume_context.cpp
http/rfc2616.cpp
http/rfc7230.cpp http/rfc7230.cpp
http/status.cpp http/status.cpp
http/streambuf_body.cpp http/streambuf_body.cpp

View File

@@ -15,6 +15,7 @@ add_executable (core-tests
buffer_concepts.cpp buffer_concepts.cpp
buffers_adapter.cpp buffers_adapter.cpp
consuming_buffers.cpp consuming_buffers.cpp
dynabuf_readstream.cpp
error.cpp error.cpp
handler_alloc.cpp handler_alloc.cpp
handler_concepts.cpp handler_concepts.cpp
@@ -24,9 +25,8 @@ add_executable (core-tests
static_string.cpp static_string.cpp
stream_concepts.cpp stream_concepts.cpp
streambuf.cpp streambuf.cpp
streambuf_readstream.cpp
to_string.cpp to_string.cpp
write_streambuf.cpp write_dynabuf.cpp
detail/base64.cpp detail/base64.cpp
detail/empty_base_optimization.cpp detail/empty_base_optimization.cpp
detail/get_lowest_layer.cpp detail/get_lowest_layer.cpp

View File

@@ -6,7 +6,7 @@
// //
// Test that header file is self-contained. // Test that header file is self-contained.
#include <beast/core/streambuf_readstream.hpp> #include <beast/core/dynabuf_readstream.hpp>
#include <beast/core/streambuf.hpp> #include <beast/core/streambuf.hpp>
#include <beast/test/fail_stream.hpp> #include <beast/test/fail_stream.hpp>
@@ -17,11 +17,11 @@
namespace beast { namespace beast {
class streambuf_readstream_test class dynabuf_readstream_test
: public unit_test::suite : public unit_test::suite
, public test::enable_yield_to , public test::enable_yield_to
{ {
using self = streambuf_readstream_test; using self = dynabuf_readstream_test;
public: public:
void testSpecialMembers() void testSpecialMembers()
@@ -29,16 +29,16 @@ public:
using socket_type = boost::asio::ip::tcp::socket; using socket_type = boost::asio::ip::tcp::socket;
boost::asio::io_service ios; boost::asio::io_service ios;
{ {
streambuf_readstream<socket_type, streambuf> srs(ios); dynabuf_readstream<socket_type, streambuf> srs(ios);
streambuf_readstream<socket_type, streambuf> srs2(std::move(srs)); dynabuf_readstream<socket_type, streambuf> srs2(std::move(srs));
srs = std::move(srs2); srs = std::move(srs2);
expect(&srs.get_io_service() == &ios); expect(&srs.get_io_service() == &ios);
expect(&srs.get_io_service() == &srs2.get_io_service()); expect(&srs.get_io_service() == &srs2.get_io_service());
} }
{ {
socket_type sock(ios); socket_type sock(ios);
streambuf_readstream<socket_type&, streambuf> srs(sock); dynabuf_readstream<socket_type&, streambuf> srs(sock);
streambuf_readstream<socket_type&, streambuf> srs2(std::move(srs)); dynabuf_readstream<socket_type&, streambuf> srs2(std::move(srs));
} }
} }
@@ -55,7 +55,7 @@ public:
{ {
test::fail_stream< test::fail_stream<
test::string_stream> fs(n, ios_, ", world!"); test::string_stream> fs(n, ios_, ", world!");
streambuf_readstream< dynabuf_readstream<
decltype(fs)&, streambuf> srs(fs); decltype(fs)&, streambuf> srs(fs);
srs.buffer().commit(buffer_copy( srs.buffer().commit(buffer_copy(
srs.buffer().prepare(5), buffer("Hello", 5))); srs.buffer().prepare(5), buffer("Hello", 5)));
@@ -73,7 +73,7 @@ public:
{ {
test::fail_stream< test::fail_stream<
test::string_stream> fs(n, ios_, ", world!"); test::string_stream> fs(n, ios_, ", world!");
streambuf_readstream< dynabuf_readstream<
decltype(fs)&, streambuf> srs(fs); decltype(fs)&, streambuf> srs(fs);
srs.capacity(3); srs.capacity(3);
srs.buffer().commit(buffer_copy( srs.buffer().commit(buffer_copy(
@@ -92,7 +92,7 @@ public:
{ {
test::fail_stream< test::fail_stream<
test::string_stream> fs(n, ios_, ", world!"); test::string_stream> fs(n, ios_, ", world!");
streambuf_readstream< dynabuf_readstream<
decltype(fs)&, streambuf> srs(fs); decltype(fs)&, streambuf> srs(fs);
srs.buffer().commit(buffer_copy( srs.buffer().commit(buffer_copy(
srs.buffer().prepare(5), buffer("Hello", 5))); srs.buffer().prepare(5), buffer("Hello", 5)));
@@ -111,7 +111,7 @@ public:
{ {
test::fail_stream< test::fail_stream<
test::string_stream> fs(n, ios_, ", world!"); test::string_stream> fs(n, ios_, ", world!");
streambuf_readstream< dynabuf_readstream<
decltype(fs)&, streambuf> srs(fs); decltype(fs)&, streambuf> srs(fs);
srs.capacity(3); srs.capacity(3);
srs.buffer().commit(buffer_copy( srs.buffer().commit(buffer_copy(
@@ -137,7 +137,7 @@ public:
} }
}; };
BEAST_DEFINE_TESTSUITE(streambuf_readstream,core,beast); BEAST_DEFINE_TESTSUITE(dynabuf_readstream,core,beast);
} // beast } // beast

View File

@@ -6,14 +6,14 @@
// //
// Test that header file is self-contained. // Test that header file is self-contained.
#include <beast/core/write_streambuf.hpp> #include <beast/core/write_dynabuf.hpp>
#include <beast/core/streambuf.hpp> #include <beast/core/streambuf.hpp>
#include <beast/unit_test/suite.hpp> #include <beast/unit_test/suite.hpp>
namespace beast { namespace beast {
class write_streambuf_test : public beast::unit_test::suite class write_dynabuf_test : public beast::unit_test::suite
{ {
public: public:
void run() override void run() override
@@ -31,6 +31,6 @@ public:
} }
}; };
BEAST_DEFINE_TESTSUITE(write_streambuf,core,beast); BEAST_DEFINE_TESTSUITE(write_dynabuf,core,beast);
} // beast } // beast

View File

@@ -6,7 +6,10 @@ GroupSources(test/http "/")
add_executable (http-tests add_executable (http-tests
${BEAST_INCLUDES} ${BEAST_INCLUDES}
message_fuzz.hpp
fail_parser.hpp
../../extras/beast/unit_test/main.cpp ../../extras/beast/unit_test/main.cpp
basic_dynabuf_body.cpp
basic_headers.cpp basic_headers.cpp
basic_parser_v1.cpp basic_parser_v1.cpp
body_type.cpp body_type.cpp
@@ -20,7 +23,6 @@ add_executable (http-tests
read.cpp read.cpp
reason.cpp reason.cpp
resume_context.cpp resume_context.cpp
rfc2616.cpp
rfc7230.cpp rfc7230.cpp
status.cpp status.cpp
streambuf_body.cpp streambuf_body.cpp
@@ -35,6 +37,7 @@ endif()
add_executable (bench-tests add_executable (bench-tests
${BEAST_INCLUDES} ${BEAST_INCLUDES}
nodejs_parser.hpp
../../extras/beast/unit_test/main.cpp ../../extras/beast/unit_test/main.cpp
nodejs_parser.cpp nodejs_parser.cpp
parser_bench.cpp parser_bench.cpp

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/basic_dynabuf_body.hpp>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,112 @@
//
// 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_TEST_FAIL_PARSER_HPP
#define BEAST_HTTP_TEST_FAIL_PARSER_HPP
#include <beast/http/basic_parser_v1.hpp>
#include <beast/test/fail_counter.hpp>
namespace beast {
namespace http {
template<bool isRequest>
class fail_parser
: public basic_parser_v1<isRequest, fail_parser<isRequest>>
{
test::fail_counter& fc_;
std::uint64_t content_length_ = no_content_length;
int body_rv_ = 0;
public:
std::string body;
template<class... Args>
explicit
fail_parser(test::fail_counter& fc, Args&&... args)
: fc_(fc)
{
}
void
on_body_rv(int rv)
{
body_rv_ = rv;
}
// valid on successful parse
std::uint64_t
content_length() const
{
return content_length_;
}
void on_start(error_code& ec)
{
fc_.fail(ec);
}
void on_method(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_uri(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_reason(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_request(error_code& ec)
{
fc_.fail(ec);
}
void on_response(error_code& ec)
{
fc_.fail(ec);
}
void on_field(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_value(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
int on_headers(std::uint64_t content_length, error_code& ec)
{
if(fc_.fail(ec))
return 0;
content_length_ = content_length;
return body_rv_;
}
void on_body(boost::string_ref const& s, error_code& ec)
{
if(fc_.fail(ec))
return;
body.append(s.data(), s.size());
}
void on_complete(error_code& ec)
{
fc_.fail(ec);
}
};
} // http
} // beast
#endif

View File

@@ -9,6 +9,7 @@
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/headers.hpp> #include <beast/http/headers.hpp>
#include <beast/http/string_body.hpp>
#include <beast/unit_test/suite.hpp> #include <beast/unit_test/suite.hpp>
#include <type_traits> #include <type_traits>
@@ -126,9 +127,30 @@ public:
} }
} }
void testSwap()
{
message<true, string_body, headers> m1;
message<true, string_body, headers> m2;
m1.url = "u";
m1.body = "1";
m1.headers.insert("h", "v");
m2.method = "G";
m2.body = "2";
swap(m1, m2);
expect(m1.method == "G");
expect(m2.method.empty());
expect(m1.url.empty());
expect(m2.url == "u");
expect(m1.body == "2");
expect(m2.body == "1");
expect(! m1.headers.exists("h"));
expect(m2.headers.exists("h"));
}
void run() override void run() override
{ {
testConstruction(); testConstruction();
testSwap();
} }
}; };

View File

@@ -8,7 +8,7 @@
#ifndef BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP #ifndef BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP
#define BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP #define BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP
#include <beast/core/write_streambuf.hpp> #include <beast/core/write_dynabuf.hpp>
#include <beast/http/detail/basic_parser_v1.hpp> #include <beast/http/detail/basic_parser_v1.hpp>
#include <cstdint> #include <cstdint>
#include <random> #include <random>

View File

@@ -8,6 +8,8 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <beast/http/message_v1.hpp> #include <beast/http/message_v1.hpp>
#include <beast/http/headers.hpp>
#include <beast/http/string_body.hpp>
#include <beast/unit_test/suite.hpp> #include <beast/unit_test/suite.hpp>
#include <beast/http/empty_body.hpp> #include <beast/http/empty_body.hpp>
@@ -78,10 +80,36 @@ public:
expect(! is_keep_alive(m)); expect(! is_keep_alive(m));
} }
void testSwap()
{
message_v1<false, string_body, headers> m1;
message_v1<false, string_body, headers> m2;
m1.status = 200;
m1.version = 10;
m1.body = "1";
m1.headers.insert("h", "v");
m2.status = 404;
m2.reason = "OK";
m2.body = "2";
m2.version = 11;
swap(m1, m2);
expect(m1.status == 404);
expect(m2.status == 200);
expect(m1.reason == "OK");
expect(m2.reason.empty());
expect(m1.version == 11);
expect(m2.version == 10);
expect(m1.body == "2");
expect(m2.body == "1");
expect(! m1.headers.exists("h"));
expect(m2.headers.exists("h"));
}
void run() override void run() override
{ {
testFreeFunctions(); testFreeFunctions();
testPrepare(); testPrepare();
testSwap();
} }
}; };

View File

@@ -11,7 +11,7 @@
#include "nodejs-parser/http_parser.h" #include "nodejs-parser/http_parser.h"
#include <beast/http/message_v1.hpp> #include <beast/http/message_v1.hpp>
#include <beast/http/rfc2616.hpp> #include <beast/http/rfc7230.hpp>
#include <beast/core/buffer_concepts.hpp> #include <beast/core/buffer_concepts.hpp>
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
@@ -611,7 +611,7 @@ nodejs_basic_parser<Derived>::check_header()
{ {
if (! value_.empty()) if (! value_.empty())
{ {
rfc2616::trim_right_in_place(value_); //detail::trim(value_);
call_on_field(field_, value_, call_on_field(field_, value_,
has_on_field<Derived>{}); has_on_field<Derived>{});
field_.clear(); field_.clear();

View File

@@ -38,8 +38,8 @@ public:
check("http", parse_error::bad_version); check("http", parse_error::bad_version);
check("http", parse_error::bad_crlf); check("http", parse_error::bad_crlf);
check("http", parse_error::bad_request); check("http", parse_error::bad_request);
check("http", parse_error::bad_status_code);
check("http", parse_error::bad_status); check("http", parse_error::bad_status);
check("http", parse_error::bad_reason);
check("http", parse_error::bad_field); check("http", parse_error::bad_field);
check("http", parse_error::bad_value); check("http", parse_error::bad_value);
check("http", parse_error::bad_content_length); check("http", parse_error::bad_content_length);

View File

@@ -8,6 +8,8 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <beast/http/read.hpp> #include <beast/http/read.hpp>
#include "fail_parser.hpp"
#include <beast/http/headers.hpp> #include <beast/http/headers.hpp>
#include <beast/http/streambuf_body.hpp> #include <beast/http/streambuf_body.hpp>
#include <beast/test/fail_stream.hpp> #include <beast/test/fail_stream.hpp>
@@ -24,77 +26,6 @@ class read_test
, public test::enable_yield_to , public test::enable_yield_to
{ {
public: public:
template<bool isRequest>
class fail_parser
: public basic_parser_v1<isRequest, fail_parser<isRequest>>
{
test::fail_counter& fc_;
public:
template<class... Args>
explicit
fail_parser(test::fail_counter& fc, Args&&... args)
: fc_(fc)
{
}
void on_start(error_code& ec)
{
fc_.fail(ec);
}
void on_method(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_uri(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_reason(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_request(error_code& ec)
{
fc_.fail(ec);
}
void on_response(error_code& ec)
{
fc_.fail(ec);
}
void on_field(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_value(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
int on_headers(error_code& ec)
{
fc_.fail(ec);
return 0;
}
void on_body(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_complete(error_code& ec)
{
fc_.fail(ec);
}
};
template<bool isRequest> template<bool isRequest>
void failMatrix(const char* s, yield_context do_yield) void failMatrix(const char* s, yield_context do_yield)
{ {

View File

@@ -1,115 +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)
//
// Test that header file is self-contained.
#include <beast/http/rfc2616.hpp>
#include <beast/unit_test/suite.hpp>
#include <string>
#include <vector>
namespace beast {
namespace rfc2616 {
namespace test {
class rfc2616_test : public beast::unit_test::suite
{
public:
void
checkSplit(std::string const& s,
std::vector <std::string> const& expected)
{
auto const parsed = split_commas(s.begin(), s.end());
expect (parsed == expected);
}
void testSplit()
{
checkSplit("", {});
checkSplit(" ", {});
checkSplit(" ", {});
checkSplit("\t", {});
checkSplit(" \t ", {});
checkSplit(",", {});
checkSplit(",,", {});
checkSplit(" ,", {});
checkSplit(" , ,", {});
checkSplit("x", {"x"});
checkSplit(" x", {"x"});
checkSplit(" \t x", {"x"});
checkSplit("x ", {"x"});
checkSplit("x \t", {"x"});
checkSplit(" \t x \t ", {"x"});
checkSplit("\"\"", {});
checkSplit(" \"\"", {});
checkSplit("\"\" ", {});
checkSplit("\"x\"", {"x"});
checkSplit("\" \"", {" "});
checkSplit("\" x\"", {" x"});
checkSplit("\"x \"", {"x "});
checkSplit("\" x \"", {" x "});
checkSplit("\"\tx \"", {"\tx "});
checkSplit("x,y", {"x", "y"});
checkSplit("x ,\ty ", {"x", "y"});
checkSplit("x, y, z", {"x","y","z"});
checkSplit("x, \"y\", z", {"x","y","z"});
checkSplit(",,x,,\"y\",,", {"x","y"});
}
void
checkIter(std::string const& s,
std::vector<std::string> const& expected)
{
std::vector<std::string> got;
for(auto const& v : make_list(s))
got.emplace_back(v);
expect(got == expected);
}
void
testIter()
{
checkIter("x", {"x"});
checkIter(" x", {"x"});
checkIter("x\t", {"x"});
checkIter("\tx ", {"x"});
checkIter(",x", {"x"});
checkIter("x,", {"x"});
checkIter(",x,", {"x"});
checkIter(" , x\t,\t", {"x"});
checkIter("x,y", {"x", "y"});
checkIter("x, ,y ", {"x", "y"});
checkIter("\"x\"", {"x"});
}
void
testList()
{
expect(token_in_list("x", "x"));
expect(token_in_list("x,y", "x"));
expect(token_in_list("x,y", "y"));
expect(token_in_list("x, y ", "y"));
expect(token_in_list("x", "X"));
expect(token_in_list("Y", "y"));
expect(token_in_list("close, keepalive", "close"));
expect(token_in_list("close, keepalive", "keepalive"));
}
void
run()
{
testSplit();
testIter();
testList();
}
};
BEAST_DEFINE_TESTSUITE(rfc2616,http,beast);
} // test
} // rfc2616
} // beast

View File

@@ -7,3 +7,240 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <beast/http/rfc7230.hpp> #include <beast/http/rfc7230.hpp>
#include <beast/http/detail/rfc7230.hpp>
#include <beast/unit_test/suite.hpp>
#include <string>
#include <vector>
namespace beast {
namespace http {
namespace test {
class rfc7230_test : public beast::unit_test::suite
{
public:
static
std::string
fmt(std::string const& s)
{
return '\'' + s + '\'';
}
static
std::string
str(boost::string_ref const& s)
{
return std::string(s.data(), s.size());
}
static
std::string
str(param_list const& c)
{
std::string s;
for(auto const& p : c)
{
s.push_back(';');
s.append(str(p.first));
s.push_back('=');
s.append(str(p.second));
}
return s;
}
void
testParamList()
{
auto const ce =
[&](std::string const& s)
{
auto const got = str(param_list{s});
expect(got == s, fmt(got));
};
auto const cs =
[&](std::string const& s, std::string const& good)
{
ce(good);
auto const got = str(param_list{s});
ce(got);
expect(got == good, fmt(got));
};
auto const cq =
[&](std::string const& s, std::string const& good)
{
auto const got = str(param_list{s});
expect(got == good, fmt(got));
};
ce("");
cs(" ;\t i =\t 1 \t", ";i=1");
cq("\t; \t xyz=1 ; ijk=\"q\\\"t\"", ";xyz=1;ijk=q\"t");
// invalid strings
cs(";", "");
cs(";,", "");
cs(";xy", "");
cs(";xy", "");
cs(";xy ", "");
cs(";xy,", "");
cq(";x=,", "");
cq(";xy=\"", "");
cq(";xy=\"\x7f", "");
cq(";xy=\"\\", "");
cq(";xy=\"\\\x01\"", "");
}
static
std::string
str(ext_list const& ex)
{
std::string s;
for(auto const& e : ex)
{
if(! s.empty())
s += ',';
s.append(str(e.first));
s += str(e.second);
}
return s;
}
void
testExtList()
{
auto const ce =
[&](std::string const& s)
{
auto const got = str(ext_list{s});
expect(got == s, fmt(got));
};
auto const cs =
[&](std::string const& s, std::string const& good)
{
ce(good);
auto const got = str(ext_list{s});
ce(got);
expect(got == good, fmt(got));
};
auto const cq =
[&](std::string const& s, std::string const& good)
{
auto const got = str(ext_list{s});
expect(got == good, fmt(got));
};
/*
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
ext = token param-list
param-list = *( OWS ";" OWS param )
param = token OWS "=" OWS ( token / quoted-string )
*/
ce("");
cs(",", "");
cs(", ", "");
cs(",\t", "");
cs(", \t", "");
cs(" ", "");
cs(" ,", "");
cs("\t,", "");
cs("\t , \t", "");
cs(",,", "");
cs(" , \t,, \t,", "");
ce("a");
ce("ab");
ce("a,b");
cs(" a ", "a");
cs("\t a, b\t , c\t", "a,b,c");
cs("a; \t i\t=\t \t1\t ", "a;i=1");
ce("a;i=1;j=2;k=3");
ce("a;i=1;j=2;k=3,b;i=4;j=5;k=6");
cq("ab;x=\" \"", "ab;x= ");
cq("ab;x=\"\\\"\"", "ab;x=\"");
expect(ext_list{"a,b;i=1,c;j=2;k=3"}.exists("A"));
expect(ext_list{"a,b;i=1,c;j=2;k=3"}.exists("b"));
expect(! ext_list{"a,b;i=1,c;j=2;k=3"}.exists("d"));
// invalid strings
cs("i j", "i");
cs(";", "");
}
static
std::string
str(token_list const& c)
{
bool first = true;
std::string s;
for(auto const& p : c)
{
if(! first)
s.push_back(',');
s.append(str(p));
first = false;
}
return s;
}
void
testTokenList()
{
auto const ce =
[&](std::string const& s)
{
auto const got = str(token_list{s});
expect(got == s, fmt(got));
};
auto const cs =
[&](std::string const& s, std::string const& good)
{
ce(good);
auto const got = str(token_list{s});
ce(got);
expect(got == good, fmt(got));
};
cs("", "");
cs(" ", "");
cs(" ", "");
cs("\t", "");
cs(" \t ", "");
cs(",", "");
cs(",,", "");
cs(" ,", "");
cs(" , ,", "");
cs(" x", "x");
cs(" \t x", "x");
cs("x ", "x");
cs("x \t", "x");
cs(" \t x \t ", "x");
ce("x,y");
cs("x ,\ty ", "x,y");
cs("x, y, z", "x,y,z");
expect(token_list{"a,b,c"}.exists("A"));
expect(token_list{"a,b,c"}.exists("b"));
expect(! token_list{"a,b,c"}.exists("d"));
// invalid
cs("x y", "x");
}
void
run()
{
testParamList();
testExtList();
testTokenList();
}
};
BEAST_DEFINE_TESTSUITE(rfc7230,http,beast);
} // test
} // http
} // beast