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 Include="..\..\src\beast\include\beast\core\detail\stream_concepts.hpp">
</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 Include="..\..\src\beast\include\beast\core\error.hpp">
</ClInclude>
@@ -308,12 +310,12 @@
</None>
<None Include="..\..\src\beast\include\beast\core\impl\consuming_buffers.ipp">
</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>
<None Include="..\..\src\beast\include\beast\core\impl\static_streambuf.ipp">
</None>
<None Include="..\..\src\beast\include\beast\core\impl\streambuf_readstream.ipp">
</None>
<ClInclude Include="..\..\src\beast\include\beast\core\placeholders.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\prepare_buffers.hpp">
@@ -324,16 +326,16 @@
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\streambuf.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\streambuf_readstream.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\stream_concepts.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\to_string.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\write_streambuf.hpp">
<ClInclude Include="..\..\src\beast\include\beast\core\write_dynabuf.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\basic_dynabuf_body.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\basic_headers.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\basic_parser_v1.hpp">
@@ -348,6 +350,8 @@
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\detail\has_content_length.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\detail\rfc7230.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\empty_body.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\headers.hpp">
@@ -360,6 +364,8 @@
</None>
<None Include="..\..\src\beast\include\beast\http\impl\read.ipp">
</None>
<None Include="..\..\src\beast\include\beast\http\impl\rfc7230.ipp">
</None>
<None Include="..\..\src\beast\include\beast\http\impl\write.ipp">
</None>
<ClInclude Include="..\..\src\beast\include\beast\http\message.hpp">
@@ -376,8 +382,6 @@
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\resume_context.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\rfc2616.hpp">
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http\rfc7230.hpp">
</ClInclude>
<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">
<Filter>include\beast\core\detail</Filter>
</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>
</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">
<Filter>include\beast\core</Filter>
</ClInclude>
@@ -645,15 +648,15 @@
<None Include="..\..\src\beast\include\beast\core\impl\consuming_buffers.ipp">
<Filter>include\beast\core\impl</Filter>
</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">
<Filter>include\beast\core\impl</Filter>
</None>
<None Include="..\..\src\beast\include\beast\core\impl\static_streambuf.ipp">
<Filter>include\beast\core\impl</Filter>
</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">
<Filter>include\beast\core</Filter>
</ClInclude>
@@ -669,21 +672,21 @@
<ClInclude Include="..\..\src\beast\include\beast\core\streambuf.hpp">
<Filter>include\beast\core</Filter>
</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">
<Filter>include\beast\core</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\core\to_string.hpp">
<Filter>include\beast\core</Filter>
</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>
</ClInclude>
<ClInclude Include="..\..\src\beast\include\beast\http.hpp">
<Filter>include\beast</Filter>
</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">
<Filter>include\beast\http</Filter>
</ClInclude>
@@ -705,6 +708,9 @@
<ClInclude Include="..\..\src\beast\include\beast\http\detail\has_content_length.hpp">
<Filter>include\beast\http\detail</Filter>
</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">
<Filter>include\beast\http</Filter>
</ClInclude>
@@ -723,6 +729,9 @@
<None Include="..\..\src\beast\include\beast\http\impl\read.ipp">
<Filter>include\beast\http\impl</Filter>
</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">
<Filter>include\beast\http\impl</Filter>
</None>
@@ -747,9 +756,6 @@
<ClInclude Include="..\..\src\beast\include\beast\http\resume_context.hpp">
<Filter>include\beast\http</Filter>
</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">
<Filter>include\beast\http</Filter>
</ClInclude>

View File

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

View File

