Merge subtree Beast 1.0.0-b3

Merge commit '47eb7fcc2f30df883b0036d97aac6a0fa90b0e9f'
This commit is contained in:
Vinnie Falco
2016-05-06 14:52:15 -04:00
157 changed files with 7040 additions and 4117 deletions

View File

@@ -1,4 +1,3 @@
sudo: false
language: cpp language: cpp
env: env:
@@ -8,55 +7,107 @@ env:
# Note that for simplicity, BOOST_ROOT's final # Note that for simplicity, BOOST_ROOT's final
# namepart must match the folder name internal # namepart must match the folder name internal
# to boost's .tar.gz. # to boost's .tar.gz.
- LCOV_ROOT=$HOME/lcov
- VALGRIND_ROOT=$HOME/valgrind-install
- BOOST_ROOT=$HOME/boost_1_60_0 - BOOST_ROOT=$HOME/boost_1_60_0
- BOOST_URL='http://downloads.sourceforge.net/project/boost/boost/1.60.0/boost_1_60_0.tar.gz?r=https%3A%2F%2Fsourceforge.net%2Fprojects%2Fboost%2Ffiles%2Fboost%2F1.60.0%2Fboost_1_60_0.tar.gz&ts=1460417589&use_mirror=netix' - BOOST_URL='http://downloads.sourceforge.net/project/boost/boost/1.60.0/boost_1_60_0.tar.gz?r=https%3A%2F%2Fsourceforge.net%2Fprojects%2Fboost%2Ffiles%2Fboost%2F1.60.0%2Fboost_1_60_0.tar.gz&ts=1460417589&use_mirror=netix'
packages: &gcc5_pkgs packages: &gcc5_pkgs
- gcc-5 - gcc-5
- g++-5 - g++-5
# - gcc-5-multilib
# - g++-5-multilib
# - gcc-multilib
# - g++-multilib
# - libgd2-xpm
# - ia32-libs
# - ia32-libs-multiarch
- python-software-properties - python-software-properties
- protobuf-compiler - libssl-dev
- libffi-dev
- libstdc++6 - libstdc++6
- binutils-gold - binutils-gold
# Provides a backtrace if the unittests crash # Provides a backtrace if the unittests crash
- gdb - gdb
# Needed for installing valgrind
- subversion
- automake
- autotools-dev
- libc6-dbg
packages: &clang36_pkgs packages: &clang36_pkgs
- clang-3.6 - clang-3.6
- g++-5 - g++-5
- python-software-properties - python-software-properties
- libssl-dev - libssl-dev
- libffi-dev
- libstdc++6 - libstdc++6
- binutils-gold - binutils-gold
# Provides a backtrace if the unittests crash # Provides a backtrace if the unittests crash
- gdb - gdb
# Needed for installing valgrind
- subversion
- automake
- autotools-dev
- libc6-dbg
matrix: matrix:
include: include:
# GCC/Debug
- compiler: gcc - compiler: gcc
env: GCC_VER=5 VARIANT=debug env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=64
addons: &ao_gcc5 addons: &ao_gcc5
apt: apt:
sources: ['ubuntu-toolchain-r-test'] sources: ['ubuntu-toolchain-r-test']
packages: *gcc5_pkgs packages: *gcc5_pkgs
# - compiler: gcc
# env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=32
# addons: *ao_gcc5
# GCC/Release
- compiler: gcc - compiler: gcc
env: GCC_VER=5 VARIANT=release env: GCC_VER=5 VARIANT=release ADDRESS_MODEL=64
addons: *ao_gcc5 addons: *ao_gcc5
# # - compiler: gcc
# # env: GCC_VER=5 VARIANT=release ADDRESS_MODEL=32
# # addons: *ao_gcc5
# Clang/Debug
- compiler: clang - compiler: clang
env: GCC_VER=5 VARIANT=debug CLANG_VER=3.6 env: GCC_VER=5 VARIANT=debug CLANG_VER=3.6 ADDRESS_MODEL=64
addons: &ao_clang36 addons: &ao_clang36
apt: apt:
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
packages: *clang36_pkgs packages: *clang36_pkgs
# # - compiler: clang
# # env: GCC_VER=5 VARIANT=debug CLANG_VER=3.6 ADDRESS_MODEL=32
# # addons: *ao_clang36
# Clang/Release
- compiler: clang - compiler: clang
env: GCC_VER=5 VARIANT=release CLANG_VER=3.6 env: GCC_VER=5 VARIANT=release CLANG_VER=3.6 ADDRESS_MODEL=64
addons: *ao_clang36 addons: *ao_clang36
# # - compiler: clang
# # env: GCC_VER=5 VARIANT=release CLANG_VER=3.6 ADDRESS_MODEL=32
# # addons: *ao_clang36
# Coverage
- compiler: gcc
env: GCC_VER=5 VARIANT=coverage ADDRESS_MODEL=64
addons: *ao_gcc5
# ASAN
- compiler: gcc
env: GCC_VER=5 VARIANT=asan ADDRESS_MODEL=64
addons: *ao_gcc5
cache: cache:
directories: directories:
- $BOOST_ROOT - $BOOST_ROOT
- $VALGRIND_ROOT
before_install: before_install:
- scripts/install-dependencies.sh - scripts/install-dependencies.sh
@@ -64,6 +115,9 @@ before_install:
script: script:
- scripts/build-and-test.sh - scripts/build-and-test.sh
after_script:
- cat nohup.out || echo "nohup.out already deleted"
notifications: notifications:
email: email:
false false

View File

@@ -1,14 +1,26 @@
# Part of Beast # Part of Beast
cmake_minimum_required (VERSION 3.5) cmake_minimum_required (VERSION 3.2)
project (Beast) project (Beast)
set_property (GLOBAL PROPERTY USE_FOLDERS ON) set_property (GLOBAL PROPERTY USE_FOLDERS ON)
if (WIN32) if (WIN32)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /wd4100 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4 /wd4100 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
else() else()
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
find_package(Boost REQUIRED COMPONENTS filesystem program_options system)
include_directories(${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIR})
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads)
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -g -std=c++11 -Wall /Wextra /Wpedantic /Wconversion -Wno-unused-variable")
endif() endif()
message ("cxx Flags: " ${CMAKE_CXX_FLAGS}) message ("cxx Flags: " ${CMAKE_CXX_FLAGS})

View File

@@ -45,48 +45,65 @@ else
lib crypto ; lib crypto ;
} }
variant coverage
:
debug
:
<cxxflags>"-fprofile-arcs -ftest-coverage"
<linkflags>"-lgcov"
;
variant asan
:
release
:
<cxxflags>"-fsanitize=address -fno-omit-frame-pointer"
<linkflags>"-fsanitize=address"
;
project beast project beast
: requirements : requirements
<include>. <include>.
<include>./include <include>./include
#<use>/boost//headers #<use>/boost//headers
<library>/boost/system//boost_system <library>/boost/system//boost_system
<library>/boost/filesystem//boost_filesystem <library>/boost/coroutine//boost_coroutine
<library>/boost/program_options//boost_program_options <library>/boost/filesystem//boost_filesystem
<library>/boost/program_options//boost_program_options
# <library>ssl # <library>ssl
# <library>crypto # <library>crypto
<define>BOOST_ALL_NO_LIB=1 <define>BOOST_ALL_NO_LIB=1
<define>BOOST_SYSTEM_NO_DEPRECATED=1 <define>BOOST_SYSTEM_NO_DEPRECATED=1
<threading>multi <threading>multi
<link>static <link>static
<runtime-link>shared <runtime-link>shared
<debug-symbols>on <debug-symbols>on
<toolset>gcc:<cxxflags>-std=c++11 <toolset>gcc:<cxxflags>-std=c++11
<toolset>gcc:<cxxflags>-Wno-unused-variable <toolset>gcc:<cxxflags>-Wno-unused-variable
<toolset>clang:<cxxflags>-std=c++11 <toolset>clang:<cxxflags>-std=c++11
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1 <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1 <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
<os>LINUX:<define>_XOPEN_SOURCE=600 <os>LINUX:<define>_XOPEN_SOURCE=600
<os>LINUX:<define>_GNU_SOURCE=1 <os>LINUX:<define>_GNU_SOURCE=1
<os>SOLARIS:<define>_XOPEN_SOURCE=500 <os>SOLARIS:<define>_XOPEN_SOURCE=500
<os>SOLARIS:<define>__EXTENSIONS__ <os>SOLARIS:<define>__EXTENSIONS__
<os>SOLARIS:<library>socket <os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl <os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501 <os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>cw:<library>ws2_32 <os>NT,<toolset>cw:<library>ws2_32
<os>NT,<toolset>cw:<library>mswsock <os>NT,<toolset>cw:<library>mswsock
<os>NT,<toolset>gcc:<library>ws2_32 <os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock <os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS <os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED <os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6 <os>HPUX:<library>ipv6
<os>QNXNTO:<library>socket <os>QNXNTO:<library>socket
<os>HAIKU:<library>network <os>HAIKU:<library>network
: usage-requirements : usage-requirements
<include>. <include>.
: :
build-dir bin build-dir bin
; ;
build-project test ; build-project test ;
build-project examples ; build-project examples ;

View File

@@ -1,4 +1,4 @@
# Beast [![Build Status](https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) # Beast [![Build Status](https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) [![codecov](https://codecov.io/gh/sublimator/Beast/branch/master/graph/badge.svg)](https://codecov.io/gh/sublimator/Beast)
Beast provides implementations of the HTTP and WebSocket protocols Beast provides implementations of the HTTP and WebSocket protocols
built on top of Boost.Asio and other parts of boost. built on top of Boost.Asio and other parts of boost.

View File

@@ -1,32 +1,45 @@
* Change build options to C++11 only * Add writer::prepare(msg&) interface to set Content-Type
* Replace Jamroot with Jamfile
* Fix failing test/message.cpp General:
* Complete allocator testing in basic_streambuf, basic_headers * Use SFINAE on return values (search for "class =")
* Tidy up type_checks
- Derive from std::integral_constant Boost.Http
* Check DOXYGEN, GENERATIC_DOCS directives in source * Use enum instead of bool in isRequest
- See if we can include them now that xsl is fixed * move version to a subclass of message
* Go over each header and split header material into detail and impl files
* Make buffers_debug a detail Docs:
* Roll header-only http parser * Include Example program listings in the docs
* Define Parser concept in HTTP
* melpon sandbox?
* invokable unit test
* trim public interface of rfc2616.h to essentials only
* Use new http routines in JSONRPCClient
* Remove or change http::headers alias
* Do something about the methods.hpp and fields.hpp type headers
* Fix index in docs * Fix index in docs
* Fix integer warning in file_body.hpp
* Use make_error_code in websocket to set the category right
* Figure out why namespace rfc2616 is included in docs * Figure out why namespace rfc2616 is included in docs
(currently disabled via GENERATING_DOCS macro) (currently disabled via GENERATING_DOCS macro)
* Include Example program listings in the docs * melpon sandbox?
* Update for rfc7230 * 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
Core:
* Replace Jamroot with Jamfile
* Fix bidirectional buffers iterators operator->()
* Complete allocator testing in basic_streambuf, basic_headers
WebSocket:
* optimized versions of key/masking, choose prepared_key size
* invokable unit test
* Finish up all of static_string including tests
* Don't rely on default constructible mutable buffers
type in read_frame_op (smb_type). To see the error, use
boost::asio::streambuf in websocket_async_echo_peer
HTTP:
* Define Parser concept in HTTP
* 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 size limit with test (configurable?)
* HTTP parser trailers with test * HTTP parser trailers with test
* URL parser, strong URL checking in HTTP parser * URL parser, strong URL checking in HTTP parser
* Fix method, use string instead of enum * Update for rfc7230
* More fine grained parser errors * Consider rename to MessageBody concept
* Fix all the warnings in all projects/build configs
* Fix bidirectional buffers iterators operator->()

View File

@@ -103,12 +103,16 @@ WARN_LOGFILE =
# Configuration options related to the input files # Configuration options related to the input files
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
INPUT = \ INPUT = \
../include/beast/ \
../include/beast/http \
../include/beast/websocket \
../include/beast/doc_debug.hpp \
../include/beast/async_completion.hpp \ ../include/beast/async_completion.hpp \
../include/beast/basic_streambuf.hpp \ ../include/beast/basic_streambuf.hpp \
../include/beast/bind_handler.hpp \ ../include/beast/bind_handler.hpp \
../include/beast/buffer_cat.hpp \ ../include/beast/buffer_cat.hpp \
../include/beast/buffers_adapter.hpp \ ../include/beast/buffers_adapter.hpp \
../include/beast/buffers_debug.hpp \
../include/beast/consuming_buffers.hpp \ ../include/beast/consuming_buffers.hpp \
../include/beast/handler_alloc.hpp \ ../include/beast/handler_alloc.hpp \
../include/beast/http.hpp \ ../include/beast/http.hpp \
@@ -117,17 +121,20 @@ INPUT = \
../include/beast/static_streambuf.hpp \ ../include/beast/static_streambuf.hpp \
../include/beast/streambuf.hpp \ ../include/beast/streambuf.hpp \
../include/beast/streambuf_readstream.hpp \ ../include/beast/streambuf_readstream.hpp \
../include/beast/to_string.hpp \
../include/beast/type_check.hpp \ ../include/beast/type_check.hpp \
../include/beast/websocket.hpp \ ../include/beast/websocket.hpp \
../include/beast/write_streambuf.hpp \ ../include/beast/write_streambuf.hpp \
../include/beast/http/basic_headers.hpp \ ../include/beast/http/basic_headers.hpp \
../include/beast/http/basic_parser.hpp \ ../include/beast/http/basic_parser_v1.hpp \
../include/beast/http/body_writer.hpp \
../include/beast/http/chunk_encode.hpp \ ../include/beast/http/chunk_encode.hpp \
../include/beast/http/empty_body.hpp \ ../include/beast/http/empty_body.hpp \
../include/beast/http/error.hpp \ ../include/beast/http/error.hpp \
../include/beast/http/fields.hpp \ ../include/beast/http/fields.hpp \
../include/beast/http/headers.hpp \ ../include/beast/http/headers.hpp \
../include/beast/http/message.hpp \ ../include/beast/http/message.hpp \
../include/beast/http/message_v1.hpp \
../include/beast/http/method.hpp \ ../include/beast/http/method.hpp \
../include/beast/http/parse_error.hpp \ ../include/beast/http/parse_error.hpp \
../include/beast/http/parser.hpp \ ../include/beast/http/parser.hpp \

View File

@@ -115,9 +115,10 @@ int main()
using namespace beast::http; using namespace beast::http;
// Send HTTP request using beast // Send HTTP request using beast
request<empty_body> req({method_t::http_get, "/", 11}); request<empty_body> req({"GET", "/", 11});
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port())); req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast"); req.headers.replace("User-Agent", "Beast");
prepare(req);
write(sock, req); write(sock, req);
// Receive and print HTTP response using beast // Receive and print HTTP response using beast
@@ -130,8 +131,8 @@ int main()
Establish a WebSocket connection, send a message and receive the reply: Establish a WebSocket connection, send a message and receive the reply:
``` ```
#include <beast/to_string.hpp>
#include <beast/websocket.hpp> #include <beast/websocket.hpp>
#include <beast/buffers_debug.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <iostream> #include <iostream>
#include <string> #include <string>
@@ -158,8 +159,7 @@ int main()
opcode op; opcode op;
ws.read(op, sb); ws.read(op, sb);
ws.close(close_code::normal); ws.close(close_code::normal);
std::cout << std::cout << to_string(sb.data()) << "\n";
beast::debug::buffers_to_string(sb.data()) << "\n";
} }
``` ```
@@ -186,7 +186,12 @@ documentation is based.
[include http.qbk] [include http.qbk]
[include websocket.qbk] [include websocket.qbk]
[include types.qbk]
[section:types Type Requirements]
[include core_types.qbk]
[include http_types.qbk]
[endsect]
[include design.qbk] [include design.qbk]
[section:quickref Quick Reference] [section:quickref Quick Reference]
[xinclude quickref.xml] [xinclude quickref.xml]

View File