@@ -14,13 +14,6 @@ env:
packages: &gcc5_pkgs
- gcc-5
- g++-5
# - gcc-5-multilib
# - g++-5-multilib
# - gcc-multilib
# - g++-multilib
# - libgd2-xpm
# - ia32-libs
# - ia32-libs-multiarch
- python-software-properties
- libssl-dev
- libffi-dev
@@ -52,20 +45,7 @@ packages: &clang38_pkgs
matrix:
include:
# GCC/Debug
# - 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
# GCC/Coverage
- compiler: gcc
env: GCC_VER=5 VARIANT=coverage ADDRESS_MODEL=64
addons: &ao_gcc5
@@ -73,36 +53,25 @@ matrix:
sources: ['ubuntu-toolchain-r-test']
packages: *gcc5_pkgs
# # Clang/Debug
# - compiler: clang
# env: GCC_VER=5 VARIANT=debug CLANG_VER=3.8 ADDRESS_MODEL=64
# addons: &ao_clang38
# apt:
# sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8']
# packages: *clang38_pkgs
# # GCC/Debug
# - compiler: gcc
# env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=64
# addons: *ao_gcc5
# branches: # NOTE: this does NOT work, though it SHOULD
# - master
# - develop
# # Clang/Release
# - compiler: clang
# env: GCC_VER=5 VARIANT=release CLANG_VER=3.8 ADDRESS_MODEL=64
# addons: *ao_clang38
# Clang/AddressSanitizer
# Clang/UndefinedBehaviourSanitizer
- 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
apt:
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8']
packages: *clang38_pkgs
# Clang/MemorySanitizer
# 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
# Clang/AddressSanitizer
- 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
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
[![Build Status](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) [![Documentation]
[![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://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/license-boost-brightgreen.svg)](LICENSE_1_0.txt)
@@ -14,6 +16,11 @@ Requirements:
* C++11 or greater
* 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:
```C++
#include <beast/to_string.hpp>

View File

@@ -1,21 +1,12 @@
* 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
* Use enum instead of bool in isRequest
* move version to a subclass of message
Docs:
* Include Example program listings in the docs
* Fix index in docs
* Figure out why namespace rfc2616 is included in docs
(currently disabled via GENERATING_DOCS macro)
* 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
e.g. in basic_streambuf move constructor members
* Don't put using namespace at file scope in examples,
@@ -27,15 +18,12 @@ Core:
* Complete allocator testing in basic_streambuf
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
* invokable unit test
* Don't try to read requests into empty_body
* Give callers control over the http request/response used during handshake
* Investigate poor autobahn results in Debug builds
* 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
* Make request_type, response_type public APIs,
use in stream member function signatures
@@ -44,24 +32,25 @@ HTTP:
* Define Parser concept in HTTP
- Need parse version of read() so caller can set parser options
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
of eof return value from write and async_write
* http type_check, e.g. is_WritableBody
* More fine grained parser errors
* HTTP parser size limit with test (configurable?)
* HTTP parser trailers with test
* Decode chunk encoding parameters
* URL parser, strong URL character checking in HTTP parser
* Update for rfc7230
* Consider rename to MessageBody concept
* Fix prepare() calling content_length() without init()
* Use construct,destroy allocator routines in 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
* 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
* 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]
[include types/Body.qbk]
[include types/BufferSequence.qbk]
[include types/DynamicBuffer.qbk]
[include types/Field.qbk]
[include types/FieldSequence.qbk]
[include types/Parser.qbk]
[include types/Reader.qbk]
[include types/Streambuf.qbk]
[include types/Streams.qbk]
[include types/Writer.qbk]
[endsect]

View File

@@ -25,13 +25,13 @@ libraries:
* Let library users make the important decisions such as how to
allocate memory or how to leverage flow control.
Beast formalizes the [link beast.types.Streambuf [*`Streambuf`]] concept,
and relies heavily on the Boost.Asio [*`ConstBufferSequence`] and
[*`MutableBufferSequence`] concepts for passing buffers to functions.
The authors have found the `Streambuf` and buffer sequence interfaces
to be optimal for interacting with Asio, and for other tasks such as
incremental parsing of data in buffers (for example, parsing websocket
frames stored in a [link beast.ref.static_streambuf `static_streambuf`]).
Beast uses the [link beast.types.DynamicBuffer [*`DynamicBuffer`]] concept
presented in the Netwoking TS, and relies heavily on the Boost.Asio
[*`ConstBufferSequence`] and [*`MutableBufferSequence`] concepts for passing
buffers to functions. The authors have found the dynamic buffer and buffer
sequence interfaces to be optimal for interacting with Asio, and for other
tasks such as incremental parsing of data in buffers (for example, parsing
websocket frames stored in a [link beast.ref.static_streambuf `static_streambuf`]).
During the development of Beast the authors have studied other software
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";
```
* [link beast.ref.http__basic_streambuf_body [*`basic_streambuf_body`:]] A body with a
`value_type` of `streambuf`. A streambuf is an efficient storage object which
uses multiple octet arrays of varying lengths to represent data. Here is
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;
}
```
* [link beast.ref.http__streambuf_body [*`streambuf_body`:]] A body with a
`value_type` of [link beast.ref.streambuf `streambuf`]: an efficient storage
object which uses multiple octet arrays of varying lengths to represent data.
[heading Sockets]
@@ -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
a single byte at a time which is unsuitable for performance reasons). To
store and re-use these extra bytes on subsequent messages, the read interface
requires an additional 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
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
[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);
...
@@ -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
[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.
[endsect]

View File

@@ -29,9 +29,9 @@
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__basic_dynabuf_body">basic_dynabuf_body</link></member>
<member><link linkend="beast.ref.http__basic_headers">basic_headers</link></member>
<member><link linkend="beast.ref.http__basic_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__headers">headers</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__string_body">string_body</link></member>
</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>
<simplelist type="vert" columns="1">
<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__prepare">prepare</link></member>
<member><link linkend="beast.ref.http__read">read</link></member>
<member><link linkend="beast.ref.http__swap">swap</link></member>
<member><link linkend="beast.ref.http__write">write</link></member>
</simplelist>
<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.buffers_adapter">buffers_adapter</link></member>
<member><link linkend="beast.ref.consuming_buffers">consuming_buffers</link></member>
<member><link linkend="beast.ref.dynabuf_readstream">dynabuf_readstream</link></member>
<member><link linkend="beast.ref.error_code">error_code</link></member>
<member><link linkend="beast.ref.handler_alloc">handler_alloc</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_string">static_string</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>
</simplelist>
</entry>
@@ -163,8 +169,8 @@
<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_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_Streambuf">is_Streambuf</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_SyncWriteStream">is_SyncWriteStream</link></member>
@@ -173,10 +179,10 @@
<entry valign="top">
<bridgehead renderas="sect3">Concepts</bridgehead>
<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.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.Streambuf">Streambuf</link></member>
<member><link linkend="beast.types.streams.SyncStream">SyncStream</link></member>
</simplelist>
</entry>

View File

@@ -1552,15 +1552,15 @@
<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: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: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 test="declname = 'Stream' or type = 'class Stream'">
<xsl:text>class ``[link beast.types.streams.Stream [*Stream]]``</xsl:text>
</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:text>class ``[link beast.types.streams.SyncStream [*SyncStream]]``</xsl:text>
</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
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`].
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,
class ReadHandler>
#if GENERATING_DOCS
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
ReadHandler, void(error_code)>::result_type
#endif
#endif
async_read(message_v1<isRequest, Body, Headers>& msg,
ReadHandler&& handler);
@@ -416,12 +416,12 @@ public:
*/
template<bool isRequest, class Body, class Headers,
class WriteHandler>
#if GENERATING_DOCS
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
WriteHandler, void(error_code)>::result_type
#endif
#endif
async_write(message_v1<isRequest, Body, Headers> const& msg,
WriteHandler&& handler);

View File

@@ -13,10 +13,78 @@
namespace beast {
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.
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
{
@@ -31,10 +99,10 @@ public:
@param The 0-based index of the operation to fail on or after.
*/
explicit
fail_counter(std::size_t n = 0)
fail_counter(std::size_t n,
error_code ev = make_error_code(fail_error))
: n_(n)
, ec_(boost::system::errc::make_error_code(
boost::system::errc::errc_t::invalid_argument))
, ec_(ev)
{
}
@@ -66,4 +134,14 @@ public:
} // test
} // beast
namespace boost {
namespace system {
template<>
struct is_error_code_enum<beast::test::error>
{
static bool const value = true;
};
} // system
} // boost
#endif

View File

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

View File

@@ -24,8 +24,8 @@
#include <beast/core/static_string.hpp>
#include <beast/core/stream_concepts.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/write_streambuf.hpp>
#include <beast/core/write_dynabuf.hpp>
#endif

View File

@@ -18,14 +18,14 @@
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
of varying sizes. Additional character array objects are appended to
the sequence to accommodate changes in the size of the character
sequence.
@note Meets the requirements of @b Streambuf.
@note Meets the requirements of @b `DynamicBuffer`.
@tparam Allocator The allocator to use for managing memory.
*/
@@ -202,26 +202,37 @@ public:
basic_streambuf(std::size_t alloc_size = 1024,
Allocator const& alloc = allocator_type{});
/// Get the associated allocator
/// Returns a copy of the associated allocator.
allocator_type
get_allocator() const
{
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
max_size() const
{
return std::numeric_limits<std::size_t>::max();
}
/// Get the size of the input sequence.
size_type
size() const
{
return in_size_;
}
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
std::size_t
capacity() const;
/** 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.
@@ -239,22 +250,11 @@ public:
void
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.
void
consume(size_type n);
/// Clear everything.
void
clear();
// Helper for read_until
// Helper for boost::asio::read_until
template<class OtherAllocator>
friend
std::size_t
@@ -262,6 +262,9 @@ public:
OtherAllocator> const& streambuf, std::size_t max_size);
private:
void
clear();
void
move_assign(basic_streambuf& other, std::false_type);
@@ -277,20 +280,17 @@ private:
void
delete_list();
std::size_t
prepare_size() const;
void
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.
@return The stream buffer.
@return A reference to the @ref basic_streambuf.
*/
template<class Allocator, class T>
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`.
template<class T>
#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
#endif

View File

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

View File

@@ -8,76 +8,96 @@
#ifndef 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 <algorithm>
#include <type_traits>
#include <cctype>
#include <iterator>
#include <string>
#include <utility>
#include <array>
#include <cstdint>
namespace beast {
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
{
static bool const is_transparent = true;
template<class S1, class S2>
bool
operator()(boost::string_ref const& lhs,
boost::string_ref const& rhs) const noexcept
operator()(S1 const& lhs, S2 const& rhs) const noexcept
{
using std::begin;
using std::end;
auto const s1 = string_helper(lhs);
auto const s2 = string_helper(rhs);
return std::lexicographical_compare(
begin(lhs), end(lhs), begin(rhs), end(rhs),
begin(s1), end(s1), begin(s2), end(s2),
[](char lhs, char rhs)
{
return std::tolower(lhs) < std::tolower(rhs);
return tolower(lhs) < tolower(rhs);
}
);
}
};
inline
bool
ci_equal(std::pair<const char*, std::size_t> lhs,
std::pair<const char*, std::size_t> rhs)
// Case-insensitive equal
struct ci_equal_pred
{
if(lhs.second != rhs.second)
return false;
return std::equal (lhs.first, lhs.first + lhs.second,
rhs.first,
[] (char lhs, char rhs)
bool
operator()(char c1, char c2) const noexcept
{
return std::tolower(lhs) == std::tolower(rhs);
return tolower(c1) == tolower(c2);
}
);
}
};
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
// Case-insensitive equal
template<class S1, class S2>
bool
ci_equal(String1 const& lhs, String2 const& rhs)
ci_equal(S1 const& lhs, S2 const& rhs)
{
return ci_equal(view(lhs), view(rhs));
return boost::range::equal(
string_helper(lhs), string_helper(rhs),
ci_equal_pred{});
}
} // detail

View File

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

View File

@@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_STREAMBUF_READSTREAM_HPP
#define BEAST_STREAMBUF_READSTREAM_HPP
#ifndef BEAST_DYNABUF_READSTREAM_HPP
#define BEAST_DYNABUF_READSTREAM_HPP
#include <beast/core/async_completion.hpp>
#include <beast/core/buffer_concepts.hpp>
@@ -22,11 +22,11 @@
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
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.
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,
// leaving excess bytes behind for the next call.
//
template<class Streambuf>
template<class DynamicBuffer>
void process_http_message(
streambuf_readstream<Streambuf>& stream)
dynabuf_readstream<DynamicBuffer>& stream)
{
// Read up to and including the end of the HTTP
// headers, leaving the sequence in the stream's
@@ -85,26 +85,24 @@ namespace beast {
@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>
class streambuf_readstream
template<class Stream, class DynamicBuffer>
class dynabuf_readstream
{
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
using error_code = boost::system::error_code;
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
template<class Buffers, class Handler>
class read_some_op;
Streambuf sb_;
DynamicBuffer sb_;
std::size_t capacity_ = 0;
Stream next_layer_;
public:
/// The type of the internal buffer
using streambuf_type = Streambuf;
using dynabuf_type = DynamicBuffer;
/// The type of the next layer.
using next_layer_type =
@@ -124,14 +122,14 @@ public:
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
streambuf_readstream(streambuf_readstream&&) = default;
dynabuf_readstream(dynabuf_readstream&&) = default;
/** Move assignment.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
streambuf_readstream& operator=(streambuf_readstream&&) = default;
dynabuf_readstream& operator=(dynabuf_readstream&&) = default;
/** Construct the wrapping stream.
@@ -139,7 +137,7 @@ public:
*/
template<class... Args>
explicit
streambuf_readstream(Args&&... args);
dynabuf_readstream(Args&&... args);
/// Get a reference to the next layer.
next_layer_type&
@@ -176,7 +174,7 @@ public:
by causing the internal buffer size to increase beyond
the caller defined maximum.
*/
Streambuf&
DynamicBuffer&
buffer()
{
return sb_;
@@ -189,7 +187,7 @@ public:
by causing the internal buffer size to increase beyond
the caller defined maximum.
*/
Streambuf const&
DynamicBuffer const&
buffer() const
{
return sb_;
@@ -277,6 +275,6 @@ public:
} // beast
#include <beast/core/impl/streambuf_readstream.ipp>
#include <beast/core/impl/dynabuf_readstream.ipp>
#endif

View File

@@ -8,7 +8,7 @@
#ifndef 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 <cassert>
#include <exception>
@@ -527,6 +527,28 @@ basic_streambuf<Allocator>::basic_streambuf(
"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>
auto
basic_streambuf<Allocator>::prepare(size_type n) ->
@@ -646,14 +668,6 @@ basic_streambuf<Allocator>::commit(size_type n)
debug_check();
}
template<class Allocator>
auto
basic_streambuf<Allocator>::data() const ->
const_buffers_type
{
return const_buffers_type(*this);
}
template<class Allocator>
void
basic_streambuf<Allocator>::consume(size_type n)
@@ -717,7 +731,8 @@ basic_streambuf<Allocator>::consume(size_type n)
template<class Allocator>
void
basic_streambuf<Allocator>::clear()
basic_streambuf<Allocator>::
clear()
{
delete_list();
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>
void
basic_streambuf<Allocator>::debug_check() const
@@ -855,7 +855,7 @@ std::size_t
read_size_helper(basic_streambuf<
Allocator> const& streambuf, std::size_t max_size)
{
auto const avail = streambuf.prepare_size();
auto const avail = streambuf.capacity() - streambuf.size();
if(avail == 0)
return std::min(max_size,
std::max<std::size_t>(512, streambuf.alloc_size_));
@@ -866,7 +866,7 @@ template<class Alloc, class T>
basic_streambuf<Alloc>&
operator<<(basic_streambuf<Alloc>& streambuf, T const& t)
{
detail::write_streambuf(streambuf, t);
detail::write_dynabuf(streambuf, t);
return streambuf;
}

View File

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

View File

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

View File

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

View File

@@ -25,15 +25,16 @@ namespace beast {
@return A string representing the contents of the input area.
@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
#if ! GENERATING_DOCS
,class = std::enable_if<is_ConstBufferSequence<
ConstBufferSequence>::value>
#endif
>
template<class ConstBufferSequence>
#if GENERATING_DOCS
std::string
#else
typename std::enable_if<
is_ConstBufferSequence<ConstBufferSequence>::value,
std::string>::type
#endif
to_string(ConstBufferSequence const& buffers)
{
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)
//
#ifndef BEAST_WRITE_STREAMBUF_HPP
#define BEAST_WRITE_STREAMBUF_HPP
#ifndef BEAST_WRITE_DYNABUF_HPP
#define BEAST_WRITE_DYNABUF_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 <utility>
namespace beast {
/** Write to a Streambuf.
/** Write to a @b `DynamicBuffer`.
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:
@li `boost::asio::const_buffer`
@@ -33,29 +33,29 @@ namespace beast {
For all types not listed above, the function will invoke
`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
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.
@throws unspecified Any exceptions thrown by `boost::lexical_cast`.
@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
void
#else
typename std::enable_if<is_Streambuf<Streambuf>::value>::type
typename std::enable_if<is_DynamicBuffer<DynamicBuffer>::value>::type
#endif
write(Streambuf& streambuf, Args const&... args)
write(DynamicBuffer& dynabuf, Args const&... args)
{
detail::write_streambuf(streambuf, args...);
detail::write_dynabuf(dynabuf, args...);
}
} // beast

View File

@@ -20,7 +20,7 @@
#include <beast/http/read.hpp>
#include <beast/http/reason.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/string_body.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.
The container preserves the order of insertion of fields with
different names. For fields with the same name, the implementation
concatenates values inserted with duplicate names as per the
rules in rfc2616 section 4.2.
concatenates values inserted with duplicate names as per rfc7230.
@note Meets the requirements of @b `FieldSequence`.
*/
@@ -393,18 +392,16 @@ public:
*/
// VFALCO TODO Consider allowing rvalue references for std::move?
void
insert(boost::string_ref const& name,
boost::string_ref const& value);
insert(boost::string_ref const& name, boost::string_ref value);
/** Insert a field value.
If a field value already exists the new value will be
extended as per RFC2616 Section 4.2.
*/
template<class T,
class = typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type>
void
template<class T>
typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type
insert(boost::string_ref name, T const& value)
{
insert(name,
@@ -414,21 +411,19 @@ public:
/** Replace a field value.
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
replace(boost::string_ref const& name,
boost::string_ref const& value);
replace(boost::string_ref const& name, boost::string_ref value);
/** Replace a field value.
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,
class = typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type>
void
template<class T>
typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type
replace(boost::string_ref const& name, T const& value)
{
replace(name,

View File

@@ -25,17 +25,65 @@ namespace http {
namespace parse_flag {
enum values
{
chunked = 1 << 0,
connection_keep_alive = 1 << 1,
connection_close = 1 << 2,
connection_upgrade = 1 << 3,
trailing = 1 << 4,
upgrade = 1 << 5,
skipbody = 1 << 6,
contentlength = 1 << 7
chunked = 1,
connection_keep_alive = 2,
connection_close = 4,
connection_upgrade = 8,
trailing = 16,
upgrade = 32,
skipbody = 64,
contentlength = 128
};
} // parse_flag
/** Headers maximum size option.
Sets the maximum number of cumulative bytes allowed
including all header octets. A value of zero indicates
no limit on the number of header octets
The default headers maximum size is 16KB (16,384 bytes).
@note Objects of this type are passed to @ref set_option.
*/
struct headers_max_size
{
std::size_t value;
explicit
headers_max_size(std::size_t v)
: value(v)
{
}
};
/** Body maximum size option.
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.
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.
@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.
@@ -126,90 +174,12 @@ enum values
presented with request or response message.
*/
template<bool isRequest, class Derived>
class basic_parser_v1
class basic_parser_v1 : public detail::parser_base
{
private:
using self = basic_parser_v1;
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
{
h_general = 0,
@@ -224,25 +194,36 @@ private:
h_matching_upgrade,
h_connection,
h_content_length0,
h_content_length,
h_content_length_ows,
h_transfer_encoding,
h_upgrade,
h_matching_transfer_encoding_chunked,
h_matching_connection_token_start,
h_matching_transfer_encoding_general,
h_matching_connection_keep_alive,
h_matching_connection_close,
h_matching_connection_upgrade,
h_matching_connection_token,
h_transfer_encoding_chunked,
h_transfer_encoding_chunked_ows,
h_connection_keep_alive,
h_connection_keep_alive_ows,
h_connection_close,
h_connection_close_ows,
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 nread_;
pmf_t cb_;
state s_ : 8;
unsigned flags_ : 8;
@@ -260,10 +241,42 @@ public:
/// Copy assignment.
basic_parser_v1& operator=(basic_parser_v1 const&) = default;
/// Constructor
basic_parser_v1()
/// Default constructor
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.
@@ -373,14 +386,14 @@ public:
@return The number of bytes consumed in the input sequence.
*/
template<class ConstBufferSequence>
#if GENERATING_DOCS
#if GENERATING_DOCS
std::size_t
#else
#else
typename std::enable_if<
! std::is_convertible<ConstBufferSequence,
boost::asio::const_buffer>::value,
std::size_t>::type
#endif
#endif
write(ConstBufferSequence const& buffers, error_code& ec);
/** Write a single buffer of data to the parser.
@@ -413,17 +426,48 @@ private:
}
void
init(std::true_type)
reset(std::true_type)
{
s_ = s_req_start;
}
void
init(std::false_type)
reset(std::false_type)
{
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
needs_eof(std::true_type) const;
@@ -584,7 +628,7 @@ private:
{
template<class T, class R = std::is_same<int,
decltype(std::declval<T>().on_headers(
std::declval<error_code&>()))>>
std::declval<std::uint64_t>(), std::declval<error_code&>()))>>
static R check(int);
template <class>
static std::false_type check(...);
@@ -661,9 +705,17 @@ private:
void call_on_method(error_code& ec,
boost::string_ref const& s)
{
if(! h_max_ || s.size() <= h_left_)
{
h_left_ -= s.size();
call_on_method(ec, s, std::integral_constant<bool,
isRequest && has_on_method<Derived>::value>{});
}
else
{
ec = parse_error::headers_too_big;
}
}
void call_on_uri(error_code& ec,
boost::string_ref const& s, std::true_type)
@@ -678,9 +730,17 @@ private:
void call_on_uri(error_code& ec, boost::string_ref const& s)
{
if(! h_max_ || s.size() <= h_left_)
{
h_left_ -= s.size();
call_on_uri(ec, s, std::integral_constant<bool,
isRequest && has_on_uri<Derived>::value>{});
}
else
{
ec = parse_error::headers_too_big;
}
}
void call_on_reason(error_code& ec,
boost::string_ref const& s, std::true_type)
@@ -695,9 +755,17 @@ private:
void call_on_reason(error_code& ec, boost::string_ref const& s)
{
if(! h_max_ || s.size() <= h_left_)
{
h_left_ -= s.size();
call_on_reason(ec, s, std::integral_constant<bool,
! isRequest && has_on_reason<Derived>::value>{});
}
else
{
ec = parse_error::headers_too_big;
}
}
void call_on_request(error_code& ec, std::true_type)
{
@@ -742,8 +810,16 @@ private:
void call_on_field(error_code& ec, boost::string_ref const& s)
{
if(! h_max_ || s.size() <= h_left_)
{
h_left_ -= s.size();
call_on_field(ec, s, has_on_field<Derived>{});
}
else
{
ec = parse_error::headers_too_big;
}
}
void call_on_value(error_code& ec,
boost::string_ref const& s, std::true_type)
@@ -758,22 +834,32 @@ private:
void call_on_value(error_code& ec, boost::string_ref const& s)
{
if(! h_max_ || s.size() <= h_left_)
{
h_left_ -= s.size();
call_on_value(ec, s, has_on_value<Derived>{});
}
int call_on_headers(error_code& ec, std::true_type)
else
{
return impl().on_headers(ec);
ec = parse_error::headers_too_big;
}
}
int call_on_headers(error_code& ec, std::false_type)
int call_on_headers(error_code& ec,
std::uint64_t content_length, std::true_type)
{
return impl().on_headers(content_length, ec);
}
int call_on_headers(error_code& ec, std::uint64_t, std::false_type)
{
return 0;
}
int call_on_headers(error_code& ec)
{
return call_on_headers(ec, has_on_headers<Derived>{});
return call_on_headers(ec, content_length_,
has_on_headers<Derived>{});
}
void call_on_body(error_code& ec,
@@ -789,8 +875,16 @@ private:
void call_on_body(error_code& ec, boost::string_ref const& s)
{
if(! b_max_ || s.size() <= b_left_)
{
b_left_ -= s.size();
call_on_body(ec, s, has_on_body<Derived>{});
}
else
{
ec = parse_error::body_too_big;
}
}
void call_on_complete(error_code& ec, std::true_type)
{

View File

@@ -17,137 +17,6 @@ namespace beast {
namespace http {
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>
struct parser_str_t
{
@@ -196,6 +65,82 @@ parser_str_t<_>::transfer_encoding[18];
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
} // http
} // 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
#include <beast/http/body_type.hpp>
#include <beast/core/streambuf.hpp>
#include <boost/asio/buffer.hpp>
#include <memory>
#include <string>

View File

@@ -8,6 +8,8 @@
#ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#include <beast/http/detail/rfc7230.hpp>
namespace beast {
namespace http {
@@ -257,12 +259,13 @@ template<class Allocator>
void
basic_headers<Allocator>::
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;
auto const result =
set_.insert_check(name, less{}, d);
if (result.second)
if(result.second)
{
auto const p = alloc_traits::allocate(
this->member(), 1);
@@ -284,8 +287,9 @@ template<class Allocator>
void
basic_headers<Allocator>::
replace(boost::string_ref const& name,
boost::string_ref const& value)
boost::string_ref value)
{
value = detail::trim(value);
erase(name);
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
#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 <boost/optional.hpp>
#include <stdexcept>
@@ -22,13 +22,11 @@ is_keep_alive(message_v1<isRequest, Body, Headers> const& msg)
{
if(msg.version >= 11)
{
if(rfc2616::token_in_list(
msg.headers["Connection"], "close"))
if(token_list{msg.headers["Connection"]}.exists("close"))
return false;
return true;
}
if(rfc2616::token_in_list(
msg.headers["Connection"], "keep-alive"))
if(token_list{msg.headers["Connection"]}.exists("keep-alive"))
return true;
return false;
}
@@ -39,8 +37,7 @@ is_upgrade(message_v1<isRequest, Body, Headers> const& msg)
{
if(msg.version < 11)
return false;
if(rfc2616::token_in_list(
msg.headers["Connection"], "upgrade"))
if(token_list{msg.headers["Connection"]}.exists("upgrade"))
return true;
return false;
}
@@ -129,8 +126,7 @@ prepare(message_v1<isRequest, Body, Headers>& msg,
throw std::invalid_argument(
"prepare called with Content-Length field set");
if(rfc2616::token_in_list(
msg.headers["Transfer-Encoding"], "chunked"))
if(token_list{msg.headers["Transfer-Encoding"]}.exists("chunked"))
throw std::invalid_argument(
"prepare called with Transfer-Encoding: chunked set");
@@ -175,8 +171,8 @@ prepare(message_v1<isRequest, Body, Headers>& msg,
}
// rfc7230 6.7.
if(msg.version < 11 && rfc2616::token_in_list(
msg.headers["Connection"], "upgrade"))
if(msg.version < 11 && token_list{
msg.headers["Connection"]}.exists("upgrade"))
throw std::invalid_argument(
"invalid version for Connection: upgrade");
}

View File

@@ -21,7 +21,7 @@ namespace http {
namespace detail {
template<class Stream,
class Streambuf, class Parser, class Handler>
class DynamicBuffer, class Parser, class Handler>
class parse_op
{
using alloc_type =
@@ -30,7 +30,7 @@ class parse_op
struct data
{
Stream& s;
Streambuf& sb;
DynamicBuffer& db;
Parser& p;
Handler h;
bool started = false;
@@ -39,9 +39,9 @@ class parse_op
template<class DeducedHandler>
data(DeducedHandler&& h_, Stream& s_,
Streambuf& sb_, Parser& p_)
DynamicBuffer& sb_, Parser& p_)
: s(s_)
, sb(sb_)
, db(sb_)
, p(p_)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
@@ -101,9 +101,9 @@ public:
};
template<class Stream,
class Streambuf, class Parser, class Handler>
class DynamicBuffer, class Parser, class Handler>
void
parse_op<Stream, Streambuf, Parser, Handler>::
parse_op<Stream, DynamicBuffer, Parser, Handler>::
operator()(error_code ec, std::size_t bytes_transferred, bool again)
{
auto& d = *d_;
@@ -115,7 +115,7 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
case 0:
{
auto const used =
d.p.write(d.sb.data(), ec);
d.p.write(d.db.data(), ec);
if(ec)
{
// call handler
@@ -126,7 +126,7 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
}
if(used > 0)
d.started = true;
d.sb.consume(used);
d.db.consume(used);
if(d.p.complete())
{
// call handler
@@ -142,8 +142,8 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
case 1:
// read
d.state = 2;
d.s.async_read_some(d.sb.prepare(
read_size_helper(d.sb, 65536)),
d.s.async_read_some(d.db.prepare(
read_size_helper(d.db, 65536)),
std::move(*this));
return;
@@ -172,8 +172,8 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
d.state = 99;
break;
}
d.sb.commit(bytes_transferred);
auto const used = d.p.write(d.sb.data(), ec);
d.db.commit(bytes_transferred);
auto const used = d.p.write(d.db.data(), ec);
if(ec)
{
// call handler
@@ -182,7 +182,7 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
}
if(used > 0)
d.started = true;
d.sb.consume(used);
d.db.consume(used);
if(d.p.complete())
{
// 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,
class Handler>
class read_op
@@ -216,7 +216,7 @@ class read_op
struct data
{
Stream& s;
Streambuf& sb;
DynamicBuffer& db;
message_type& m;
parser_type p;
Handler h;
@@ -226,9 +226,9 @@ class read_op
template<class DeducedHandler>
data(DeducedHandler&& h_, Stream& s_,
Streambuf& sb_, message_type& m_)
DynamicBuffer& sb_, message_type& m_)
: s(s_)
, sb(sb_)
, db(sb_)
, m(m_)
, h(std::forward<DeducedHandler>(h_))
, 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,
class Handler>
void
read_op<Stream, Streambuf, isRequest, Body, Headers, Handler>::
read_op<Stream, DynamicBuffer, isRequest, Body, Headers, Handler>::
operator()(error_code ec, bool again)
{
auto& d = *d_;
@@ -301,7 +301,7 @@ operator()(error_code ec, bool again)
{
case 0:
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;
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
parse(SyncReadStream& stream,
Streambuf& streambuf, Parser& parser)
DynamicBuffer& dynabuf, Parser& parser)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_Parser<Parser>::value,
"Parser requirements not met");
error_code ec;
parse(stream, streambuf, parser, ec);
parse(stream, dynabuf, parser, ec);
if(ec)
throw boost::system::system_error{ec};
}
template<class SyncReadStream, class Streambuf, class Parser>
template<class SyncReadStream, class DynamicBuffer, class Parser>
void
parse(SyncReadStream& stream, Streambuf& streambuf,
parse(SyncReadStream& stream, DynamicBuffer& dynabuf,
Parser& parser, error_code& ec)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_Parser<Parser>::value,
"Parser requirements not met");
bool started = false;
for(;;)
{
auto used =
parser.write(streambuf.data(), ec);
parser.write(dynabuf.data(), ec);
if(ec)
return;
streambuf.consume(used);
dynabuf.consume(used);
if(used > 0)
started = true;
if(parser.complete())
break;
streambuf.commit(stream.read_some(
streambuf.prepare(read_size_helper(
streambuf, 65536)), ec));
dynabuf.commit(stream.read_some(
dynabuf.prepare(read_size_helper(
dynabuf, 65536)), ec));
if(ec && ec != boost::asio::error::eof)
return;
if(ec == boost::asio::error::eof)
@@ -379,86 +379,86 @@ parse(SyncReadStream& stream, Streambuf& streambuf,
}
template<class AsyncReadStream,
class Streambuf, class Parser, class ReadHandler>
class DynamicBuffer, class Parser, class ReadHandler>
typename async_completion<
ReadHandler, void(error_code)>::result_type
async_parse(AsyncReadStream& stream,
Streambuf& streambuf, Parser& parser, ReadHandler&& handler)
DynamicBuffer& dynabuf, Parser& parser, ReadHandler&& handler)
{
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_Parser<Parser>::value,
"Parser requirements not met");
beast::async_completion<ReadHandler,
void(error_code)> completion(handler);
detail::parse_op<AsyncReadStream, Streambuf,
detail::parse_op<AsyncReadStream, DynamicBuffer,
Parser, decltype(completion.handler)>{
completion.handler, stream, streambuf, parser};
completion.handler, stream, dynabuf, parser};
return completion.result.get();
}
template<class SyncReadStream, class Streambuf,
template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Headers>
void
read(SyncReadStream& stream, Streambuf& streambuf,
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& msg)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
error_code ec;
read(stream, streambuf, msg, ec);
read(stream, dynabuf, msg, ec);
if(ec)
throw system_error{ec};
}
template<class SyncReadStream, class Streambuf,
template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Headers>
void
read(SyncReadStream& stream, Streambuf& streambuf,
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& m,
error_code& ec)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
parser_v1<isRequest, Body, Headers> p;
parse(stream, streambuf, p, ec);
parse(stream, dynabuf, p, ec);
if(ec)
return;
assert(p.complete());
m = p.release();
}
template<class AsyncReadStream, class Streambuf,
template<class AsyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Headers,
class ReadHandler>
typename async_completion<
ReadHandler, void(error_code)>::result_type
async_read(AsyncReadStream& stream, Streambuf& streambuf,
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& m,
ReadHandler&& handler)
{
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
beast::async_completion<ReadHandler,
void(error_code)> completion(handler);
detail::read_op<AsyncReadStream, Streambuf,
detail::read_op<AsyncReadStream, DynamicBuffer,
isRequest, Body, Headers, decltype(
completion.handler)>{completion.handler,
stream, streambuf, m};
stream, dynabuf, m};
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/stream_concepts.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/logic/tribool.hpp>
#include <condition_variable>
@@ -32,51 +32,51 @@ namespace http {
namespace detail {
template<class Streambuf, class Body, class Headers>
template<class DynamicBuffer, class Body, class Headers>
void
write_firstline(Streambuf& streambuf,
write_firstline(DynamicBuffer& dynabuf,
message_v1<true, Body, Headers> const& msg)
{
write(streambuf, msg.method);
write(streambuf, " ");
write(streambuf, msg.url);
write(streambuf, " HTTP/");
write(streambuf, msg.version / 10);
write(streambuf, ".");
write(streambuf, msg.version % 10);
write(streambuf, "\r\n");
write(dynabuf, msg.method);
write(dynabuf, " ");
write(dynabuf, msg.url);
write(dynabuf, " HTTP/");
write(dynabuf, msg.version / 10);
write(dynabuf, ".");
write(dynabuf, msg.version % 10);
write(dynabuf, "\r\n");
}
template<class Streambuf, class Body, class Headers>
template<class DynamicBuffer, class Body, class Headers>
void
write_firstline(Streambuf& streambuf,
write_firstline(DynamicBuffer& dynabuf,
message_v1<false, Body, Headers> const& msg)
{
write(streambuf, "HTTP/");
write(streambuf, msg.version / 10);
write(streambuf, ".");
write(streambuf, msg.version % 10);
write(streambuf, " ");
write(streambuf, msg.status);
write(streambuf, " ");
write(streambuf, msg.reason);
write(streambuf, "\r\n");
write(dynabuf, "HTTP/");
write(dynabuf, msg.version / 10);
write(dynabuf, ".");
write(dynabuf, msg.version % 10);
write(dynabuf, " ");
write(dynabuf, msg.status);
write(dynabuf, " ");
write(dynabuf, msg.reason);
write(dynabuf, "\r\n");
}
template<class Streambuf, class FieldSequence>
template<class DynamicBuffer, class FieldSequence>
void
write_fields(Streambuf& streambuf, FieldSequence const& fields)
write_fields(DynamicBuffer& dynabuf, FieldSequence const& fields)
{
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
//static_assert(is_FieldSequence<FieldSequence>::value,
// "FieldSequence requirements not met");
for(auto const& field : fields)
{
write(streambuf, field.name());
write(streambuf, ": ");
write(streambuf, field.value());
write(streambuf, "\r\n");
write(dynabuf, field.name());
write(dynabuf, ": ");
write(dynabuf, field.value());
write(dynabuf, "\r\n");
}
}
@@ -97,10 +97,10 @@ struct write_preparation
message_v1<isRequest, Body, Headers> const& msg_)
: msg(msg_)
, w(msg)
, chunked(rfc2616::token_in_list(
msg.headers["Transfer-Encoding"], "chunked"))
, close(rfc2616::token_in_list(
msg.headers["Connection"], "close") ||
, chunked(token_list{
msg.headers["Transfer-Encoding"]}.exists("chunked"))
, close(token_list{
msg.headers["Connection"]}.exists("close") ||
(msg.version < 11 && ! msg.headers.exists(
"Content-Length")))
{
@@ -378,17 +378,17 @@ operator()(error_code ec, std::size_t, bool again)
d.copy = {};
}
template<class SyncWriteStream, class Streambuf>
template<class SyncWriteStream, class DynamicBuffer>
class writef0_lambda
{
Streambuf const& sb_;
DynamicBuffer const& sb_;
SyncWriteStream& stream_;
bool chunked_;
error_code& ec_;
public:
writef0_lambda(SyncWriteStream& stream,
Streambuf const& sb, bool chunked, error_code& ec)
DynamicBuffer const& sb, bool chunked, error_code& ec)
: sb_(sb)
, stream_(stream)
, chunked_(chunked)
@@ -548,8 +548,7 @@ async_write(AsyncWriteStream& stream,
message_v1<isRequest, Body, Headers> const& msg,
WriteHandler&& handler)
{
static_assert(
is_AsyncWriteStream<AsyncWriteStream>::value,
static_assert(is_AsyncWriteStream<AsyncWriteStream>::value,
"AsyncWriteStream requirements not met");
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");

View File

@@ -24,12 +24,30 @@ struct request_fields
{
std::string method;
std::string url;
protected:
void
swap(request_fields& other)
{
using std::swap;
swap(method, other.method);
swap(url, other.url);
}
};
struct response_fields
{
int status;
std::string reason;
protected:
void
swap(response_fields& other)
{
using std::swap;
swap(status, other.status);
swap(reason, other.reason);
}
};
} // detail
@@ -125,7 +143,14 @@ struct message
{
}
/// Swap this message for another message.
void
swap(message& other);
private:
using base = typename std::conditional<isRequest,
detail::request_fields, detail::response_fields>::type;
template<class... Un, size_t... IUn>
message(std::piecewise_construct_t,
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
template<class Body,
@@ -157,8 +201,6 @@ template<class Body,
class Headers = basic_headers<std::allocator<char>>>
using response = message<false, Body, Headers>;
#endif
} // http
} // beast

View File

@@ -48,9 +48,31 @@ struct message_v1 : message<isRequest, Body, Headers>
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
template<class Body,
@@ -62,8 +84,6 @@ template<class Body,
class Headers = basic_headers<std::allocator<char>>>
using response_v1 = message_v1<false, Body, Headers>;
#endif
/// Returns `true` if a HTTP/1 message indicates a keep alive
template<bool isRequest, class Body, class Headers>
bool

View File

@@ -23,8 +23,8 @@ enum class parse_error
bad_crlf,
bad_request,
bad_status_code,
bad_status,
bad_reason,
bad_field,
bad_value,
@@ -33,7 +33,11 @@ enum class parse_error
bad_on_headers_rv,
invalid_chunk_size,
invalid_ext_name,
invalid_ext_val,
headers_too_big,
body_too_big,
short_read,
general
@@ -60,7 +64,7 @@ public:
return "bad method";
case parse_error::bad_uri:
return "bad Request-URI";
return "bad request-target";
case parse_error::bad_version:
return "bad HTTP-Version";
@@ -69,13 +73,13 @@ public:
return "missing CRLF";
case parse_error::bad_request:
return "bad Request-Line";
case parse_error::bad_status_code:
return "bad Status-Code";
return "bad reason-phrase";
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:
return "bad field token";
@@ -95,6 +99,18 @@ public:
case parse_error::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:
return "unexpected end of data";

View File

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

View File

@@ -12,7 +12,6 @@
#include <beast/core/error.hpp>
#include <beast/core/async_completion.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/system/error_code.hpp>
namespace beast {
namespace http {
@@ -36,7 +35,7 @@ namespace http {
@param stream The stream from which the data is to be read.
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
an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser
@@ -47,10 +46,10 @@ namespace http {
@throws boost::system::system_error on failure.
*/
template<class SyncReadStream, class Streambuf, class Parser>
template<class SyncReadStream, class DynamicBuffer, class Parser>
void
parse(SyncReadStream& stream,
Streambuf& streambuf, Parser& parser);
DynamicBuffer& dynabuf, Parser& parser);
/** 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.
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
an input and an output parameter; on entry, any data in the
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.
*/
template<class SyncReadStream, class Streambuf, class Parser>
template<class SyncReadStream, class DynamicBuffer, class Parser>
void
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.
@@ -106,7 +105,7 @@ parse(SyncReadStream& stream,
@param stream The stream from which the data is to be read.
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
an input and an output parameter; on entry, any data in the
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`.
*/
template<class AsyncReadStream,
class Streambuf, class Parser, class ReadHandler>
class DynamicBuffer, class Parser, class ReadHandler>
#if GENERATING_DOCS
void_or_deduced
#else
typename async_completion<
ReadHandler, void(error_code)>::result_type
#endif
async_parse(AsyncReadStream& stream, Streambuf& streambuf,
async_parse(AsyncReadStream& stream, DynamicBuffer& dynabuf,
Parser& parser, ReadHandler&& handler);
/** 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.
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
an input and an output parameter; on entry, any data in the
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.
*/
template<class SyncReadStream, class Streambuf,
template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Headers>
void
read(SyncReadStream& stream, Streambuf& streambuf,
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& msg);
/** 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.
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
an input and an output parameter; on entry, any data in the
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.
*/
template<class SyncReadStream, class Streambuf,
template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Headers>
void
read(SyncReadStream& stream, Streambuf& streambuf,
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& msg,
error_code& ec);
@@ -229,7 +228,7 @@ read(SyncReadStream& stream, Streambuf& streambuf,
@param stream The stream to read the message from.
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
an input and an output parameter; on entry, any data in the
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
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,
class ReadHandler>
#if GENERATING_DOCS
@@ -258,7 +257,7 @@ void_or_deduced
typename async_completion<
ReadHandler, void(error_code)>::result_type
#endif
async_read(AsyncReadStream& stream, Streambuf& streambuf,
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
message_v1<isRequest, Body, Headers>& msg,
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
#define BEAST_HTTP_RFC7230_HPP
#include <array>
#include <cstdint>
#include <beast/http/detail/rfc7230.hpp>
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
#include <beast/http/impl/rfc7230.ipp>
#endif

View File

@@ -8,91 +8,17 @@
#ifndef BEAST_HTTP_STREAMBUF_BODY_HPP
#define BEAST_HTTP_STREAMBUF_BODY_HPP
#include <beast/http/body_type.hpp>
#include <beast/core/buffer_cat.hpp>
#include <beast/http/basic_dynabuf_body.hpp>
#include <beast/core/streambuf.hpp>
#include <memory>
#include <string>
namespace beast {
namespace http {
/** A message body represented by a Streambuf
/** A message body represented by a @ref streambuf
Meets the requirements of @b `Body`.
*/
template<class 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>;
using streambuf_body = basic_dynabuf_body<streambuf>;
} // http
} // beast

View File

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

View File

@@ -14,8 +14,8 @@
// BEAST_VERSION / 100 % 1000 is the minor 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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -120,7 +120,7 @@ decorate(Decorator&& d)
This setting only affects the behavior of HTTP requests that
implicitly or explicitly ask for a keepalive. For HTTP requests
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
upgrade request.

View File

@@ -12,7 +12,7 @@
#include <beast/websocket/detail/stream_base.hpp>
#include <beast/http/message_v1.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/detail/get_lowest_layer.hpp>
#include <boost/asio.hpp>
@@ -78,7 +78,7 @@ struct frame_info
@par Concepts
@b `AsyncStream`,
@b `Decorator`,
@b `Streambuf`,
@b `DynamicBuffer`,
@b `SyncStream`
*/
template<class NextLayer>
@@ -86,7 +86,7 @@ class stream : public detail::stream_base
{
friend class stream_test;
streambuf_readstream<NextLayer, streambuf> stream_;
dynabuf_readstream<NextLayer, streambuf> stream_;
public:
/// The type of the next layer.
@@ -95,12 +95,12 @@ public:
/// The type of the lowest layer.
using lowest_layer_type =
#if GENERATING_DOCS
#if GENERATING_DOCS
implementation_defined;
#else
#else
typename beast::detail::get_lowest_layer<
next_layer_type>::type;
#endif
#endif
/** Move-construct a stream.
@@ -404,12 +404,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class AcceptHandler>
#if GENERATING_DOCS
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
AcceptHandler, void(error_code)>::result_type
#endif
#endif
async_accept(AcceptHandler&& handler);
/** Read and respond to a WebSocket HTTP Upgrade request.
@@ -530,12 +530,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class ConstBufferSequence, class AcceptHandler>
#if GENERATING_DOCS
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
AcceptHandler, void(error_code)>::result_type
#endif
#endif
async_accept(ConstBufferSequence const& buffers,
AcceptHandler&& handler);
@@ -567,7 +567,7 @@ public:
@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>
void
accept(http::request_v1<Body, Headers> const& request);
@@ -647,12 +647,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class Body, class Headers, class AcceptHandler>
#if GENERATING_DOCS
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
AcceptHandler, void(error_code)>::result_type
#endif
#endif
async_accept(http::request_v1<Body, Headers> const& request,
AcceptHandler&& handler);
@@ -784,12 +784,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class HandshakeHandler>
#if GENERATING_DOCS
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
HandshakeHandler, void(error_code)>::result_type
#endif
#endif
async_handshake(boost::string_ref const& host,
boost::string_ref const& resource, HandshakeHandler&& h);
@@ -895,12 +895,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class CloseHandler>
#if GENERATING_DOCS
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
CloseHandler, void(error_code)>::result_type
#endif
#endif
async_close(close_reason const& cr, CloseHandler&& handler);
/** Send a WebSocket ping frame.
@@ -973,12 +973,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class PingHandler>
#if GENERATING_DOCS
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
PingHandler, void(error_code)>::result_type
#endif
#endif
async_ping(ping_data const& payload, PingHandler&& handler);
/** Read a message from the stream.
@@ -1007,14 +1007,14 @@ public:
@param op A value to receive the message type.
This object must remain valid until the handler is called.
@param streambuf A stream buffer to hold the message data.
This object must remain valid until the handler is called.
@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.
*/
template<class Streambuf>
template<class DynamicBuffer>
void
read(opcode& op, Streambuf& streambuf);
read(opcode& op, DynamicBuffer& dynabuf);
/** Read a message from the stream.
@@ -1042,15 +1042,14 @@ public:
@param op A value to receive the message type.
This object must remain valid until the handler is called.
@param streambuf A stream buffer to hold the message data.
This object must remain valid until the handler is called.
@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.
*/
template<class Streambuf>
template<class DynamicBuffer>
void
read(opcode& op,
Streambuf& streambuf, error_code& ec);
read(opcode& op, DynamicBuffer& dynabuf, error_code& ec);
/** 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.
This object must remain valid until the handler is called.
@param streambuf A stream buffer to hold the message data.
This object must remain valid until the handler is called.
@param dynabuf A dynamic buffer to hold the message data after
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
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
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class Streambuf, class ReadHandler>
#if GENERATING_DOCS
template<class DynamicBuffer, class ReadHandler>
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
ReadHandler, void(error_code)>::result_type
#endif
async_read(opcode& op,
Streambuf& streambuf, ReadHandler&& handler);
#endif
async_read(opcode& op, DynamicBuffer& dynabuf, ReadHandler&& handler);
/** Read a message frame from the stream.
@@ -1141,13 +1140,14 @@ public:
@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.
*/
template<class Streambuf>
template<class DynamicBuffer>
void
read_frame(frame_info& fi, Streambuf& streambuf);
read_frame(frame_info& fi, DynamicBuffer& dynabuf);
/** Read a message frame from the stream.
@@ -1178,13 +1178,14 @@ public:
@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.
*/
template<class Streambuf>
template<class DynamicBuffer>
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.
@@ -1225,7 +1226,7 @@ public:
@param fi An object to store metadata about the message.
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
remain valid until the handler is called.
@@ -1242,15 +1243,15 @@ public:
this function. Invocation of the handler will be performed in a
manner equivalent to using boost::asio::io_service::post().
*/
template<class Streambuf, class ReadHandler>
#if GENERATING_DOCS
template<class DynamicBuffer, class ReadHandler>
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
ReadHandler, void(error_code)>::result_type
#endif
#endif
async_read_frame(frame_info& fi,
Streambuf& streambuf, ReadHandler&& handler);
DynamicBuffer& dynabuf, ReadHandler&& handler);
/** Write a message to the stream.
@@ -1369,12 +1370,12 @@ public:
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class ConstBufferSequence, class WriteHandler>
#if GENERATING_DOCS
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
WriteHandler, void(error_code)>::result_type
#endif
#endif
async_write(ConstBufferSequence const& buffers,
WriteHandler&& handler);
@@ -1478,12 +1479,12 @@ public:
); @endcode
*/
template<class ConstBufferSequence, class WriteHandler>
#if GENERATING_DOCS
#if GENERATING_DOCS
void_or_deduced
#else
#else
typename async_completion<
WriteHandler, void(error_code)>::result_type
#endif
#endif
async_write_frame(bool fin,
ConstBufferSequence const& buffers, WriteHandler&& handler);
@@ -1495,8 +1496,8 @@ private:
template<class Handler> class response_op;
template<class Buffers, class Handler> class write_op;
template<class Buffers, class Handler> class write_frame_op;
template<class Streambuf, class Handler> class read_op;
template<class Streambuf, class Handler> class read_frame_op;
template<class DynamicBuffer, class Handler> class read_op;
template<class DynamicBuffer, class Handler> class read_frame_op;
void
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
# We use set -e and bash with -u to bail on first non zero exit code of any
# processes launched or upon any unbound variable
#!/usr/bin/env bash
# We use set -e to bail on first non zero exit code of any processes launched
# 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
set -ex
################################## ENVIRONMENT #################################
@@ -17,36 +20,90 @@ else
export PATH=$VALGRIND_ROOT/bin:$LCOV_ROOT/usr/bin:$PATH
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 variant: $VARIANT"
echo "using address-model: $ADDRESS_MODEL"
echo "using PATH: $PATH"
echo "using MAIN_BRANCH: $MAIN_BRANCH"
echo "using BOOST_ROOT: $BOOST_ROOT"
#################################### HELPERS ###################################
function run_tests_with_gdb {
for x in bin/**/*-tests; do scripts/run-with-gdb.sh "$x"; done
function run_tests_with_debugger {
for x in bin/**/$VARIANT/**/*-tests; do
scripts/run-with-debugger.sh "$x"
done
}
function run_tests {
for x in bin/**/*-tests; do "$x"; done
for x in bin/**/$VARIANT/**/*-tests; do
$x
done
}
num_procs=1
if [[ $(uname) == "Darwin" ]]; then
num_procs=$(sysctl -n hw.ncpu)
elif [[ $(uname -s) == "Linux" ]]; then
num_procs=$(lscpu -p | grep -v '^#' | sort -u -t, -k 2,4 | wc -l) # physical cores
virt_num_procs=$(nproc) # CircleCI returns 32 phys procs, but 1 virt proc
if (("$virt_num_procs" < "$num_procs")); then
num_procs=$virt_num_procs
function run_tests_with_valgrind {
for x in bin/**/$VARIANT/**/*-tests; do
if [[ $(basename $x) == "bench-tests" ]]; then
$x
else
# TODO --max-stackframe=8388608
# see: https://travis-ci.org/vinniefalco/Beast/jobs/132486245
valgrind --error-exitcode=1 "$x"
fi
fi
done
}
function build_beast {
$BOOST_ROOT/bjam toolset=$CC \
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 ####################################
@@ -62,24 +119,12 @@ if [[ $VARIANT == "coverage" ]]; then
lcov --no-external -c -i -d . -o baseline.info > /dev/null
# Perform test
if [[ $MAIN_BRANCH == "1" ]]; then
run_tests_with_valgrind
run_autobahn_test_suite
else
run_tests
# Run autobahn tests
export SERVER=$(find . -name "websocket-echo")
nohup $SERVER&
# 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
fi
# Create test coverage data file
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
# 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
else
# TODO: make a function
run_tests_with_gdb
cat lcov.info | node_modules/.bin/coveralls
if [[ $VARIANT == "debug" ]]; then
for x in bin/**/*-tests; do
# if [[ $x != "bench-tests" ]]; then
valgrind --error-exitcode=1 "$x"
## declare -i RESULT=$RESULT + $?
# fi
done
echo
fi
# Clean up these stragglers so BOOST_ROOT cache is clean
find $BOOST_ROOT/bin.v2 -name "*.gcda" | xargs rm -f
else
run_tests_with_debugger
fi

View File

@@ -1,4 +1,4 @@
#!/bin/bash -u
#!/usr/bin/env bash
# Assumptions:
# 1) BOOST_ROOT and BOOST_URL are already defined,
# and contain valid values.
@@ -6,7 +6,7 @@
# folder name internal to boost's .tar.gz
# When testing you can force a boost build by clearing travis caches:
# https://travis-ci.org/ripple/rippled/caches
set -e
set -eu
if [ ! -d "$BOOST_ROOT/lib" ]
then
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.
set -e
set -eux
HERE=$PWD
# Override gcc version to $GCC_VER.
# Put an appropriate symlink at the front of the path.
mkdir -v $HOME/bin
@@ -44,3 +47,8 @@ tar xfvz lcov-1.12.tar.gz -C $HOME
# Set install path
mkdir -p $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:
# 1) VALGRIND_ROOT is already defined, and contains a valid values
set -e
set -eu
if [ ! -d "$VALGRIND_ROOT/bin" ]
then
# 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/buffers_adapter.cpp
core/consuming_buffers.cpp
core/dynabuf_readstream.cpp
core/error.cpp
core/handler_alloc.cpp
core/handler_concepts.cpp
@@ -30,9 +31,8 @@ unit-test core-tests :
core/static_string.cpp
core/stream_concepts.cpp
core/streambuf.cpp
core/streambuf_readstream.cpp
core/to_string.cpp
core/write_streambuf.cpp
core/write_dynabuf.cpp
core/detail/base64.cpp
core/detail/empty_base_optimization.cpp
core/detail/get_lowest_layer.cpp
@@ -41,6 +41,7 @@ unit-test core-tests :
unit-test http-tests :
../extras/beast/unit_test/main.cpp
http/basic_dynabuf_body.cpp
http/basic_headers.cpp
http/basic_parser_v1.cpp
http/body_type.cpp
@@ -54,7 +55,6 @@ unit-test http-tests :
http/read.cpp
http/reason.cpp
http/resume_context.cpp
http/rfc2616.cpp
http/rfc7230.cpp
http/status.cpp
http/streambuf_body.cpp

View File

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

View File

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

View File

@@ -6,14 +6,14 @@
//
// 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/unit_test/suite.hpp>
namespace beast {
class write_streambuf_test : public beast::unit_test::suite
class write_dynabuf_test : public beast::unit_test::suite
{
public:
void run() override
@@ -31,6 +31,6 @@ public:
}
};
BEAST_DEFINE_TESTSUITE(write_streambuf,core,beast);
BEAST_DEFINE_TESTSUITE(write_dynabuf,core,beast);
} // beast

View File

@@ -6,7 +6,10 @@ GroupSources(test/http "/")
add_executable (http-tests
${BEAST_INCLUDES}
message_fuzz.hpp
fail_parser.hpp
../../extras/beast/unit_test/main.cpp
basic_dynabuf_body.cpp
basic_headers.cpp
basic_parser_v1.cpp
body_type.cpp
@@ -20,7 +23,6 @@ add_executable (http-tests
read.cpp
reason.cpp
resume_context.cpp
rfc2616.cpp
rfc7230.cpp
status.cpp
streambuf_body.cpp
@@ -35,6 +37,7 @@ endif()
add_executable (bench-tests
${BEAST_INCLUDES}
nodejs_parser.hpp
../../extras/beast/unit_test/main.cpp
nodejs_parser.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/headers.hpp>
#include <beast/http/string_body.hpp>
#include <beast/unit_test/suite.hpp>
#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
{
testConstruction();
testSwap();
}
};

View File

@@ -8,7 +8,7 @@
#ifndef 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 <cstdint>
#include <random>

View File

@@ -8,6 +8,8 @@
// Test that header file is self-contained.
#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/http/empty_body.hpp>
@@ -78,10 +80,36 @@ public:
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
{
testFreeFunctions();
testPrepare();
testSwap();
}
};

View File

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

View File

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

View File

@@ -8,6 +8,8 @@
// Test that header file is self-contained.
#include <beast/http/read.hpp>
#include "fail_parser.hpp"
#include <beast/http/headers.hpp>
#include <beast/http/streambuf_body.hpp>
#include <beast/test/fail_stream.hpp>
@@ -24,77 +26,6 @@ class read_test
, public test::enable_yield_to
{
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>
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.
#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