@@ -0,0 +1,118 @@
[/
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:BufferSequence BufferSequence]
A `BufferSequence` is a type meeting either of the following requirements:
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*`ConstBufferSequence`]]
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*`MutableBufferSequence`]]
[endsect]
[section:stream Streams]
Stream types represent objects capable of performing synchronous or
asynchronous I/O. They are based on concepts from `boost::asio`.
[heading:Stream Stream]
A type modeling [*`Stream`] meets either or both of the following requirements:
* [link beast.types.stream.AsyncStream [*`AsyncStream`]]
* [link beast.types.stream.SyncStream [*`SyncStream`]]
[heading:AsyncStream AsyncStream]
A type modeling [*`AsyncStream`] meets the following requirements:
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html [*`AsyncReadStream`]]
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*`AsyncWriteStream`]]
[heading:SyncStream SyncStream]
A type modeling [*`SyncStream`] meets the following requirements:
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html [*`SyncReadStream`]]
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html [*`SyncWriteStream`]]
[endsect]
[section:Streambuf Streambuf]
In the table below:
* `X` denotes a class
* `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 `ConstBufferSequence`.]
]
[
[`X::mutable_buffers_type`]
[`U`]
[`U` meets the requirements for `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 `Streambuf`.]
]
[
[`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

@@ -8,7 +8,7 @@
[section:design Design choices] [section:design Design choices]
The implementations are driven by business needs of cryptocurrency server The implementations are driven by business needs of cryptocurrency server
applications ([link https://ripple.com Ripple] written in C++. These applications ([@https://ripple.com Ripple] written in C++. These
needs were not met by existing solutions so new code was written. The new needs were not met by existing solutions so new code was written. The new
code tries to avoid design flaws encountered in the already-existing software code tries to avoid design flaws encountered in the already-existing software
libraries: libraries:
@@ -194,8 +194,8 @@ start. Other design goals:
The WebSocket implementation [*does] provides support for shutting down The WebSocket implementation [*does] provides support for shutting down
the TLS connection through the use of the ADL compile-time virtual functions the TLS connection through the use of the ADL compile-time virtual functions
[link beast.ref.wsproto__teardown `teardown`] and [link beast.ref.websocket__teardown `teardown`] and
[link beast.ref.wsproto__async_teardown `async_teardown`]. These will [link beast.ref.websocket__async_teardown `async_teardown`]. These will
properly close the connection as per rfc6455 and overloads are available properly close the connection as per rfc6455 and overloads are available
for TLS streams. Callers may provide their own overloads of these functions for TLS streams. Callers may provide their own overloads of these functions
for user-defined next layer types. for user-defined next layer types.

View File

@@ -9,7 +9,7 @@
Beast.HTTP offers programmers simple and performant models of HTTP messages and Beast.HTTP offers programmers simple and performant models of HTTP messages and
their associated operations including synchronous and asynchronous reading and their associated operations including synchronous and asynchronous reading and
writing using Boost.Asio. writing of messages in the HTTP/1 wire format using Boost.Asio.
The HTTP protocol is described fully in The HTTP protocol is described fully in
[@https://tools.ietf.org/html/rfc2616 rfc2616] [@https://tools.ietf.org/html/rfc2616 rfc2616]
@@ -25,20 +25,43 @@ compliant with the Hypertext Transfer Protocol and the supplements that
follow. Unfortunately reliable implementations or industry standards do not follow. Unfortunately reliable implementations or industry standards do not
exist in C++. exist in C++.
Beast.HTTP is built on Boost.Asio and uses HTTP parser from NodeJS, which is Beast.HTTP is built on Boost.Asio and uses its own robust header-only HTTP/1
extensively field tested and exceptionally robust. A proposal to add networking message parser modeled after the nodejs http-parser (written in C). A proposal
functionality to the C++ standard library, based on Boost.Asio, is under to add networking functionality to the C++ standard library, based on
consideration by the standards committee. Since the final approved networking Boost.Asio, is under consideration by the standards committee. Since the final
interface for the C++ standard library will likely closely resemble the current approved networking interface for the C++ standard library will likely closely
interface of Boost.Asio, it is logical for Beast.HTTP to use Boost.Asio as its resemble the current interface of Boost.Asio, it is logical for Beast.HTTP to
network transport. use Boost.Asio as its network transport.
[heading Scope] [endsect]
The scope of this library is meant to include only the functionality of
modeling the HTTP message, serializing and deserializing the message, and
sending and receiving messages on sockets or streams. It is designed to [section:scope Scope]
be a building block for creating higher level abstractions.
This library is designed to be a building block for creating higher level
libraries. It is not designed to be end-user facing. There is no convenient
class that implements the core of a web server, nor is there a convenient
class to quickly perform common operations such as fetching a file or
connecting and retrieving a document from a secure connection. These
use-cases are important. But this library does not try to do that. Instead,
it offers primitives that can be used to build those user-facing algorithms.
A HTTP message (referred to hereafter as "message") contains request or
response specific attributes, a series of zero or more name/value pairs
(collectively termed "headers"), and a series of octets called the message
body which may be zero in length. The HTTP protocol defines the client and
server roles: clients send messages called requests and servers send back
messages called responses. `http::message` models both requests and responses.
Beast aims to offer this functionality:
* [*Model]: Provide a universal HTTP message class model.
* [*Build]: Construct a new message and manipulate its contents.
* [*Parse]: Deserialize a message from a network or memory stream in HTTP/1 wire format.
* [*Serialize]: Serialize a message into a network or memory stream in HTTP/1 wire format.
[note The documentation which follows assumes familiarity with [note The documentation which follows assumes familiarity with
both Boost.Asio and the HTTP protocol specification described in both Boost.Asio and the HTTP protocol specification described in
@@ -59,35 +82,17 @@ both Boost.Asio and the HTTP protocol specification described in
``` ```
] ]
A HTTP message (referred to hereafter as "message") contains a request or
response line, a series of zero or more name/value pairs (collectively
termed "headers"), and a series of octets called the message body which may
be zero in length. The HTTP protocol defines the client and server roles:
clients send messages called requests and servers send back messages called
responses. `http::message` models both requests and responses. The library
provides interfaces to perform these operations on messages:
* [*Parse] a new message from a series of octets.
* [*Assemble] a new message from scratch or from an existing message. In the paragraphs that follow we describe the available interfaces for
performing typical operations such as interacting with a HTTP server
* [*Serialize] a message into a series of octets. or handling simple requests. Subsequent sections cover the message model
and its customization points in more depth, for advanced applications.
* [*Read] a message from a stream. This can be thought of as a compound
operation; a network read, followed by a [*parse].
* [*Write] a message to a stream. This can be thought of as a compound
operation: a [*serialize] followed by a network write.
In the paragraphs that follow we describe simple interfaces that will serve
the majority of users looking merely to interact with a HTTP server, or
handle simple HTTP requests from clients. Subsequent sections cover the
message model in more depth, for advanced applications.
[heading Declarations] [heading Declarations]
To do anything, a message must be declared. The message class template To do anything, a message must be declared. The message class template
requires at mininum, a bool indicating whether the message is a request requires at mininum, a value indicating whether the message is a request
(versus a response), and a `Body` type. The choice of `Body` determines the (versus a response), and a `Body` type. The choice of `Body` determines the
kind of container used to represent the message body. Here we will kind of container used to represent the message body. Here we will
declare a request that has a `std::string` for the body container: declare a request that has a `std::string` for the body container:

View File

@@ -5,8 +5,6 @@
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
] ]
[section:types Type Requirements]
[section:Body Body] [section:Body Body]
@@ -57,10 +55,10 @@ In this table:
[section:BufferSequence BufferSequence] [section:BufferSequence BufferSequence]
A `BufferSequence` meets [*one of] the following requirements: A `BufferSequence` is a type meeting either of the following requirements:
* `ConstBufferSequence` * [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*`ConstBufferSequence`]]
* `MutableBufferSequence` * [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*`MutableBufferSequence`]]
[endsect] [endsect]
@@ -223,7 +221,7 @@ In this table:
* `m` denotes a value of type `message const&` where * `m` denotes a value of type `message const&` where
`std::is_same<decltype(m.body), Body::value_type>:value == true`. `std::is_same<decltype(m.body), Body::value_type>:value == true`.
* `rc` is an object of type [link beast.reference.http__resume_context resume_context]. * `rc` is an object of type [link beast.ref.http__resume_context resume_context].
* `ec` is a value of type `error_code&`. * `ec` is a value of type `error_code&`.
@@ -252,7 +250,7 @@ In this table:
] ]
[ [
[`a.content_length()`] [`a.content_length()`]
[`std::size_t`] [`std::uint64_t`]
[ [
If this member is present, it is called after initialization If this member is present, it is called after initialization
and before calls to provide buffers. The serialized message will and before calls to provide buffers. The serialized message will
@@ -373,74 +371,3 @@ public:
[endsect] [endsect]
[section:Stream Stream]
A `Stream` meets the following requirements:
* `SyncReadStream`
* `SyncWriteStream`
* `AsyncReadStream`
* `AsyncWriteStream`
[endsect]
[section:Streambuf Streambuf]
In the table below, `X` denotes a class, `a` denotes a value
of type `X`, `n` denotes a value convertible to `std::size_t`,
and `U` and `T` denote unspecified types.
[table Streambuf requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X::const_buffers_type`]
[`T`]
[`T` meets the requirements for `ConstBufferSequence`.]
]
[
[`X::mutable_buffers_type`]
[`U`]
[`U` meets the requirements for `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 `Streambuf`.]
]
]
[endsect]
[endsect]

View File

@@ -30,10 +30,11 @@
<bridgehead renderas="sect3">Classes</bridgehead> <bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__basic_headers">basic_headers</link></member> <member><link linkend="beast.ref.http__basic_headers">basic_headers</link></member>
<member><link linkend="beast.ref.http__basic_parser">basic_parser</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__basic_streambuf_body">basic_streambuf_body</link></member>
<member><link linkend="beast.ref.http__empty_body">empty_body</link></member> <member><link linkend="beast.ref.http__empty_body">empty_body</link></member>
<member><link linkend="beast.ref.http__error_code">error_code</link></member> <member><link linkend="beast.ref.http__error_code">error_code</link></member>
<member><link linkend="beast.ref.http__headers">headers</link></member>
<member><link linkend="beast.ref.http__message">message</link></member> <member><link linkend="beast.ref.http__message">message</link></member>
<member><link linkend="beast.ref.http__resume_context">resume_context</link></member> <member><link linkend="beast.ref.http__resume_context">resume_context</link></member>
<member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member> <member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member>
@@ -41,7 +42,7 @@
</simplelist> </simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead> <bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member> <member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member>
</simplelist> </simplelist>
</entry> </entry>
<entry valign="top"> <entry valign="top">
@@ -49,11 +50,14 @@
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__async_read">async_read</link></member> <member><link linkend="beast.ref.http__async_read">async_read</link></member>
<member><link linkend="beast.ref.http__async_write">async_write</link></member> <member><link linkend="beast.ref.http__async_write">async_write</link></member>
<member><link linkend="beast.ref.http__chunk_encode">chunk_encode</link></member> <member><link linkend="beast.ref.http__prepare">prepare</link></member>
<member><link linkend="beast.ref.http__chunk_encode_final">chunk_encode_final</link></member>
<member><link linkend="beast.ref.http__read">read</link></member> <member><link linkend="beast.ref.http__read">read</link></member>
<member><link linkend="beast.ref.http__write">write</link></member> <member><link linkend="beast.ref.http__write">write</link></member>
</simplelist> </simplelist>
<bridgehead renderas="sect3">Constants</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__connection">connection</link></member>
</simplelist>
<bridgehead renderas="sect3">Concepts</bridgehead> <bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.types.Body">Body</link></member> <member><link linkend="beast.types.Body">Body</link></member>
@@ -67,7 +71,6 @@
<bridgehead renderas="sect3">Classes</bridgehead> <bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.websocket__close_reason">close_reason</link></member> <member><link linkend="beast.ref.websocket__close_reason">close_reason</link></member>
<member><link linkend="beast.ref.websocket__static_string">static_string</link></member>
<member><link linkend="beast.ref.websocket__stream">stream</link></member> <member><link linkend="beast.ref.websocket__stream">stream</link></member>
</simplelist> </simplelist>
<bridgehead renderas="sect3">Options</bridgehead> <bridgehead renderas="sect3">Options</bridgehead>
@@ -120,6 +123,7 @@
<member><link linkend="beast.ref.prepared_buffers">prepared_buffers</link></member> <member><link linkend="beast.ref.prepared_buffers">prepared_buffers</link></member>
<member><link linkend="beast.ref.static_streambuf">static_streambuf</link></member> <member><link linkend="beast.ref.static_streambuf">static_streambuf</link></member>
<member><link linkend="beast.ref.static_streambuf_n">static_streambuf_n</link></member> <member><link linkend="beast.ref.static_streambuf_n">static_streambuf_n</link></member>
<member><link linkend="beast.ref.static_string">static_string</link></member>
<member><link linkend="beast.ref.streambuf">streambuf</link></member> <member><link linkend="beast.ref.streambuf">streambuf</link></member>
<member><link linkend="beast.ref.streambuf_readstream">streambuf_readstream</link></member> <member><link linkend="beast.ref.streambuf_readstream">streambuf_readstream</link></member>
</simplelist> </simplelist>
@@ -129,23 +133,28 @@
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.bind_handler">bind_handler</link></member> <member><link linkend="beast.ref.bind_handler">bind_handler</link></member>
<member><link linkend="beast.ref.buffer_cat">buffer_cat</link></member> <member><link linkend="beast.ref.buffer_cat">buffer_cat</link></member>
<member><link linkend="beast.ref.consumed_buffers">consumed_buffers</link></member>
<member><link linkend="beast.ref.prepare_buffer">prepare_buffer</link></member> <member><link linkend="beast.ref.prepare_buffer">prepare_buffer</link></member>
<member><link linkend="beast.ref.prepare_buffers">prepare_buffers</link></member> <member><link linkend="beast.ref.prepare_buffers">prepare_buffers</link></member>
<member><link linkend="beast.ref.to_string">to_string</link></member>
<member><link linkend="beast.ref.write">write</link></member> <member><link linkend="beast.ref.write">write</link></member>
</simplelist> </simplelist>
</entry> </entry>
<entry valign="top"> <entry valign="top">
<bridgehead renderas="sect3">Type Traits</bridgehead> <bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.is_AsyncReadStream">is_AsyncReadStream</link></member> <member><link linkend="beast.ref.is_AsyncReadStream">is_AsyncReadStream</link></member>
<member><link linkend="beast.ref.is_AsyncWriteStream">is_AsyncWriteStream</link></member> <member><link linkend="beast.ref.is_AsyncWriteStream">is_AsyncWriteStream</link></member>
<member><link linkend="beast.ref.is_AsyncStream">is_AsyncStream</link></member>
<member><link linkend="beast.ref.is_BufferSequence">is_BufferSequence</link></member> <member><link linkend="beast.ref.is_BufferSequence">is_BufferSequence</link></member>
<member><link linkend="beast.ref.is_CompletionHandler">is_CompletionHandler</link></member>
<member><link linkend="beast.ref.is_ConstBufferSequence">is_ConstBufferSequence</link></member> <member><link linkend="beast.ref.is_ConstBufferSequence">is_ConstBufferSequence</link></member>
<member><link linkend="beast.ref.is_Handler">is_Handler</link></member>
<member><link linkend="beast.ref.is_MutableBufferSequence">is_MutableBufferSequence</link></member> <member><link linkend="beast.ref.is_MutableBufferSequence">is_MutableBufferSequence</link></member>
<member><link linkend="beast.ref.is_Stream">is_Stream</link></member>
<member><link linkend="beast.ref.is_Streambuf">is_Streambuf</link></member> <member><link linkend="beast.ref.is_Streambuf">is_Streambuf</link></member>
<member><link linkend="beast.ref.is_SyncReadStream">is_SyncReadStream</link></member> <member><link linkend="beast.ref.is_SyncReadStream">is_SyncReadStream</link></member>
<member><link linkend="beast.ref.is_SyncStream">is_SyncStream</link></member>
<member><link linkend="beast.ref.is_SyncWriteStream">is_SyncWriteStream</link></member> <member><link linkend="beast.ref.is_SyncWriteStream">is_SyncWriteStream</link></member>
</simplelist> </simplelist>
</entry> </entry>
@@ -153,8 +162,30 @@
<bridgehead renderas="sect3">Concepts</bridgehead> <bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.types.BufferSequence">BufferSequence</link></member> <member><link linkend="beast.types.BufferSequence">BufferSequence</link></member>
<member><link linkend="beast.types.Stream">Stream</link></member> <member><link linkend="beast.types.stream.AsyncStream">AsyncStream</link></member>
<member><link linkend="beast.types.stream.Stream">Stream</link></member>
<member><link linkend="beast.types.Streambuf">Streambuf</link></member> <member><link linkend="beast.types.Streambuf">Streambuf</link></member>
<member><link linkend="beast.types.stream.SyncStream">SyncStream</link></member>
</simplelist>
</entry>
</row>
</tbody>
</tgroup>
<tgroup cols="1">
<colspec colname="a"/>
<thead>
<row>
<entry valign="center" namest="a" nameend="a">
<bridgehead renderas="sect2">Diagnostic</bridgehead>
</entry>
</row>
</thead>
<tbody>
<row>
<entry valign="top">
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.doc_debug">doc_debug</link></member>
<member><link linkend="beast.ref.nested__nested_doc_debug">nested_doc_debug</link></member>
</simplelist> </simplelist>
</entry> </entry>
</row> </row>

View File

@@ -43,6 +43,7 @@
<xsl:when test="@kind='class' or @kind='struct'"> <xsl:when test="@kind='class' or @kind='struct'">
<xsl:if test=" <xsl:if test="
contains(compoundname, 'beast::') and contains(compoundname, 'beast::') and
not(contains(compoundname, 'boost::')) and
not(contains(compoundname, '::detail')) and not(contains(compoundname, '::detail')) and
not(contains(compoundname, 'rfc2616')) and not(contains(compoundname, 'rfc2616')) and
not(contains(@prot, 'private'))"> not(contains(@prot, 'private'))">
@@ -61,7 +62,6 @@
<xsl:text>&#xd;[endsect]</xsl:text> <xsl:text>&#xd;[endsect]</xsl:text>
</xsl:template> </xsl:template>
<!--========== Utilities ==========--> <!--========== Utilities ==========-->
<xsl:template name="strip-beast-ns"> <xsl:template name="strip-beast-ns">
@@ -73,6 +73,9 @@
<xsl:when test="contains($name, 'beast::')"> <xsl:when test="contains($name, 'beast::')">
<xsl:value-of select="substring-after($name, 'beast::')"/> <xsl:value-of select="substring-after($name, 'beast::')"/>
</xsl:when> </xsl:when>
<xsl:when test="$name = 'beast'">
<xsl:text></xsl:text>
</xsl:when>
<xsl:otherwise> <xsl:otherwise>
<xsl:value-of select="$name"/> <xsl:value-of select="$name"/>
</xsl:otherwise> </xsl:otherwise>
@@ -108,6 +111,38 @@
</xsl:choose> </xsl:choose>
</xsl:template> </xsl:template>
<xsl:template name="cleanup-param">
<xsl:param name="name"/>
<xsl:variable name="clean">
<xsl:value-of select="$name"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="' *' = substring($clean, string-length($clean) - 1)">
<xsl:value-of select="substring($clean, 1, string-length($clean) - 2)"/>
<xsl:text>*</xsl:text>
</xsl:when>
<xsl:when test="' &amp;' = substring($clean, string-length($clean) - 1)">
<xsl:value-of select="substring($clean, 1, string-length($clean) - 2)"/>
<xsl:text>&amp;</xsl:text>
</xsl:when>
<xsl:when test="' &amp;...' = substring($clean, string-length($clean) - 4)">
<xsl:value-of select="substring($clean, 1, string-length($clean) - 5)"/>
<xsl:text>&amp;...</xsl:text>
</xsl:when>
<xsl:when test="' &amp;&amp;' = substring($clean, string-length($clean) - 2)">
<xsl:value-of select="substring($clean, 1, string-length($clean) - 3)"/>
<xsl:text>&amp;&amp;</xsl:text>
</xsl:when>
<xsl:when test="' &amp;&amp;...' = substring($clean, string-length($clean) - 5)">
<xsl:value-of select="substring($clean, 1, string-length($clean) - 6)"/>
<xsl:text>&amp;&amp;...</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$clean"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="cleanup-type"> <xsl:template name="cleanup-type">
<xsl:param name="name"/> <xsl:param name="name"/>
<xsl:variable name="type"> <xsl:variable name="type">
@@ -124,20 +159,22 @@
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>
</xsl:variable> </xsl:variable>
<xsl:choose> <xsl:variable name="cleaned">
<xsl:when test="$type='ConstBufferSequence'"> <xsl:choose>
<xsl:text>``[link beast.ref.ConstBufferSequence ['ConstBufferSequence]]``</xsl:text> <xsl:when test="$type='implementation_defined'">
</xsl:when> <xsl:text>``['implementation-defined]``</xsl:text>
<xsl:when test="$type='implementation_defined'"> </xsl:when>
<xsl:text>``['implementation-defined]``</xsl:text> <xsl:when test="$type='void_or_deduced'">
</xsl:when> <xsl:text>``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.return_type_of_an_initiating_function ['void-or-deduced]]``</xsl:text>
<xsl:when test="$type='void_or_deduced'"> </xsl:when>
<xsl:text>``[link beast.ref.asynchronous_operations.return_type_of_an_initiating_function ['void-or-deduced]]``</xsl:text> <xsl:otherwise>
</xsl:when> <xsl:value-of select="$type"/>
<xsl:otherwise> </xsl:otherwise>
<xsl:value-of select="$type"/> </xsl:choose>
</xsl:otherwise> </xsl:variable>
</xsl:choose> <xsl:call-template name="cleanup-param">
<xsl:with-param name="name" select="$cleaned"/>
</xsl:call-template>
</xsl:template> </xsl:template>
<xsl:template name="make-id"> <xsl:template name="make-id">
@@ -260,8 +297,9 @@
<!--========== Markup ==========--> <!--========== Markup ==========-->
<xsl:template match="para" mode="markup"> <xsl:template match="para" mode="markup">
<xsl:apply-templates mode="markup"/> <xsl:value-of select="$newline"/>
<xsl:text>&#xd;</xsl:text> <xsl:apply-templates mode="markup"/>
<xsl:value-of select="$newline"/>
</xsl:template> </xsl:template>
<xsl:template match="para" mode="markup-nested"> <xsl:template match="para" mode="markup-nested">
@@ -391,6 +429,8 @@
<xsl:apply-templates mode="markup"/> <xsl:apply-templates mode="markup"/>
</xsl:when> </xsl:when>
<xsl:when test="@kind='see'"> <xsl:when test="@kind='see'">
<xsl:text>[heading See Also]&#xd;</xsl:text>
<xsl:apply-templates mode="markup"/>
</xsl:when> </xsl:when>
<xsl:when test="@kind='note'"> <xsl:when test="@kind='note'">
<xsl:text>[heading Remarks]&#xd;</xsl:text> <xsl:text>[heading Remarks]&#xd;</xsl:text>
@@ -536,12 +576,13 @@
</xsl:template> </xsl:template>
<xsl:template match="ref[@kindref='compound']" mode="markup"> <xsl:template match="ref[@kindref='compound']" mode="markup">
<xsl:variable name="name"> <xsl:variable name="name">
<xsl:value-of select="."/> <xsl:value-of select="."/>
</xsl:variable> </xsl:variable>
<xsl:choose> <xsl:choose>
<xsl:when test="contains(@refid, 'asio') or contains($name, 'asio::')"> <xsl:when test="contains(@refid, 'beast')">
<xsl:variable name="dox-ref-id" select="@refid"/> <xsl:variable name="dox-ref-id" select="@refid"/>
<xsl:variable name="ref-name"> <xsl:variable name="ref-name">
<xsl:call-template name="strip-beast-ns"> <xsl:call-template name="strip-beast-ns">
@@ -554,6 +595,14 @@
<xsl:with-param name="name" select="$ref-name"/> <xsl:with-param name="name" select="$ref-name"/>
</xsl:call-template> </xsl:call-template>
</xsl:variable> </xsl:variable>
<!--<xsl:text>|1|</xsl:text>-->
<!--
<xsl:text>[role red ref-name='</xsl:text>
<xsl:value-of select="$ref-name"/>
<xsl:text>'|ref-id='</xsl:text>
<xsl:value-of select="$ref-id"/>
<xsl:text>']|</xsl:text>
-->
<xsl:text>[link beast.ref.</xsl:text> <xsl:text>[link beast.ref.</xsl:text>
<xsl:value-of select="$ref-id"/> <xsl:value-of select="$ref-id"/>
<xsl:text> `</xsl:text> <xsl:text> `</xsl:text>
@@ -561,89 +610,106 @@
<xsl:text>`]</xsl:text> <xsl:text>`]</xsl:text>
</xsl:when> </xsl:when>
<xsl:otherwise> <xsl:otherwise>
<xsl:text>`</xsl:text> <xsl:text>[role red |1|</xsl:text>
<xsl:value-of select="."/> <xsl:value-of select="."/>
<xsl:text>`</xsl:text> <xsl:text>]</xsl:text>
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>
</xsl:template> </xsl:template>
<xsl:template match="ref[@kindref='member']" mode="markup">
<xsl:variable name="name">
<xsl:value-of select="."/>
</xsl:variable>
<xsl:variable name="dox-ref-id" select="@refid"/>
<xsl:variable name="memberdefs" select="/doxygen//compounddef/sectiondef/memberdef[@id=$dox-ref-id]"/>
<xsl:variable name="def-kind" select="($memberdefs)/../../@kind"/>
<xsl:variable name="sec-kind" select="($memberdefs)/../@kind"/>
<xsl:choose>
<xsl:when test="contains(@refid, 'beast') and count($memberdefs) &gt; 0">
<xsl:variable name="dox-compound-name" select="($memberdefs)[1]/../../compoundname"/>
<xsl:variable name="dox-name" select="($memberdefs)[1]/name"/>
<xsl:variable name="ref-name">
<xsl:call-template name="strip-beast-ns">
<xsl:with-param name="name" select="$dox-compound-name"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="ref-id">
<xsl:call-template name="make-id">
<xsl:with-param name="name" select="$ref-name"/>
</xsl:call-template>
</xsl:variable>
<!--<xsl:text>|2|</xsl:text>-->
<!--
<xsl:text>[role red def-kind='</xsl:text>
<xsl:value-of select="$def-kind"/>
<xsl:text>', sec-kind='</xsl:text>
<xsl:value-of select="$sec-kind"/>
<xsl:text>', ref-id='</xsl:text>
<xsl:value-of select="$ref-id"/>
<xsl:text>'] </xsl:text>
-->
<xsl:choose>
<xsl:when test="$def-kind = 'namespace'">
<xsl:text>[link beast.ref.</xsl:text>
<xsl:choose>
<xsl:when test="string-length($ref-id) &gt; 0">
<xsl:value-of select="concat($ref-id,'__',$dox-name)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$dox-name"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text> `</xsl:text>
<xsl:value-of name="text" select="$dox-name"/>
<xsl:text>`]</xsl:text>
</xsl:when>
<xsl:when test="$def-kind = 'class' or $def-kind = 'struct'">
<xsl:text>[link beast.ref.</xsl:text>
<xsl:value-of select="concat($ref-id,'.',$dox-name)"/>
<xsl:text> `</xsl:text>
<xsl:value-of name="text" select="$name"/>
<xsl:text>`]</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>[role red </xsl:text>
<xsl:value-of select="$name"/>
<xsl:text>]</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:text>[role red </xsl:text>
<xsl:value-of select="$name"/>
<xsl:text>]</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="ref[@kindref='compound']" mode="markup-nested"> <xsl:template match="ref[@kindref='compound']" mode="markup-nested">
<xsl:variable name="name"> <xsl:variable name="name">
<xsl:value-of select="."/> <xsl:value-of select="."/>
</xsl:variable> </xsl:variable>
<xsl:choose> <xsl:text>[role red |3|</xsl:text>
<xsl:when test="contains(@refid, 'asio') or contains($name, 'asio::')"> <xsl:value-of select="."/>
<xsl:variable name="dox-ref-id" select="@refid"/> <xsl:text>]</xsl:text>
<xsl:variable name="ref-name">
<xsl:call-template name="strip-beast-ns">
<xsl:with-param name="name"
select="(/doxygen//compounddef[@id=$dox-ref-id])[1]/compoundname"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="ref-id">
<xsl:call-template name="make-id">
<xsl:with-param name="name" select="$ref-name"/>
</xsl:call-template>
</xsl:variable>
<xsl:text>[link beast.ref.</xsl:text>
<xsl:value-of select="$ref-id"/>
<xsl:text> `</xsl:text>
<xsl:value-of name="text" select="$ref-name"/>
<xsl:text>`]</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>`</xsl:text>
<xsl:value-of select="."/>
<xsl:text>`</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template> </xsl:template>
<xsl:template match="ref[@kindref='member']" mode="markup">
<xsl:variable name="dox-ref-id" select="@refid"/>
<xsl:variable name="memberdefs" select="/doxygen//compounddef/sectiondef/memberdef[@id=$dox-ref-id]"/>
<xsl:choose>
<xsl:when test="contains(@refid, 'namespaceboost_1_1asio') and count($memberdefs) &gt; 0">
<xsl:variable name="dox-compound-name" select="($memberdefs)[1]/../../compoundname"/>
<xsl:variable name="dox-name" select="($memberdefs)[1]/name"/>
<xsl:variable name="ref-name">
<xsl:call-template name="strip-beast-ns">
<xsl:with-param name="name" select="concat($dox-compound-name,'::',$dox-name)"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="ref-id">
<xsl:call-template name="make-id">
<xsl:with-param name="name" select="$ref-name"/>
</xsl:call-template>
</xsl:variable>
<xsl:text>[link beast.ref.</xsl:text>
<xsl:value-of select="$ref-id"/>
<xsl:text> `</xsl:text>
<xsl:value-of name="text" select="$ref-name"/>
<xsl:text>`]</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>`</xsl:text>
<xsl:value-of select="."/>
<xsl:text>`</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="ref[@kindref='member']" mode="markup-nested"> <xsl:template match="ref[@kindref='member']" mode="markup-nested">
<xsl:variable name="name">
<xsl:value-of select="."/>
</xsl:variable>
<xsl:variable name="dox-ref-id" select="@refid"/> <xsl:variable name="dox-ref-id" select="@refid"/>
<xsl:variable name="memberdefs" select="/doxygen//compounddef/sectiondef/memberdef[@id=$dox-ref-id]"/> <xsl:variable name="memberdefs" select="/doxygen//compounddef/sectiondef/memberdef[@id=$dox-ref-id]"/>
<xsl:variable name="def-kind" select="($memberdefs)/../../@kind"/>
<xsl:variable name="sec-kind" select="($memberdefs)/../@kind"/>
<xsl:choose> <xsl:choose>
<xsl:when test="contains(@refid, 'namespaceboost_1_1asio') and count($memberdefs) &gt; 0"> <xsl:when test="contains(@refid, 'beast') and count($memberdefs) &gt; 0">
<xsl:variable name="dox-compound-name" select="($memberdefs)[1]/../../compoundname"/> <xsl:variable name="dox-compound-name" select="($memberdefs)[1]/../../compoundname"/>
<xsl:variable name="dox-name" select="($memberdefs)[1]/name"/> <xsl:variable name="dox-name" select="($memberdefs)[1]/name"/>
<xsl:variable name="ref-name"> <xsl:variable name="ref-name">
<xsl:call-template name="strip-beast-ns"> <xsl:call-template name="strip-beast-ns">
<xsl:with-param name="name" select="concat($dox-compound-name,'::',$dox-name)"/> <xsl:with-param name="name" select="$dox-compound-name"/>
</xsl:call-template> </xsl:call-template>
</xsl:variable> </xsl:variable>
<xsl:variable name="ref-id"> <xsl:variable name="ref-id">
@@ -651,16 +717,49 @@
<xsl:with-param name="name" select="$ref-name"/> <xsl:with-param name="name" select="$ref-name"/>
</xsl:call-template> </xsl:call-template>
</xsl:variable> </xsl:variable>
<xsl:text>[link beast.ref.</xsl:text> <!--<xsl:text>|2|</xsl:text>-->
<!--
<xsl:text>[role red def-kind='</xsl:text>
<xsl:value-of select="$def-kind"/>
<xsl:text>', sec-kind='</xsl:text>
<xsl:value-of select="$sec-kind"/>
<xsl:text>', ref-id='</xsl:text>
<xsl:value-of select="$ref-id"/> <xsl:value-of select="$ref-id"/>
<xsl:text> `</xsl:text> <xsl:text>'] </xsl:text>
<xsl:value-of name="text" select="$ref-name"/> -->
<xsl:text>`]</xsl:text> <xsl:choose>
<xsl:when test="$def-kind = 'namespace'">
<xsl:text>[link beast.ref.</xsl:text>
<xsl:choose>
<xsl:when test="string-length($ref-id) &gt; 0">
<xsl:value-of select="concat($ref-id,'__',$dox-name)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$dox-name"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text> `</xsl:text>
<xsl:value-of name="text" select="$dox-name"/>
<xsl:text>`]</xsl:text>
</xsl:when>
<xsl:when test="$def-kind = 'class' or $def-kind = 'struct'">
<xsl:text>[link beast.ref.</xsl:text>
<xsl:value-of select="concat($ref-id,'.',$dox-name)"/>
<xsl:text> `</xsl:text>
<xsl:value-of name="text" select="$name"/>
<xsl:text>`]</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>[role red </xsl:text>
<xsl:value-of select="$name"/>
<xsl:text>]</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when> </xsl:when>
<xsl:otherwise> <xsl:otherwise>
<xsl:text>`</xsl:text> <xsl:text>[role red </xsl:text>
<xsl:value-of select="."/> <xsl:value-of select="$name"/>
<xsl:text>`</xsl:text> <xsl:text>]</xsl:text>
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>
</xsl:template> </xsl:template>
@@ -1185,7 +1284,8 @@
</xsl:call-template> </xsl:call-template>
</xsl:variable> </xsl:variable>
<xsl:if test="string-length($stripped-type) &gt; 0"> <xsl:if test="string-length($stripped-type) &gt; 0">
<xsl:value-of select="$stripped-type"/><xsl:text> </xsl:text> <xsl:value-of select="$stripped-type"/>
<xsl:text>&#xd;</xsl:text>
</xsl:if> </xsl:if>
<xsl:text>``[link beast.ref.</xsl:text> <xsl:text>``[link beast.ref.</xsl:text>
<xsl:value-of select="$class-id"/> <xsl:value-of select="$class-id"/>
@@ -1412,21 +1512,62 @@
</xsl:template> </xsl:template>
<xsl:template match="templateparamlist" mode="class-detail"> <xsl:template match="templateparamlist" mode="class-detail">
<xsl:text>template&lt;&#xd;</xsl:text> <xsl:text>template&lt;&#xd;</xsl:text>
<xsl:apply-templates select="param" mode="class-detail-template"/> <xsl:apply-templates select="param" mode="class-detail-template"/>
<xsl:text>&gt;&#xd;</xsl:text> <xsl:text>&gt;&#xd;</xsl:text>
</xsl:template> </xsl:template>
<xsl:template match="param" mode="class-detail-template"> <xsl:template match="param" mode="class-detail-template">
<xsl:text> </xsl:text> <xsl:text> </xsl:text>
<xsl:choose> <xsl:choose>
<xsl:when test="type = 'class AsyncStream'">
<xsl:text>class ``[link beast.types.stream.AsyncStream [*AsyncStream]]``</xsl:text>
</xsl:when>
<xsl:when test="type = 'class AsyncReadStream'">
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html [*AsyncReadStream]]``</xsl:text>
</xsl:when>
<xsl:when test="type = 'class AsyncWriteStream'">
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*AsyncWriteStream]]``</xsl:text>
</xsl:when>
<xsl:when test="type = 'class Body'"> <xsl:when test="type = 'class Body'">
<xsl:text>class ``[link beast.types.Body [*Body]]``</xsl:text> <xsl:text>class ``[link beast.types.Body [*Body]]``</xsl:text>
</xsl:when> </xsl:when>
<xsl:when test="type = 'class Streambuf'"> <xsl:when test="type = 'class BufferSequence'">
<xsl:text>class ``[link beast.types.BufferSequence [*BufferSequence]]``</xsl:text>
</xsl:when>
<xsl:when test="(type = 'class' or type = 'class...') and declname = 'BufferSequence'">
<xsl:value-of select="type"/>
<xsl:text> ``[link beast.types.BufferSequence [*BufferSequence]]``</xsl:text>
</xsl:when>
<xsl:when test="declname = 'CompletionHandler' or type = 'class CompletionHandler'">
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/CompletionHandler.html [*CompletionHandler]]``</xsl:text>
</xsl:when>
<xsl:when test="declname = 'ConstBufferSequence' or type = 'class ConstBufferSequence'">
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*ConstBufferSequence]]``</xsl:text>
</xsl: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.stream.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:text>class ``[link beast.types.Streambuf [*Streambuf]]``</xsl:text>
</xsl:when> </xsl:when>
<xsl:when test="type = 'class SyncStream'">
<xsl:text>class ``[link beast.types.stream.SyncStream [*SyncStream]]``</xsl:text>
</xsl:when>
<xsl:when test="declname = 'SyncReadStream' or type = 'class SyncReadStream'">
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html [*SyncReadStream]]``</xsl:text>
</xsl:when>
<xsl:when test="declname = 'SyncWriteStream' or type = 'class SyncWriteStream'">
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html [*SyncWriteStream]]``</xsl:text>
</xsl:when>
<xsl:when test="declname = 'T'"> <xsl:when test="declname = 'T'">
<xsl:value-of select="declname"/> <xsl:value-of select="declname"/>
</xsl:when> </xsl:when>
@@ -1466,28 +1607,19 @@
<xsl:value-of select="array"/> <xsl:value-of select="array"/>
</xsl:when> </xsl:when>
<xsl:otherwise> <xsl:otherwise>
<xsl:choose> <xsl:call-template name="cleanup-param">
<xsl:when test="' &amp;&amp;' = substring(type, string-length(type) - 2)"> <xsl:with-param name="name" select="type"/>
<xsl:value-of select="substring(type, 1, string-length(type) - 3)"/> </xsl:call-template>
<xsl:text>&amp;&amp;</xsl:text> <xsl:if test="count(declname) > 0">
</xsl:when> <xsl:text> </xsl:text>
<xsl:when test="' &amp;' = substring(type, string-length(type) - 1)"> <xsl:value-of select="declname"/>
<xsl:value-of select="substring(type, 1, string-length(type) - 2)"/> </xsl:if>
<xsl:text>&amp;</xsl:text>
</xsl:when>
<xsl:when test="' *' = substring(type, string-length(type) - 1)">
<xsl:value-of select="substring(type, 1, string-length(type) - 2)"/>
<xsl:text>*</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="type"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text> </xsl:text>
<xsl:value-of select="declname"/>
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>
<xsl:if test="count(defval) > 0"> = <xsl:value-of select="defval"/></xsl:if> <xsl:if test="count(defval) > 0">
<xsl:text> = </xsl:text>
<xsl:value-of select="defval"/>
</xsl:if>
<xsl:if test="not(position() = last())"> <xsl:if test="not(position() = last())">
<xsl:text>,</xsl:text> <xsl:text>,</xsl:text>
</xsl:if> </xsl:if>

View File

@@ -2,29 +2,48 @@
GroupSources(include/beast) GroupSources(include/beast)
GroupSources(examples) GroupSources(examples)
GroupSources(test)
add_executable (http-crawl add_executable (http-crawl
${BEAST_INCLUDES} ${BEAST_INCLUDES}
http_crawl.cpp urls_large_data.hpp
urls_large_data.cpp urls_large_data.cpp
http_crawl.cpp
) )
if (NOT WIN32)
target_link_libraries(http-crawl ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif()
add_executable (http-server add_executable (http-server
${BEAST_INCLUDES} ${BEAST_INCLUDES}
file_body.hpp
http_async_server.hpp
http_stream.hpp
http_stream.ipp
http_sync_server.hpp
../test/sig_wait.hpp
http_server.cpp http_server.cpp
) )
if (NOT WIN32)
target_link_libraries(http-server ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif()
add_executable (http-example add_executable (http-example
${BEAST_INCLUDES} ${BEAST_INCLUDES}
http_example.cpp http_example.cpp
) )
add_executable (websocket-echo if (NOT WIN32)
${BEAST_INCLUDES} target_link_libraries(http-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
websocket_echo.cpp endif()
)
add_executable (websocket-example add_executable (websocket-example
${BEAST_INCLUDES} ${BEAST_INCLUDES}
websocket_example.cpp websocket_example.cpp
) )
if (NOT WIN32)
target_link_libraries(websocket-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif()

View File

@@ -7,24 +7,20 @@
import os ; import os ;
exe http_crawl : exe http-crawl :
http_crawl.cpp http_crawl.cpp
urls_large_data.cpp urls_large_data.cpp
; ;
exe http_server : exe http-server :
http_server.cpp http_server.cpp
; ;
exe http_example : exe http-example :
http_example.cpp http_example.cpp
; ;
exe websocket_echo : exe websocket-example :
websocket_echo.cpp
;
exe websocket_example :
websocket_example.cpp websocket_example.cpp
; ;

View File

@@ -20,8 +20,7 @@
#ifndef BEAST_EXAMPLE_FILE_BODY_H_INCLUDED #ifndef BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
#define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED #define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
#include <beast/http/message.hpp> #include <beast/http/body_type.hpp>
#include <beast/http/resume_context.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <cstdio> #include <cstdio>
@@ -36,15 +35,16 @@ struct file_body
class writer class writer
{ {
std::size_t size_; std::uint64_t size_;
std::size_t offset_ = 0; std::uint64_t offset_ = 0;
std::string const& path_; std::string const& path_;
FILE* file_ = nullptr; FILE* file_ = nullptr;
char buf_[4096]; char buf_[4096];
std::size_t buf_len_; std::size_t buf_len_;
public: public:
static bool constexpr is_single_pass = false; writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers> template<bool isRequest, class Headers>
writer(message<isRequest, file_body, Headers> const& m) noexcept writer(message<isRequest, file_body, Headers> const& m) noexcept
@@ -69,7 +69,7 @@ struct file_body
size_ = boost::filesystem::file_size(path_); size_ = boost::filesystem::file_size(path_);
} }
std::size_t std::uint64_t
content_length() const content_length() const
{ {
return size_; return size_;
@@ -79,7 +79,11 @@ struct file_body
boost::tribool boost::tribool
operator()(resume_context&&, error_code&, Write&& write) operator()(resume_context&&, error_code&, Write&& write)
{ {
buf_len_ = std::min(size_ - offset_, sizeof(buf_)); if(size_ - offset_ < sizeof(buf_))
buf_len_ = static_cast<std::size_t>(
size_ - offset_);
else
buf_len_ = sizeof(buf_);
auto const nread = fread(buf_, 1, sizeof(buf_), file_); auto const nread = fread(buf_, 1, sizeof(buf_), file_);
(void)nread; (void)nread;
offset_ += buf_len_; offset_ += buf_len_;

View File

@@ -20,8 +20,8 @@
#ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED #ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED #define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#include "file_body.h" #include "file_body.hpp"
#include "http_stream.h" #include "http_stream.hpp"
#include <beast/placeholders.hpp> #include <beast/placeholders.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@@ -41,8 +41,8 @@ class http_async_server
using address_type = boost::asio::ip::address; using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket; using socket_type = boost::asio::ip::tcp::socket;
using req_type = request<string_body>; using req_type = request_v1<string_body>;
using resp_type = response<file_body>; using resp_type = response_v1<file_body>;
boost::asio::io_service ios_; boost::asio::io_service ios_;
socket_type sock_; socket_type sock_;
@@ -127,20 +127,22 @@ private:
path = root_ + path; path = root_ + path;
if(! boost::filesystem::exists(path)) if(! boost::filesystem::exists(path))
{ {
response<string_body> resp( response_v1<string_body> resp(
{404, "Not Found", req_.version}); {404, "Not Found", req_.version});
resp.headers.replace("Server", "http_async_server"); resp.headers.replace("Server", "http_async_server");
resp.body = "The file '" + path + "' was not found"; resp.body = "The file '" + path + "' was not found";
prepare(resp);
stream_.async_write(std::move(resp), stream_.async_write(std::move(resp),
std::bind(&peer::on_write, shared_from_this(), std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error)); asio::placeholders::error));
return; return;
} }
response<file_body> resp( resp_type resp(
{200, "OK", req_.version}); {200, "OK", req_.version});
resp.headers.replace("Server", "http_async_server"); resp.headers.replace("Server", "http_async_server");
resp.headers.replace("Content-Type", "text/html"); resp.headers.replace("Content-Type", "text/html");
resp.body = path; resp.body = path;
prepare(resp);
stream_.async_write(std::move(resp), stream_.async_write(std::move(resp),
std::bind(&peer::on_write, shared_from_this(), std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error)); asio::placeholders::error));

View File

@@ -17,8 +17,8 @@
*/ */
//============================================================================== //==============================================================================
#include "http_stream.h" #include "http_stream.hpp"
#include "urls_large_data.h" #include "urls_large_data.hpp"
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <iostream> #include <iostream>
@@ -46,12 +46,13 @@ int main(int, char const*[])
stream<ip::tcp::socket> hs(ios); stream<ip::tcp::socket> hs(ios);
connect(hs.lowest_layer(), it); connect(hs.lowest_layer(), it);
auto ep = hs.lowest_layer().remote_endpoint(); auto ep = hs.lowest_layer().remote_endpoint();
request<empty_body> req({method_t::http_get, "/", 11}); request_v1<empty_body> req({"GET", "/", 11});
req.headers.insert("Host", host + req.headers.insert("Host", host +
std::string(":") + std::to_string(ep.port())); std::string(":") + std::to_string(ep.port()));
req.headers.insert("User-Agent", "beast/http"); req.headers.insert("User-Agent", "beast/http");
prepare(req);
hs.write(req); hs.write(req);
response<string_body> resp; response_v1<string_body> resp;
hs.read(resp); hs.read(resp);
std::cout << resp; std::cout << resp;
} }

View File

@@ -23,14 +23,15 @@ int main()
using namespace beast::http; using namespace beast::http;
// Send HTTP request using beast // Send HTTP request using beast
request<empty_body> req({method_t::http_get, "/", 11}); request_v1<empty_body> req({"GET", "/", 11});
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port())); req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast"); req.headers.replace("User-Agent", "Beast");
prepare(req);
write(sock, req); write(sock, req);
// Receive and print HTTP response using beast // Receive and print HTTP response using beast
beast::streambuf sb; beast::streambuf sb;
response<streambuf_body> resp; response_v1<streambuf_body> resp;
read(sock, sb, resp); read(sock, sb, resp);
std::cout << resp; std::cout << resp;
} }

View File

@@ -17,9 +17,9 @@
*/ */
//============================================================================== //==============================================================================
#include "http_async_server.h" #include "http_async_server.hpp"
#include "http_sync_server.h" #include "http_sync_server.hpp"
#include "sig_wait.h" #include "../test/sig_wait.hpp"
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
@@ -69,13 +69,8 @@ int main(int ac, char const* av[])
endpoint_type ep{address_type::from_string(ip), port}; endpoint_type ep{address_type::from_string(ip), port};
if(sync) if(sync)
{
http_sync_server server(ep, root); http_sync_server server(ep, root);
sig_wait();
}
else else
{
http_async_server server(ep, threads, root); http_async_server server(ep, threads, root);
sig_wait(); sig_wait();
}
} }

View File

@@ -268,7 +268,7 @@ public:
*/ */
template<bool isRequest, class Body, class Headers> template<bool isRequest, class Body, class Headers>
void void
read(message<isRequest, Body, Headers>& msg) read(message_v1<isRequest, Body, Headers>& msg)
{ {
error_code ec; error_code ec;
read(msg, ec); read(msg, ec);
@@ -295,7 +295,7 @@ public:
*/ */
template<bool isRequest, class Body, class Headers> template<bool isRequest, class Body, class Headers>
void void
read(message<isRequest, Body, Headers>& msg, read(message_v1<isRequest, Body, Headers>& msg,
error_code& ec); error_code& ec);
/** Start reading a HTTP message from the stream asynchronously. /** Start reading a HTTP message from the stream asynchronously.
@@ -339,7 +339,7 @@ public:
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
#endif #endif
async_read(message<isRequest, Body, Headers>& msg, async_read(message_v1<isRequest, Body, Headers>& msg,
ReadHandler&& handler); ReadHandler&& handler);
/** Write a HTTP message to the stream. /** Write a HTTP message to the stream.
@@ -365,7 +365,7 @@ public:
*/ */
template<bool isRequest, class Body, class Headers> template<bool isRequest, class Body, class Headers>
void void
write(message<isRequest, Body, Headers> const& msg) write(message_v1<isRequest, Body, Headers> const& msg)
{ {
error_code ec; error_code ec;
write(msg, ec); write(msg, ec);
@@ -396,7 +396,7 @@ public:
*/ */
template<bool isRequest, class Body, class Headers> template<bool isRequest, class Body, class Headers>
void void
write(message<isRequest, Body, Headers> const& msg, write(message_v1<isRequest, Body, Headers> const& msg,
error_code& ec); error_code& ec);
/** Start pipelining a HTTP message to the stream asynchronously. /** Start pipelining a HTTP message to the stream asynchronously.
@@ -434,7 +434,7 @@ public:
typename async_completion< typename async_completion<
WriteHandler, void(error_code)>::result_type WriteHandler, void(error_code)>::result_type
#endif #endif
async_write(message<isRequest, Body, Headers> const& msg, async_write(message_v1<isRequest, Body, Headers> const& msg,
WriteHandler&& handler); WriteHandler&& handler);
/** Start pipelining a HTTP message to the stream asynchronously. /** Start pipelining a HTTP message to the stream asynchronously.
@@ -473,7 +473,7 @@ public:
typename async_completion< typename async_completion<
WriteHandler, void(error_code)>::result_type WriteHandler, void(error_code)>::result_type
#endif #endif
async_write(message<isRequest, Body, Headers>&& msg, async_write(message_v1<isRequest, Body, Headers>&& msg,
WriteHandler&& handler); WriteHandler&& handler);
private: private:

View File

@@ -20,10 +20,11 @@
#ifndef BEAST_HTTP_STREAM_IPP_INCLUDED #ifndef BEAST_HTTP_STREAM_IPP_INCLUDED
#define BEAST_HTTP_STREAM_IPP_INCLUDED #define BEAST_HTTP_STREAM_IPP_INCLUDED
#include <beast/bind_handler.hpp> #include <beast/http/message_v1.hpp>
#include <beast/handler_alloc.hpp>
#include <beast/http/read.hpp> #include <beast/http/read.hpp>
#include <beast/http/write.hpp> #include <beast/http/write.hpp>
#include <beast/bind_handler.hpp>
#include <beast/handler_alloc.hpp>
#include <cassert> #include <cassert>
namespace beast { namespace beast {
@@ -40,14 +41,14 @@ class stream<NextLayer, Allocator>::read_op
struct data struct data
{ {
stream<NextLayer>& s; stream<NextLayer>& s;
message<isRequest, Body, Headers>& m; message_v1<isRequest, Body, Headers>& m;
Handler h; Handler h;
bool cont; bool cont;
int state = 0; int state = 0;
template<class DeducedHandler> template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& s_, data(DeducedHandler&& h_, stream<NextLayer>& s_,
message<isRequest, Body, Headers>& m_) message_v1<isRequest, Body, Headers>& m_)
: s(s_) : s(s_)
, m(m_) , m(m_)
, h(std::forward<DeducedHandler>(h_)) , h(std::forward<DeducedHandler>(h_))
@@ -142,14 +143,14 @@ class stream<NextLayer, Allocator>::write_op : public op
struct data struct data
{ {
stream<NextLayer>& s; stream<NextLayer>& s;
message<isRequest, Body, Headers> m; message_v1<isRequest, Body, Headers> m;
Handler h; Handler h;
bool cont; bool cont;
int state = 0; int state = 0;
template<class DeducedHandler> template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& s_, data(DeducedHandler&& h_, stream<NextLayer>& s_,
message<isRequest, Body, Headers> const& m_, message_v1<isRequest, Body, Headers> const& m_,
bool cont_) bool cont_)
: s(s_) : s(s_)
, m(m_) , m(m_)
@@ -160,7 +161,7 @@ class stream<NextLayer, Allocator>::write_op : public op
template<class DeducedHandler> template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& s_, data(DeducedHandler&& h_, stream<NextLayer>& s_,
message<isRequest, Body, Headers>&& m_, message_v1<isRequest, Body, Headers>&& m_,
bool cont_) bool cont_)
: s(s_) : s(s_)
, m(std::move(m_)) , m(std::move(m_))
@@ -305,7 +306,7 @@ template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers> template<bool isRequest, class Body, class Headers>
void void
stream<NextLayer, Allocator>:: stream<NextLayer, Allocator>::
read(message<isRequest, Body, Headers>& msg, read(message_v1<isRequest, Body, Headers>& msg,
error_code& ec) error_code& ec)
{ {
beast::http::read(next_layer_, rd_buf_, msg, ec); beast::http::read(next_layer_, rd_buf_, msg, ec);
@@ -316,7 +317,7 @@ template<bool isRequest, class Body, class Headers,
class ReadHandler> class ReadHandler>
auto auto
stream<NextLayer, Allocator>:: stream<NextLayer, Allocator>::
async_read(message<isRequest, Body, Headers>& msg, async_read(message_v1<isRequest, Body, Headers>& msg,
ReadHandler&& handler) -> ReadHandler&& handler) ->
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
@@ -334,7 +335,7 @@ template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers> template<bool isRequest, class Body, class Headers>
void void
stream<NextLayer, Allocator>:: stream<NextLayer, Allocator>::
write(message<isRequest, Body, Headers> const& msg, write(message_v1<isRequest, Body, Headers> const& msg,
error_code& ec) error_code& ec)
{ {
beast::http::write(next_layer_, msg, ec); beast::http::write(next_layer_, msg, ec);
@@ -345,7 +346,7 @@ template<bool isRequest, class Body, class Headers,
class WriteHandler> class WriteHandler>
auto auto
stream<NextLayer, Allocator>:: stream<NextLayer, Allocator>::
async_write(message<isRequest, Body, Headers> const& msg, async_write(message_v1<isRequest, Body, Headers> const& msg,
WriteHandler&& handler) -> WriteHandler&& handler) ->
typename async_completion< typename async_completion<
WriteHandler, void(error_code)>::result_type WriteHandler, void(error_code)>::result_type
@@ -376,7 +377,7 @@ template<bool isRequest, class Body, class Headers,
class WriteHandler> class WriteHandler>
auto auto
stream<NextLayer, Allocator>:: stream<NextLayer, Allocator>::
async_write(message<isRequest, Body, Headers>&& msg, async_write(message_v1<isRequest, Body, Headers>&& msg,
WriteHandler&& handler) -> WriteHandler&& handler) ->
typename async_completion< typename async_completion<
WriteHandler, void(error_code)>::result_type WriteHandler, void(error_code)>::result_type

View File

@@ -20,8 +20,8 @@
#ifndef BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED #ifndef BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED #define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
#include "file_body.h" #include "file_body.hpp"
#include "http_stream.h" #include "http_stream.hpp"
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <cstdint> #include <cstdint>
@@ -43,8 +43,8 @@ class http_sync_server
using address_type = boost::asio::ip::address; using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket; using socket_type = boost::asio::ip::tcp::socket;
using req_type = request<string_body>; using req_type = request_v1<string_body>;
using resp_type = response<file_body>; using resp_type = response_v1<file_body>;
boost::asio::io_service ios_; boost::asio::io_service ios_;
socket_type sock_; socket_type sock_;
@@ -155,19 +155,21 @@ public:
path = root_ + path; path = root_ + path;
if(! boost::filesystem::exists(path)) if(! boost::filesystem::exists(path))
{ {
response<string_body> resp( response_v1<string_body> resp(
{404, "Not Found", req.version}); {404, "Not Found", req.version});
resp.headers.replace("Server", "http_sync_server"); resp.headers.replace("Server", "http_sync_server");
resp.body = "The file '" + path + "' was not found"; resp.body = "The file '" + path + "' was not found";
prepare(resp);
hs.write(resp, ec); hs.write(resp, ec);
if(ec) if(ec)
break; break;
} }
response<file_body> resp( resp_type resp(
{200, "OK", req.version}); {200, "OK", req.version});
resp.headers.replace("Server", "http_sync_server"); resp.headers.replace("Server", "http_sync_server");
resp.headers.replace("Content-Type", "text/html"); resp.headers.replace("Content-Type", "text/html");
resp.body = path; resp.body = path;
prepare(resp);
hs.write(resp, ec); hs.write(resp, ec);
if(ec) if(ec)
break; break;

View File

@@ -17,7 +17,7 @@
*/ */
//============================================================================== //==============================================================================
#include "urls_large_data.h" #include "urls_large_data.hpp"
// Data from Alexa top 1 million sites // Data from Alexa top 1 million sites
// http://s3.amazonaws.com/alexa-static/top-1m.csv.zip // http://s3.amazonaws.com/alexa-static/top-1m.csv.zip

View File

@@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#include <beast/to_string.hpp>
#include <beast/websocket.hpp> #include <beast/websocket.hpp>
#include <beast/buffers_debug.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <iostream> #include <iostream>
#include <string> #include <string>
@@ -33,6 +33,5 @@ int main()
opcode op; opcode op;
ws.read(op, sb); ws.read(op, sb);
ws.close(close_code::normal); ws.close(close_code::normal);
std::cout << std::cout << to_string(sb.data()) << "\n";
beast::debug::buffers_to_string(sb.data()) << "\n";
} }

View File

@@ -8,7 +8,7 @@
#ifndef BEAST_ASYNC_COMPLETION_HPP #ifndef BEAST_ASYNC_COMPLETION_HPP
#define BEAST_ASYNC_COMPLETION_HPP #define BEAST_ASYNC_COMPLETION_HPP
#include <beast/type_check.hpp> #include <beast/handler_concepts.hpp>
#include <boost/asio/async_result.hpp> #include <boost/asio/async_result.hpp>
#include <boost/asio/handler_type.hpp> #include <boost/asio/handler_type.hpp>
#include <type_traits> #include <type_traits>
@@ -19,11 +19,11 @@ namespace beast {
/** Helper for customizing the return type of asynchronous initiation functions. /** Helper for customizing the return type of asynchronous initiation functions.
This class template is used to transform caller-provided completion This class template is used to transform caller-provided completion
tokens in calls to asynchronous initiation functions. The transformation handlers in calls to asynchronous initiation functions. The transformation
allows customization of the return type of the initiating function, and the allows customization of the return type of the initiating function, and the
function signature of the final handler. function signature of the final handler.
@tparam CompletionToken A CompletionHandler, or a user defined type @tparam CompletionHandler A completion handler, or a user defined type
with specializations for customizing the return type (for example, with specializations for customizing the return type (for example,
`boost::asio::use_future` or `boost::asio::yield_context`). `boost::asio::use_future` or `boost::asio::yield_context`).
@@ -32,22 +32,22 @@ namespace beast {
Example: Example:
@code @code
... ...
template<class CompletionToken> template<class CompletionHandler>
typename async_completion<CompletionToken, typename async_completion<CompletionHandler,
void(boost::system::error_code)>::result_type void(boost::system::error_code)>::result_type
async_initfn(..., CompletionToken&& token) async_initfn(..., CompletionHandler&& handler)
{ {
async_completion<CompletionToken, async_completion<CompletionHandler,
void(boost::system::error_code)> completion(token); void(boost::system::error_code)> completion(handler);
... ...
return completion.result.get(); return completion.result.get();
} }
@endcode @endcode
See <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf"> @note See <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf">
Library Foundations For Asynchronous Operations</a> Library Foundations For Asynchronous Operations</a>
*/ */
template <class CompletionToken, class Signature> template <class CompletionHandler, class Signature>
struct async_completion struct async_completion
{ {
/** The type of the final handler called by the asynchronous initiation function. /** The type of the final handler called by the asynchronous initiation function.
@@ -56,7 +56,7 @@ struct async_completion
*/ */
using handler_type = using handler_type =
typename boost::asio::handler_type< typename boost::asio::handler_type<
CompletionToken, Signature>::type; CompletionHandler, Signature>::type;
/// The type of the value returned by the asynchronous initiation function. /// The type of the value returned by the asynchronous initiation function.
using result_type = typename using result_type = typename
@@ -64,14 +64,14 @@ struct async_completion
/** Construct the helper. /** Construct the helper.
@param token The completion token. Copies will be made as @param token The completion handler. Copies will be made as
required. If `CompletionToken` is movable, it may also be moved. required. If `CompletionHandler` is movable, it may also be moved.
*/ */
async_completion(typename std::remove_reference<CompletionToken>::type& token) async_completion(typename std::remove_reference<CompletionHandler>::type& token)
: handler(std::forward<CompletionToken>(token)) : handler(std::forward<CompletionHandler>(token))
, result(handler) , result(handler)
{ {
static_assert(is_Handler<handler_type, Signature>::value, static_assert(is_CompletionHandler<handler_type, Signature>::value,
"Handler requirements not met"); "Handler requirements not met");
} }

View File

@@ -18,13 +18,15 @@
namespace beast { namespace beast {
/** A `Streambuf` that uses multiple buffers internally. /** A @b `Streambuf` that uses multiple buffers internally.
The implementation uses a sequence of one or more character arrays The implementation uses a sequence of one or more character arrays
of varying sizes. Additional character array objects are appended to of varying sizes. Additional character array objects are appended to
the sequence to accommodate changes in the size of the character the sequence to accommodate changes in the size of the character
sequence. sequence.
@note Meets the requirements of @b Streambuf.
@tparam Allocator The allocator to use for managing memory. @tparam Allocator The allocator to use for managing memory.
*/ */
template<class Allocator> template<class Allocator>
@@ -36,10 +38,14 @@ class basic_streambuf
#endif #endif
{ {
public: public:
#if GENERATING_DOCS
/// The type of allocator used. /// The type of allocator used.
using allocator_type = Allocator;
#else
using allocator_type = typename using allocator_type = typename
std::allocator_traits<Allocator>:: std::allocator_traits<Allocator>::
template rebind_alloc<std::uint8_t>; template rebind_alloc<std::uint8_t>;
#endif
private: private:
// Storage for the list of buffers representing the input // Storage for the list of buffers representing the input
@@ -93,106 +99,104 @@ public:
/** Move constructor. /** Move constructor.
The output sequence of this object will be empty. The new object will have the input sequence of
the other stream buffer, and an empty output sequence.
After the move, the moved-from object will have an @note After the move, the moved-from object will have
empty input and output sequence, with no internal an empty input and output sequence, with no internal
buffers allocated. buffers allocated.
@param other The stream buffer to move from.
*/ */
basic_streambuf(basic_streambuf&& other); basic_streambuf(basic_streambuf&&);
/** Move constructor. /** Move constructor.
The output sequence of this object will be empty. The new object will have the input sequence of
the other stream buffer, and an empty output sequence.
After the move, the moved-from object will have an @note After the move, the moved-from object will have
empty input and output sequence, with no internal an empty input and output sequence, with no internal
buffers allocated. buffers allocated.
@param other The stream buffer to move from.
@param alloc The allocator to associate with the @param alloc The allocator to associate with the
stream buffer. stream buffer.
*/ */
basic_streambuf(basic_streambuf&& other, basic_streambuf(basic_streambuf&&,
allocator_type const& alloc); allocator_type const& alloc);
/** Move assignment. /** Move assignment.
The output sequence of this object will be empty. This object will have the input sequence of
the other stream buffer, and an empty output sequence.
After the move, the moved-from object will have an @note After the move, the moved-from object will have
empty input and output sequence, with no internal an empty input and output sequence, with no internal
buffers allocated. buffers allocated.
@param other The stream buffer to move from.
*/ */
basic_streambuf& basic_streambuf&
operator=(basic_streambuf&& other); operator=(basic_streambuf&&);
/// Copy constructor.
basic_streambuf(basic_streambuf const& other);
/** Copy constructor. /** Copy constructor.
The output sequence of this object will be empty. This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
basic_streambuf(basic_streambuf const&);
@param other The stream buffer to copy. /** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
@param alloc The allocator to associate with the @param alloc The allocator to associate with the
stream buffer. stream buffer.
*/ */
basic_streambuf(basic_streambuf const& other, basic_streambuf(basic_streambuf const&,
allocator_type const& alloc); allocator_type const& alloc);
/** Copy assignment. /** Copy assignment.
The output sequence of this object will be empty. This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
@param other The stream buffer to copy.
*/ */
basic_streambuf& operator=(basic_streambuf const& other); basic_streambuf& operator=(basic_streambuf const&);
/** Copy constructor. /** Copy constructor.
The output sequence of this object will be empty. This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
@param other The stream buffer to copy.
*/ */
template<class OtherAlloc> template<class OtherAlloc>
basic_streambuf(basic_streambuf<OtherAlloc> const& other); basic_streambuf(basic_streambuf<OtherAlloc> const&);
/** Copy constructor. /** Copy constructor.
The output sequence of this object will be empty. This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
@param other The stream buffer to copy.
@param alloc The allocator to associate with the @param alloc The allocator to associate with the
stream buffer. stream buffer.
*/ */
template<class OtherAlloc> template<class OtherAlloc>
basic_streambuf(basic_streambuf<OtherAlloc> const& other, basic_streambuf(basic_streambuf<OtherAlloc> const&,
allocator_type const& alloc); allocator_type const& alloc);
/** Copy assignment. /** Copy assignment.
The output sequence of this object will be empty. This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
@param other The stream buffer to copy.
*/ */
template<class OtherAlloc> template<class OtherAlloc>
basic_streambuf& operator=(basic_streambuf<OtherAlloc> const& other); basic_streambuf& operator=(basic_streambuf<OtherAlloc> const&);
/** Default constructor. /** Construct a stream buffer.
@param alloc_size The size of buffer to allocate. This is a soft @param alloc_size The size of buffer to allocate. This is a
limit, calls to prepare for buffers exceeding this size will allocate soft limit, calls to prepare for buffers exceeding this size
the larger size. will allocate the larger size. The default allocation size
is 1KB (1024 bytes).
@param alloc The allocator to use. @param alloc The allocator to use. If this parameter is
unspecified, a default constructed allocator will be used.
*/ */
explicit explicit
basic_streambuf(std::size_t alloc_size = 1024, basic_streambuf(std::size_t alloc_size = 1024,
@@ -219,15 +223,26 @@ public:
return in_size_; return in_size_;
} }
/// Get a list of buffers that represents the output sequence, with the given size. /** Get a list of buffers that represents the output sequence, with the given size.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
mutable_buffers_type mutable_buffers_type
prepare(size_type n); prepare(size_type n);
/// Move bytes from the output sequence to the input sequence. /** Move bytes from the output sequence to the input sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
void void
commit(size_type n); commit(size_type n);
/// Get a list of buffers that represents the input sequence. /** Get a list of buffers that represents the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type const_buffers_type
data() const; data() const;
@@ -277,19 +292,9 @@ private:
@return The stream buffer. @return The stream buffer.
*/ */
template<class Alloc, class T> template<class Allocator, class T>
basic_streambuf<Alloc>& basic_streambuf<Allocator>&
operator<<(basic_streambuf<Alloc>& streambuf, T const& t); operator<<(basic_streambuf<Allocator>& streambuf, T const& t);
/** Convert the entire basic_streambuf to a string.
@param streambuf The streambuf to convert.
@return A string representing the contents of the input sequence.
*/
template<class Allocator>
std::string
to_string(basic_streambuf<Allocator> const& streambuf);
} // beast } // beast

View File

@@ -8,110 +8,19 @@
#ifndef BEAST_BIND_HANDLER_HPP #ifndef BEAST_BIND_HANDLER_HPP
#define BEAST_BIND_HANDLER_HPP #define BEAST_BIND_HANDLER_HPP
#include <beast/detail/integer_sequence.hpp> #include <beast/handler_concepts.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp> #include <beast/detail/bind_handler.hpp>
#include <boost/asio/detail/handler_cont_helpers.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <functional>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
namespace beast { namespace beast {
namespace detail {
/* Nullary handler that calls Handler with bound arguments.
The bound handler provides the same io_service execution
guarantees as the original handler.
*/
template<class Handler, class... Args>
class bound_handler
{
private:
using args_type = std::tuple<typename std::decay<Args>::type...>;
Handler h_;
args_type args_;
template<class Tuple, std::size_t... S>
static void invoke(Handler& h, Tuple& args,
index_sequence<S...>)
{
h(std::get<S>(args)...);
}
public:
using result_type = void;
template<class DeducedHandler>
explicit
bound_handler(DeducedHandler&& handler, Args&&... args)
: h_(std::forward<DeducedHandler>(handler))
, args_(std::forward<Args>(args)...)
{
}
void
operator()()
{
invoke(h_, args_,
index_sequence_for<Args...> ());
}
void
operator()() const
{
invoke(h_, args_,
index_sequence_for<Args...> ());
}
friend
void*
asio_handler_allocate(
std::size_t size, bound_handler* h)
{
return boost_asio_handler_alloc_helpers::
allocate(size, h->h_);
}
friend
void
asio_handler_deallocate(
void* p, std::size_t size, bound_handler* h)
{
boost_asio_handler_alloc_helpers::
deallocate(p, size, h->h_);
}
friend
bool
asio_handler_is_continuation(bound_handler* h)
{
return boost_asio_handler_cont_helpers::
is_continuation (h->h_);
}
template<class F>
friend
void
asio_handler_invoke(F&& f, bound_handler* h)
{
boost_asio_handler_invoke_helpers::
invoke(f, h->h_);
}
};
} // detail
//------------------------------------------------------------------------------
/** Bind parameters to a completion handler, creating a wrapped handler. /** Bind parameters to a completion handler, creating a wrapped handler.
This function creates a new handler which invoked with no parameters This function creates a new handler which, when invoked with no
calls the original handler with the list of bound arguments. The passed parameters, calls the original handler with the list of bound arguments.
handler and arguments are forwarded into the returned handler, which The passed handler and arguments are forwarded into the returned handler,
provides the same `io_service` execution guarantees as the original which provides the same `io_service` execution guarantees as the original
handler. handler.
Unlike `io_service::wrap`, the returned handler can be used in a Unlike `io_service::wrap`, the returned handler can be used in a
@@ -145,6 +54,9 @@ detail::bound_handler<
#endif #endif
bind_handler(CompletionHandler&& handler, Args&&... args) bind_handler(CompletionHandler&& handler, Args&&... args)
{ {
static_assert(is_CompletionHandler<
CompletionHandler, void(Args...)>::value,
"CompletionHandler requirements not met");
return detail::bound_handler<typename std::decay< return detail::bound_handler<typename std::decay<
CompletionHandler>::type, Args...>(std::forward< CompletionHandler>::type, Args...>(std::forward<
CompletionHandler>(handler), CompletionHandler>(handler),
@@ -153,10 +65,4 @@ bind_handler(CompletionHandler&& handler, Args&&... args)
} // beast } // beast
namespace std {
template<class Handler, class... Args>
void bind(beast::detail::bound_handler<
Handler, Args...>, ...) = delete;
} // std
#endif #endif

View File

@@ -8,6 +8,7 @@
#ifndef BEAST_BUFFER_CAT_HPP #ifndef BEAST_BUFFER_CAT_HPP
#define BEAST_BUFFER_CAT_HPP #define BEAST_BUFFER_CAT_HPP
#include <beast/detail/buffer_cat.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <cstdint> #include <cstdint>
#include <iterator> #include <iterator>
@@ -18,463 +19,9 @@
namespace beast { namespace beast {
namespace detail {
template<class ValueType, class... Bs>
class buffer_cat_helper
{
std::tuple<Bs...> bs_;
public:
using value_type = ValueType;
class const_iterator;
buffer_cat_helper(buffer_cat_helper&&) = default;
buffer_cat_helper(buffer_cat_helper const&) = default;
buffer_cat_helper& operator=(buffer_cat_helper&&) = default;
buffer_cat_helper& operator=(buffer_cat_helper const&) = default;
explicit
buffer_cat_helper(Bs const&... bs)
: bs_(bs...)
{
}
const_iterator
begin() const;
const_iterator
end() const;
};
template<class U>
std::size_t constexpr
max_sizeof()
{
return sizeof(U);
}
template<class U0, class U1, class... Us>
std::size_t constexpr
max_sizeof()
{
return
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
max_sizeof<U0>() : max_sizeof<U1, Us...>();
}
template<class ValueType, class... Bs>
class buffer_cat_helper<
ValueType, Bs...>::const_iterator
{
std::size_t n_;
std::tuple<Bs...> const* bs_;
std::array<std::uint8_t,
max_sizeof<typename Bs::const_iterator...>()> buf_;
friend class buffer_cat_helper<ValueType, Bs...>;
template<std::size_t I>
using C = std::integral_constant<std::size_t, I>;
template<std::size_t I>
using iter_t = typename std::tuple_element<
I, std::tuple<Bs...>>::type::const_iterator;
template<std::size_t I>
iter_t<I>&
iter()
{
return *reinterpret_cast<
iter_t<I>*>(buf_.data());
}
template<std::size_t I>
iter_t<I> const&
iter() const
{
return *reinterpret_cast<
iter_t<I> const*>(buf_.data());
}
public:
using value_type = ValueType;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
~const_iterator();
const_iterator();
const_iterator(const_iterator&& other);
const_iterator(const_iterator const& other);
const_iterator& operator=(const_iterator&& other);
const_iterator& operator=(const_iterator const& other);
bool
operator==(const_iterator const& other) const;
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const;
pointer
operator->() const = delete;
const_iterator&
operator++();
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--();
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
const_iterator(
std::tuple<Bs...> const& bs, bool at_end);
void
construct(C<sizeof...(Bs)>)
{
auto constexpr I = sizeof...(Bs);
n_ = I;
}
template<std::size_t I>
void
construct(C<I>)
{
if(std::get<I>(*bs_).begin() !=
std::get<I>(*bs_).end())
{
n_ = I;
new(buf_.data()) iter_t<I>{
std::get<I>(*bs_).begin()};
return;
}
construct(C<I+1>{});
}
void
destroy(C<sizeof...(Bs)>)
{
return;
}
template<std::size_t I>
void
destroy(C<I>)
{
if(n_ == I)
{
using Iter = iter_t<I>;
iter<I>().~Iter();
return;
}
destroy(C<I+1>{});
}
void
move(C<sizeof...(Bs)>, const_iterator&&)
{
return;
}
template<std::size_t I>
void
move(C<I>, const_iterator&& other)
{
if(n_ == I)
{
new(buf_.data()) iter_t<I>{
std::move(other.iter<I>())};
return;
}
move(C<I+1>{}, std::move(other));
}
void
copy(C<sizeof...(Bs)>, const_iterator const&)
{
return;
}
template<std::size_t I>
void
copy(C<I>, const_iterator const& other)
{
if(n_ == I)
{
new(buf_.data()) iter_t<I>{
other.iter<I>()};
return;
}
copy(C<I+1>{}, other);
}
bool
equal(C<sizeof...(Bs)>,
const_iterator const&) const
{
return true;
}
template<std::size_t I>
bool
equal(C<I>, const_iterator const& other) const
{
if(n_ == I)
return iter<I>() == other.iter<I>();
return equal(C<I+1>{}, other);
}
[[noreturn]]
reference
dereference(C<sizeof...(Bs)>) const
{
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
reference
dereference(C<I>) const
{
if(n_ == I)
return *iter<I>();
return dereference(C<I+1>{});
}
[[noreturn]]
void
increment(C<sizeof...(Bs)>)
{
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
void
increment(C<I>)
{
if(n_ == I)
{
if(++iter<I>() !=
std::get<I>(*bs_).end())
return;
using Iter = iter_t<I>;
iter<I>().~Iter();
return construct(C<I+1>{});
}
increment(C<I+1>{});
}
void
decrement(C<sizeof...(Bs)>)
{
auto constexpr I = sizeof...(Bs);
if(n_ == I)
{
--n_;
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()};
}
decrement(C<I-1>{});
}
void
decrement(C<0>)
{
auto constexpr I = 0;
if(iter<I>() != std::get<I>(*bs_).begin())
{
--iter<I>();
return;
}
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
void
decrement(C<I>)
{
if(n_ == I)
{
if(iter<I>() != std::get<I>(*bs_).begin())
{
--iter<I>();
return;
}
--n_;
using Iter = iter_t<I>;
iter<I>().~Iter();
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()};
}
decrement(C<I-1>{});
}
};
//------------------------------------------------------------------------------
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::~const_iterator()
{
destroy(C<0>{});
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator()
: n_(sizeof...(Bs))
, bs_(nullptr)
{
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator(
std::tuple<Bs...> const& bs, bool at_end)
: bs_(&bs)
{
if(at_end)
n_ = sizeof...(Bs);
else
construct(C<0>{});
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator(const_iterator&& other)
: n_(other.n_)
, bs_(other.bs_)
{
move(C<0>{}, std::move(other));
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator(const_iterator const& other)
: n_(other.n_)
, bs_(other.bs_)
{
copy(C<0>{}, other);
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator=(const_iterator&& other) ->
const_iterator&
{
if(&other == this)
return *this;
destroy(C<0>{});
n_ = other.n_;
bs_ = other.bs_;
move(C<0>{}, std::move(other));
return *this;
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator=(const_iterator const& other) ->
const_iterator&
{
if(&other == this)
return *this;
destroy(C<0>{});
n_ = other.n_;
bs_ = other.bs_;
copy(C<0>{}, other);
return *this;
}
template<class ValueType, class... Bs>
bool
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator==(const_iterator const& other) const
{
if(bs_ != other.bs_)
return false;
if(n_ != other.n_)
return false;
return equal(C<0>{}, other);
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator*() const ->
reference
{
return dereference(C<0>{});
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator++() ->
const_iterator&
{
increment(C<0>{});
return *this;
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator--() ->
const_iterator&
{
decrement(C<sizeof...(Bs)>{});
return *this;
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::begin() const ->
const_iterator
{
return const_iterator(bs_, false);
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::end() const ->
const_iterator
{
return const_iterator(bs_, true);
}
} // detail
//------------------------------------------------------------------------------
/** Concatenate 2 or more buffer sequences to form a `ConstBufferSequence`. /** Concatenate 2 or more buffer sequences to form a `ConstBufferSequence`.
This function returns a `ConstBufferSequence` that when iterated, This function returns a @b `ConstBufferSequence` that when iterated,
efficiently concatenates the input buffer sequences. Copies of the efficiently concatenates the input buffer sequences. Copies of the
arguments passed will be made; however, the returned object does arguments passed will be made; however, the returned object does
not take ownership of the underlying memory. The application is still not take ownership of the underlying memory. The application is still
@@ -482,8 +29,8 @@ buffer_cat_helper<ValueType, Bs...>::end() const ->
@param buffers The list of buffer sequences to concatenate. @param buffers The list of buffer sequences to concatenate.
@return A new `ConstBufferSequence` that represents the concatenation @return A new @b `ConstBufferSequence` that represents the
of the input buffer sequences. concatenation of the input buffer sequences.
*/ */
#if GENERATING_DOCS #if GENERATING_DOCS
template<class... BufferSequence> template<class... BufferSequence>

View File

@@ -0,0 +1,61 @@
//
// 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_BUFFER_CONCEPTS_HPP
#define BEAST_BUFFER_CONCEPTS_HPP
#include <beast/detail/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <type_traits>
namespace beast {
/// Determine if `T` meets the requirements of @b `BufferSequence`.
template<class T, class BufferType>
#if GENERATING_DOCS
struct is_BufferSequence : std::integral_constant<bool, ...>
#else
struct is_BufferSequence : detail::is_BufferSequence<T, BufferType>::type
#endif
{
};
/// Determine if `T` meets the requirements of @b `ConstBufferSequence`.
template<class T>
#if GENERATING_DOCS
struct is_ConstBufferSequence : std::integral_constant<bool, ...>
#else
struct is_ConstBufferSequence :
is_BufferSequence<T, boost::asio::const_buffer>
#endif
{
};
/// Determine if `T` meets the requirements of @b `MutableBufferSequence`.
template<class T>
#if GENERATING_DOCS
struct is_MutableBufferSequence : std::integral_constant<bool, ...>
#else
struct is_MutableBufferSequence :
is_BufferSequence<T, boost::asio::mutable_buffer>
#endif
{
};
/// 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

@@ -8,15 +8,16 @@
#ifndef BEAST_BUFFERS_ADAPTER_HPP #ifndef BEAST_BUFFERS_ADAPTER_HPP
#define BEAST_BUFFERS_ADAPTER_HPP #define BEAST_BUFFERS_ADAPTER_HPP
#include <beast/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <type_traits> #include <type_traits>
namespace beast { namespace beast {
/** Adapts a `MutableBufferSequence` into a `Streambuf`. /** Adapts a @b `MutableBufferSequence` into a @b `Streambuf`.
This class wraps a `MutableBufferSequence` to meet the requirements This class wraps a @b `MutableBufferSequence` to meet the requirements
of `Streambuf`. Upon construction the input and output sequences are of @b `Streambuf`. Upon construction the input and output sequences are
empty. A copy of the mutable buffer sequence object is stored; however, empty. A copy of the mutable buffer sequence object is stored; however,
ownership of the underlying memory is not transferred. The caller is ownership of the underlying memory is not transferred. The caller is
responsible for making sure that referenced memory remains valid responsible for making sure that referenced memory remains valid
@@ -25,20 +26,18 @@ namespace beast {
The size of the mutable buffer sequence determines the maximum The size of the mutable buffer sequence determines the maximum
number of bytes which may be prepared and committed. number of bytes which may be prepared and committed.
@tparam Buffers The type of mutable buffer sequence to wrap. @tparam MutableBufferSequence The type of mutable buffer sequence to wrap.
*/ */
template<class Buffers> template<class MutableBufferSequence>
class buffers_adapter class buffers_adapter
{ {
private: private:
using buffers_type = typename std::decay<Buffers>::type; static_assert(is_MutableBufferSequence<MutableBufferSequence>::value,
using iter_type = typename buffers_type::const_iterator; "MutableBufferSequence requirements not met");
static auto constexpr is_mutable = using iter_type = typename MutableBufferSequence::const_iterator;
std::is_constructible<boost::asio::mutable_buffer,
typename std::iterator_traits<iter_type>::value_type>::value;
Buffers bs_; MutableBufferSequence bs_;
iter_type begin_; iter_type begin_;
iter_type out_; iter_type out_;
iter_type end_; iter_type end_;
@@ -98,7 +97,7 @@ public:
transferred. transferred.
*/ */
explicit explicit
buffers_adapter(Buffers const& buffers); buffers_adapter(MutableBufferSequence const& buffers);
/// Returns the largest size output sequence possible. /// Returns the largest size output sequence possible.
std::size_t std::size_t
@@ -118,15 +117,25 @@ public:
@throws std::length_error if the size would exceed the limit @throws std::length_error if the size would exceed the limit
imposed by the underlying mutable buffer sequence. imposed by the underlying mutable buffer sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/ */
mutable_buffers_type mutable_buffers_type
prepare(std::size_t n); prepare(std::size_t n);
/// Move bytes from the output sequence to the input sequence. /** Move bytes from the output sequence to the input sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
void void
commit(std::size_t n); commit(std::size_t n);
/// Get a list of buffers that represents the input sequence. /** Get a list of buffers that represents the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type const_buffers_type
data() const; data() const;

View File

@@ -1,44 +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_BUFFERS_DEBUG_HPP
#define BEAST_BUFFERS_DEBUG_HPP
#include <boost/asio/buffer.hpp>
#include <string>
namespace beast {
namespace debug {
/** Diagnostic utility to convert a `ConstBufferSequence` to a string.
@note Carriage returns and linefeeds will have additional escape
representations printed for visibility.
*/
template<class Buffers>
std::string
buffers_to_string(Buffers const& bs)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(bs));
for(auto const& b : bs)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
for(auto i = s.size(); i-- > 0;)
if(s[i] == '\r')
s.replace(i, 1, "\\r");
else if(s[i] == '\n')
s.replace(i, 1, "\\n\n");
return s;
}
} // debug
} // beast
#endif

View File

@@ -8,6 +8,7 @@
#ifndef BEAST_CONSUMING_BUFFERS_HPP #ifndef BEAST_CONSUMING_BUFFERS_HPP
#define BEAST_CONSUMING_BUFFERS_HPP #define BEAST_CONSUMING_BUFFERS_HPP
#include <beast/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <cstdint> #include <cstdint>
#include <iterator> #include <iterator>
@@ -28,26 +29,29 @@ namespace beast {
Ownership of the underlying memory is not transferred, the application Ownership of the underlying memory is not transferred, the application
is still responsible for managing its lifetime. is still responsible for managing its lifetime.
@tparam Buffers The buffer sequence to wrap. @tparam BufferSequence The buffer sequence to wrap.
@ptaram ValueType The type of buffer of the final buffer sequence. This @tparam ValueType The type of buffer of the final buffer sequence. This
can be different from the buffer type of the wrapped sequence. For can be different from the buffer type of the wrapped sequence. For
example, a `MutableBufferSequence` can be transformed into a example, a `MutableBufferSequence` can be transformed into a
consumable `ConstBufferSequence`. Violations of buffer const safety consumable `ConstBufferSequence`. Violations of buffer const safety
are not permitted, and will result in a compile error. are not permitted, and will result in a compile error.
*/ */
template<class Buffers, template<class BufferSequence,
class ValueType = typename Buffers::value_type> class ValueType = typename BufferSequence::value_type>
class consuming_buffers class consuming_buffers
{ {
using iter_type = using iter_type =
typename Buffers::const_iterator; typename BufferSequence::const_iterator;
static_assert(is_BufferSequence<BufferSequence, ValueType>::value,
"BufferSequence requirements not met");
static_assert(std::is_constructible<ValueType, static_assert(std::is_constructible<ValueType,
typename std::iterator_traits<iter_type>::value_type>::value, typename std::iterator_traits<iter_type>::value_type>::value,
"ValueType requirements not met"); "ValueType requirements not met");
Buffers bs_; BufferSequence bs_;
iter_type begin_; iter_type begin_;
std::size_t skip_ = 0; std::size_t skip_ = 0;
@@ -90,7 +94,7 @@ public:
underlying memory is not transferred or copied. underlying memory is not transferred or copied.
*/ */
explicit explicit
consuming_buffers(Buffers const& buffers); consuming_buffers(BufferSequence const& buffers);
/// Get a bidirectional iterator to the first element. /// Get a bidirectional iterator to the first element.
const_iterator const_iterator
@@ -110,10 +114,24 @@ public:
consume(std::size_t n); consume(std::size_t n);
}; };
/// Returns a consumed buffer /** Returns a new, consumed buffer sequence.
template<class Buffers>
consuming_buffers<Buffers, typename Buffers::value_type> This function returns a new buffer sequence which when iterated,
consumed_buffers(Buffers const& bs, std::size_t n); efficiently represents the portion of the original buffer sequence
with `n` bytes removed from the beginning.
Copies will be made of the buffer sequence passed, but ownership
of the underlying memory is not transferred.
@param buffers The buffer sequence to consume.
@param n The number of bytes to remove from the front. If this is
larger than the size of the buffer sequence, an empty buffer sequence
is returned.
*/
template<class BufferSequence>
consuming_buffers<BufferSequence, typename BufferSequence::value_type>
consumed_buffers(BufferSequence const& buffers, std::size_t n);
} // beast } // beast

View File

@@ -0,0 +1,113 @@
//
// 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_BIND_DETAIL_HANDLER_HPP
#define BEAST_BIND_DETAIL_HANDLER_HPP
#include <beast/detail/integer_sequence.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/detail/handler_cont_helpers.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <utility>
namespace beast {
namespace detail {
/* Nullary handler that calls Handler with bound arguments.
The bound handler provides the same io_service execution
guarantees as the original handler.
*/
template<class Handler, class... Args>
class bound_handler
{
private:
using args_type = std::tuple<
typename std::decay<Args>::type...>;
Handler h_;
args_type args_;
template<class Tuple, std::size_t... S>
static void invoke(Handler& h, Tuple& args,
index_sequence<S...>)
{
h(std::get<S>(args)...);
}
public:
using result_type = void;
template<class DeducedHandler>
explicit
bound_handler(DeducedHandler&& handler, Args&&... args)
: h_(std::forward<DeducedHandler>(handler))
, args_(std::forward<Args>(args)...)
{
}
void
operator()()
{
invoke(h_, args_,
index_sequence_for<Args...> ());
}
void
operator()() const
{
invoke(h_, args_,
index_sequence_for<Args...> ());
}
friend
void*
asio_handler_allocate(
std::size_t size, bound_handler* h)
{
return boost_asio_handler_alloc_helpers::
allocate(size, h->h_);
}
friend
void
asio_handler_deallocate(
void* p, std::size_t size, bound_handler* h)
{
boost_asio_handler_alloc_helpers::
deallocate(p, size, h->h_);
}
friend
bool
asio_handler_is_continuation(bound_handler* h)
{
return boost_asio_handler_cont_helpers::
is_continuation (h->h_);
}
template<class F>
friend
void
asio_handler_invoke(F&& f, bound_handler* h)
{
boost_asio_handler_invoke_helpers::
invoke(f, h->h_);
}
};
} // detail
} // beast
#include <functional>
namespace std {
template<class Handler, class... Args>
void bind(beast::detail::bound_handler<
Handler, Args...>, ...) = delete;
} // std
#endif

View File

@@ -0,0 +1,473 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_BUFFER_CAT_HPP
#define BEAST_DETAIL_BUFFER_CAT_HPP
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <iterator>
#include <new>
#include <stdexcept>
#include <tuple>
#include <utility>
namespace beast {
namespace detail {
template<class ValueType, class... Bs>
class buffer_cat_helper
{
std::tuple<Bs...> bs_;
public:
using value_type = ValueType;
class const_iterator;
buffer_cat_helper(buffer_cat_helper&&) = default;
buffer_cat_helper(buffer_cat_helper const&) = default;
buffer_cat_helper& operator=(buffer_cat_helper&&) = default;
buffer_cat_helper& operator=(buffer_cat_helper const&) = default;
explicit
buffer_cat_helper(Bs const&... bs)
: bs_(bs...)
{
}
const_iterator
begin() const;
const_iterator
end() const;
};
template<class U>
std::size_t constexpr
max_sizeof()
{
return sizeof(U);
}
template<class U0, class U1, class... Us>
std::size_t constexpr
max_sizeof()
{
return
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
max_sizeof<U0>() : max_sizeof<U1, Us...>();
}
template<class ValueType, class... Bs>
class buffer_cat_helper<
ValueType, Bs...>::const_iterator
{
std::size_t n_;
std::tuple<Bs...> const* bs_;
std::array<std::uint8_t,
max_sizeof<typename Bs::const_iterator...>()> buf_;
friend class buffer_cat_helper<ValueType, Bs...>;
template<std::size_t I>
using C = std::integral_constant<std::size_t, I>;
template<std::size_t I>
using iter_t = typename std::tuple_element<
I, std::tuple<Bs...>>::type::const_iterator;
template<std::size_t I>
iter_t<I>&
iter()
{
return *reinterpret_cast<
iter_t<I>*>(buf_.data());
}
template<std::size_t I>
iter_t<I> const&
iter() const
{
return *reinterpret_cast<
iter_t<I> const*>(buf_.data());
}
public:
using value_type = ValueType;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
~const_iterator();
const_iterator();
const_iterator(const_iterator&& other);
const_iterator(const_iterator const& other);
const_iterator& operator=(const_iterator&& other);
const_iterator& operator=(const_iterator const& other);
bool
operator==(const_iterator const& other) const;
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const;
pointer
operator->() const = delete;
const_iterator&
operator++();
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--();
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
const_iterator(
std::tuple<Bs...> const& bs, bool at_end);
void
construct(C<sizeof...(Bs)>)
{
auto constexpr I = sizeof...(Bs);
n_ = I;
}
template<std::size_t I>
void
construct(C<I>)
{
if(std::get<I>(*bs_).begin() !=
std::get<I>(*bs_).end())
{
n_ = I;
new(buf_.data()) iter_t<I>{
std::get<I>(*bs_).begin()};
return;
}
construct(C<I+1>{});
}
void
destroy(C<sizeof...(Bs)>)
{
return;
}
template<std::size_t I>
void
destroy(C<I>)
{
if(n_ == I)
{
using Iter = iter_t<I>;
iter<I>().~Iter();
return;
}
destroy(C<I+1>{});
}
void
move(C<sizeof...(Bs)>, const_iterator&&)
{
return;
}
template<std::size_t I>
void
move(C<I>, const_iterator&& other)
{
if(n_ == I)
{
new(buf_.data()) iter_t<I>{
std::move(other.iter<I>())};
return;
}
move(C<I+1>{}, std::move(other));
}
void
copy(C<sizeof...(Bs)>, const_iterator const&)
{
return;
}
template<std::size_t I>
void
copy(C<I>, const_iterator const& other)
{
if(n_ == I)
{
new(buf_.data()) iter_t<I>{
other.iter<I>()};
return;
}
copy(C<I+1>{}, other);
}
bool
equal(C<sizeof...(Bs)>,
const_iterator const&) const
{
return true;
}
template<std::size_t I>
bool
equal(C<I>, const_iterator const& other) const
{
if(n_ == I)
return iter<I>() == other.iter<I>();
return equal(C<I+1>{}, other);
}
[[noreturn]]
reference
dereference(C<sizeof...(Bs)>) const
{
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
reference
dereference(C<I>) const
{
if(n_ == I)
return *iter<I>();
return dereference(C<I+1>{});
}
[[noreturn]]
void
increment(C<sizeof...(Bs)>)
{
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
void
increment(C<I>)
{
if(n_ == I)
{
if(++iter<I>() !=
std::get<I>(*bs_).end())
return;
using Iter = iter_t<I>;
iter<I>().~Iter();
return construct(C<I+1>{});
}
increment(C<I+1>{});
}
void
decrement(C<sizeof...(Bs)>)
{
auto constexpr I = sizeof...(Bs);
if(n_ == I)
{
--n_;
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()};
}
decrement(C<I-1>{});
}
void
decrement(C<0>)
{
auto constexpr I = 0;
if(iter<I>() != std::get<I>(*bs_).begin())
{
--iter<I>();
return;
}
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
void
decrement(C<I>)
{
if(n_ == I)
{
if(iter<I>() != std::get<I>(*bs_).begin())
{
--iter<I>();
return;
}
--n_;
using Iter = iter_t<I>;
iter<I>().~Iter();
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()};
}
decrement(C<I-1>{});
}
};
//------------------------------------------------------------------------------
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::~const_iterator()
{
destroy(C<0>{});
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator()
: n_(sizeof...(Bs))
, bs_(nullptr)
{
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator(
std::tuple<Bs...> const& bs, bool at_end)
: bs_(&bs)
{
if(at_end)
n_ = sizeof...(Bs);
else
construct(C<0>{});
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator(const_iterator&& other)
: n_(other.n_)
, bs_(other.bs_)
{
move(C<0>{}, std::move(other));
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
const_iterator::const_iterator(const_iterator const& other)
: n_(other.n_)
, bs_(other.bs_)
{
copy(C<0>{}, other);
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator=(const_iterator&& other) ->
const_iterator&
{
if(&other == this)
return *this;
destroy(C<0>{});
n_ = other.n_;
bs_ = other.bs_;
move(C<0>{}, std::move(other));
return *this;
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator=(const_iterator const& other) ->
const_iterator&
{
if(&other == this)
return *this;
destroy(C<0>{});
n_ = other.n_;
bs_ = other.bs_;
copy(C<0>{}, other);
return *this;
}
template<class ValueType, class... Bs>
bool
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator==(const_iterator const& other) const
{
if(bs_ != other.bs_)
return false;
if(n_ != other.n_)
return false;
return equal(C<0>{}, other);
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator*() const ->
reference
{
return dereference(C<0>{});
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator++() ->
const_iterator&
{
increment(C<0>{});
return *this;
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::
const_iterator::operator--() ->
const_iterator&
{
decrement(C<sizeof...(Bs)>{});
return *this;
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::begin() const ->
const_iterator
{
return const_iterator(bs_, false);
}
template<class ValueType, class... Bs>
auto
buffer_cat_helper<ValueType, Bs...>::end() const ->
const_iterator
{
return const_iterator(bs_, true);
}
} // detail
} // beast
#endif

View File

@@ -0,0 +1,140 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_BUFFER_CONCEPTS_HPP
#define BEAST_DETAIL_BUFFER_CONCEPTS_HPP
#include <boost/asio/buffer.hpp>
#include <iterator>
#include <type_traits>
namespace beast {
namespace detail {
// Types that meet the requirements,
// for use with std::declval only.
template<class BufferType>
struct BufferSequence
{
using value_type = BufferType;
using const_iterator = BufferType const*;
~BufferSequence();
BufferSequence(BufferSequence const&) = default;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
};
using ConstBufferSequence =
BufferSequence<boost::asio::const_buffer>;
using MutableBufferSequence =
BufferSequence<boost::asio::mutable_buffer>;
template<class T, class BufferType>
class is_BufferSequence
{
template<class U, class R = std::is_convertible<
typename U::value_type, BufferType> >
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_base_of<
#if 0
std::bidirectional_iterator_tag,
typename std::iterator_traits<
typename U::const_iterator>::iterator_category>>
#else
// workaround:
// boost::asio::detail::consuming_buffers::const_iterator
// is not bidirectional
std::forward_iterator_tag,
typename std::iterator_traits<
typename U::const_iterator>::iterator_category>>
#endif
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R = typename
std::is_convertible<decltype(
std::declval<U>().begin()),
typename U::const_iterator>::type>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
template<class U, class R = typename std::is_convertible<decltype(
std::declval<U>().end()),
typename U::const_iterator>::type>
static R check4(int);
template<class>
static std::false_type check4(...);
using type4 = decltype(check4<T>(0));
public:
using type = std::integral_constant<bool,
std::is_copy_constructible<T>::value &&
std::is_destructible<T>::value &&
type1::value && type2::value &&
type3::value && type4::value>;
};
template<class T>
class is_Streambuf
{
template<class U, class R = std::integral_constant<
bool, is_BufferSequence<decltype(
std::declval<U>().prepare(1)),
boost::asio::mutable_buffer>::type::value>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::integral_constant<
bool, is_BufferSequence<decltype(
std::declval<U>().data()),
boost::asio::const_buffer>::type::value>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R = decltype(
std::declval<U>().commit(1), std::true_type{})>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
template<class U, class R = decltype(
std::declval<U>().consume(1), std::true_type{})>
static R check4(int);
template<class>
static std::false_type check4(...);
using type4 = decltype(check4<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().size()), std::size_t>>
static R check5(int);
template<class>
static std::false_type check5(...);
using type5 = decltype(check5<T>(0));
public:
using type = std::integral_constant<bool,
type1::value && type2::value &&
type3::value && type4::value &&
type5::value>;
};
} // detail
} // beast
#endif

View File

@@ -0,0 +1,53 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_GET_LOWEST_LAYER_HPP
#define BEAST_DETAIL_GET_LOWEST_LAYER_HPP
#include <type_traits>
namespace beast {
namespace detail {
template<class T>
class has_lowest_layer
{
template<class U, class R =
typename U::lowest_layer_type>
static std::true_type check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
static bool constexpr value = type::value;
};
template<class T, bool B>
struct maybe_get_lowest_layer
{
using type = T;
};
template<class T>
struct maybe_get_lowest_layer<T, true>
{
using type = typename T::lowest_layer_type;
};
// If T has a nested type lowest_layer_type,
// returns that, else returns T.
template<class T>
struct get_lowest_layer
{
using type = typename maybe_get_lowest_layer<T,
has_lowest_layer<T>::value>::type;
};
} // detail
} // beast
#endif

View File

@@ -0,0 +1,140 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_STREAM_CONCEPTS_HPP
#define BEAST_DETAIL_STREAM_CONCEPTS_HPP
#include <beast/buffer_concepts.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/system/error_code.hpp>
#include <type_traits>
#include <utility>
namespace beast {
namespace detail {
// Types that meet the requirements,
// for use with std::declval only.
struct StreamHandler
{
StreamHandler(StreamHandler const&) = default;
void operator()(boost::system::error_code ec, std::size_t);
};
using ReadHandler = StreamHandler;
using WriteHandler = StreamHandler;
template<class T>
class has_get_io_service
{
template<class U, class R = typename std::is_same<
decltype(std::declval<U>().get_io_service()),
boost::asio::io_service&>>
static R check(int);
template <class>
static std::false_type check(...);
public:
using type = decltype(check<T>(0));
};
template<class T>
class is_AsyncReadStream
{
template<class U, class R = decltype(
std::declval<U>().async_read_some(
std::declval<MutableBufferSequence>(),
std::declval<ReadHandler>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type1 = decltype(check<T>(0));
public:
using type = std::integral_constant<bool,
type1::value &&
has_get_io_service<T>::type::value>;
};
template<class T>
class is_AsyncWriteStream
{
template<class U, class R = decltype(
std::declval<U>().async_write_some(
std::declval<ConstBufferSequence>(),
std::declval<WriteHandler>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type1 = decltype(check<T>(0));
public:
using type = std::integral_constant<bool,
type1::value &&
has_get_io_service<T>::type::value>;
};
template<class T>
class is_SyncReadStream
{
using error_code =
boost::system::error_code;
template<class U, class R = std::is_same<decltype(
std::declval<U>().read_some(
std::declval<MutableBufferSequence>())),
std::size_t>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().read_some(
std::declval<MutableBufferSequence>(),
std::declval<error_code&>())), std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
public:
using type = std::integral_constant<bool,
type1::value && type2::value>;
};
template<class T>
class is_SyncWriteStream
{
using error_code =
boost::system::error_code;
template<class U, class R = std::is_same<decltype(
std::declval<U>().write_some(
std::declval<ConstBufferSequence>())),
std::size_t>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().write_some(
std::declval<ConstBufferSequence>(),
std::declval<error_code&>())), std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
public:
using type = std::integral_constant<bool,
type1::value && type2::value>;
};
} // detail
} // beast
#endif

View File

@@ -8,6 +8,7 @@
#ifndef BEAST_DETAIL_WRITE_STREAMBUF_HPP #ifndef BEAST_DETAIL_WRITE_STREAMBUF_HPP
#define BEAST_DETAIL_WRITE_STREAMBUF_HPP #define BEAST_DETAIL_WRITE_STREAMBUF_HPP
#include <beast/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <utility> #include <utility>
@@ -41,13 +42,6 @@ public:
! is_string_literal<T>::value; ! is_string_literal<T>::value;
}; };
template<class Streambuf>
inline
void
write_streambuf(Streambuf&)
{
}
template<class Streambuf> template<class Streambuf>
void void
write_streambuf(Streambuf& streambuf, write_streambuf(Streambuf& streambuf,
@@ -133,11 +127,11 @@ write_streambuf(Streambuf& streambuf, T const& t)
template<class Streambuf, class T0, class T1, class... TN> template<class Streambuf, class T0, class T1, class... TN>
void void
write_streambuf(Streambuf& streambuf, T0&& t0, T1&& t1, TN... tn) write_streambuf(Streambuf& streambuf,
T0 const& t0, T1 const& t1, TN const&... tn)
{ {
write_streambuf(streambuf, std::forward<T0>(t0)); write_streambuf(streambuf, t0);
write_streambuf(streambuf, std::forward<T1>(t1)); write_streambuf(streambuf, t1, tn...);
write_streambuf(streambuf, std::forward<TN>(tn)...);
} }
} // detail } // detail

View File

@@ -0,0 +1,170 @@
//
// 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_DOC_DEBUG_HPP
#define BEAST_DOC_DEBUG_HPP
namespace beast {
#if GENERATING_DOCS
/// doc type (documentation debug helper)
using doc_type = int;
/// doc enum (documentation debug helper)
enum doc_enum
{
/// One (documentation debug helper)
one,
/// Two (documentation debug helper)
two
};
/// doc enum class (documentation debug helper)
enum class doc_enum_class : unsigned
{
/// one (documentation debug helper)
one,
/// two (documentation debug helper)
two
};
/// doc func (documentation debug helper)
void doc_func();
/// doc class (documentation debug helper)
struct doc_class
{
/// doc class member func (documentation debug helper)
void func();
};
/// (documentation debug helper)
namespace nested {
/// doc type (documentation debug helper)
using nested_doc_type = int;
/// doc enum (documentation debug helper)
enum nested_doc_enum
{
/// One (documentation debug helper)
one,
/// Two (documentation debug helper)
two
};
/// doc enum class (documentation debug helper)
enum class nested_doc_enum_class : unsigned
{
/// one (documentation debug helper)
one,
/// two (documentation debug helper)
two
};
/// doc func (documentation debug helper)
void nested_doc_func();
/// doc class (documentation debug helper)
struct nested_doc_class
{
/// doc class member func (documentation debug helper)
void func();
};
} // nested
/** This is here to help troubleshoot doc/reference.xsl problems
Embedded references:
@li type @ref doc_type
@li enum @ref doc_enum
@li enum item @ref doc_enum::one
@li enum_class @ref doc_enum_class
@li enum_class item @ref doc_enum_class::one
@li func @ref doc_func
@li class @ref doc_class
@li class func @ref doc_class::func
@li nested type @ref nested::nested_doc_type
@li nested enum @ref nested::nested_doc_enum
@li nested enum item @ref nested::nested_doc_enum::one
@li nested enum_class @ref nested::nested_doc_enum_class
@li nested enum_class item @ref nested::nested_doc_enum_class::one
@li nested func @ref nested::nested_doc_func
@li nested class @ref nested::nested_doc_class
@li nested class func @ref nested::nested_doc_class::func
*/
void doc_debug();
namespace nested {
/** This is here to help troubleshoot doc/reference.xsl problems
Embedded references:
@li type @ref doc_type
@li enum @ref doc_enum
@li enum item @ref doc_enum::one
@li enum_class @ref doc_enum_class
@li enum_class item @ref doc_enum_class::one
@li func @ref doc_func
@li class @ref doc_class
@li class func @ref doc_class::func
@li nested type @ref nested_doc_type
@li nested enum @ref nested_doc_enum
@li nested enum item @ref nested_doc_enum::one
@li nested enum_class @ref nested_doc_enum_class
@li nested enum_class item @ref nested_doc_enum_class::one
@li nested func @ref nested_doc_func
@li nested class @ref nested_doc_class
@li nested class func @ref nested_doc_class::func
*/
void nested_doc_debug();
} // nested
#endif
} // beast
#endif

View File

@@ -28,28 +28,28 @@ namespace beast {
@tparam T The type of objects allocated by the allocator. @tparam T The type of objects allocated by the allocator.
@tparam Handler The type of handler. @tparam CompletionHandler The type of handler.
@note Allocated memory is only valid until the handler is called. The @note Allocated memory is only valid until the handler is called. The
caller is still responsible for freeing memory. caller is still responsible for freeing memory.
*/ */
#if GENERATING_DOCS #if GENERATING_DOCS
template <class T, class Handler> template <class T, class CompletionHandler>
class handler_alloc; class handler_alloc;
#else #else
template <class T, class Handler> template <class T, class CompletionHandler>
class handler_alloc class handler_alloc
{ {
private: private:
// We want a partial template specialization as a friend // We want a partial template specialization as a friend
// but that isn't allowed so we friend all versions. This // but that isn't allowed so we friend all versions. This
// should produce a compile error if Handler is not // should produce a compile error if CompletionHandler is not
// constructible from H. // constructible from H.
// //
template <class U, class H> template <class U, class H>
friend class handler_alloc; friend class handler_alloc;
Handler h_; CompletionHandler h_;
public: public:
using value_type = T; using value_type = T;
@@ -66,7 +66,7 @@ public:
The handler is moved or copied into the allocator. The handler is moved or copied into the allocator.
*/ */
explicit explicit
handler_alloc(Handler&& h) handler_alloc(CompletionHandler&& h)
: h_(std::move(h)) : h_(std::move(h))
{ {
} }
@@ -76,21 +76,21 @@ public:
A copy of the handler is made. A copy of the handler is made.
*/ */
explicit explicit
handler_alloc(Handler const& h) handler_alloc(CompletionHandler const& h)
: h_(h) : h_(h)
{ {
} }
template<class U> template<class U>
handler_alloc( handler_alloc(
handler_alloc<U, Handler>&& other) handler_alloc<U, CompletionHandler>&& other)
: h_(std::move(other.h_)) : h_(std::move(other.h_))
{ {
} }
template<class U> template<class U>
handler_alloc( handler_alloc(
handler_alloc<U, Handler> const& other) handler_alloc<U, CompletionHandler> const& other)
: h_(other.h_) : h_(other.h_)
{ {
} }
@@ -127,7 +127,7 @@ public:
friend friend
bool bool
operator==(handler_alloc const& lhs, operator==(handler_alloc const& lhs,
handler_alloc<U, Handler> const& rhs) handler_alloc<U, CompletionHandler> const& rhs)
{ {
return true; return true;
} }
@@ -136,7 +136,7 @@ public:
friend friend
bool bool
operator!=(handler_alloc const& lhs, operator!=(handler_alloc const& lhs,
handler_alloc<U, Handler> const& rhs) handler_alloc<U, CompletionHandler> const& rhs)
{ {
return !(lhs == rhs); return !(lhs == rhs);
} }

View File

@@ -0,0 +1,27 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HANDLER_CONCEPTS_HPP
#define BEAST_HANDLER_CONCEPTS_HPP
#include <beast/detail/is_call_possible.hpp>
#include <type_traits>
namespace beast {
/// Determine if `T` meets the requirements of @b `CompletionHandler`.
template<class T, class Signature>
#if GENERATING_DOCS
using is_CompletionHandler = std::integral_constant<bool, ...>;
#else
using is_CompletionHandler = std::integral_constant<bool,
std::is_copy_constructible<typename std::decay<T>::type>::value &&
detail::is_call_possible<T, Signature>::value>;
#endif
} // beast
#endif

View File

@@ -5,18 +5,19 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_HTTP_HPP_INCLUDED #ifndef BEAST_HTTP_HPP
#define BEAST_HTTP_HPP_INCLUDED #define BEAST_HTTP_HPP
#include <beast/http/basic_headers.hpp> #include <beast/http/basic_headers.hpp>
#include <beast/http/basic_parser.hpp> #include <beast/http/basic_parser_v1.hpp>
#include <beast/http/chunk_encode.hpp> #include <beast/http/body_type.hpp>
#include <beast/http/empty_body.hpp> #include <beast/http/empty_body.hpp>
#include <beast/http/error.hpp> #include <beast/http/error.hpp>
#include <beast/http/headers.hpp> #include <beast/http/headers.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/message_v1.hpp>
#include <beast/http/parse_error.hpp> #include <beast/http/parse_error.hpp>
#include <beast/http/parser.hpp> #include <beast/http/parser_v1.hpp>
#include <beast/http/read.hpp> #include <beast/http/read.hpp>
#include <beast/http/reason.hpp> #include <beast/http/reason.hpp>
#include <beast/http/resume_context.hpp> #include <beast/http/resume_context.hpp>

View File

@@ -8,7 +8,6 @@
#ifndef BEAST_HTTP_BASIC_HEADERS_HPP #ifndef BEAST_HTTP_BASIC_HEADERS_HPP
#define BEAST_HTTP_BASIC_HEADERS_HPP #define BEAST_HTTP_BASIC_HEADERS_HPP
#include <beast/type_check.hpp>
#include <beast/detail/ci_char_traits.hpp> #include <beast/detail/ci_char_traits.hpp>
#include <beast/detail/empty_base_optimization.hpp> #include <beast/detail/empty_base_optimization.hpp>
#include <boost/intrusive/list.hpp> #include <boost/intrusive/list.hpp>
@@ -237,9 +236,20 @@ public:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** Container to store HTTP headers. /** A container for storing HTTP headers.
Meets the requirements of `FieldSequence`. This container is designed to store the field value pairs that make
up the headers and trailers in a HTTP message. Objects of this type
are iterable, which each element holding the field name and field
value.
Field names are stored as-is, but comparison are case-insensitive.
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.
@note Meets the requirements of @b `FieldSequence`.
*/ */
template<class Allocator> template<class Allocator>
class basic_headers class basic_headers

View File

@@ -5,15 +5,14 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_HTTP_BASIC_PARSER_HPP #ifndef BEAST_HTTP_BASIC_PARSER_v1_HPP
#define BEAST_HTTP_BASIC_PARSER_HPP #define BEAST_HTTP_BASIC_PARSER_v1_HPP
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/parse_error.hpp> #include <beast/http/parse_error.hpp>
#include <beast/http/rfc7230.hpp> #include <beast/http/rfc7230.hpp>
#include <beast/http/detail/basic_parser.hpp> #include <beast/http/detail/basic_parser_v1.hpp>
#include <beast/type_check.hpp> #include <boost/asio/buffer.hpp>
#include <beast/detail/ci_char_traits.hpp>
#include <array> #include <array>
#include <cassert> #include <cassert>
#include <climits> #include <climits>
@@ -37,55 +36,55 @@ enum values
}; };
} // parse_flag } // parse_flag
/** Parser for producing HTTP requests and responses. /** Base class for parsing HTTP/1 requests and responses.
During parsing, callbacks will be made to the derived class During parsing, callbacks will be made to the derived class
if those members are present (detected through SFINAE). The if those members are present (detected through SFINAE). The
signatures which can be present in the derived class are:<br> signatures which can be present in the derived class are:<br>
@li `void on_method(boost::string_ref const&, error_code& ec)` @li `void on_method(boost::string_ref const&, error_code&)`
Called for each piece of the Request-Method Called for each piece of the Request-Method
@li `void on_uri(boost::string_ref const&, error_code& ec)` @li `void on_uri(boost::string_ref const&, error_code&)`
Called for each piece of the Request-URI Called for each piece of the Request-URI
@li `void on_reason(boost::string_ref const&, error_code& ec)` @li `void on_reason(boost::string_ref const&, error_code&)`
Called for each piece of the reason-phrase Called for each piece of the reason-phrase
@li `void on_request(error_code& ec)` @li `void on_request(error_code&)`
Called after the entire Request-Line has been parsed successfully. Called after the entire Request-Line has been parsed successfully.
@li `void on_response(error_code& ec)` @li `void on_response(error_code&)`
Called after the entire Response-Line has been parsed successfully. Called after the entire Response-Line has been parsed successfully.
@li `void on_field(boost::string_ref const&, error_code& ec)` @li `void on_field(boost::string_ref const&, error_code&)`
Called for each piece of the current header field. Called for each piece of the current header field.
@li `void on_value(boost::string_ref const&, error_code& ec)` @li `void on_value(boost::string_ref const&, error_code&)`
Called for each piece of the current header value. Called for each piece of the current header value.
@li `int on_headers(error_code& ec)` @li `int on_headers(error_code&)`
Called when all the headers have been parsed successfully. Called when all the headers have been parsed successfully.
@li `void on_body(boost::string_ref const&, error_code& ec)` @li `void on_body(boost::string_ref const&, error_code&)`
Called for each piece of the body. If the headers indicated Called for each piece of the body. If the headers indicated
chunked encoding, the chunk encoding is removed from the chunked encoding, the chunk encoding is removed from the
buffer before being passed to the callback. buffer before being passed to the callback.
@li `void on_complete(error_code& ec)` @li `void on_complete(error_code&)`
Called when the entire message has been parsed successfully. Called when the entire message has been parsed successfully.
At this point, basic_parser::complete() returns `true`, and At this point, @ref basic_parser_v1::complete returns `true`, and
the parser is ready to parse another message if keep_alive() the parser is ready to parse another message if keep_alive
would return `true`. would return `true`.
The return value of `on_headers` is special, it controls whether The return value of `on_headers` is special, it controls whether
@@ -101,7 +100,7 @@ enum values
The parser uses traits to determine if the callback is possible. The parser uses traits to determine if the callback is possible.
If the Derived type omits one or more callbacks, they are simply If the Derived type omits one or more callbacks, they are simply
skipped with no compilation error. The default behavior of on_body skipped with no compilation error. The default behavior of `on_body`
when the derived class does not provide the member, is to specify that when the derived class does not provide the member, is to specify that
the body should not be skipped. the body should not be skipped.
@@ -109,10 +108,10 @@ enum values
and the error is returned to the caller. and the error is returned to the caller.
*/ */
template<bool isRequest, class Derived> template<bool isRequest, class Derived>
class basic_parser class basic_parser_v1
{ {
private: private:
using self = basic_parser; using self = basic_parser_v1;
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&); typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
static std::uint64_t constexpr no_content_length = static std::uint64_t constexpr no_content_length =
@@ -237,13 +236,13 @@ private:
public: public:
/// Copy constructor. /// Copy constructor.
basic_parser(basic_parser const&) = default; basic_parser_v1(basic_parser_v1 const&) = default;
/// Copy assignment. /// Copy assignment.
basic_parser& operator=(basic_parser const&) = default; basic_parser_v1& operator=(basic_parser_v1 const&) = default;
/// Constructor /// Constructor
basic_parser() basic_parser_v1()
{ {
init(std::integral_constant<bool, isRequest>{}); init(std::integral_constant<bool, isRequest>{});
} }
@@ -345,7 +344,7 @@ public:
return s_ == s_restart; return s_ == s_restart;
} }
/** Write data to the parser. /** Write a sequence of buffers to the parser.
@param buffers An object meeting the requirements of @param buffers An object meeting the requirements of
ConstBufferSequence that represents the input sequence. ConstBufferSequence that represents the input sequence.
@@ -355,19 +354,25 @@ public:
@return The number of bytes consumed in the input sequence. @return The number of bytes consumed in the input sequence.
*/ */
template<class ConstBufferSequence> template<class ConstBufferSequence>
#if GENERATING_DOCS
std::size_t std::size_t
#else
typename std::enable_if<
! std::is_convertible<ConstBufferSequence,
boost::asio::const_buffer>::value,
std::size_t>::type
#endif
write(ConstBufferSequence const& buffers, error_code& ec); write(ConstBufferSequence const& buffers, error_code& ec);
/** Write data to the parser. /** Write a single buffer of data to the parser.
@param data A pointer to a buffer representing the input sequence. @param buffer The buffer to write.
@param size The number of bytes in the buffer pointed to by data.
@param ec Set to the error, if any error occurred. @param ec Set to the error, if any error occurred.
@return The number of bytes consumed in the input sequence. @return The number of bytes consumed in the buffer.
*/ */
std::size_t std::size_t
write(void const* data, std::size_t size, error_code& ec); write(boost::asio::const_buffer const& buffer, error_code& ec);
/** Called to indicate the end of file. /** Called to indicate the end of file.
@@ -377,8 +382,6 @@ public:
Callbacks and errors will still be processed as usual. Callbacks and errors will still be processed as usual.
@note This is typically called when a socket read returns eof. @note This is typically called when a socket read returns eof.
@throws boost::system::system_error Thrown on failure.
*/ */
void void
write_eof(error_code& ec); write_eof(error_code& ec);
@@ -756,6 +759,6 @@ private:
} // http } // http
} // beast } // beast
#include <beast/http/impl/basic_parser.ipp> #include <beast/http/impl/basic_parser_v1.ipp>
#endif #endif

View File

@@ -0,0 +1,19 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_BODY_TYPE_HPP
#define BEAST_HTTP_BODY_TYPE_HPP
// Convenience header to include everything
// needed when declarating a user defined Body type.
#include <beast/http/error.hpp>
#include <beast/http/message.hpp>
#include <beast/http/resume_context.hpp>
#include <boost/logic/tribool.hpp>
#endif

View File

@@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_HPP #ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
#define BEAST_HTTP_DETAIL_BASIC_PARSER_HPP #define BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
#include <boost/utility/string_ref.hpp> #include <boost/utility/string_ref.hpp>

View File

@@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_HTTP_CHUNK_ENCODE_HPP #ifndef BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
#define BEAST_HTTP_CHUNK_ENCODE_HPP #define BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <algorithm> #include <algorithm>
@@ -18,7 +18,6 @@
namespace beast { namespace beast {
namespace http { namespace http {
namespace detail { namespace detail {
template <class Buffers> template <class Buffers>
@@ -237,9 +236,7 @@ chunk_encoded_buffers<Buffers>::const_iterator::const_iterator(
{ {
} }
} // detail /* Returns a chunk-encoded BufferSequence.
/** Returns a chunk-encoded BufferSequence.
See: See:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
@@ -276,6 +273,7 @@ chunk_encode_final()
"0\r\n\r\n", 5); "0\r\n\r\n", 5);
} }
} // detail
} // http } // http
} // beast } // beast

View File

@@ -0,0 +1,43 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_DETAIL_HAS_CONTENT_LENGTH_HPP
#define BEAST_HTTP_DETAIL_HAS_CONTENT_LENGTH_HPP
#include <cstdint>
#include <type_traits>
namespace beast {
namespace http {
namespace detail {
template<class T>
class has_content_length_value
{
template<class U, class R = typename std::is_convertible<
decltype(std::declval<U>().content_length()),
std::uint64_t>>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
// `true` if `T` meets the requirements.
static bool const value = type::value;
};
// Determines if the writer can provide the content length
template<class T>
using has_content_length =
std::integral_constant<bool,
has_content_length_value<T>::value>;
} // detail
} // http
} // beast
#endif

View File

@@ -1,123 +0,0 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_DETAIL_WRITE_PREPARATION_HPP
#define BEAST_HTTP_DETAIL_WRITE_PREPARATION_HPP
#include <beast/http/error.hpp>
#include <beast/streambuf.hpp>
#include <beast/write_streambuf.hpp>
namespace beast {
namespace http {
namespace detail {
template<class T>
class has_content_length_value
{
template<class U, class R = typename std::is_convertible<
decltype(std::declval<U>().content_length()),
std::size_t>>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
// `true` if `T` meets the requirements.
static bool const value = type::value;
};
// Determines if the writer can provide the content length
template<class T>
using has_content_length =
std::integral_constant<bool,
has_content_length_value<T>::value>;
template<bool isRequest, class Body, class Headers>
struct write_preparation
{
using headers_type =
basic_headers<std::allocator<char>>;
message<isRequest, Body, Headers> const& msg;
typename Body::writer w;
streambuf sb;
bool chunked;
bool close;
explicit
write_preparation(
message<isRequest, Body, Headers> const& msg_)
: msg(msg_)
, w(msg)
{
}
void
init(error_code& ec)
{
w.init(ec);
if(ec)
return;
// VFALCO TODO This implementation requires making a
// copy of the headers, we can do better.
// VFALCO Should we be using handler_alloc?
headers_type h(msg.headers.begin(), msg.headers.end());
set_content_length(h, has_content_length<
typename Body::writer>{});
// VFALCO TODO Keep-Alive
if(close)
{
if(msg.version >= 11)
h.insert("Connection", "close");
}
else
{
if(msg.version < 11)
h.insert("Connection", "keep-alive");
}
msg.write_firstline(sb);
write_fields(sb, h);
beast::write(sb, "\r\n");
}
private:
void
set_content_length(headers_type& h,
std::true_type)
{
close = false;
chunked = false;
h.insert("Content-Length", w.content_length());
}
void
set_content_length(headers_type& h,
std::false_type)
{
if(msg.version >= 11)
{
close = false;
chunked = true;
h.insert("Transfer-Encoding", "chunked");
}
else
{
close = true;
chunked = false;
}
}
};
} // detail
} // http
} // beast
#endif

View File

@@ -8,8 +8,7 @@
#ifndef BEAST_HTTP_EMPTY_BODY_HPP #ifndef BEAST_HTTP_EMPTY_BODY_HPP
#define BEAST_HTTP_EMPTY_BODY_HPP #define BEAST_HTTP_EMPTY_BODY_HPP
#include <beast/http/error.hpp> #include <beast/http/body_type.hpp>
#include <beast/http/message.hpp>
#include <beast/streambuf.hpp> #include <beast/streambuf.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <memory> #include <memory>
@@ -19,6 +18,8 @@ namespace beast {
namespace http { namespace http {
/** An empty content-body. /** An empty content-body.
Meets the requirements of @b `Body`.
*/ */
struct empty_body struct empty_body
{ {
@@ -35,9 +36,9 @@ private:
struct reader struct reader
{ {
template<bool isRequest, class Allocator> template<bool isRequest, class Headers>
explicit explicit
reader(message<isRequest, empty_body, Allocator>&) reader(message<isRequest, empty_body, Headers>&)
{ {
} }
@@ -49,9 +50,12 @@ private:
struct writer struct writer
{ {
template<bool isRequest, class Allocator> writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
explicit explicit
writer(message<isRequest, empty_body, Allocator> const& m) writer(message<isRequest, empty_body, Headers> const& m)
{ {
} }
@@ -60,7 +64,7 @@ private:
{ {
} }
std::size_t std::uint64_t
content_length() const content_length() const
{ {
return 0; return 0;

View File

@@ -9,6 +9,7 @@
#define BEAST_HTTP_ERROR_HPP #define BEAST_HTTP_ERROR_HPP
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
namespace beast { namespace beast {
namespace http { namespace http {

View File

@@ -14,10 +14,7 @@
namespace beast { namespace beast {
namespace http { namespace http {
template<class Allocator> using headers =
using headers = basic_headers<Allocator>;
using http_headers =
basic_headers<std::allocator<char>>; basic_headers<std::allocator<char>>;
} // http } // http

View File

@@ -8,8 +8,6 @@
#ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP #ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP #define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#include <beast/type_check.hpp>
namespace beast { namespace beast {
namespace http { namespace http {
@@ -59,7 +57,7 @@ delete_all()
for(auto it = list_.begin(); it != list_.end();) for(auto it = list_.begin(); it != list_.end();)
{ {
auto& e = *it++; auto& e = *it++;
e.~element(); alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate( alloc_traits::deallocate(
this->member(), &e, 1); this->member(), &e, 1);
} }
@@ -252,6 +250,7 @@ erase(boost::string_ref const& name)
auto& e = *it; auto& e = *it;
set_.erase(set_.iterator_to(e)); set_.erase(set_.iterator_to(e));
list_.erase(list_.iterator_to(e)); list_.erase(list_.iterator_to(e));
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(), &e, 1); alloc_traits::deallocate(this->member(), &e, 1);
return 1; return 1;
} }

View File

@@ -5,15 +5,17 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_HTTP_IMPL_BASIC_PARSER_IPP #ifndef BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP
#define BEAST_HTTP_IMPL_BASIC_PARSER_IPP #define BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP
#include <beast/buffer_concepts.hpp>
namespace beast { namespace beast {
namespace http { namespace http {
template<bool isRequest, class Derived> template<bool isRequest, class Derived>
bool bool
basic_parser<isRequest, Derived>:: basic_parser_v1<isRequest, Derived>::
keep_alive() const keep_alive() const
{ {
if(http_major_ > 0 && http_minor_ > 0) if(http_major_ > 0 && http_minor_ > 0)
@@ -31,10 +33,31 @@ keep_alive() const
// Implementation inspired by nodejs/http-parser // Implementation inspired by nodejs/http-parser
template<bool isRequest, class Derived>
template<class ConstBufferSequence>
typename std::enable_if<
! std::is_convertible<ConstBufferSequence,
boost::asio::const_buffer>::value,
std::size_t>::type
basic_parser_v1<isRequest, Derived>::
write(ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(is_ConstBufferSequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
std::size_t used = 0;
for(auto const& buffer : buffers)
{
used += write(buffer, ec);
if(ec)
break;
}
return used;
}
template<bool isRequest, class Derived> template<bool isRequest, class Derived>
std::size_t std::size_t
basic_parser<isRequest, Derived>:: basic_parser_v1<isRequest, Derived>::
write(void const* data, std::size_t size, error_code& ec) write(boost::asio::const_buffer const& buffer, error_code& ec)
{ {
using beast::http::detail::is_digit; using beast::http::detail::is_digit;
using beast::http::detail::is_token; using beast::http::detail::is_token;
@@ -42,7 +65,12 @@ write(void const* data, std::size_t size, error_code& ec)
using beast::http::detail::to_field_char; using beast::http::detail::to_field_char;
using beast::http::detail::to_value_char; using beast::http::detail::to_value_char;
using beast::http::detail::unhex; using beast::http::detail::unhex;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
auto const data = buffer_cast<void const*>(buffer);
auto const size = buffer_size(buffer);
if(size == 0 && s_ != s_closed) if(size == 0 && s_ != s_closed)
return 0; return 0;
@@ -997,30 +1025,9 @@ write(void const* data, std::size_t size, error_code& ec)
return used(); return used();
} }
template<bool isRequest, class Derived>
template<class ConstBufferSequence>
std::size_t
basic_parser<isRequest, Derived>::
write(ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(is_ConstBufferSequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
std::size_t used = 0;
for(auto const& buffer : buffers)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
used += write(buffer_cast<void const*>(buffer),
buffer_size(buffer), ec);
if(ec)
break;
}
return used;
}
template<bool isRequest, class Derived> template<bool isRequest, class Derived>
void void
basic_parser<isRequest, Derived>:: basic_parser_v1<isRequest, Derived>::
write_eof(error_code& ec) write_eof(error_code& ec)
{ {
switch(s_) switch(s_)
@@ -1040,7 +1047,7 @@ write_eof(error_code& ec)
template<bool isRequest, class Derived> template<bool isRequest, class Derived>
bool bool
basic_parser<isRequest, Derived>:: basic_parser_v1<isRequest, Derived>::
needs_eof(std::true_type) const needs_eof(std::true_type) const
{ {
return false; return false;
@@ -1048,7 +1055,7 @@ needs_eof(std::true_type) const
template<bool isRequest, class Derived> template<bool isRequest, class Derived>
bool bool
basic_parser<isRequest, Derived>:: basic_parser_v1<isRequest, Derived>::
needs_eof(std::false_type) const needs_eof(std::false_type) const
{ {
// See RFC 2616 section 4.4 // See RFC 2616 section 4.4

View File

@@ -1,318 +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_IMPL_MESSAGE_IPP
#define BEAST_HTTP_IMPL_MESSAGE_IPP
#include <beast/http/chunk_encode.hpp>
#include <beast/http/resume_context.hpp>
#include <beast/http/rfc2616.hpp>
#include <beast/write_streambuf.hpp>
#include <beast/type_check.hpp>
#include <beast/http/detail/write_preparation.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/logic/tribool.hpp>
#include <condition_variable>
#include <mutex>
namespace beast {
namespace http {
template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message()
{
}
template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message(request_params params)
{
static_assert(isRequest, "message is not a request");
this->method = params.method;
this->url = std::move(params.url);
version = params.version;
}
template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message(response_params params)
{
static_assert(! isRequest, "message is not a response");
this->status = params.status;
this->reason = std::move(params.reason);
version = params.version;
}
template<bool isRequest, class Body, class Headers>
template<class Streambuf>
void
message<isRequest, Body, Headers>::
write_firstline(Streambuf& streambuf,
std::true_type) const
{
write(streambuf, to_string(this->method));
write(streambuf, " ");
write(streambuf, this->url);
switch(version)
{
case 10:
write(streambuf, " HTTP/1.0\r\n");
break;
case 11:
write(streambuf, " HTTP/1.1\r\n");
break;
default:
write(streambuf, " HTTP/");
write(streambuf, version / 10);
write(streambuf, ".");
write(streambuf, version % 10);
write(streambuf, "\r\n");
break;
}
}
template<bool isRequest, class Body, class Headers>
template<class Streambuf>
void
message<isRequest, Body, Headers>::
write_firstline(Streambuf& streambuf,
std::false_type) const
{
switch(version)
{
case 10:
write(streambuf, "HTTP/1.0 ");
break;
case 11:
write(streambuf, "HTTP/1.1 ");
break;
default:
write(streambuf, " HTTP/");
write(streambuf, version / 10);
write(streambuf, ".");
write(streambuf, version % 10);
write(streambuf, " ");
break;
}
write(streambuf, this->status);
write(streambuf, " ");
write(streambuf, this->reason);
write(streambuf, "\r\n");
}
namespace detail {
template<class ConstBufferSequence>
std::string
buffers_to_string(ConstBufferSequence const& buffers)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(buffers));
for(auto const& b : buffers)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
return s;
}
class writef_ostream
{
std::ostream& os_;
bool chunked_;
public:
writef_ostream(std::ostream& os, bool chunked)
: os_(os)
, chunked_(chunked)
{
}
template<class ConstBufferSequence>
void
operator()(ConstBufferSequence const& buffers)
{
if(chunked_)
os_ << buffers_to_string(
chunk_encode(buffers));
else
os_ << buffers_to_string(
buffers);
}
};
} // detail
// Diagnostic output only
template<bool isRequest, class Body, class Headers>
std::ostream&
operator<<(std::ostream& os,
message<isRequest, Body, Headers> const& msg)
{
error_code ec;
detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec);
if(ec)
return os;
std::mutex m;
std::condition_variable cv;
bool ready = false;
resume_context resume{
[&]
{
std::lock_guard<std::mutex> lock(m);
ready = true;
cv.notify_one();
}};
auto copy = resume;
os << detail::buffers_to_string(wp.sb.data());
wp.sb.consume(wp.sb.size());
detail::writef_ostream writef(os, wp.chunked);
for(;;)
{
{
auto result = wp.w(std::move(copy), ec, writef);
if(ec)
return os;
if(result)
break;
if(boost::indeterminate(result))
{
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
}
wp.sb.consume(wp.sb.size());
for(;;)
{
auto result = wp.w(std::move(copy), ec, writef);
if(ec)
return os;
if(result)
break;
if(boost::indeterminate(result))
{
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
}
}
if(wp.chunked)
{
// VFALCO Unfortunately the current interface to the
// Writer concept prevents us from using coalescing the
// final body chunk with the final chunk delimiter.
//
// write final chunk
os << detail::buffers_to_string(chunk_encode_final());
if(ec)
return os;
}
os << std::endl;
return os;
}
//------------------------------------------------------------------------------
template<bool isRequest, class Body, class Headers>
void
set_connection(bool keep_alive,
message<isRequest, Body, Headers>& req)
{
if(req.version >= 11)
{
if(! keep_alive)
req.headers.replace("Connection", "close");
else
req.headers.erase("Connection");
}
else
{
if(keep_alive)
req.headers.replace("Connection", "keep-alive");
else
req.headers.erase("Connection");
}
}
template<class Body, class Headers,
class OtherBody, class OtherAllocator>
void
set_connection(bool keep_alive,
message<false, Body, Headers>& resp,
message<true, OtherBody, OtherAllocator> const& req)
{
if(req.version >= 11)
{
if(rfc2616::token_in_list(req["Connection"], "close"))
keep_alive = false;
}
else
{
if(! rfc2616::token_in_list(req["Connection"], "keep-alive"))
keep_alive = false;
}
set_connection(keep_alive, resp);
}
template<class Streambuf, class FieldSequence>
void
write_fields(Streambuf& streambuf, FieldSequence const& fields)
{
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf 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");
}
}
template<bool isRequest, class Body, class Headers>
bool
is_keep_alive(message<isRequest, Body, Headers> const& msg)
{
if(msg.version >= 11)
{
if(rfc2616::token_in_list(
msg.headers["Connection"], "close"))
return false;
return true;
}
if(rfc2616::token_in_list(
msg.headers["Connection"], "keep-alive"))
return true;
return false;
}
template<bool isRequest, class Body, class Headers>
bool
is_upgrade(message<isRequest, Body, Headers> const& msg)
{
if(msg.version < 11)
return false;
if(rfc2616::token_in_list(
msg.headers["Connection"], "upgrade"))
return true;
return false;
}
} // http
} // beast
#include <beast/http/impl/message.ipp>
#endif

View File

@@ -0,0 +1,242 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_IMPL_MESSAGE_V1_IPP
#define BEAST_HTTP_IMPL_MESSAGE_V1_IPP
#include <beast/http/rfc2616.hpp>
#include <beast/http/detail/has_content_length.hpp>
#include <boost/optional.hpp>
#include <stdexcept>
namespace beast {
namespace http {
template<bool isRequest, class Body, class Headers>
message_v1<isRequest, Body, Headers>::
message_v1(request_params params)
{
static_assert(isRequest, "message is not a request");
this->method = params.method;
this->url = std::move(params.url);
version = params.version;
}
template<bool isRequest, class Body, class Headers>
message_v1<isRequest, Body, Headers>::
message_v1(response_params params)
{
static_assert(! isRequest, "message is not a response");
this->status = params.status;
this->reason = std::move(params.reason);
version = params.version;
}
//------------------------------------------------------------------------------
template<bool isRequest, class Body, class Headers>
bool
is_keep_alive(message_v1<isRequest, Body, Headers> const& msg)
{
if(msg.version >= 11)
{
if(rfc2616::token_in_list(
msg.headers["Connection"], "close"))
return false;
return true;
}
if(rfc2616::token_in_list(
msg.headers["Connection"], "keep-alive"))
return true;
return false;
}
template<bool isRequest, class Body, class Headers>
bool
is_upgrade(message_v1<isRequest, Body, Headers> const& msg)
{
if(msg.version < 11)
return false;
if(rfc2616::token_in_list(
msg.headers["Connection"], "upgrade"))
return true;
return false;
}
namespace detail {
struct prepare_info
{
boost::optional<connection> connection_value;
boost::optional<std::uint64_t> content_length;
};
template<bool isRequest, class Body, class Headers>
inline
void
prepare_options(prepare_info& pi,
message_v1<isRequest, Body, Headers>& msg)
{
}
template<bool isRequest, class Body, class Headers>
void
prepare_option(prepare_info& pi,
message_v1<isRequest, Body, Headers>& msg,
connection value)
{
pi.connection_value = value;
}
template<
bool isRequest, class Body, class Headers,
class Opt, class... Opts>
void
prepare_options(prepare_info& pi,
message_v1<isRequest, Body, Headers>& msg,
Opt&& opt, Opts&&... opts)
{
prepare_option(pi, msg, opt);
prepare_options(pi, msg,
std::forward<Opts>(opts)...);
}
template<bool isRequest, class Body, class Headers>
void
prepare_content_length(prepare_info& pi,
message_v1<isRequest, Body, Headers> const& msg,
std::true_type)
{
typename Body::writer w(msg);
//w.init(ec); // VFALCO This is a design problem!
pi.content_length = w.content_length();
}
template<bool isRequest, class Body, class Headers>
void
prepare_content_length(prepare_info& pi,
message_v1<isRequest, Body, Headers> const& msg,
std::false_type)
{
pi.content_length = boost::none;
}
} // detail
template<bool isRequest, class Body, class Headers>
void
prepare_connection(
message_v1<isRequest, Body, Headers>& msg)
{
if(msg.version >= 11)
{
if(! msg.headers.exists("Content-Length") &&
! rfc2616::token_in_list(
msg.headers["Transfer-Encoding"], "chunked"))
if(! rfc2616::token_in_list(
msg.headers["Connection"], "close"))
msg.headers.insert("Connection", "close");
}
else
{
if(! msg.headers.exists("Content-Length"))
{
// VFALCO We are erasing the whole header when we
// should be removing just the keep-alive.
if(rfc2616::token_in_list(
msg.headers["Connection"], "keep-alive"))
msg.headers.erase("Connection");
}
}
}
template<
bool isRequest, class Body, class Headers,
class... Options>
void
prepare(message_v1<isRequest, Body, Headers>& msg,
Options&&... options)
{
// VFALCO TODO
//static_assert(is_WritableBody<Body>::value,
// "WritableBody requirements not met");
detail::prepare_info pi;
detail::prepare_content_length(pi, msg,
detail::has_content_length<typename Body::writer>{});
detail::prepare_options(pi, msg,
std::forward<Options>(options)...);
if(msg.headers.exists("Connection"))
throw std::invalid_argument(
"prepare called with Connection field set");
if(msg.headers.exists("Content-Length"))
throw std::invalid_argument(
"prepare called with Content-Length field set");
if(rfc2616::token_in_list(
msg.headers["Transfer-Encoding"], "chunked"))
throw std::invalid_argument(
"prepare called with Transfer-Encoding: chunked set");
if(pi.connection_value != connection::upgrade)
{
if(pi.content_length)
{
// VFALCO TODO Use a static string here
msg.headers.insert("Content-Length",
std::to_string(*pi.content_length));
}
else if(msg.version >= 11)
{
msg.headers.insert("Transfer-Encoding", "chunked");
}
}
auto const content_length =
msg.headers.exists("Content-Length");
if(pi.connection_value)
{
switch(*pi.connection_value)
{
case connection::upgrade:
msg.headers.insert("Connection", "upgrade");
break;
case connection::keep_alive:
if(msg.version < 11)
{
if(content_length)
msg.headers.insert("Connection", "keep-alive");
}
break;
case connection::close:
if(msg.version >= 11)
msg.headers.insert("Connection", "close");
break;
}
}
// rfc7230 6.7.
if(msg.version < 11 && rfc2616::token_in_list(
msg.headers["Connection"], "upgrade"))
throw std::invalid_argument(
"invalid version for Connection: upgrade");
// rfc7230 3.3.2
if(msg.headers.exists("Content-Length") &&
msg.headers.exists("Transfer-Encoding"))
throw std::invalid_argument(
"Content-Length and Transfer-Encoding cannot be combined");
}
} // http
} // beast
#endif

View File

@@ -8,8 +8,10 @@
#ifndef BEAST_HTTP_IMPL_READ_IPP_HPP #ifndef BEAST_HTTP_IMPL_READ_IPP_HPP
#define BEAST_HTTP_IMPL_READ_IPP_HPP #define BEAST_HTTP_IMPL_READ_IPP_HPP
#include <beast/http/parser_v1.hpp>
#include <beast/bind_handler.hpp> #include <beast/bind_handler.hpp>
#include <beast/handler_alloc.hpp> #include <beast/handler_alloc.hpp>
#include <beast/stream_concepts.hpp>
#include <cassert> #include <cassert>
namespace beast { namespace beast {
@@ -26,10 +28,10 @@ class read_op
handler_alloc<char, Handler>; handler_alloc<char, Handler>;
using parser_type = using parser_type =
parser<isRequest, Body, Headers>; parser_v1<isRequest, Body, Headers>;
using message_type = using message_type =
message<isRequest, Body, Headers>; message_v1<isRequest, Body, Headers>;
struct data struct data
{ {
@@ -214,14 +216,14 @@ template<class SyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void
read(SyncReadStream& stream, Streambuf& streambuf, read(SyncReadStream& stream, Streambuf& streambuf,
message<isRequest, Body, Headers>& m, message_v1<isRequest, Body, Headers>& m,
error_code& ec) error_code& ec)
{ {
static_assert(is_SyncReadStream<SyncReadStream>::value, static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met"); "SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met"); "Streambuf requirements not met");
parser<isRequest, Body, Headers> p; parser_v1<isRequest, Body, Headers> p;
bool started = false; bool started = false;
for(;;) for(;;)
{ {
@@ -264,7 +266,7 @@ template<class AsyncReadStream, class Streambuf,
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
async_read(AsyncReadStream& stream, Streambuf& streambuf, async_read(AsyncReadStream& stream, Streambuf& streambuf,
message<isRequest, Body, Headers>& m, message_v1<isRequest, Body, Headers>& m,
ReadHandler&& handler) ReadHandler&& handler)
{ {
static_assert(is_AsyncReadStream<AsyncReadStream>::value, static_assert(is_AsyncReadStream<AsyncReadStream>::value,

View File

@@ -8,18 +8,22 @@
#ifndef BEAST_HTTP_IMPL_WRITE_IPP #ifndef BEAST_HTTP_IMPL_WRITE_IPP
#define BEAST_HTTP_IMPL_WRITE_IPP #define BEAST_HTTP_IMPL_WRITE_IPP
#include <beast/http/chunk_encode.hpp>
#include <beast/http/resume_context.hpp> #include <beast/http/resume_context.hpp>
#include <beast/http/detail/write_preparation.hpp> #include <beast/http/detail/chunk_encode.hpp>
#include <beast/http/detail/has_content_length.hpp>
#include <beast/buffer_cat.hpp> #include <beast/buffer_cat.hpp>
#include <beast/bind_handler.hpp> #include <beast/bind_handler.hpp>
#include <beast/buffer_concepts.hpp>
#include <beast/handler_alloc.hpp> #include <beast/handler_alloc.hpp>
#include <beast/stream_concepts.hpp>
#include <beast/streambuf.hpp> #include <beast/streambuf.hpp>
#include <beast/type_check.hpp> #include <beast/write_streambuf.hpp>
#include <boost/asio/write.hpp> #include <boost/asio/write.hpp>
#include <boost/logic/tribool.hpp> #include <boost/logic/tribool.hpp>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <ostream>
#include <sstream>
#include <type_traits> #include <type_traits>
namespace beast { namespace beast {
@@ -27,6 +31,114 @@ namespace http {
namespace detail { namespace detail {
template<class Streambuf, class Body, class Headers>
void
write_firstline(Streambuf& streambuf,
message_v1<true, Body, Headers> const& msg)
{
write(streambuf, msg.method);
write(streambuf, " ");
write(streambuf, msg.url);
switch(msg.version)
{
case 10:
write(streambuf, " HTTP/1.0\r\n");
break;
case 11:
write(streambuf, " HTTP/1.1\r\n");
break;
default:
write(streambuf, " HTTP/");
write(streambuf, msg.version / 10);
write(streambuf, ".");
write(streambuf, msg.version % 10);
write(streambuf, "\r\n");
break;
}
}
template<class Streambuf, class Body, class Headers>
void
write_firstline(Streambuf& streambuf,
message_v1<false, Body, Headers> const& msg)
{
switch(msg.version)
{
case 10:
write(streambuf, "HTTP/1.0 ");
break;
case 11:
write(streambuf, "HTTP/1.1 ");
break;
default:
write(streambuf, " HTTP/");
write(streambuf, msg.version / 10);
write(streambuf, ".");
write(streambuf, msg.version % 10);
write(streambuf, " ");
break;
}
write(streambuf, msg.status);
write(streambuf, " ");
write(streambuf, msg.reason);
write(streambuf, "\r\n");
}
template<class Streambuf, class FieldSequence>
void
write_fields(Streambuf& streambuf, FieldSequence const& fields)
{
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf 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");
}
}
template<bool isRequest, class Body, class Headers>
struct write_preparation
{
using headers_type =
basic_headers<std::allocator<char>>;
message_v1<isRequest, Body, Headers> const& msg;
typename Body::writer w;
streambuf sb;
bool chunked;
bool close;
explicit
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") ||
(msg.version < 11 && ! msg.headers.exists(
"Content-Length")))
{
}
void
init(error_code& ec)
{
w.init(ec);
if(ec)
return;
write_firstline(sb, msg);
write_fields(sb, msg.headers);
beast::write(sb, "\r\n");
}
};
template<class Stream, class Handler, template<class Stream, class Handler,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
class write_op class write_op
@@ -48,7 +160,7 @@ class write_op
template<class DeducedHandler> template<class DeducedHandler>
data(DeducedHandler&& h_, Stream& s_, data(DeducedHandler&& h_, Stream& s_,
message<isRequest, Body, Headers> const& m_) message_v1<isRequest, Body, Headers> const& m_)
: s(s_) : s(s_)
, wp(m_) , wp(m_)
, h(std::forward<DeducedHandler>(h_)) , h(std::forward<DeducedHandler>(h_))
@@ -77,7 +189,7 @@ class write_op
if(d.wp.chunked) if(d.wp.chunked)
boost::asio::async_write(d.s, boost::asio::async_write(d.s,
buffer_cat(d.wp.sb.data(), buffer_cat(d.wp.sb.data(),
chunk_encode(buffers)), detail::chunk_encode(buffers)),
std::move(self_)); std::move(self_));
else else
boost::asio::async_write(d.s, boost::asio::async_write(d.s,
@@ -104,7 +216,7 @@ class write_op
// write body // write body
if(d.wp.chunked) if(d.wp.chunked)
boost::asio::async_write(d.s, boost::asio::async_write(d.s,
chunk_encode(buffers), detail::chunk_encode(buffers),
std::move(self_)); std::move(self_));
else else
boost::asio::async_write(d.s, boost::asio::async_write(d.s,
@@ -269,7 +381,7 @@ operator()(error_code ec, std::size_t, bool again)
// write final chunk // write final chunk
d.state = 5; d.state = 5;
boost::asio::async_write(d.s, boost::asio::async_write(d.s,
chunk_encode_final(), std::move(*this)); detail::chunk_encode_final(), std::move(*this));
return; return;
case 5: case 5:
@@ -311,7 +423,7 @@ public:
// write headers and body // write headers and body
if(chunked_) if(chunked_)
boost::asio::write(stream_, buffer_cat( boost::asio::write(stream_, buffer_cat(
sb_.data(), chunk_encode(buffers)), ec_); sb_.data(), detail::chunk_encode(buffers)), ec_);
else else
boost::asio::write(stream_, buffer_cat( boost::asio::write(stream_, buffer_cat(
sb_.data(), buffers), ec_); sb_.data(), buffers), ec_);
@@ -340,7 +452,7 @@ public:
// write body // write body
if(chunked_) if(chunked_)
boost::asio::write(stream_, boost::asio::write(stream_,
chunk_encode(buffers), ec_); detail::chunk_encode(buffers), ec_);
else else
boost::asio::write(stream_, buffers, ec_); boost::asio::write(stream_, buffers, ec_);
} }
@@ -354,9 +466,25 @@ template<class SyncWriteStream,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void
write(SyncWriteStream& stream, write(SyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg, message_v1<isRequest, Body, Headers> const& msg)
{
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
error_code ec;
write(stream, msg, ec);
if(ec)
throw boost::system::system_error{ec};
}
template<class SyncWriteStream,
bool isRequest, class Body, class Headers>
void
write(SyncWriteStream& stream,
message_v1<isRequest, Body, Headers> const& msg,
boost::system::error_code& ec) boost::system::error_code& ec)
{ {
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
detail::write_preparation<isRequest, Body, Headers> wp(msg); detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec); wp.init(ec);
if(ec) if(ec)
@@ -420,7 +548,7 @@ write(SyncWriteStream& stream,
// final body chunk with the final chunk delimiter. // final body chunk with the final chunk delimiter.
// //
// write final chunk // write final chunk
boost::asio::write(stream, chunk_encode_final(), ec); boost::asio::write(stream, detail::chunk_encode_final(), ec);
if(ec) if(ec)
return; return;
} }
@@ -437,7 +565,7 @@ template<class AsyncWriteStream,
typename async_completion< typename async_completion<
WriteHandler, void(error_code)>::result_type WriteHandler, void(error_code)>::result_type
async_write(AsyncWriteStream& stream, async_write(AsyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg, message_v1<isRequest, Body, Headers> const& msg,
WriteHandler&& handler) WriteHandler&& handler)
{ {
static_assert( static_assert(
@@ -450,6 +578,68 @@ async_write(AsyncWriteStream& stream,
return completion.result.get(); return completion.result.get();
} }
namespace detail {
class ostream_SyncStream
{
std::ostream& os_;
public:
ostream_SyncStream(std::ostream& os)
: os_(os)
{
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
error_code ec;
auto const n = write_some(buffers, ec);
if(ec)
throw boost::system::system_error{ec};
return n;
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers,
error_code& ec)
{
std::size_t n = 0;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
for(auto const& buffer : buffers)
{
os_.write(buffer_cast<char const*>(buffer),
buffer_size(buffer));
if(os_.fail())
{
ec = boost::system::errc::make_error_code(
boost::system::errc::no_stream_resources);
break;
}
n += buffer_size(buffer);
}
return n;
}
};
} // detail
template<bool isRequest, class Body, class Headers>
std::ostream&
operator<<(std::ostream& os,
message_v1<isRequest, Body, Headers> const& msg)
{
detail::ostream_SyncStream oss(os);
error_code ec;
write(oss, msg, ec);
if(ec && ec != boost::asio::error::eof)
throw boost::system::system_error{ec};
return os;
}
} // http } // http
} // beast } // beast

View File

@@ -9,11 +9,7 @@
#define BEAST_HTTP_MESSAGE_HPP #define BEAST_HTTP_MESSAGE_HPP
#include <beast/http/basic_headers.hpp> #include <beast/http/basic_headers.hpp>
#include <beast/http/method.hpp>
#include <beast/buffers_debug.hpp>
#include <beast/type_check.hpp>
#include <memory> #include <memory>
#include <ostream>
#include <string> #include <string>
namespace beast { namespace beast {
@@ -23,7 +19,7 @@ namespace detail {
struct request_fields struct request_fields
{ {
http::method_t method; std::string method;
std::string url; std::string url;
}; };
@@ -35,24 +31,6 @@ struct response_fields
} // detail } // detail
#if ! GENERATING_DOCS
struct request_params
{
http::method_t method;
std::string url;
int version;
};
struct response_params
{
int status;
std::string reason;
int version;
};
#endif
/** A HTTP message. /** A HTTP message.
A message can be a request or response, depending on the `isRequest` A message can be a request or response, depending on the `isRequest`
@@ -73,62 +51,24 @@ struct message
: std::conditional<isRequest, : std::conditional<isRequest,
detail::request_fields, detail::response_fields>::type detail::request_fields, detail::response_fields>::type
{ {
/** The trait type characterizing the body. /** The type controlling the body traits.
The body member will be of type body_type::value_type. The body member will be of type `body_type::value_type`.
*/ */
using body_type = Body; using body_type = Body;
/// The type representing the headers.
using headers_type = Headers; using headers_type = Headers;
/// Indicates if the message is a request.
using is_request = using is_request =
std::integral_constant<bool, isRequest>; std::integral_constant<bool, isRequest>;
int version; // 10 or 11 /// The container holding the headers.
headers_type headers; headers_type headers;
/// A container representing the body.
typename Body::value_type body; typename Body::value_type body;
message();
message(message&&) = default;
message(message const&) = default;
message& operator=(message&&) = default;
message& operator=(message const&) = default;
/** Construct a HTTP request.
*/
explicit
message(request_params params);
/** Construct a HTTP response.
*/
explicit
message(response_params params);
/// Serialize the request or response line to a Streambuf.
template<class Streambuf>
void
write_firstline(Streambuf& streambuf) const
{
write_firstline(streambuf,
std::integral_constant<bool, isRequest>{});
}
/// Diagnostics only
template<bool, class, class>
friend
std::ostream&
operator<<(std::ostream& os,
message const& m);
private:
template<class Streambuf>
void
write_firstline(Streambuf& streambuf,
std::true_type) const;
template<class Streambuf>
void
write_firstline(Streambuf& streambuf,
std::false_type) const;
}; };
#if ! GENERATING_DOCS #if ! GENERATING_DOCS
@@ -145,30 +85,7 @@ using response = message<false, Body, Headers>;
#endif #endif
// For diagnostic output only
template<bool isRequest, class Body, class Headers>
std::ostream&
operator<<(std::ostream& os,
message<isRequest, Body, Headers> const& m);
/// Write a FieldSequence to a Streambuf.
template<class Streambuf, class FieldSequence>
void
write_fields(Streambuf& streambuf, FieldSequence const& fields);
/// Returns `true` if a message indicates a keep alive
template<bool isRequest, class Body, class Headers>
bool
is_keep_alive(message<isRequest, Body, Headers> const& msg);
/// Returns `true` if a message indicates a HTTP Upgrade request or response
template<bool isRequest, class Body, class Headers>
bool
is_upgrade(message<isRequest, Body, Headers> const& msg);
} // http } // http
} // beast } // beast
#include <beast/http/impl/message.ipp>
#endif #endif

View File

@@ -0,0 +1,130 @@
//
// 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_MESSAGE_V1_HPP
#define BEAST_HTTP_MESSAGE_V1_HPP
#include <beast/http/message.hpp>
#include <memory>
#include <string>
namespace beast {
namespace http {
#if ! GENERATING_DOCS
struct request_params
{
std::string method;
std::string url;
int version;
};
struct response_params
{
int status;
std::string reason;
int version;
};
#endif
/** A HTTP/1 message.
A message can be a request or response, depending on the `isRequest`
template argument value. Requests and responses have different types,
so functions may be overloaded on them if desired.
The `Body` template argument type determines the model used
to read or write the content body of the message.
@tparam isRequest `true` if this is a request.
@tparam Body A type meeting the requirements of Body.
@tparam Headers A type meeting the requirements of Headers.
*/
template<bool isRequest, class Body, class Headers>
struct message_v1 : message<isRequest, Body, Headers>
{
/// HTTP/1 version (10 or 11)
int version;
message_v1() = default;
/// Construct a HTTP/1 request.
explicit
message_v1(request_params params);
/// Construct a HTTP/1 response.
explicit
message_v1(response_params params);
};
#if ! GENERATING_DOCS
/// A typical HTTP/1 request
template<class Body,
class Headers = basic_headers<std::allocator<char>>>
using request_v1 = message_v1<true, Body, Headers>;
/// A typical HTTP/1 response
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
is_keep_alive(message_v1<isRequest, Body, Headers> const& msg);
/// Returns `true` if a HTTP/1 message indicates an Upgrade request or response
template<bool isRequest, class Body, class Headers>
bool
is_upgrade(message_v1<isRequest, Body, Headers> const& msg);
/** HTTP/1 connection prepare options.
@note These values are used with @ref prepare.
*/
enum class connection
{
/// Specify Connection: close.
close,
/// Specify Connection: keep-alive where possible.
keep_alive,
/// Specify Connection: upgrade.
upgrade
};
/** Prepare a HTTP/1 message.
This function will adjust the Content-Length, Transfer-Encoding,
and Connection headers of the message based on the properties of
the body and the options passed in.
@param msg The message to prepare. The headers may be modified.
@param options A list of prepare options.
*/
template<
bool isRequest, class Body, class Headers,
class... Options>
void
prepare(message_v1<isRequest, Body, Headers>& msg,
Options&&... options);
} // http
} // beast
#include <beast/http/impl/message_v1.ipp>
#endif

View File

@@ -1,179 +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_METHOD_HPP
#define BEAST_HTTP_METHOD_HPP
#include <cassert>
#include <memory>
#include <string>
namespace beast {
namespace http {
enum class method_t
{
http_delete,
http_get,
http_head,
http_post,
http_put,
// pathological
http_connect,
http_options,
http_trace,
// webdav
http_copy,
http_lock,
http_mkcol,
http_move,
http_propfind,
http_proppatch,
http_search,
http_unlock,
http_bind,
http_rebind,
http_unbind,
http_acl,
// subversion
http_report,
http_mkactivity,
http_checkout,
http_merge,
// upnp
http_msearch,
http_notify,
http_subscribe,
http_unsubscribe,
// RFC-5789
http_patch,
http_purge,
// CalDav
http_mkcalendar,
// RFC-2068, section 19.6.1.2
http_link,
http_unlink
};
template<class = void>
std::string
to_string(method_t m)
{
switch(m)
{
case method_t::http_delete: return "DELETE";
case method_t::http_get: return "GET";
case method_t::http_head: return "HEAD";
case method_t::http_post: return "POST";
case method_t::http_put: return "PUT";
case method_t::http_connect: return "CONNECT";
case method_t::http_options: return "OPTIONS";
case method_t::http_trace: return "TRACE";
case method_t::http_copy: return "COPY";
case method_t::http_lock: return "LOCK";
case method_t::http_mkcol: return "MKCOL";
case method_t::http_move: return "MOVE";
case method_t::http_propfind: return "PROPFIND";
case method_t::http_proppatch: return "PROPPATCH";
case method_t::http_search: return "SEARCH";
case method_t::http_unlock: return "UNLOCK";
case method_t::http_report: return "REPORT";
case method_t::http_mkactivity: return "MKACTIVITY";
case method_t::http_checkout: return "CHECKOUT";
case method_t::http_merge: return "MERGE";
case method_t::http_msearch: return "MSEARCH";
case method_t::http_notify: return "NOTIFY";
case method_t::http_subscribe: return "SUBSCRIBE";
case method_t::http_unsubscribe: return "UNSUBSCRIBE";
case method_t::http_patch: return "PATCH";
case method_t::http_purge: return "PURGE";
default:
assert(false);
break;
};
return "GET";
}
template <class Stream>
Stream&
operator<< (Stream& s, method_t m)
{
return s << to_string(m);
}
/** Returns the string corresponding to the numeric HTTP status code. */
template<class = void>
std::string
status_text (int status)
{
switch(status)
{
case 100: return "Continue";
case 101: return "Switching Protocols";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
//case 306: return "<reserved>";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Request Entity Too Large";
case 414: return "Request-URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
default:
break;
}
return "Unknown HTTP status";
}
} // http
} // beast
#endif

View File

@@ -1,234 +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_PARSER_HPP
#define BEAST_HTTP_PARSER_HPP
#include <beast/http/basic_parser.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message.hpp>
#include <boost/optional.hpp>
#include <functional>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
namespace detail {
struct parser_request
{
std::string method_;
std::string uri_;
};
struct parser_response
{
std::string reason_;
};
} // detail
template<bool isRequest, class Body, class Headers>
class parser
: public basic_parser<isRequest,
parser<isRequest, Body, Headers>>
, private std::conditional<isRequest,
detail::parser_request, detail::parser_response>::type
{
using message_type =
message<isRequest, Body, Headers>;
std::string field_;
std::string value_;
message_type m_;
typename message_type::body_type::reader r_;
public:
parser(parser&&) = default;
parser()
: r_(m_)
{
}
message_type
release()
{
return std::move(m_);
}
private:
friend class basic_parser<isRequest, parser>;
void flush()
{
if(! value_.empty())
{
rfc2616::trim_right_in_place(value_);
// VFALCO could std::move
m_.headers.insert(field_, value_);
field_.clear();
value_.clear();
}
}
void on_method(boost::string_ref const& s, error_code&)
{
this->method_.append(s.data(), s.size());
}
void on_uri(boost::string_ref const& s, error_code&)
{
this->uri_.append(s.data(), s.size());
}
void on_reason(boost::string_ref const& s, error_code&)
{
this->reason_.append(s.data(), s.size());
}
void on_field(boost::string_ref const& s, error_code&)
{
flush();
field_.append(s.data(), s.size());
}
void on_value(boost::string_ref const& s, error_code&)
{
value_.append(s.data(), s.size());
}
void set(std::true_type)
{
// VFALCO This is terrible for setting method
auto m =
[&](char const* s, method_t m)
{
if(this->method_ == s)
{
m_.method = m;
return true;
}
return false;
};
do
{
if(m("DELETE", method_t::http_delete))
break;
if(m("GET", method_t::http_get))
break;
if(m("HEAD", method_t::http_head))
break;
if(m("POST", method_t::http_post))
break;
if(m("PUT", method_t::http_put))
break;
if(m("CONNECT", method_t::http_connect))
break;
if(m("OPTIONS", method_t::http_options))
break;
if(m("TRACE", method_t::http_trace))
break;
if(m("COPY", method_t::http_copy))
break;
if(m("LOCK", method_t::http_lock))
break;
if(m("MKCOL", method_t::http_mkcol))
break;
if(m("MOVE", method_t::http_move))
break;
if(m("PROPFIND", method_t::http_propfind))
break;
if(m("PROPPATCH", method_t::http_proppatch))
break;
if(m("SEARCH", method_t::http_search))
break;
if(m("UNLOCK", method_t::http_unlock))
break;
if(m("BIND", method_t::http_bind))
break;
if(m("REBID", method_t::http_rebind))
break;
if(m("UNBIND", method_t::http_unbind))
break;
if(m("ACL", method_t::http_acl))
break;
if(m("REPORT", method_t::http_report))
break;
if(m("MKACTIVITY", method_t::http_mkactivity))
break;
if(m("CHECKOUT", method_t::http_checkout))
break;
if(m("MERGE", method_t::http_merge))
break;
if(m("MSEARCH", method_t::http_msearch))
break;
if(m("NOTIFY", method_t::http_notify))
break;
if(m("SUBSCRIBE", method_t::http_subscribe))
break;
if(m("UNSUBSCRIBE",method_t::http_unsubscribe))
break;
if(m("PATCH", method_t::http_patch))
break;
if(m("PURGE", method_t::http_purge))
break;
if(m("MKCALENDAR", method_t::http_mkcalendar))
break;
if(m("LINK", method_t::http_link))
break;
if(m("UNLINK", method_t::http_unlink))
break;
}
while(false);
m_.url = std::move(this->uri_);
}
void set(std::false_type)
{
m_.status = this->status_code();
m_.reason = this->reason_;
}
int on_headers(error_code&)
{
flush();
m_.version = 10 * this->http_major() + this->http_minor();
return 0;
}
void on_request(error_code& ec)
{
set(std::integral_constant<
bool, isRequest>{});
}
void on_response(error_code& ec)
{
set(std::integral_constant<
bool, isRequest>{});
}
void on_body(boost::string_ref const& s, error_code& ec)
{
r_.write(s.data(), s.size(), ec);
}
void on_complete(error_code&)
{
}
};
} // http
} // beast
#endif

View File

@@ -0,0 +1,157 @@
//
// 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_PARSER_V1_HPP
#define BEAST_HTTP_PARSER_V1_HPP
#include <beast/http/basic_parser_v1.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message_v1.hpp>
#include <boost/optional.hpp>
#include <functional>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
namespace detail {
struct parser_request
{
std::string method_;
std::string uri_;
};
struct parser_response
{
std::string reason_;
};
} // detail
/** A parser for producing HTTP/1 messages.
This class uses the basic HTTP/1 wire format parser to convert
a series of octets into a `message_v1`.
*/
template<bool isRequest, class Body, class Headers>
class parser_v1
: public basic_parser_v1<isRequest,
parser_v1<isRequest, Body, Headers>>
, private std::conditional<isRequest,
detail::parser_request, detail::parser_response>::type
{
using message_type =
message_v1<isRequest, Body, Headers>;
std::string field_;
std::string value_;
message_type m_;
typename message_type::body_type::reader r_;
public:
parser_v1(parser_v1&&) = default;
parser_v1()
: r_(m_)
{
}
message_type
release()
{
return std::move(m_);
}
private:
friend class basic_parser_v1<isRequest, parser_v1>;
void flush()
{
if(! value_.empty())
{
rfc2616::trim_right_in_place(value_);
// VFALCO could std::move
m_.headers.insert(field_, value_);
field_.clear();
value_.clear();
}
}
void on_method(boost::string_ref const& s, error_code&)
{
this->method_.append(s.data(), s.size());
}
void on_uri(boost::string_ref const& s, error_code&)
{
this->uri_.append(s.data(), s.size());
}
void on_reason(boost::string_ref const& s, error_code&)
{
this->reason_.append(s.data(), s.size());
}
void on_field(boost::string_ref const& s, error_code&)
{
flush();
field_.append(s.data(), s.size());
}
void on_value(boost::string_ref const& s, error_code&)
{
value_.append(s.data(), s.size());
}
void set(std::true_type)
{
m_.method = std::move(this->method_);
m_.url = std::move(this->uri_);
}
void set(std::false_type)
{
m_.status = this->status_code();
m_.reason = std::move(this->reason_);
}
int on_headers(error_code&)
{
flush();
m_.version = 10 * this->http_major() + this->http_minor();
return 0;
}
void on_request(error_code& ec)
{
set(std::integral_constant<
bool, isRequest>{});
}
void on_response(error_code& ec)
{
set(std::integral_constant<
bool, isRequest>{});
}
void on_body(boost::string_ref const& s, error_code& ec)
{
r_.write(s.data(), s.size(), ec);
}
void on_complete(error_code&)
{
}
};
} // http
} // beast
#endif

View File

@@ -8,34 +8,46 @@
#ifndef BEAST_HTTP_READ_HPP #ifndef BEAST_HTTP_READ_HPP
#define BEAST_HTTP_READ_HPP #define BEAST_HTTP_READ_HPP
#include <beast/async_completion.hpp>
#include <beast/http/error.hpp> #include <beast/http/error.hpp>
#include <beast/http/parser.hpp> #include <beast/http/parser_v1.hpp>
#include <beast/async_completion.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
namespace beast { namespace beast {
namespace http { namespace http {
/** Read a HTTP message from a stream. /** Read a HTTP/1 message from a stream.
@param stream The stream to read the message from. This function is used to synchronously read a message from
the stream. The call blocks until one of the following conditions
is true:
@param streambuf A Streambuf used to hold unread bytes. The @li A complete message is read in.
implementation may read past the end of the message. The extra
bytes are stored here, to be presented in a subsequent call to
read.
@param msg An object used to store the read message. Any @li An error occurs on the stream.
This function is implemented in terms of one or more calls to the
stream's `read_some` function.
@param stream The stream to which the data is to be written.
The type must support the @b `SyncReadStream` concept.
@param streambuf An object meeting the @b `Streambuf` type requirements
used to hold unread bytes. The implementation may read past the end of
the message. The extra bytes are stored here, to be presented in a
subsequent call to @ref read.
@param msg An object used to store the message. Any
contents will be overwritten. contents will be overwritten.
@throws boost::system::system_error on failure. @throws boost::system::system_error Thrown on failure.
*/ */
template<class SyncReadStream, class Streambuf, template<class SyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void
read(SyncReadStream& stream, Streambuf& streambuf, read(SyncReadStream& stream, Streambuf& streambuf,
message<isRequest, Body, Headers>& msg) message_v1<isRequest, Body, Headers>& msg)
{ {
error_code ec; error_code ec;
read(stream, streambuf, msg, ec); read(stream, streambuf, msg, ec);
@@ -43,17 +55,29 @@ read(SyncReadStream& stream, Streambuf& streambuf,
throw boost::system::system_error{ec}; throw boost::system::system_error{ec};
} }
/** Read a HTTP message from a stream. /** Read a HTTP/1 message from a stream.
@param stream The stream to read the message from. This function is used to synchronously read a message from
the stream. The call blocks until one of the following conditions
is true:
@param streambuf A Streambuf used to hold unread bytes. The @li A complete message is read in.
implementation may read past the end of the message. The extra
bytes are stored here, to be presented in a subsequent call to
read.
@param msg An object used to store the read message. Any @li An error occurs on the stream.
contents will be overwritten.
This function is implemented in terms of one or more calls to the
stream's `read_some` function.
@param stream The stream to which the data is to be written.
The type must support the @b `SyncReadStream` concept.
@param streambuf An object meeting the @b `Streambuf` type requirements
used to hold unread bytes. The implementation may read past the end of
the message. The extra bytes are stored here, to be presented in a
subsequent call to @ref read.
@param msg An object used to store the message. Any contents
will be overwritten.
@param ec Set to the error, if any occurred. @param ec Set to the error, if any occurred.
*/ */
@@ -61,20 +85,34 @@ template<class SyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void
read(SyncReadStream& stream, Streambuf& streambuf, read(SyncReadStream& stream, Streambuf& streambuf,
message<isRequest, Body, Headers>& msg, message_v1<isRequest, Body, Headers>& msg,
error_code& ec); error_code& ec);
/** Start reading a HTTP message from a stream asynchronously. /** Start an asynchronous operation to read a HTTP/1 message from a stream.
This function is used to asynchronously read a message from the
stream. The function call always returns immediately. The asynchronous
operation will continue until one of the following conditions is true:
@li A complete message is read in.
@li An error occurs on the stream.
This operation is implemented in terms of one or more calls to the
next layer's `async_read_some` function, and is known as a
<em>composed operation</em>. The program must ensure that the stream
performs no other operations until this operation completes.
@param stream The stream to read the message from. @param stream The stream to read the message from.
The type must support the @b `AsyncReadStream` concept.
@param streambuf A Streambuf used to hold unread bytes. The @param streambuf A Streambuf used to hold unread bytes. The
implementation may read past the end of the message. The extra implementation may read past the end of the message. The extra
bytes are stored here, to be presented in a subsequent call to bytes are stored here, to be presented in a subsequent call to
async_read. @ref async_read.
@param msg An object used to store the read message. Any @param msg An object used to store the message. Any contents
contents will be overwritten. will be overwritten.
@param handler The handler to be called when the request completes. @param handler The handler to be called when the request completes.
Copies will be made of the handler as required. The equivalent Copies will be made of the handler as required. The equivalent
@@ -85,7 +123,7 @@ read(SyncReadStream& stream, Streambuf& streambuf,
Regardless of whether the asynchronous operation completes Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a this function. Invocation of the handler will be performed in a
manner equivalent to using boost::asio::io_service::post(). manner equivalent to using `boost::asio::io_service::post`.
*/ */
template<class AsyncReadStream, class Streambuf, template<class AsyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers, bool isRequest, class Body, class Headers,
@@ -97,7 +135,7 @@ typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
#endif #endif
async_read(AsyncReadStream& stream, Streambuf& streambuf, async_read(AsyncReadStream& stream, Streambuf& streambuf,
message<isRequest, Body, Headers>& msg, message_v1<isRequest, Body, Headers>& msg,
ReadHandler&& handler); ReadHandler&& handler);
} // http } // http

View File

@@ -0,0 +1,71 @@
//
// 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_STATUS_HPP
#define BEAST_HTTP_STATUS_HPP
namespace beast {
namespace http {
/** Returns the string corresponding to the numeric HTTP status code. */
template<class = void>
char const*
status_text(int status)
{
switch(status)
{
case 100: return "Continue";
case 101: return "Switching Protocols";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
// case 306: return "<reserved>";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Request Entity Too Large";
case 414: return "Request-URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
default:
break;
}
return "Unknown HTTP status";
}
} // http
} // beast
#endif

View File

@@ -8,8 +8,7 @@
#ifndef BEAST_HTTP_STREAMBUF_BODY_HPP #ifndef BEAST_HTTP_STREAMBUF_BODY_HPP
#define BEAST_HTTP_STREAMBUF_BODY_HPP #define BEAST_HTTP_STREAMBUF_BODY_HPP
#include <beast/http/error.hpp> #include <beast/http/body_type.hpp>
#include <beast/http/message.hpp>
#include <beast/buffer_cat.hpp> #include <beast/buffer_cat.hpp>
#include <beast/streambuf.hpp> #include <beast/streambuf.hpp>
#include <memory> #include <memory>
@@ -18,7 +17,9 @@
namespace beast { namespace beast {
namespace http { namespace http {
/** A Body represented by a Streambuf /** A message body represented by a Streambuf
Meets the requirements of @b `Body`.
*/ */
template<class Streambuf> template<class Streambuf>
struct basic_streambuf_body struct basic_streambuf_body
@@ -35,10 +36,10 @@ private:
value_type& sb_; value_type& sb_;
public: public:
template<bool isRequest, class Allocator> template<bool isRequest, class Headers>
explicit explicit
reader(message<isRequest, reader(message<isRequest,
basic_streambuf_body, Allocator>& m) noexcept basic_streambuf_body, Headers>& m) noexcept
: sb_(m.body) : sb_(m.body)
{ {
} }
@@ -59,10 +60,13 @@ private:
Streambuf const& body_; Streambuf const& body_;
public: public:
template<bool isRequest, class Allocator> writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
explicit explicit
writer(message<isRequest, basic_streambuf_body, writer(message<
Allocator> const& m) isRequest, basic_streambuf_body, Headers> const& m)
: body_(m.body) : body_(m.body)
{ {
} }
@@ -72,7 +76,7 @@ private:
{ {
} }
std::size_t std::uint64_t
content_length() const content_length() const
{ {
return body_.size(); return body_.size();

View File

@@ -8,9 +8,7 @@
#ifndef BEAST_HTTP_STRING_BODY_HPP #ifndef BEAST_HTTP_STRING_BODY_HPP
#define BEAST_HTTP_STRING_BODY_HPP #define BEAST_HTTP_STRING_BODY_HPP
#include <beast/http/error.hpp> #include <beast/http/body_type.hpp>
#include <beast/http/message.hpp>
#include <beast/http/resume_context.hpp>
#include <beast/buffer_cat.hpp> #include <beast/buffer_cat.hpp>
#include <beast/streambuf.hpp> #include <beast/streambuf.hpp>
#include <memory> #include <memory>
@@ -20,6 +18,8 @@ namespace beast {
namespace http { namespace http {
/** A Body represented by a std::string. /** A Body represented by a std::string.
Meets the requirements of @b `Body`.
*/ */
struct string_body struct string_body
{ {
@@ -35,10 +35,10 @@ private:
value_type& s_; value_type& s_;
public: public:
template<bool isRequest, class Allocator> template<bool isRequest, class Headers>
explicit explicit
reader(message<isRequest, reader(message<isRequest,
string_body, Allocator>& m) noexcept string_body, Headers>& m) noexcept
: s_(m.body) : s_(m.body)
{ {
} }
@@ -58,9 +58,13 @@ private:
value_type const& body_; value_type const& body_;
public: public:
template<bool isRequest, class Allocator> writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
explicit explicit
writer(message<isRequest, string_body, Allocator> const& msg) writer(message<
isRequest, string_body, Headers> const& msg)
: body_(msg.body) : body_(msg.body)
{ {
} }
@@ -70,7 +74,7 @@ private:
{ {
} }
std::size_t std::uint64_t
content_length() const content_length() const
{ {
return body_.size(); return body_.size();

View File

@@ -0,0 +1,62 @@
//
// 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_TYPE_CHECK_HPP
#define BEAST_HTTP_TYPE_CHECK_HPP
#include <beast/http/error.hpp>
#include <boost/asio/buffer.hpp>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/// Determine if `T` meets the requirements of `Parser`.
template<class T>
class is_Parser
{
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().complete()),
bool>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().write(
std::declval<boost::asio::const_buffer const&>(),
std::declval<error_code&>())),
std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().write_eof(
std::declval<error_code&>())),
std::size_t>>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
public:
/// `true` if `T` meets the requirements.
static bool const value =
type1::value && type2::value && type3::value;
};
} // http
} // beast
#endif

View File

@@ -9,39 +9,68 @@
#define BEAST_HTTP_WRITE_HPP #define BEAST_HTTP_WRITE_HPP
#include <beast/http/error.hpp> #include <beast/http/error.hpp>
#include <beast/http/message.hpp> #include <beast/http/message_v1.hpp>
#include <beast/async_completion.hpp> #include <beast/async_completion.hpp>
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
#include <ostream>
#include <type_traits> #include <type_traits>
namespace beast { namespace beast {
namespace http { namespace http {
/** Write a HTTP message to a stream. /** Write a HTTP/1 message on a stream.
@param stream The stream to send the message on. This function is used to write a message to a stream. The call
will block until one of the following conditions is true:
@param msg The message to send. @li The entire message is sent.
@throws boost::system::error code on failure. @li An error occurs.
This operation is implemented in terms of one or more calls
to the stream's `write_some` function.
The implementation will automatically perform chunk encoding if
the contents of the message indicate that chunk encoding is required.
If the semantics of the message indicate that the connection should
be closed after the message is sent, the error returns from this
function will be `boost::asio::error::eof`.
@param stream The stream to which the data is to be written.
The type must support the @b `SyncWriteStream` concept.
@param msg The message to write.
@throws boost::system::error Thrown on failure.
*/ */
template<class SyncWriteStream, template<class SyncWriteStream,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void
write(SyncWriteStream& stream, write(SyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg) message_v1<isRequest, Body, Headers> const& msg);
{
error_code ec;
write(stream, msg, ec);
if(ec)
throw boost::system::system_error{ec};
}
/** Write a HTTP message to a stream. /** Write a HTTP/1 message on a stream.
@param stream The stream to send the message on. This function is used to write a message to a stream. The call
will block until one of the following conditions is true:
@param msg The message to send. @li The entire message is sent.
@li An error occurs.
This operation is implemented in terms of one or more calls
to the stream's `write_some` function.
The implementation will automatically perform chunk encoding if
the contents of the message indicate that chunk encoding is required.
If the semantics of the message indicate that the connection should
be closed after the message is sent, the error returns from this
function will be `boost::asio::error::eof`.
@param stream The stream to which the data is to be written.
The type must support the @b `SyncWriteStream` concept.
@param msg The message to write.
@param ec Set to the error, if any occurred. @param ec Set to the error, if any occurred.
*/ */
@@ -49,16 +78,38 @@ template<class SyncWriteStream,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void
write(SyncWriteStream& stream, write(SyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg, message_v1<isRequest, Body, Headers> const& msg,
error_code& ec); error_code& ec);
/** Start writing a HTTP message to a stream asynchronously. /** Start an asynchronous operation to write a HTTP/1 message to a stream.
@param stream The stream to send the message on. This function is used to asynchronously write a message to a stream.
The function call always returns immediately. The asynchronous
operation will continue until one of the following conditions is true:
@li The entire message is sent.
@li An error occurs.
This operation is implemented in terms of one or more calls to the
stream's `async_write_some` functions, and is known as a <em>composed
operation</em>. The program must ensure that the stream performs no
other write operations (such as @ref async_write, the stream's
`async_write_some` function, or any other composed operations that
perform writes) until this operation completes.
The implementation will automatically perform chunk encoding if
the contents of the message indicate that chunk encoding is required.
If the semantics of the message indicate that the connection should
be closed after the message is sent, the operation will complete with
the error set to `boost::asio::error::eof`.
@param stream The stream to which the data is to be written.
The type must support the @b `AsyncWriteStream` concept.
@param msg The message to send. @param msg The message to send.
@param token The handler to be called when the request completes. @param handler The handler to be called when the request completes.
Copies will be made of the handler as required. The equivalent Copies will be made of the handler as required. The equivalent
function signature of the handler must be: function signature of the handler must be:
@code void handler( @code void handler(
@@ -67,9 +118,9 @@ write(SyncWriteStream& stream,
Regardless of whether the asynchronous operation completes Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a this function. Invocation of the handler will be performed in a
manner equivalent to using boost::asio::io_service::post(). manner equivalent to using `boost::asio::io_service::post`.
@note The message must remain valid at least until the @note The message object must remain valid at least until the
completion handler is called, no copies are made. completion handler is called, no copies are made.
*/ */
template<class AsyncWriteStream, template<class AsyncWriteStream,
@@ -82,9 +133,26 @@ typename async_completion<
WriteHandler, void(error_code)>::result_type WriteHandler, void(error_code)>::result_type
#endif #endif
async_write(AsyncWriteStream& stream, async_write(AsyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg, message_v1<isRequest, Body, Headers> const& msg,
WriteHandler&& handler); WriteHandler&& handler);
/** Serialize a HTTP/1 message to an ostream.
The function converts the message to its HTTP/1 serialized
representation and stores the result in the output stream.
The implementation will automatically perform chunk encoding if
the contents of the message indicate that chunk encoding is required.
@param os The ostream to write to.
@param msg The message to write.
*/
template<bool isRequest, class Body, class Headers>
std::ostream&
operator<<(std::ostream& os,
message_v1<isRequest, Body, Headers> const& msg);
} // http } // http
} // beast } // beast

View File

@@ -8,6 +8,7 @@
#ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP #ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP
#define BEAST_IMPL_BASIC_STREAMBUF_IPP #define BEAST_IMPL_BASIC_STREAMBUF_IPP
#include <beast/detail/write_streambuf.hpp>
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <exception> #include <exception>
@@ -118,7 +119,7 @@ public:
template<class Allocator> template<class Allocator>
class basic_streambuf<Allocator>::const_buffers_type class basic_streambuf<Allocator>::const_buffers_type
{ {
basic_streambuf const* sb_ = nullptr; basic_streambuf const* sb_;
friend class basic_streambuf; friend class basic_streambuf;
@@ -126,12 +127,12 @@ class basic_streambuf<Allocator>::const_buffers_type
const_buffers_type(basic_streambuf const& sb); const_buffers_type(basic_streambuf const& sb);
public: public:
/// Why? // Why?
using value_type = boost::asio::const_buffer; using value_type = boost::asio::const_buffer;
class const_iterator; class const_iterator;
const_buffers_type() = default; const_buffers_type() = delete;
const_buffers_type(const_buffers_type const&) = default; const_buffers_type(const_buffers_type const&) = default;
const_buffers_type& operator=(const_buffers_type const&) = default; const_buffers_type& operator=(const_buffers_type const&) = default;
@@ -157,7 +158,7 @@ public:
class const_iterator; class const_iterator;
mutable_buffers_type() = default; mutable_buffers_type() = delete;
mutable_buffers_type(mutable_buffers_type const&) = default; mutable_buffers_type(mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(mutable_buffers_type const&) = default; mutable_buffers_type& operator=(mutable_buffers_type const&) = default;
@@ -849,22 +850,6 @@ basic_streambuf<Allocator>::debug_check() const
#endif #endif
} }
template<class Alloc, class T>
basic_streambuf<Alloc>&
operator<<(basic_streambuf<Alloc>& streambuf, T const& t)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
std::stringstream ss;
ss << t;
auto const& s = ss.str();
streambuf.commit(buffer_copy(
streambuf.prepare(s.size()), buffer(s)));
return streambuf;
}
//------------------------------------------------------------------------------
template<class Allocator> template<class Allocator>
std::size_t std::size_t
read_size_helper(basic_streambuf< read_size_helper(basic_streambuf<
@@ -874,17 +859,12 @@ read_size_helper(basic_streambuf<
std::max<std::size_t>(512, streambuf.prepare_size())); std::max<std::size_t>(512, streambuf.prepare_size()));
} }
template<class Allocator> template<class Alloc, class T>
std::string basic_streambuf<Alloc>&
to_string(basic_streambuf<Allocator> const& streambuf) operator<<(basic_streambuf<Alloc>& streambuf, T const& t)
{ {
using boost::asio::buffer; detail::write_streambuf(streambuf, t);
using boost::asio::buffer_copy; return streambuf;
std::string s;
s.resize(streambuf.size());
buffer_copy(
buffer(&s[0], s.size()), streambuf.data());
return s;
} }
} // beast } // beast

View File

@@ -17,8 +17,9 @@
namespace beast { namespace beast {
template<class Buffers> template<class MutableBufferSequence>
class buffers_adapter<Buffers>::const_buffers_type class buffers_adapter<MutableBufferSequence>::
const_buffers_type
{ {
buffers_adapter const* ba_; buffers_adapter const* ba_;
@@ -27,7 +28,7 @@ public:
class const_iterator; class const_iterator;
const_buffers_type() = default; const_buffers_type() = delete;
const_buffers_type( const_buffers_type(
const_buffers_type const&) = default; const_buffers_type const&) = default;
const_buffers_type& operator=( const_buffers_type& operator=(
@@ -48,11 +49,12 @@ private:
} }
}; };
template<class Buffers> template<class MutableBufferSequence>
class buffers_adapter<Buffers>::const_buffers_type::const_iterator class buffers_adapter<MutableBufferSequence>::
const_buffers_type::const_iterator
{ {
iter_type it_; iter_type it_;
buffers_adapter const* ba_; buffers_adapter const* ba_ = nullptr;
public: public:
using value_type = boost::asio::const_buffer; using value_type = boost::asio::const_buffer;
@@ -136,19 +138,19 @@ private:
} }
}; };
template<class Buffers> template<class MutableBufferSequence>
inline inline
auto auto
buffers_adapter<Buffers>::const_buffers_type::begin() const -> buffers_adapter<MutableBufferSequence>::const_buffers_type::begin() const ->
const_iterator const_iterator
{ {
return const_iterator{*ba_, ba_->begin_}; return const_iterator{*ba_, ba_->begin_};
} }
template<class Buffers> template<class MutableBufferSequence>
inline inline
auto auto
buffers_adapter<Buffers>::const_buffers_type::end() const -> buffers_adapter<MutableBufferSequence>::const_buffers_type::end() const ->
const_iterator const_iterator
{ {
return const_iterator{*ba_, ba_->out_ == return const_iterator{*ba_, ba_->out_ ==
@@ -157,8 +159,9 @@ buffers_adapter<Buffers>::const_buffers_type::end() const ->
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class Buffers> template<class MutableBufferSequence>
class buffers_adapter<Buffers>::mutable_buffers_type class buffers_adapter<MutableBufferSequence>::
mutable_buffers_type
{ {
buffers_adapter const* ba_; buffers_adapter const* ba_;
@@ -167,7 +170,7 @@ public:
class const_iterator; class const_iterator;
mutable_buffers_type() = default; mutable_buffers_type() = delete;
mutable_buffers_type( mutable_buffers_type(
mutable_buffers_type const&) = default; mutable_buffers_type const&) = default;
mutable_buffers_type& operator=( mutable_buffers_type& operator=(
@@ -189,11 +192,12 @@ private:
} }
}; };
template<class Buffers> template<class MutableBufferSequence>
class buffers_adapter<Buffers>::mutable_buffers_type::const_iterator class buffers_adapter<MutableBufferSequence>::
mutable_buffers_type::const_iterator
{ {
iter_type it_; iter_type it_;
buffers_adapter const* ba_; buffers_adapter const* ba_ = nullptr;
public: public:
using value_type = boost::asio::mutable_buffer; using value_type = boost::asio::mutable_buffer;
@@ -277,19 +281,19 @@ private:
} }
}; };
template<class Buffers> template<class MutableBufferSequence>
inline inline
auto auto
buffers_adapter<Buffers>::mutable_buffers_type::begin() const -> buffers_adapter<MutableBufferSequence>::mutable_buffers_type::begin() const ->
const_iterator const_iterator
{ {
return const_iterator{*ba_, ba_->out_}; return const_iterator{*ba_, ba_->out_};
} }
template<class Buffers> template<class MutableBufferSequence>
inline inline
auto auto
buffers_adapter<Buffers>::mutable_buffers_type::end() const -> buffers_adapter<MutableBufferSequence>::mutable_buffers_type::end() const ->
const_iterator const_iterator
{ {
return const_iterator{*ba_, ba_->end_}; return const_iterator{*ba_, ba_->end_};
@@ -297,8 +301,8 @@ buffers_adapter<Buffers>::mutable_buffers_type::end() const ->
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class Buffers> template<class MutableBufferSequence>
buffers_adapter<Buffers>::buffers_adapter( buffers_adapter<MutableBufferSequence>::buffers_adapter(
buffers_adapter&& other) buffers_adapter&& other)
: buffers_adapter(std::move(other), : buffers_adapter(std::move(other),
std::distance<iter_type>(other.bs_.begin(), other.begin_), std::distance<iter_type>(other.bs_.begin(), other.begin_),
@@ -307,8 +311,8 @@ buffers_adapter<Buffers>::buffers_adapter(
{ {
} }
template<class Buffers> template<class MutableBufferSequence>
buffers_adapter<Buffers>::buffers_adapter( buffers_adapter<MutableBufferSequence>::buffers_adapter(
buffers_adapter const& other) buffers_adapter const& other)
: buffers_adapter(other, : buffers_adapter(other,
std::distance<iter_type>(other.bs_.begin(), other.begin_), std::distance<iter_type>(other.bs_.begin(), other.begin_),
@@ -317,9 +321,9 @@ buffers_adapter<Buffers>::buffers_adapter(
{ {
} }
template<class Buffers> template<class MutableBufferSequence>
auto auto
buffers_adapter<Buffers>::operator=( buffers_adapter<MutableBufferSequence>::operator=(
buffers_adapter&& other) -> buffers_adapter& buffers_adapter&& other) -> buffers_adapter&
{ {
auto const nbegin = std::distance<iter_type>( auto const nbegin = std::distance<iter_type>(
@@ -340,9 +344,9 @@ buffers_adapter<Buffers>::operator=(
return *this; return *this;
} }
template<class Buffers> template<class MutableBufferSequence>
auto auto
buffers_adapter<Buffers>::operator=( buffers_adapter<MutableBufferSequence>::operator=(
buffers_adapter const& other) -> buffers_adapter& buffers_adapter const& other) -> buffers_adapter&
{ {
auto const nbegin = std::distance<iter_type>( auto const nbegin = std::distance<iter_type>(
@@ -363,9 +367,9 @@ buffers_adapter<Buffers>::operator=(
return *this; return *this;
} }
template<class Buffers> template<class MutableBufferSequence>
buffers_adapter<Buffers>::buffers_adapter( buffers_adapter<MutableBufferSequence>::buffers_adapter(
Buffers const& bs) MutableBufferSequence const& bs)
: bs_(bs) : bs_(bs)
, begin_(bs_.begin()) , begin_(bs_.begin())
, out_(bs_.begin()) , out_(bs_.begin())
@@ -374,14 +378,12 @@ buffers_adapter<Buffers>::buffers_adapter(
{ {
} }
template<class Buffers> template<class MutableBufferSequence>
auto auto
buffers_adapter<Buffers>::prepare(std::size_t n) -> buffers_adapter<MutableBufferSequence>::prepare(std::size_t n) ->
mutable_buffers_type mutable_buffers_type
{ {
using boost::asio::buffer_size; using boost::asio::buffer_size;
static_assert(is_mutable,
"Operation not valid for ConstBufferSequence");
end_ = out_; end_ = out_;
if(end_ != bs_.end()) if(end_ != bs_.end())
{ {
@@ -416,13 +418,11 @@ buffers_adapter<Buffers>::prepare(std::size_t n) ->
return mutable_buffers_type{*this}; return mutable_buffers_type{*this};
} }
template<class Buffers> template<class MutableBufferSequence>
void void
buffers_adapter<Buffers>::commit(std::size_t n) buffers_adapter<MutableBufferSequence>::commit(std::size_t n)
{ {
using boost::asio::buffer_size; using boost::asio::buffer_size;
static_assert(is_mutable,
"Operation not valid for ConstBufferSequence");
if(out_ == end_) if(out_ == end_)
return; return;
auto const last = std::prev(end_); auto const last = std::prev(end_);
@@ -456,18 +456,18 @@ buffers_adapter<Buffers>::commit(std::size_t n)
} }
} }
template<class Buffers> template<class MutableBufferSequence>
inline inline
auto auto
buffers_adapter<Buffers>::data() const -> buffers_adapter<MutableBufferSequence>::data() const ->
const_buffers_type const_buffers_type
{ {
return const_buffers_type{*this}; return const_buffers_type{*this};
} }
template<class Buffers> template<class MutableBufferSequence>
void void
buffers_adapter<Buffers>::consume(std::size_t n) buffers_adapter<MutableBufferSequence>::consume(std::size_t n)
{ {
for(;;) for(;;)
{ {

View File

@@ -8,7 +8,7 @@
#ifndef BEAST_IMPL_CONSUMING_BUFFERS_IPP #ifndef BEAST_IMPL_CONSUMING_BUFFERS_IPP
#define BEAST_IMPL_CONSUMING_BUFFERS_IPP #define BEAST_IMPL_CONSUMING_BUFFERS_IPP
#include <beast/type_check.hpp> #include <beast/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
@@ -18,16 +18,16 @@
namespace beast { namespace beast {
template<class Buffers, class ValueType> template<class BufferSequence, class ValueType>
class consuming_buffers<Buffers, ValueType>::const_iterator class consuming_buffers<BufferSequence, ValueType>::const_iterator
{ {
friend class consuming_buffers<Buffers, ValueType>; friend class consuming_buffers<BufferSequence, ValueType>;
using iter_type = using iter_type =
typename Buffers::const_iterator; typename BufferSequence::const_iterator;
iter_type it_; iter_type it_;
consuming_buffers const* b_; consuming_buffers const* b_ = nullptr;
public: public:
using value_type = using value_type =
@@ -59,9 +59,8 @@ public:
reference reference
operator*() const operator*() const
{ {
if(it_ == b_->begin_) return it_ == b_->begin_ ?
return *it_ + b_->skip_; *it_ + b_->skip_ : *it_;
return *it_;
} }
pointer pointer
@@ -106,8 +105,8 @@ private:
} }
}; };
template<class Buffers, class ValueType> template<class BufferSequence, class ValueType>
consuming_buffers<Buffers, ValueType>:: consuming_buffers<BufferSequence, ValueType>::
consuming_buffers(consuming_buffers&& other) consuming_buffers(consuming_buffers&& other)
: consuming_buffers(std::move(other), : consuming_buffers(std::move(other),
std::distance<iter_type>( std::distance<iter_type>(
@@ -115,8 +114,8 @@ consuming_buffers(consuming_buffers&& other)
{ {
} }
template<class Buffers, class ValueType> template<class BufferSequence, class ValueType>
consuming_buffers<Buffers, ValueType>:: consuming_buffers<BufferSequence, ValueType>::
consuming_buffers(consuming_buffers const& other) consuming_buffers(consuming_buffers const& other)
: consuming_buffers(other, : consuming_buffers(other,
std::distance<iter_type>( std::distance<iter_type>(
@@ -124,9 +123,9 @@ consuming_buffers(consuming_buffers const& other)
{ {
} }
template<class Buffers, class ValueType> template<class BufferSequence, class ValueType>
auto auto
consuming_buffers<Buffers, ValueType>:: consuming_buffers<BufferSequence, ValueType>::
operator=(consuming_buffers&& other) -> operator=(consuming_buffers&& other) ->
consuming_buffers& consuming_buffers&
{ {
@@ -138,9 +137,9 @@ operator=(consuming_buffers&& other) ->
return *this; return *this;
} }
template<class Buffers, class ValueType> template<class BufferSequence, class ValueType>
auto auto
consuming_buffers<Buffers, ValueType>:: consuming_buffers<BufferSequence, ValueType>::
operator=(consuming_buffers const& other) -> operator=(consuming_buffers const& other) ->
consuming_buffers& consuming_buffers&
{ {
@@ -152,35 +151,35 @@ operator=(consuming_buffers const& other) ->
return *this; return *this;
} }
template<class Buffers, class ValueType> template<class BufferSequence, class ValueType>
consuming_buffers<Buffers, ValueType>:: consuming_buffers<BufferSequence, ValueType>::
consuming_buffers(Buffers const& bs) consuming_buffers(BufferSequence const& bs)
: bs_(bs) : bs_(bs)
, begin_(bs_.begin()) , begin_(bs_.begin())
{ {
static_assert(is_BufferSequence<Buffers, ValueType>::value, static_assert(is_BufferSequence<BufferSequence, ValueType>::value,
"BufferSequence requirements not met"); "BufferSequence requirements not met");
} }
template<class Buffers, class ValueType> template<class BufferSequence, class ValueType>
auto auto
consuming_buffers<Buffers, ValueType>::begin() const -> consuming_buffers<BufferSequence, ValueType>::begin() const ->
const_iterator const_iterator
{ {
return const_iterator{*this, begin_}; return const_iterator{*this, begin_};
} }
template<class Buffers, class ValueType> template<class BufferSequence, class ValueType>
auto auto
consuming_buffers<Buffers, ValueType>::end() const -> consuming_buffers<BufferSequence, ValueType>::end() const ->
const_iterator const_iterator
{ {
return const_iterator{*this, bs_.end()}; return const_iterator{*this, bs_.end()};
} }
template<class Buffers, class ValueType> template<class BufferSequence, class ValueType>
void void
consuming_buffers<Buffers, ValueType>::consume(std::size_t n) consuming_buffers<BufferSequence, ValueType>::consume(std::size_t n)
{ {
using boost::asio::buffer_size; using boost::asio::buffer_size;
for(;n > 0 && begin_ != bs_.end(); ++begin_) for(;n > 0 && begin_ != bs_.end(); ++begin_)
@@ -197,11 +196,11 @@ consuming_buffers<Buffers, ValueType>::consume(std::size_t n)
} }
} }
template<class Buffers> template<class BufferSequence>
consuming_buffers<Buffers, typename Buffers::value_type> consuming_buffers<BufferSequence, typename BufferSequence::value_type>
consumed_buffers(Buffers const& bs, std::size_t n) consumed_buffers(BufferSequence const& bs, std::size_t n)
{ {
consuming_buffers<Buffers> cb(bs); consuming_buffers<BufferSequence> cb(bs);
cb.consume(n); cb.consume(n);
return cb; return cb;
} }

View File

@@ -46,7 +46,7 @@ class prepared_buffers<BufferSequence>::const_iterator
using iter_type = using iter_type =
typename BufferSequence::const_iterator; typename BufferSequence::const_iterator;
prepared_buffers const* b_; prepared_buffers const* b_ = nullptr;
typename BufferSequence::const_iterator it_; typename BufferSequence::const_iterator it_;
public: public:

View File

@@ -26,7 +26,7 @@ public:
class const_iterator; class const_iterator;
const_buffers_type() = default; const_buffers_type() = delete;
const_buffers_type( const_buffers_type(
const_buffers_type const&) = default; const_buffers_type const&) = default;
const_buffers_type& operator=( const_buffers_type& operator=(
@@ -51,8 +51,8 @@ private:
class static_streambuf::const_buffers_type::const_iterator class static_streambuf::const_buffers_type::const_iterator
{ {
std::size_t n_; std::size_t n_ = 0;
std::uint8_t const* p_; std::uint8_t const* p_ = nullptr;
public: public:
using value_type = boost::asio::const_buffer; using value_type = boost::asio::const_buffer;
@@ -158,7 +158,7 @@ public:
class const_iterator; class const_iterator;
mutable_buffers_type() = default; mutable_buffers_type() = delete;
mutable_buffers_type( mutable_buffers_type(
mutable_buffers_type const&) = default; mutable_buffers_type const&) = default;
mutable_buffers_type& operator=( mutable_buffers_type& operator=(
@@ -183,8 +183,8 @@ private:
class static_streambuf::mutable_buffers_type::const_iterator class static_streambuf::mutable_buffers_type::const_iterator
{ {
std::size_t n_; std::size_t n_ = 0;
std::uint8_t* p_; std::uint8_t* p_ = nullptr;
public: public:
using value_type = boost::asio::mutable_buffer; using value_type = boost::asio::mutable_buffer;

View File

@@ -9,8 +9,10 @@
#define BEAST_IMPL_STREAMBUF_READSTREAM_IPP #define BEAST_IMPL_STREAMBUF_READSTREAM_IPP
#include <beast/bind_handler.hpp> #include <beast/bind_handler.hpp>
#include <beast/handler_concepts.hpp>
#include <beast/handler_alloc.hpp> #include <beast/handler_alloc.hpp>
#include <beast/type_check.hpp> #include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
namespace beast { namespace beast {
@@ -158,10 +160,6 @@ streambuf_readstream<Stream, Streambuf>::
streambuf_readstream(Args&&... args) streambuf_readstream(Args&&... args)
: next_layer_(std::forward<Args>(args)...) : next_layer_(std::forward<Args>(args)...)
{ {
static_assert(is_Stream<next_layer_type>::value,
"Stream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
} }
template<class Stream, class Streambuf> template<class Stream, class Streambuf>
@@ -173,10 +171,12 @@ async_write_some(ConstBufferSequence const& buffers,
typename async_completion< typename async_completion<
WriteHandler, void(error_code)>::result_type WriteHandler, void(error_code)>::result_type
{ {
static_assert(is_AsyncWriteStream<next_layer_type>::value,
"AsyncWriteStream requirements not met");
static_assert(is_ConstBufferSequence< static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value, ConstBufferSequence>::value,
"ConstBufferSequence requirements not met"); "ConstBufferSequence requirements not met");
static_assert(is_Handler<WriteHandler, static_assert(is_CompletionHandler<WriteHandler,
void(error_code, std::size_t)>::value, void(error_code, std::size_t)>::value,
"WriteHandler requirements not met"); "WriteHandler requirements not met");
return next_layer_.async_write_some(buffers, return next_layer_.async_write_some(buffers,
@@ -190,6 +190,8 @@ streambuf_readstream<Stream, Streambuf>::
read_some( read_some(
MutableBufferSequence const& buffers) MutableBufferSequence const& buffers)
{ {
static_assert(is_SyncReadStream<next_layer_type>::value,
"SyncReadStream requirements not met");
static_assert(is_MutableBufferSequence< static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value, MutableBufferSequence>::value,
"MutableBufferSequence requirements not met"); "MutableBufferSequence requirements not met");
@@ -207,6 +209,8 @@ streambuf_readstream<Stream, Streambuf>::
read_some(MutableBufferSequence const& buffers, read_some(MutableBufferSequence const& buffers,
error_code& ec) error_code& ec)
{ {
static_assert(is_SyncReadStream<next_layer_type>::value,
"SyncReadStream requirements not met");
static_assert(is_MutableBufferSequence< static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value, MutableBufferSequence>::value,
"MutableBufferSequence requirements not met"); "MutableBufferSequence requirements not met");
@@ -239,6 +243,8 @@ async_read_some(
typename async_completion< typename async_completion<
ReadHandler, void(error_code)>::result_type ReadHandler, void(error_code)>::result_type
{ {
static_assert(is_AsyncReadStream<next_layer_type>::value,
"Stream requirements not met");
static_assert(is_MutableBufferSequence< static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value, MutableBufferSequence>::value,
"MutableBufferSequence requirements not met"); "MutableBufferSequence requirements not met");

View File

@@ -15,15 +15,15 @@
namespace beast { namespace beast {
/** A `Streambuf` with a fixed size internal buffer. /** A @b `Streambuf` with a fixed size internal buffer.
Ownership of the underlying storage belongs to the derived class. Ownership of the underlying storage belongs to the derived class.
@note Variables are usually declared using the template class @note Variables are usually declared using the template class
`static_streambuf_n`; however, to reduce the number of instantiations @ref static_streambuf_n; however, to reduce the number of instantiations
of template functions receiving static stream buffer arguments in a of template functions receiving static stream buffer arguments in a
deduced context, the signature of the receiving function should use deduced context, the signature of the receiving function should use
`static_streambuf`. @ref static_streambuf.
*/ */
class static_streambuf class static_streambuf
{ {
@@ -75,18 +75,28 @@ public:
@throws std::length_error if the size would exceed the limit @throws std::length_error if the size would exceed the limit
imposed by the underlying mutable buffer sequence. imposed by the underlying mutable buffer sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/ */
mutable_buffers_type mutable_buffers_type
prepare(std::size_t n); prepare(std::size_t n);
/// Move bytes from the output sequence to the input sequence. /** Move bytes from the output sequence to the input sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
void void
commit(std::size_t n) commit(std::size_t n)
{ {
out_ += std::min<std::size_t>(n, last_ - out_); out_ += std::min<std::size_t>(n, last_ - out_);
} }
/// Get a list of buffers that represents the input sequence. /** Get a list of buffers that represents the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type const_buffers_type
data() const; data() const;
@@ -129,9 +139,11 @@ protected:
*/ */
template<std::size_t N> template<std::size_t N>
class static_streambuf_n class static_streambuf_n
: private boost::base_from_member< : public static_streambuf
#if ! GENERATING_DOCS
, private boost::base_from_member<
std::array<std::uint8_t, N>> std::array<std::uint8_t, N>>
, public static_streambuf #endif
{ {
using member_type = boost::base_from_member< using member_type = boost::base_from_member<
std::array<std::uint8_t, N>>; std::array<std::uint8_t, N>>;

View File

@@ -0,0 +1,692 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP
#define BEAST_WEBSOCKET_STATIC_STRING_HPP
#include <array>
#include <cstdint>
#include <iterator>
#include <stdexcept>
#include <string>
namespace beast {
/** A string with a fixed-size storage area.
These objects behave like `std::string` except that the storage
is not dynamically allocated but rather fixed in size.
These strings offer performance advantages when a protocol
imposes a natural small upper limit on the size of a value.
@note The stored string is always null-terminated.
*/
template<
std::size_t N,
class CharT = char,
class Traits = std::char_traits<CharT>>
class static_string
{
template<std::size_t, class, class>
friend class static_string;
std::size_t n_;
std::array<CharT, N+1> s_;
public:
using traits_type = Traits;
using value_type = typename Traits::char_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using const_pointer = value_type const*;
using const_reference = value_type const&;
using iterator = value_type*;
using const_iterator = value_type const*;
using reverse_iterator =
std::reverse_iterator<iterator>;
using const_reverse_iterator =
std::reverse_iterator<const_iterator>;
/** Default constructor.
The string is initially empty, and null terminated.
*/
static_string();
/// Copy constructor.
static_string(static_string const& s);
/// Copy constructor.
template<std::size_t M>
static_string(static_string<M, CharT, Traits> const& s);
/// Copy assignment.
static_string&
operator=(static_string const& s);
/// Copy assignment.
template<std::size_t M>
static_string&
operator=(static_string<M, CharT, Traits> const& s);
/// Construct from string literal.
template<std::size_t M>
static_string(const CharT (&s)[M]);
/// Assign from string literal.
template<std::size_t M>
static_string& operator=(const CharT (&s)[M]);
/// Access specified character with bounds checking.
reference
at(size_type pos);
/// Access specified character with bounds checking.
const_reference
at(size_type pos) const;
/// Access specified character.
reference
operator[](size_type pos)
{
return s_[pos];
}
/// Access specified character.
const_reference
operator[](size_type pos) const
{
return s_[pos];
}
/// Accesses the first character.
CharT&
front()
{
return s_[0];
}
/// Accesses the first character.
CharT const&
front() const
{
return s_[0];
}
/// Accesses the last character.
CharT&
back()
{
return s_[n_-1];
}
/// Accesses the last character.
CharT const&
back() const
{
return s_[n_-1];
}
/// Returns a pointer to the first character of a string.
CharT*
data()
{
return &s_[0];
}
/// Returns a pointer to the first character of a string.
CharT const*
data() const
{
return &s_[0];
}
/// Returns a non-modifiable standard C character array version of the string.
CharT const*
c_str() const
{
return &s_[0];
}
/// Returns an iterator to the beginning.
iterator
begin()
{
return &s_[0];
}
/// Returns an iterator to the beginning.
const_iterator
begin() const
{
return &s_[0];
}
/// Returns an iterator to the beginning.
const_iterator
cbegin() const
{
return &s_[0];
}
/// Returns an iterator to the end.
iterator
end()
{
return &s_[n_];
}
/// Returns an iterator to the end.
const_iterator
end() const
{
return &s_[n_];
}
/// Returns an iterator to the end.
const_iterator
cend() const
{
return &s_[n_];
}
/// Returns a reverse iterator to the beginning.
reverse_iterator
rbegin()
{
return reverse_iterator{end()};
}
/// Returns a reverse iterator to the beginning.
const_reverse_iterator
rbegin() const
{
return const_reverse_iterator{cend()};
}
/// Returns a reverse iterator to the beginning.
const_reverse_iterator
crbegin() const
{
return const_reverse_iterator{cend()};
}
/// Returns a reverse iterator to the end.
reverse_iterator
rend()
{
return reverse_iterator{begin()};
}
/// Returns a reverse iterator to the end.
const_reverse_iterator
rend() const
{
return const_reverse_iterator{cbegin()};
}
/// Returns a reverse iterator to the end.
const_reverse_iterator
crend() const
{
return const_reverse_iterator{cbegin()};
}
/// Returns `true` if the string is empty.
bool
empty() const
{
return n_ == 0;
}
/// Returns the number of characters, excluding the null terminator.
size_type
size() const
{
return n_;
}
/// Returns the maximum number of characters that can be stored, excluding the null terminator.
size_type constexpr
max_size() const
{
return N;
}
/// Returns the number of characters that can be held in currently allocated storage.
size_type
capacity() const
{
return N;
}
/// Reduces memory usage by freeing unused memory.
void
shrink_to_fit()
{
// no-op
}
/// Clears the contents.
void
clear()
{
resize(0);
}
/** Changes the number of characters stored.
@note No value-initialization is performed.
*/
void
resize(std::size_t n);
/** Changes the number of characters stored.
If the resulting string is larger, the new
characters are initialized to the value of `c`.
*/
void
resize(std::size_t n, CharT c);
/// Compare two character sequences.
template<std::size_t M>
int compare(static_string<M, CharT, Traits> const& rhs) const;
/// Return the characters as a `basic_string`.
std::basic_string<CharT, Traits>
to_string() const
{
return std::basic_string<
CharT, Traits>{&s_[0], n_};
}
private:
void
assign(CharT const* s);
};
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string()
: n_(0)
{
s_[0] = 0;
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(static_string const& s)
: n_(s.n_)
{
Traits::copy(&s_[0], &s.s_[0], n_ + 1);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
static_string<N, CharT, Traits>::
static_string(static_string<M, CharT, Traits> const& s)
{
if(s.size() > N)
throw std::length_error("static_string overflow");
n_ = s.size();
Traits::copy(&s_[0], &s.s_[0], n_ + 1);
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
operator=(static_string const& s) ->
static_string&
{
n_ = s.n_;
Traits::copy(&s_[0], &s.s_[0], n_ + 1);
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
auto
static_string<N, CharT, Traits>::
operator=(static_string<M, CharT, Traits> const& s) ->
static_string&
{
if(s.size() > N)
throw std::length_error("static_string overflow");
n_ = s.size();
Traits::copy(&s_[0], &s.s_[0], n_ + 1);
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
static_string<N, CharT, Traits>::
static_string(const CharT (&s)[M])
: n_(M-1)
{
static_assert(M-1 <= N,
"static_string overflow");
Traits::copy(&s_[0], &s[0], M);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
auto
static_string<N, CharT, Traits>::
operator=(const CharT (&s)[M]) ->
static_string&
{
static_assert(M-1 <= N,
"static_string overflow");
n_ = M-1;
Traits::copy(&s_[0], &s[0], M);
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
at(size_type pos) ->
reference
{
if(pos >= n_)
throw std::out_of_range("static_string::at");
return s_[pos];
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
at(size_type pos) const ->
const_reference
{
if(pos >= n_)
throw std::out_of_range("static_string::at");
return s_[pos];
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
resize(std::size_t n)
{
if(n > N)
throw std::length_error("static_string overflow");
n_ = n;
s_[n_] = 0;
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
resize(std::size_t n, CharT c)
{
if(n > N)
throw std::length_error("static_string overflow");
if(n > n_)
Traits::assign(&s_[n_], n - n_, c);
n_ = n;
s_[n_] = 0;
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
int
static_string<N, CharT, Traits>::
compare(static_string<M, CharT, Traits> const& rhs) const
{
if(size() < rhs.size())
{
auto const v = Traits::compare(
data(), rhs.data(), size());
if(v == 0)
return -1;
return v;
}
else if(size() > rhs.size())
{
auto const v = Traits::compare(
data(), rhs.data(), rhs.size());
if(v == 0)
return 1;
return v;
}
return Traits::compare(data(), rhs.data(), size());
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
assign(CharT const* s)
{
auto const n = Traits::length(s);
if(n > N)
throw std::out_of_range("too large");
n_ = n;
Traits::copy(&s_[0], s, n_ + 1);
}
namespace detail {
template<std::size_t N, std::size_t M, class CharT, class Traits>
int compare(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
if(lhs.size() < M-1)
{
auto const v = Traits::compare(
lhs.data(), &s[0], lhs.size());
if(v == 0)
return -1;
return v;
}
else if(lhs.size() > M-1)
{
auto const v = Traits::compare(
lhs.data(), &s[0], M-1);
if(v == 0)
return 1;
return v;
}
return Traits::compare(lhs.data(), &s[0], lhs.size());
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
int compare(
const CharT (&s)[M],
static_string<N, CharT, Traits> const& rhs)
{
if(M-1 < rhs.size())
{
auto const v = Traits::compare(
&s[0], rhs.data(), M-1);
if(v == 0)
return -1;
return v;
}
else if(M-1 > rhs.size())
{
auto const v = Traits::compare(
&s[0], rhs.data(), rhs.size());
if(v == 0)
return 1;
return v;
}
return Traits::compare(&s[0], rhs.data(), M-1);
}
} // detail
#if ! GENERATING_DOCS
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator==(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) == 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator!=(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) != 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) < 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<=(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) <= 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) > 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>=(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) >= 0;
}
//---
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator==(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) == 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator==(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) == 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator!=(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) != 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator!=(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) != 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) < 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator<(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) < 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<=(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) <= 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator<=(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) <= 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) > 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator>(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) > 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>=(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) >= 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator>=(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) >= 0;
}
#endif
} // beast
#endif

View File

@@ -0,0 +1,76 @@
//
// 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_STREAM_CONCEPTS_HPP
#define BEAST_STREAM_CONCEPTS_HPP
#include <beast/detail/stream_concepts.hpp>
#include <type_traits>
namespace beast {
/// Determine if `T` has the `get_io_service` member.
template<class T>
#if GENERATING_DOCS
struct has_get_io_service : std::integral_constant<bool, ...>{};
#else
using has_get_io_service = typename detail::has_get_io_service<T>::type;
#endif
/// Determine if `T` meets the requirements of @b `AsyncReadStream`.
template<class T>
#if GENERATING_DOCS
struct is_AsyncReadStream : std::integral_constant<bool, ...>{};
#else
using is_AsyncReadStream = typename detail::is_AsyncReadStream<T>::type;
#endif
/// Determine if `T` meets the requirements of @b `AsyncWriteStream`.
template<class T>
#if GENERATING_DOCS
struct is_AsyncWriteStream : std::integral_constant<bool, ...>{};
#else
using is_AsyncWriteStream = typename detail::is_AsyncWriteStream<T>::type;
#endif
/// Determine if `T` meets the requirements of @b `SyncReadStream`.
template<class T>
#if GENERATING_DOCS
struct is_SyncReadStream : std::integral_constant<bool, ...>{};
#else
using is_SyncReadStream = typename detail::is_SyncReadStream<T>::type;
#endif
/// Determine if `T` meets the requirements of @b `SyncWriterStream`.
template<class T>
#if GENERATING_DOCS
struct is_SyncWriteStream : std::integral_constant<bool, ...>{};
#else
using is_SyncWriteStream = typename detail::is_SyncWriteStream<T>::type;
#endif
/// Determine if `T` meets the requirements of @b `AsyncStream`.
template<class T>
#if GENERATING_DOCS
struct is_AsyncStream : std::integral_constant<bool, ...>{};
#else
using is_AsyncStream = std::integral_constant<bool,
is_AsyncReadStream<T>::value && is_AsyncWriteStream<T>::value>;
#endif
/// Determine if `T` meets the requirements of @b `SyncStream`.
template<class T>
#if GENERATING_DOCS
struct is_SyncStream : std::integral_constant<bool, ...>{};
#else
using is_SyncStream = std::integral_constant<bool,
is_SyncReadStream<T>::value && is_SyncWriteStream<T>::value>;
#endif
} // beast
#endif

View File

@@ -12,6 +12,15 @@
namespace beast { namespace beast {
/** A @b `Streambuf` 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`.
*/
using streambuf = basic_streambuf<std::allocator<char>>; using streambuf = basic_streambuf<std::allocator<char>>;
} // beast } // beast

View File

@@ -9,7 +9,10 @@
#define BEAST_STREAMBUF_READSTREAM_HPP #define BEAST_STREAMBUF_READSTREAM_HPP
#include <beast/async_completion.hpp> #include <beast/async_completion.hpp>
#include <beast/buffer_concepts.hpp>
#include <beast/stream_concepts.hpp>
#include <beast/streambuf.hpp> #include <beast/streambuf.hpp>
#include <beast/detail/get_lowest_layer.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp> #include <boost/asio/io_service.hpp>
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
@@ -18,11 +21,11 @@
namespace beast { namespace beast {
/** A `Stream` with attached `Streambuf` to buffer reads. /** A @b `Stream` with attached @b `Streambuf` to buffer reads.
This wraps a `Stream` implementation so that calls to write are This wraps a @b `Stream` implementation so that calls to write are
passed through to the underlying stream, while calls to read will passed through to the underlying stream, while calls to read will
first consume the input sequence stored in a `Streambuf` which first consume the input sequence stored in a @b `Streambuf` which
is part of the object. is part of the object.
The use-case for this class is different than that of the The use-case for this class is different than that of the
@@ -35,10 +38,10 @@ namespace beast {
Uses: Uses:
* Transparently leave untouched input acquired in calls @li Transparently leave untouched input acquired in calls
to `boost::asio::read_until` behind for subsequent callers. to `boost::asio::read_until` behind for subsequent callers.
* "Preload" a stream with handshake input data acquired @li "Preload" a stream with handshake input data acquired
from other sources. from other sources.
Example: Example:
@@ -83,10 +86,12 @@ namespace beast {
@tparam Streambuf The type of stream buffer to use. @tparam Streambuf The type of stream buffer to use.
*/ */
template<class Stream, template<class Stream, class Streambuf>
class Streambuf = streambuf>
class streambuf_readstream class streambuf_readstream
{ {
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
using error_code = boost::system::error_code; using error_code = boost::system::error_code;
template<class Buffers, class Handler> template<class Buffers, class Handler>
@@ -106,11 +111,27 @@ public:
/// The type of the lowest layer. /// The type of the lowest layer.
using lowest_layer_type = using lowest_layer_type =
typename next_layer_type::lowest_layer_type; #if GENERATING_DOCS
implementation_defined;
#else
typename detail::get_lowest_layer<
next_layer_type>::type;
#endif
/// Move constructor. /** Move constructor.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
streambuf_readstream(streambuf_readstream&&) = default; streambuf_readstream(streambuf_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;
/** Construct the wrapping stream. /** Construct the wrapping stream.
@param args Parameters forwarded to the `Stream` constructor. @param args Parameters forwarded to the `Stream` constructor.
@@ -200,6 +221,8 @@ public:
std::size_t std::size_t
write_some(ConstBufferSequence const& buffers) write_some(ConstBufferSequence const& buffers)
{ {
static_assert(is_SyncWriteStream<next_layer_type>::value,
"SyncWriteStream requirements not met");
return next_layer_.write_some(buffers); return next_layer_.write_some(buffers);
} }
@@ -210,14 +233,19 @@ public:
write_some(ConstBufferSequence const& buffers, write_some(ConstBufferSequence const& buffers,
error_code& ec) error_code& ec)
{ {
static_assert(is_SyncWriteStream<next_layer_type>::value,
"SyncWriteStream requirements not met");
return next_layer_.write_some(buffers, ec); return next_layer_.write_some(buffers, ec);
} }
/// Start an asynchronous write. The data being written must be valid for the /// Start an asynchronous write. The data being written must be valid for the
/// lifetime of the asynchronous operation. /// lifetime of the asynchronous operation.
template<class ConstBufferSequence, class WriteHandler> template<class ConstBufferSequence, class WriteHandler>
typename async_completion< #if GENERATING_DOCS
WriteHandler, void(error_code)>::result_type void_or_deduced
#else
typename async_completion<WriteHandler, void(error_code)>::result_type
#endif
async_write_some(ConstBufferSequence const& buffers, async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler); WriteHandler&& handler);
@@ -237,8 +265,11 @@ public:
/// Start an asynchronous read. The buffer into which the data will be read /// Start an asynchronous read. The buffer into which the data will be read
/// must be valid for the lifetime of the asynchronous operation. /// must be valid for the lifetime of the asynchronous operation.
template<class MutableBufferSequence, class ReadHandler> template<class MutableBufferSequence, class ReadHandler>
typename async_completion< #if GENERATING_DOCS
ReadHandler, void(error_code)>::result_type void_or_deduced
#else
typename async_completion<ReadHandler, void(error_code)>::result_type
#endif
async_read_some(MutableBufferSequence const& buffers, async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler); ReadHandler&& handler);
}; };

View File

@@ -0,0 +1,51 @@
//
// 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_TO_STRING_HPP
#define BEAST_TO_STRING_HPP
#include <beast/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <string>
namespace beast {
/** Convert a @b `ConstBufferSequence` to a `std::string`.
This function will convert the octets in a buffer sequence to a string.
All octets will be inserted into the resulting string, including null
or unprintable characters.
@param buffers The buffer sequence to convert.
@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`.
*/
template<class ConstBufferSequence
#if ! GENERATING_DOCS
,class = std::enable_if<is_ConstBufferSequence<
ConstBufferSequence>::value>
#endif
>
std::string
to_string(ConstBufferSequence const& buffers)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(buffers));
for(auto const& buffer : buffers)
s.append(buffer_cast<char const*>(buffer),
buffer_size(buffer));
return s;
}
} // beast
#endif

View File

@@ -1,348 +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_TYPE_CHECK_HPP
#define BEAST_TYPE_CHECK_HPP
#include <beast/detail/is_call_possible.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/io_service.hpp>
#include <iterator>
#include <type_traits>
#include <utility>
namespace beast {
//------------------------------------------------------------------------------
// Types that meet the requirements,
// for use with std::declval only.
//
#if GENERATING_DOCS
namespace detail {
#else
namespace concept {
#endif
template<class BufferType>
struct BufferSequence
{
using value_type = BufferType;
using const_iterator = BufferType const*;
~BufferSequence();
BufferSequence(BufferSequence const&) = default;
const_iterator
begin() const noexcept;
const_iterator
end() const noexcept;
};
using ConstBufferSequence =
BufferSequence<boost::asio::const_buffer>;
using MutableBufferSequence =
BufferSequence<boost::asio::mutable_buffer>;
struct StreamHandler
{
StreamHandler(StreamHandler const&) = default;
void
operator()(boost::system::error_code ec,
std::size_t);
};
using ReadHandler = StreamHandler;
using WriteHandler = StreamHandler;
} // concept
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html
//
/// Determine if `T` meets the requirements of `BufferSequence`.
template<class T, class BufferType>
class is_BufferSequence
{
template<class U, class R = std::is_convertible<
typename U::value_type, BufferType> >
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_base_of<
#if 0
std::bidirectional_iterator_tag,
typename std::iterator_traits<
typename U::const_iterator>::iterator_category>>
#else
// workaround:
// boost::asio::detail::consuming_buffers::const_iterator
// is not bidirectional
std::forward_iterator_tag,
typename std::iterator_traits<
typename U::const_iterator>::iterator_category>>
#endif
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R = typename
std::is_convertible<decltype(
std::declval<U>().begin()),
typename U::const_iterator>::type>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
template<class U, class R = typename std::is_convertible<decltype(
std::declval<U>().end()),
typename U::const_iterator>::type>
static R check4(int);
template<class>
static std::false_type check4(...);
using type4 = decltype(check4<T>(0));
public:
/// `true` if `T` meets the requirements.
static bool const value =
std::is_copy_constructible<T>::value &&
std::is_destructible<T>::value &&
type1::value && type2::value &&
type3::value && type4::value;
};
#if ! GENERATING_DOCS
/// Determine if `T` meets the requirements of `ConstBufferSequence`.
template<class T>
using is_ConstBufferSequence =
is_BufferSequence<T, boost::asio::const_buffer>;
static_assert(is_ConstBufferSequence<concept::ConstBufferSequence>::value, "");
static_assert(! is_ConstBufferSequence<int>::value, "");
/// Determine if `T` meets the requirements of `MutableBufferSequence`.
template<class C>
using is_MutableBufferSequence =
is_BufferSequence<C, boost::asio::mutable_buffer>;
static_assert(is_MutableBufferSequence<concept::MutableBufferSequence>::value, "");
static_assert(! is_MutableBufferSequence<int>::value, "");
#endif
//------------------------------------------------------------------------------
/// Determine if `T` has the `get_io_service` member.
template<class T>
class has_get_io_service
{
template<class U, class R = typename std::is_same<
decltype(std::declval<U>().get_io_service()),
boost::asio::io_service&>>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
/// `true` if `T` meets the requirements.
static bool const value = type::value;
};
static_assert(! has_get_io_service<int>::value, "");
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html
//
/// Determine if `T` meets the requirements of `AsyncReadStream`.
template<class T>
class is_AsyncReadStream
{
template<class U, class R = decltype(
std::declval<U>().async_read_some(
std::declval<concept::MutableBufferSequence>(),
std::declval<concept::ReadHandler>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
/// `true` if `T` meets the requirements.
static bool const value =
has_get_io_service<T>::value && type::value;
};
static_assert(! is_AsyncReadStream<int>::value, "");
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html
//
/// Determine if `T` meets the requirements of `AsyncWriteStream`.
template<class T>
class is_AsyncWriteStream
{
template<class U, class R = decltype(
std::declval<U>().async_write_some(
std::declval<concept::ConstBufferSequence>(),
std::declval<concept::WriteHandler>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
/// `true` if `T` meets the requirements.
static bool const value =
has_get_io_service<T>::value && type::value;
};
static_assert(! is_AsyncWriteStream<int>::value, "");
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html
//
/// Determine if `T` meets the requirements of `SyncReadStream`.
template<class T>
class is_SyncReadStream
{
using error_code =
boost::system::error_code;
template<class U, class R = std::is_same<decltype(
std::declval<U>().read_some(
std::declval<concept::MutableBufferSequence>())),
std::size_t>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().read_some(
std::declval<concept::MutableBufferSequence>(),
std::declval<error_code&>())), std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
public:
/// `true` if `T` meets the requirements.
static bool const value =
type1::value && type2::value;
};
static_assert(! is_SyncReadStream<int>::value, "");
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html
//
/// Determine if `T` meets the requirements of `SyncWriterStream`.
template<class T>
class is_SyncWriteStream
{
using error_code =
boost::system::error_code;
template<class U, class R = std::is_same<decltype(
std::declval<U>().write_some(
std::declval<concept::ConstBufferSequence>())),
std::size_t>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().write_some(
std::declval<concept::ConstBufferSequence>(),
std::declval<error_code&>())), std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
public:
/// `true` if `T` meets the requirements.
static bool const value =
type1::value && type2::value;
};
static_assert(! is_SyncWriteStream<int>::value, "");
/// Determine if `T` meets the requirements of `Stream`.
template<class T>
struct is_Stream
{
/// `true` if `T` meets the requirements.
static bool const value =
is_AsyncReadStream<T>::value &&
is_AsyncWriteStream<T>::value &&
is_SyncReadStream<T>::value &&
is_SyncWriteStream<T>::value;
};
/// Determine if `T` meets the requirements of `Streambuf`.
template<class T>
class is_Streambuf
{
template<class U, class R = std::integral_constant<
bool, is_MutableBufferSequence<decltype(
std::declval<U>().prepare(1))>::value>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::integral_constant<
bool, is_ConstBufferSequence<decltype(
std::declval<U>().data())>::value>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R = decltype(
std::declval<U>().commit(1), std::true_type{})>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
template<class U, class R = decltype(
std::declval<U>().consume(1), std::true_type{})>
static R check4(int);
template<class>
static std::false_type check4(...);
using type4 = decltype(check4<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().size()), std::size_t>>
static R check5(int);
template<class>
static std::false_type check5(...);
using type5 = decltype(check5<T>(0));
public:
/// `true` if `T` meets the requirements.
static bool const value =
type1::value && type2::value &&
type3::value && type4::value &&
type5::value;
};
#if ! GENERATING_DOCS
/// Determine if `T` meets the requirements of `CompletionHandler`.
template<class T, class Signature>
using is_Handler = std::integral_constant<bool,
std::is_copy_constructible<typename std::decay<T>::type>::value &&
detail::is_call_possible<T, Signature>::value>;
#endif
} // beast
#endif

View File

@@ -16,6 +16,6 @@
// //
#define BEAST_VERSION 100000 #define BEAST_VERSION 100000
#define BEAST_VERSION_STRING "1.0.0-b2" #define BEAST_VERSION_STRING "1.0.0-b3"
#endif #endif

View File

@@ -24,7 +24,6 @@
#include <beast/websocket/option.hpp> #include <beast/websocket/option.hpp>
#include <beast/websocket/rfc6455.hpp> #include <beast/websocket/rfc6455.hpp>
#include <beast/websocket/stream.hpp> #include <beast/websocket/stream.hpp>
#include <beast/websocket/static_string.hpp>
#include <beast/websocket/teardown.hpp> #include <beast/websocket/teardown.hpp>
#endif #endif

View File

@@ -17,10 +17,9 @@
*/ */
//============================================================================== //==============================================================================
#ifndef BEAST_WSPROTO_DEBUG_H_INCLUDED #ifndef BEAST_WEBSOCKET_DETAIL_DEBUG_HPP
#define BEAST_WSPROTO_DEBUG_H_INCLUDED #define BEAST_WEBSOCKET_DETAIL_DEBUG_HPP
#include <beast/unit_test/suite.h>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
@@ -82,7 +81,7 @@ format(std::string s)
} }
} // detail } // detail
} // wsproto } // websocket
} // beast } // beast
#endif #endif

View File

@@ -53,6 +53,17 @@ little_uint32_to_native(void const* buf)
(static_cast<std::uint64_t>(p[3])<<24); (static_cast<std::uint64_t>(p[3])<<24);
} }
inline
void
native_to_little_uint32(std::uint32_t v, void* buf)
{
auto p = reinterpret_cast<std::uint8_t*>(buf);
p[0] = v & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
p[3] = (v >> 24) & 0xff;
}
} // detail } // detail
} // websocket } // websocket
} // beast } // beast

View File

@@ -30,7 +30,7 @@ public:
const char* const char*
name() const noexcept override name() const noexcept override
{ {
return "wsproto"; return "websocket";
} }
std::string std::string
@@ -50,7 +50,7 @@ public:
case error::request_invalid: return "upgrade request invalid"; case error::request_invalid: return "upgrade request invalid";
case error::request_denied: return "upgrade request denied"; case error::request_denied: return "upgrade request denied";
default: default:
return "wsproto.error"; return "websocket error";
} }
} }

View File

@@ -9,11 +9,11 @@
#define BEAST_WEBSOCKET_DETAIL_FRAME_HPP #define BEAST_WEBSOCKET_DETAIL_FRAME_HPP
#include <beast/websocket/rfc6455.hpp> #include <beast/websocket/rfc6455.hpp>
#include <beast/websocket/static_string.hpp>
#include <beast/websocket/detail/endian.hpp> #include <beast/websocket/detail/endian.hpp>
#include <beast/websocket/detail/utf8_checker.hpp> #include <beast/websocket/detail/utf8_checker.hpp>
#include <beast/consuming_buffers.hpp> #include <beast/consuming_buffers.hpp>
#include <beast/static_streambuf.hpp> #include <beast/static_streambuf.hpp>
#include <beast/static_string.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/endian/buffers.hpp> #include <boost/endian/buffers.hpp>
#include <cassert> #include <cassert>
@@ -70,10 +70,9 @@ is_control(opcode op)
// Returns `true` if a close code is valid // Returns `true` if a close code is valid
inline inline
bool bool
is_valid(close_code code) is_valid(close_code::value code)
{ {
auto const v = static_cast< auto const v = code;
std::uint16_t>(code);
switch(v) switch(v)
{ {
case 1000: case 1000:
@@ -141,9 +140,7 @@ write(Streambuf& sb, frame_header const& fh)
} }
if(fh.mask) if(fh.mask)
{ {
little_uint32_buf_t key(fh.key); native_to_little_uint32(fh.key, &b[n]);
std::copy(key.data(),
key.data() + 4, &b[n]);
n += 4; n += 4;
} }
sb.commit(buffer_copy( sb.commit(buffer_copy(
@@ -156,7 +153,7 @@ write(Streambuf& sb, frame_header const& fh)
template<class Streambuf> template<class Streambuf>
std::size_t std::size_t
read_fh1(frame_header& fh, Streambuf& sb, read_fh1(frame_header& fh, Streambuf& sb,
role_type role, close_code& code) role_type role, close_code::value& code)
{ {
using boost::asio::buffer; using boost::asio::buffer;
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
@@ -173,7 +170,8 @@ read_fh1(frame_header& fh, Streambuf& sb,
default: default:
need = 0; need = 0;
} }
if((fh.mask = (b[1] & 0x80) != 0)) fh.mask = (b[1] & 0x80) != 0;
if(fh.mask)
need += 4; need += 4;
fh.op = static_cast<opcode>(b[0] & 0x0f); fh.op = static_cast<opcode>(b[0] & 0x0f);
fh.fin = (b[0] & 0x80) != 0; fh.fin = (b[0] & 0x80) != 0;
@@ -232,7 +230,7 @@ read_fh1(frame_header& fh, Streambuf& sb,
template<class Streambuf> template<class Streambuf>
void void
read_fh2(frame_header& fh, Streambuf& sb, read_fh2(frame_header& fh, Streambuf& sb,
role_type role, close_code& code) role_type role, close_code::value& code)
{ {
using boost::asio::buffer; using boost::asio::buffer;
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
@@ -303,7 +301,7 @@ read_fh2(frame_header& fh, Streambuf& sb,
template<class Buffers> template<class Buffers>
void void
read(ping_payload_type& data, read(ping_payload_type& data,
Buffers const& bs, close_code& code) Buffers const& bs, close_code::value& code)
{ {
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
@@ -320,7 +318,7 @@ read(ping_payload_type& data,
template<class Buffers> template<class Buffers>
void void
read(close_reason& cr, read(close_reason& cr,
Buffers const& bs, close_code& code) Buffers const& bs, close_code::value& code)
{ {
using boost::asio::buffer; using boost::asio::buffer;
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
@@ -343,15 +341,7 @@ read(close_reason& cr,
{ {
std::uint8_t b[2]; std::uint8_t b[2];
buffer_copy(buffer(b), cb); buffer_copy(buffer(b), cb);
#if 0 cr.code = big_uint16_to_native(&b[0]);
// Causes strict-aliasing warning in gcc
cr.code = static_cast<close_code>(
reinterpret_cast<
big_uint16_buf_t const*>(&b[0])->value());
#else
cr.code = static_cast<close_code>(
big_uint16_to_native(&b[0]));
#endif
cb.consume(2); cb.consume(2);
n -= 2; n -= 2;
if(! is_valid(cr.code)) if(! is_valid(cr.code))

View File

@@ -85,8 +85,6 @@ public:
#endif #endif
invokable() = default; invokable() = default;
invokable(invokable const&) = delete;
invokable& operator=(invokable const&) = delete;
invokable(invokable&& other) invokable(invokable&& other)
{ {

View File

@@ -41,9 +41,6 @@ class maskgen_t
public: public:
using result_type = typename std::mt19937::result_type; using result_type = typename std::mt19937::result_type;
maskgen_t(maskgen_t const&) = delete;
maskgen_t& operator=(maskgen_t const&) = delete;
maskgen_t(); maskgen_t();
result_type result_type

View File

@@ -108,7 +108,7 @@ protected:
template<class = void> template<class = void>
void void
prepare_fh(close_code& code); prepare_fh(close_code::value& code);
template<class Streambuf> template<class Streambuf>
void void

View File

@@ -73,12 +73,6 @@ class utf8_checker_t
std::uint32_t codepoint_ = 0; std::uint32_t codepoint_ = 0;
public: public:
utf8_checker_t() = default;
utf8_checker_t(utf8_checker_t&&) = default;
utf8_checker_t(utf8_checker_t const&) = default;
utf8_checker_t& operator=(utf8_checker_t&&) = default;
utf8_checker_t& operator=(utf8_checker_t const&) = default;
void void
reset(); reset();

View File

@@ -9,6 +9,7 @@
#define BEAST_WEBSOCKET_ERROR_HPP #define BEAST_WEBSOCKET_ERROR_HPP
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
namespace beast { namespace beast {
namespace websocket { namespace websocket {
@@ -16,7 +17,7 @@ namespace websocket {
/// The type of error used by functions and completion handlers. /// The type of error used by functions and completion handlers.
using error_code = boost::system::error_code; using error_code = boost::system::error_code;
/// Error values /// Error codes returned from @ref stream operations.
enum class error enum class error
{ {
/// Both sides performed a WebSocket close /// Both sides performed a WebSocket close
@@ -50,8 +51,10 @@ enum class error
request_denied request_denied
}; };
#if ! GENERATING_DOCS
error_code error_code
make_error_code(error e); make_error_code(error e);
#endif
} // websocket } // websocket
} // beast } // beast

View File

@@ -9,10 +9,11 @@
#define BEAST_WEBSOCKET_IMPL_ACCEPT_OP_HPP #define BEAST_WEBSOCKET_IMPL_ACCEPT_OP_HPP
#include <beast/websocket/impl/response_op.ipp> #include <beast/websocket/impl/response_op.ipp>
#include <beast/http/message_v1.hpp>
#include <beast/http/parser_v1.hpp>
#include <beast/http/read.hpp>
#include <beast/handler_alloc.hpp> #include <beast/handler_alloc.hpp>
#include <beast/prepare_buffers.hpp> #include <beast/prepare_buffers.hpp>
#include <beast/http/parser.hpp>
#include <beast/http/read.hpp>
#include <cassert> #include <cassert>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
@@ -32,7 +33,7 @@ class stream<NextLayer>::accept_op
struct data struct data
{ {
stream<NextLayer>& ws; stream<NextLayer>& ws;
http::request<http::empty_body> req; http::request_v1<http::empty_body> req;
Handler h; Handler h;
bool cont; bool cont;
int state = 0; int state = 0;
@@ -124,7 +125,7 @@ operator()(error_code const& ec,
case 0: case 0:
// read message // read message
d.state = 1; d.state = 1;
http::async_read(d.ws.next_layer_, http::async_read(d.ws.next_layer(),
d.ws.stream_.buffer(), d.req, d.ws.stream_.buffer(), d.req,
std::move(*this)); std::move(*this));
return; return;

View File

@@ -34,7 +34,6 @@ class stream<NextLayer>::close_op
close_reason cr; close_reason cr;
Handler h; Handler h;
fb_type fb; fb_type fb;
fmb_type fmb;
bool cont; bool cont;
int state = 0; int state = 0;
@@ -176,8 +175,8 @@ stream<NextLayer>::close_op<Handler>::operator()(
d.ws.error_ = true; d.ws.error_ = true;
if(d.ws.wr_block_ == &d) if(d.ws.wr_block_ == &d)
d.ws.wr_block_ = nullptr; d.ws.wr_block_ = nullptr;
d.h(ec);
d.ws.rd_op_.maybe_invoke(); d.ws.rd_op_.maybe_invoke();
d.h(ec);
} }
} // websocket } // websocket

View File

@@ -10,7 +10,7 @@
#include <beast/handler_alloc.hpp> #include <beast/handler_alloc.hpp>
#include <beast/http/empty_body.hpp> #include <beast/http/empty_body.hpp>
#include <beast/http/message.hpp> #include <beast/http/message_v1.hpp>
#include <beast/http/read.hpp> #include <beast/http/read.hpp>
#include <beast/http/write.hpp> #include <beast/http/write.hpp>
#include <cassert> #include <cassert>
@@ -33,8 +33,8 @@ class stream<NextLayer>::handshake_op
stream<NextLayer>& ws; stream<NextLayer>& ws;
Handler h; Handler h;
std::string key; std::string key;
http::request<http::empty_body> req; http::request_v1<http::empty_body> req;
http::response<http::string_body> resp; http::response_v1<http::string_body> resp;
bool cont; bool cont;
int state = 0; int state = 0;
@@ -134,7 +134,7 @@ stream<NextLayer>::handshake_op<
case 1: case 1:
// read http response // read http response
d.state = 2; d.state = 2;
http::async_read(d.ws.next_layer_, http::async_read(d.ws.next_layer(),
d.ws.stream_.buffer(), d.resp, d.ws.stream_.buffer(), d.resp,
std::move(*this)); std::move(*this));
return; return;

View File

@@ -42,10 +42,10 @@ class stream<NextLayer>::read_frame_op
stream<NextLayer>& ws; stream<NextLayer>& ws;
frame_info& fi; frame_info& fi;
Streambuf& sb; Streambuf& sb;
smb_type smb;
Handler h; Handler h;
fb_type fb; fb_type fb;
fmb_type fmb; boost::optional<smb_type> smb;
boost::optional<fmb_type> fmb;
bool cont; bool cont;
int state = 0; int state = 0;
@@ -132,7 +132,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
{ {
auto& d = *d_; auto& d = *d_;
d.cont = d.cont || again; d.cont = d.cont || again;
close_code code; close_code::value code = close_code::none;
while(! ec && d.state != 99) while(! ec && d.state != 99)
{ {
switch(d.state) switch(d.state)
@@ -161,7 +161,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
d.smb = d.sb.prepare( d.smb = d.sb.prepare(
detail::clamp(d.ws.rd_need_)); detail::clamp(d.ws.rd_need_));
d.ws.stream_.async_read_some( d.ws.stream_.async_read_some(
d.smb, std::move(*this)); *d.smb, std::move(*this));
return; return;
case 2: case 2:
@@ -176,7 +176,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
{ {
d.ws.rd_need_ -= bytes_transferred; d.ws.rd_need_ -= bytes_transferred;
auto const pb = prepare_buffers( auto const pb = prepare_buffers(
bytes_transferred, d.smb); bytes_transferred, *d.smb);
if(d.ws.rd_fh_.mask) if(d.ws.rd_fh_.mask)
detail::mask_inplace(pb, d.ws.rd_key_); detail::mask_inplace(pb, d.ws.rd_key_);
if(d.ws.rd_opcode_ == opcode::text) if(d.ws.rd_opcode_ == opcode::text)
@@ -195,7 +195,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
d.state = 4; d.state = 4;
break; break;
} }
// call handler // call handler
case 4: case 4:
d.state = 99; d.state = 99;
@@ -252,7 +252,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
d.fmb = d.fb.prepare(static_cast< d.fmb = d.fb.prepare(static_cast<
std::size_t>(d.ws.rd_fh_.len)); std::size_t>(d.ws.rd_fh_.len));
boost::asio::async_read(d.ws.stream_, boost::asio::async_read(d.ws.stream_,
d.fmb, std::move(*this)); *d.fmb, std::move(*this));
return; return;
} }
d.state = 8; d.state = 8;
@@ -276,7 +276,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
case 7: case 7:
if(d.ws.rd_fh_.mask) if(d.ws.rd_fh_.mask)
detail::mask_inplace( detail::mask_inplace(
d.fmb, d.ws.rd_key_); *d.fmb, d.ws.rd_key_);
d.fb.commit(bytes_transferred); d.fb.commit(bytes_transferred);
d.state = 8; d.state = 8;
break; break;
@@ -396,8 +396,8 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
// teardown // teardown
case 11: case 11:
d.state = 12; d.state = 12;
wsproto_helpers::call_async_teardown( websocket_helpers::call_async_teardown(
d.ws.next_layer_, std::move(*this)); d.ws.next_layer(), std::move(*this));
return; return;
case 12: case 12:
@@ -482,8 +482,8 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
// teardown // teardown
case 19: case 19:
d.state = 20; d.state = 20;
wsproto_helpers::call_async_teardown( websocket_helpers::call_async_teardown(
d.ws.next_layer_, std::move(*this)); d.ws.next_layer(), std::move(*this));
return; return;
case 20: case 20:
@@ -497,8 +497,8 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
d.ws.error_ = true; d.ws.error_ = true;
if(d.ws.wr_block_ == &d) if(d.ws.wr_block_ == &d)
d.ws.wr_block_ = nullptr; d.ws.wr_block_ = nullptr;
d.h(ec);
d.ws.wr_op_.maybe_invoke(); d.ws.wr_op_.maybe_invoke();
d.h(ec);
} }
} // websocket } // websocket

View File

@@ -9,6 +9,7 @@
#define BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP #define BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP
#include <beast/handler_alloc.hpp> #include <beast/handler_alloc.hpp>
#include <beast/http/message_v1.hpp>
#include <beast/http/string_body.hpp> #include <beast/http/string_body.hpp>
#include <beast/http/write.hpp> #include <beast/http/write.hpp>
#include <memory> #include <memory>
@@ -27,7 +28,7 @@ class stream<NextLayer>::response_op
struct data struct data
{ {
stream<NextLayer>& ws; stream<NextLayer>& ws;
http::response<http::string_body> resp; http::response_v1<http::string_body> resp;
Handler h; Handler h;
error_code final_ec; error_code final_ec;
bool cont; bool cont;
@@ -36,7 +37,7 @@ class stream<NextLayer>::response_op
template<class DeducedHandler, template<class DeducedHandler,
class Body, class Headers> class Body, class Headers>
data(DeducedHandler&& h_, stream<NextLayer>& ws_, data(DeducedHandler&& h_, stream<NextLayer>& ws_,
http::message<true, Body, Headers> const& req, http::request_v1<Body, Headers> const& req,
bool cont_) bool cont_)
: ws(ws_) : ws(ws_)
, resp(ws_.build_response(req)) , resp(ws_.build_response(req))
@@ -113,7 +114,7 @@ operator()(error_code ec, bool again)
case 0: case 0:
// send response // send response
d.state = 1; d.state = 1;
http::async_write(d.ws.next_layer_, http::async_write(d.ws.next_layer(),
d.resp, std::move(*this)); d.resp, std::move(*this));
return; return;

Some files were not shown because too many files have changed in this diff Show More