Merge subtree Beast 1.0.0-b2:

Merge commit '6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99'
This commit is contained in:
Vinnie Falco
2016-04-29 05:24:40 -04:00
87 changed files with 7083 additions and 2490 deletions

41
src/beast/CMakeLists.txt Normal file
View File

@@ -0,0 +1,41 @@
# Part of Beast
cmake_minimum_required (VERSION 3.5)
project (Beast)
set_property (GLOBAL PROPERTY USE_FOLDERS ON)
if (WIN32)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /wd4100 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1")
else()
endif()
message ("cxx Flags: " ${CMAKE_CXX_FLAGS})
macro(GroupSources curdir)
file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*)
foreach(child ${children})
if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${curdir}/${child})
GroupSources(${curdir}/${child})
elseif(${child} STREQUAL "CMakeLists.txt")
source_group("" FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
else()
string(REPLACE "/" "\\" groupname ${curdir})
string(REPLACE "src" "Common" groupname ${groupname})
source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
endif()
endforeach()
endmacro()
include_directories (include)
file(GLOB_RECURSE BEAST_INCLUDES
${PROJECT_SOURCE_DIR}/include/beast/*.hpp
${PROJECT_SOURCE_DIR}/include/beast/*.ipp
)
add_subdirectory (test)
add_subdirectory (examples)
#enable_testing()

View File

@@ -60,8 +60,12 @@ project beast
<threading>multi
<link>static
<runtime-link>shared
<debug-symbols>on
<toolset>gcc:<cxxflags>-std=c++11
<toolset>gcc:<cxxflags>-Wno-unused-variable
<toolset>clang:<cxxflags>-std=c++11
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
<os>LINUX:<define>_XOPEN_SOURCE=600
<os>LINUX:<define>_GNU_SOURCE=1
<os>SOLARIS:<define>_XOPEN_SOURCE=500
@@ -78,8 +82,6 @@ project beast
<os>HPUX:<library>ipv6
<os>QNXNTO:<library>socket
<os>HAIKU:<library>network
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
: usage-requirements
<include>.
:

View File

@@ -1,8 +1,5 @@
* kick out non-beast code
* redo directory structure
* Change build options to C++11 only
* Replace Jamroot with Jamfile
* Switch source material to Boost license
* Fix failing test/message.cpp
* Complete allocator testing in basic_streambuf, basic_headers
* Tidy up type_checks
@@ -11,7 +8,6 @@
- See if we can include them now that xsl is fixed
* Go over each header and split header material into detail and impl files
* Make buffers_debug a detail
* Ensure each public header has a unit test .cpp file
* Roll header-only http parser
* Define Parser concept in HTTP
* melpon sandbox?
@@ -21,3 +17,16 @@
* Remove or change http::headers alias
* Do something about the methods.hpp and fields.hpp type headers
* 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
(currently disabled via GENERATING_DOCS macro)
* Include Example program listings in the docs
* Update for rfc7230
* HTTP parser size limit with test (configurable?)
* HTTP parser trailers with test
* URL parser, strong URL checking in HTTP parser
* Fix method, use string instead of enum
* More fine grained parser errors
* Fix all the warnings in all projects/build configs
* Fix bidirectional buffers iterators operator->()

View File

@@ -51,13 +51,13 @@ LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_PACKAGE = YES
EXTRACT_STATIC = YES
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = NO
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = YES
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
@@ -119,6 +119,7 @@ INPUT = \
../include/beast/streambuf_readstream.hpp \
../include/beast/type_check.hpp \
../include/beast/websocket.hpp \
../include/beast/write_streambuf.hpp \
../include/beast/http/basic_headers.hpp \
../include/beast/http/basic_parser.hpp \
../include/beast/http/chunk_encode.hpp \
@@ -128,6 +129,7 @@ INPUT = \
../include/beast/http/headers.hpp \
../include/beast/http/message.hpp \
../include/beast/http/method.hpp \
../include/beast/http/parse_error.hpp \
../include/beast/http/parser.hpp \
../include/beast/http/read.hpp \
../include/beast/http/resume_context.hpp \

View File

@@ -80,9 +80,9 @@ Beast requires:
[note Tested compilers: msvc-14+, gcc 5+, clang 3.6+]
Most of the library is header-only; however, the HTTP parser used is written
in C. To link an application that uses Beast, it is necessary to add a single
.cpp file from beast into your project's build script.
The library is [*header-only]. It is not necessary to add any .cpp files,
or to edit your existing build script or project file except to provide
that the include/ directory for beast is searched for include files.
[endsect]
@@ -95,11 +95,6 @@ flavor of the library. They are complete programs which may be built
and run. Source code and build scripts for these programs may be found
in the examples directory.
[note
To link these programs, please add the file
`src/beast_http_nodejs_parser.cpp` to your build script or Makefile
]
Use HTTP to request the root page from a website and print the response:
```
#include <beast/http.hpp>

View File

@@ -33,72 +33,39 @@ interface for the C++ standard library will likely closely resemble the current
interface of Boost.Asio, it is logical for Beast.HTTP to use Boost.Asio as its
network transport.
[heading Scope]
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
be a building block for creating higher level abstractions.
[note The documentation which follows assumes familiarity with
both Boost.Asio and the HTTP protocol specification described in
[@https://tools.ietf.org/html/rfc2616 rfc2616] ]
[@https://tools.ietf.org/html/rfc7230 rfc7230] ]
[endsect]
[section:example Example]
[section:usage Usage]
All examples and identifiers mentioned in this document are written as
if the following declarations are in effect:
```
#include <boost/asio.hpp>
#include <beast/http.hpp>
using namespace beast;
using namespace boost::asio;
```
Create a HTTP request:
```
http::request<http::empty_body> req({http::method_t::http_get, "/", 11});
req.headers.insert("Host", "127.0.0.1:80");
req.headers.insert("User-Agent", "Beast.HTTP");
```
When sending a message, the Content-Length and Transfer-Encoding are set
automatically based on the properties of the body. Depending on the HTTP
version of the message and properties of the message body, the implementation
will automaticaly chunk-encode the data sent on the wire:
```
ip::tcp::socket sock(ios);
...
http::response<http::string_body> resp({200, "OK", 11});
resp.headers.insert("Server", "Beast.HTTP");
resp.body = "The command was successful";
write(sock, resp);
}
```
Receiving a message is simple, declare a value of type message and call `read`.
This example reads a message, builds a response, and sends it.
```
void handle_connection(ip::tcp::socket& sock)
{
request<string_body> req;
read(sock, req);
response<string_body> resp;
...
write(sock, resp);
}
```
[endsect]
[section:message Message model]
[note
Sample code and identifiers mentioned in this section are written
as if the following declarations are in effect:
```
#include <beast/http.hpp>
using namespace beast;
```
]
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. Applications using HTTP often perform these operations
on messages:
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.
@@ -112,6 +79,205 @@ 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]
To do anything, a message must be declared. The message class template
requires at mininum, a bool indicating whether the message is a request
(versus a response), and a `Body` type. The choice of `Body` determines the
kind of container used to represent the message body. Here we will
declare a request that has a `std::string` for the body container:
```
http::message<true, http::string_body> req;
```
Two type aliases are provided for notational convenience when declaring
messages. These two statements declare a request and a response respectively:
```
http::request<http::string_body> req;
http::response<http::string_body> resp;
```
[heading Members]
Message objects are default constructible, with public access to data members.
Request and response objects have some common members, and some members unique
to the message type. These statements set all the members in each message:
```
http::request<http::string_body> req;
req.method = http::method_t::http_get;
req.url = "/index.html";
req.version = 11; // HTTP/1.1
req.headers.insert("User-Agent", "hello_world");
req.body = "";
http::response<http::string_body> resp;
resp.status = 404;
resp.reason = "Not Found";
resp.version = 10; // HTTP/1.0
resp.headers.insert("Server", "Beast.HTTP");
resp.body = "The requested resource was not found.";
```
The following statements achieve the same effects as the statements above:
```
http::request<http::string_body> req({http::method_t::http_get, "/index.html", 11});
req.headers.insert("User-Agent", "hello_world");
req.body = "";
http::response<http::string_body> resp({404, "Not Found", 10});
resp.headers.insert("Server", "Beast.HTTP");
resp.body = "The requested resource was not found.";
```
[heading Headers]
The `message::headers` member is a container for setting the field/value
pairs in the message. These statements change the values of the headers
in the message passed:
```
template<class Body>
void set_fields(http::request<Body>& req)
{
if(! req.exists("User-Agent"))
req.insert("User-Agent", "myWebClient");
if(req.exists("Accept-Charset"))
req.erase("Accept-Charset");
req.replace("Accept", "text/plain");
}
```
[heading Body]
The `message::body` member represents the message body. Depending on the
`Body` template argument type, this could be a writable container. The
following types, provided by the library, are suitable choices for the
`Body` type:
* [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
Used in GET requests where there is no message body. Example:
```
http::request<http::empty_body> req({http::method_t::http_get, "/index.html", 11});
```
* [link beast.ref.http__string_body [*`string_body`:]] A body with a
`value_type` as `std::string`. Useful for quickly putting together a request
or response with simple text in the message body (such as an error message).
Has the same insertion complexity of `std::string`. This is the type of body
used in the examples:
```
http::response<http::string_body> resp;
static_assert(std::is_same<decltype(resp.body), std::string>::value);
resp.body = "Here is the data you requested";
```
* [link beast.ref.http__basic_streambuf_body [*`basic_streambuf_body`:]] A body with a
`value_type` of `streambuf`. A streambuf is an efficient storage object which
uses multiple octet arrays of varying lengths to represent data. Here is
a body that uses a `boost::asio::streambuf` as its container:
```
template<class ConstBufferSequence>
http::response<http::basic_streambuf_body<boost::asio::streambuf>>
make_response(ConstBufferSequence const& buffers)
{
http::response<http::streambuf_body<boost::asio::streambuf>> resp;
resp.body.commit(boost::asio::buffer_copy(resp.body.prepare(
boost::asio::buffer_size(buffers)), buffers));
return resp;
}
```
[heading Sockets]
The library provides simple free functions modeled after Boost.Asio to
send and receive messages on TCP/IP sockets, SSL streams, or any object
which meets the Boost.Asio type requirements (SyncReadStream, SyncWriteStream,
AsyncReadStream, and AsyncWriteStream depending on the types of operations
performed). To send messages synchronously, use one of the `http:write`
functions:
```
void send_request(boost::asio::ip::tcp::socket& sock)
{
http::request<http::empty_body> req({http::method_t::http_get, "/index.html", 11});
...
http::write(sock, req); // Throws exception on error
...
// Alternatively
boost::system::error:code ec;
http::write(sock, req, ec);
if(ec)
std::cerr << "error writing http message: " << ec.message();
}
```
An asynchronous interface is available:
```
void handle_write(boost::system::error_code);
...
http::request<http::empty_body> req;
...
http::async_write(sock, req, std::bind(&handle_write, std::placeholders::_1));
```
When the implementation reads messages from a socket, it can read bytes lying
after the end of the message if they are present (the alternative is to read
a single byte at a time which is unsuitable for performance reasons). To
store and re-use these extra bytes on subsequent messages, the read interface
requires an additional paramter: a [link beast.types.Streambuf [*`Streambuf`]]
object. This example reads a message from the socket, with the extra bytes
stored in the streambuf parameter for use in a subsequent call to read:
```
boost::asio::streambuf sb;
...
http::response<http::string_body> resp;
http::read(sock, sb, resp); // Throws exception on error
...
// Alternatively
boost::system::error:code ec;
http::read(sock, sb, resp, ec);
if(ec)
std::cerr << "error reading http message: " << ec.message();
```
As with the write function, an asynchronous interface is available. The
stream buffer parameter must remain valid until the completion handler is
called:
```
void handle_read(boost::system::error_code);
...
boost::asio::streambuf sb;
http::response<http::string_body> resp;
...
http::async_read(sock, resp, std::bind(&handle_read, std::placeholders::_1));
```
An alternative to using a `boost::asio::streambuf` is to use a
[link beast.ref.streambuf `beast::streambuf`], which meets the requirements of
[*`Streambuf`] and is optimized for performance:
```
void handle_read(boost::system::error_code);
...
beast::streambuf sb;
http::response<http::string_body> resp;
http::read(sock, sb, resp);
```
The `read` implementation can use any object meeting the requirements of
[link beast.types.Streambuf [*`Streambuf`]], allowing callers to define custom
memory management strategies used by the implementation.
[endsect]
[section:advanced Advanced]
The spectrum of hardware and software platforms which perform these typical
HTTP operations is vast, ranging from powerful servers in large datacenters
to tiny resource-limited embedded devices. No single concrete implementation
@@ -122,10 +288,15 @@ message body as a disk file may support sending but not parsing. Many efficient
and correct models of messages exist, supporting some or all of the
operations listed above.
To support a variety of implementation strategies, we introduce customization
points for the header and body via class template arguments. This illustration
shows the message class template (boilerplate present in the actual
declaration has been removed for clarity):
[heading Message model]
The message class template and provided Body types are suitable for casual
library users. This section explains the message model for advanced users
who wish to take control over aspects of the implementation. We introduce
customization points for the header and body via class template arguments.
This illustration shows more detail about the
[link beast.ref.http__message [*`message`]] class template (boilerplate
present in the actual declaration has been removed for clarity):
[$images/message.png [width 580px] [height 225px]]
@@ -143,45 +314,7 @@ overloaded on the type of message.
[section:body Body parameter]
The `Body` template argument in the `message` class must meet the
[link beast.types.Body [*`Body`] requirements]. It provides customization
of the data member in the message, the algorithm for parsing, and the
algorithm for serialization:
[$images/body.png [width 510px] [height 210px]]
The following types, provided by the library, meet the `Body` requirements:
* [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
Used in GET requests where there is no message body.
* [link beast.ref.http__string_body [*`string_body`:]] A body with a
`value_type` of `std::string`. Useful for quickly putting together a request
or response with simple text in the message body (such as an error message).
Subject to the performance limit of strings if large amounts of data are
inserted.
* [link beast.ref.http__basic_streambuf_body [*`basic_streambuf_body`:]] A body with a
`value_type` of `streambuf`. A streambuf is an efficient storage object which
uses multiple octet arrays of varying lengths to represent data.
Instances of the optional nested types `writer` and `reader` perform
serialization and deserialization of the message body. If either or
both of these types are present, the message becomes serializable, parsable,
or both. They model [link beast.types.Reader [*`Reader`]] and
[link beast.types.Writer [*`Writer`]] respectively.
For specialized applications, users may implement their own types which
meet the requirements. The examples included with this library provide a
Body implementation used to serve files in a HTTP server.
[endsect]
[section:headers Headers container]
[section:headers Headers Type]
The `Headers` type represents the field/value pairs present in every HTTP
message. These types implement the
@@ -208,5 +341,27 @@ has_connect(FieldSequence const& fs)
[section:body Body Type]
The `Body` template argument in the `message` class must meet the
[link beast.types.Body [*`Body`] requirements]. It provides customization
of the data member in the message, the algorithm for parsing, and the
algorithm for serialization:
[$images/body.png [width 510px] [height 210px]]
Instances of the optional nested types `writer` and `reader` perform
serialization and deserialization of the message body. If either or
both of these types are present, the message becomes serializable, parsable,
or both. They model [link beast.types.Reader [*`Reader`]] and
[link beast.types.Writer [*`Writer`]] respectively.
For specialized applications, users may implement their own types which
meet the requirements. The examples included with this library provide a
Body implementation used to serve files in a HTTP server.
[endsect]
[endsect]

View File

@@ -131,6 +131,7 @@
<member><link linkend="beast.ref.buffer_cat">buffer_cat</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.write">write</link></member>
</simplelist>
</entry>
<entry valign="top">

View File

@@ -40,31 +40,22 @@
<xsl:sort select="concat((. | ancestor::*)/compoundname, '::', name, ':x')"/>
<xsl:sort select="name"/>
<xsl:choose>
<xsl:when test="@kind='class' or @kind='struct'">
<xsl:call-template name="class"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="namespace-memberdef"/>
</xsl:otherwise>
<!--
<xsl:when test="@kind='class' or @kind='struct'">
<xsl:if test="
contains(compoundname, 'beast::') and
not(contains(compoundname, '::detail'))">
not(contains(compoundname, '::detail')) and
not(contains(compoundname, 'rfc2616')) and
not(contains(@prot, 'private'))">
<xsl:call-template name="class"/>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:if test="
not(contains(ancestor::*/compoundname, '::detail')) and
not(contains(ancestor::*/compoundname, '::service::key')) and
not(contains(ancestor::*/compoundname, '_helper')) and
not(contains(name, '_helper')) and
not(contains(name, 'io_service_impl'))">
not(contains(compoundname, 'rfc2616'))">
<xsl:call-template name="namespace-memberdef"/>
</xsl:if>
</xsl:otherwise>
-->
</xsl:choose>
</xsl:for-each>
<xsl:text>&#xd;[endsect]</xsl:text>
@@ -288,6 +279,14 @@
</xsl:if>
</xsl:template>
<xsl:template match="ulink" mode="markup">
<xsl:text>[@</xsl:text>
<xsl:value-of select="@url"/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/>
<xsl:text>]</xsl:text>
</xsl:template>
<xsl:template match="programlisting" mode="markup">
<xsl:value-of select="$newline"/>
<xsl:value-of select="$newline"/>
@@ -673,18 +672,16 @@
<xsl:value-of select="$newline"/>
<xsl:text>[heading Requirements]&#xd;</xsl:text>
<xsl:text>['Header: ][^</xsl:text>
<xsl:value-of select="substring-after($file, 'beast/')"/>
<xsl:value-of select="substring-after($file, 'include/')"/>
<xsl:text>] &#xd;&#xd;</xsl:text>
<xsl:text>['Convenience header: ]</xsl:text>
<xsl:choose>
<xsl:when test="contains($file, 'beast/asio')">
<xsl:text>[^beast/asio.h]</xsl:text>
</xsl:when>
<xsl:when test="contains($file, 'beast/http')">
<xsl:text>[^beast/http.h]</xsl:text>
<xsl:text>['Convenience header: ][^beast/http.hpp]</xsl:text>
</xsl:when>
<xsl:when test="contains($file, 'beast/wsproto')">
<xsl:text>[^beast/wsproto.h]</xsl:text>
<xsl:when test="contains($file, 'beast/websocket')">
<xsl:text>['Convenience header: ][^beast/websocket.hpp]</xsl:text>
</xsl:when>
<xsl:when test="contains($file, 'beast/')">
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$file"/>
@@ -911,7 +908,7 @@
</xsl:for-each>
<xsl:text>]&#xd;</xsl:text>
</xsl:if>
<xsl:if test="count(sectiondef[@kind='private-func' or @kind='protected-private-func']) > 0">
<xsl:if test="count(sectiondef[@kind='private-func' or @kind='protected-private-func']) &lt; 0">
<xsl:text>[heading Private Member Functions]&#xd;</xsl:text>
<xsl:text>[table&#xd; [[Name][Description]]&#xd;</xsl:text>
<xsl:for-each select="sectiondef[@kind='private-func']/memberdef" mode="class-table">
@@ -988,7 +985,7 @@
</xsl:for-each>
<xsl:text>]&#xd;</xsl:text>
</xsl:if>
<xsl:if test="count(sectiondef[@kind='private-attrib' or @kind='private-static-attrib']) > 0">
<xsl:if test="count(sectiondef[@kind='private-attrib' or @kind='private-static-attrib']) &lt; 0">
<xsl:text>[heading Private Data Members]&#xd;</xsl:text>
<xsl:text>[table&#xd; [[Name][Description]]&#xd;</xsl:text>
<xsl:for-each select="sectiondef[@kind='private-attrib' or @kind='private-static-attrib']/memberdef" mode="class-table">
@@ -1274,7 +1271,7 @@
<xsl:text>```&#xd;</xsl:text>
</xsl:when>
</xsl:choose>
<xsl:apply-templates select="detaileddescription"/>
<xsl:apply-templates select="detaileddescription" mode="markup"/>
<xsl:if test="@kind='typedef' or @kind='friend'">
<xsl:call-template name="header-requirements">
<xsl:with-param name="file" select="$class-file"/>
@@ -1423,91 +1420,12 @@
<xsl:template match="param" mode="class-detail-template">
<xsl:text> </xsl:text>
<xsl:value-of select="type"/>
<xsl:choose>
<xsl:when test="declname = 'Allocator'">
<xsl:value-of select="declname"/>
<xsl:when test="type = 'class Body'">
<xsl:text>class ``[link beast.types.Body [*Body]]``</xsl:text>
</xsl:when>
<xsl:when test="declname = 'Arg'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'BufferSequence'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'ByteType'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Clock'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'CompletionCondition'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'ConnectCondition'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Context_Service'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'DatagramSocketService1'">
<xsl:value-of select="concat('``[link beast.ref.DatagramSocketService ', declname, ']``')"/>
</xsl:when>
<xsl:when test="declname = 'EndpointIterator'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Elem'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'ErrorEnum'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Function'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Iterator'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'MatchCondition'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'N'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'OtherAllocator'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'PasswordCallback'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'PodType'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'PointerToPodType'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Protocol1'">
<xsl:value-of select="concat('``[link beast.ref.Protocol ', declname, ']``')"/>
</xsl:when>
<xsl:when test="declname = 'RawSocketService1'">
<xsl:value-of select="concat('``[link beast.ref.RawSocketService ', declname, ']``')"/>
</xsl:when>
<xsl:when test="declname = 'SeqPacketSocketService1'">
<xsl:value-of select="concat('``[link beast.ref.SeqPacketSocketService ', declname, ']``')"/>
</xsl:when>
<xsl:when test="declname = 'Signature'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'SocketAcceptorService1' or declname = 'SocketAcceptorService2'">
<xsl:value-of select="concat('``[link beast.ref.SocketAcceptorService ', declname, ']``')"/>
</xsl:when>
<xsl:when test="declname = 'SocketService1' or declname = 'SocketService2'">
<xsl:value-of select="concat('``[link beast.ref.SocketService ', declname, ']``')"/>
</xsl:when>
<xsl:when test="declname = 'Stream'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'StreamSocketService1'">
<xsl:value-of select="concat('``[link beast.ref.StreamSocketService ', declname, ']``')"/>
<xsl:when test="type = 'class Streambuf'">
<xsl:text>class ``[link beast.types.Streambuf [*Streambuf]]``</xsl:text>
</xsl:when>
<xsl:when test="declname = 'T'">
<xsl:value-of select="declname"/>
@@ -1518,22 +1436,14 @@
<xsl:when test="declname = 'TN'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Time'">
<xsl:when test="count(declname) > 0">
<xsl:value-of select="type"/>
<xsl:text> </xsl:text>
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'TimeType'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Traits'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'VerifyCallback'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="count(declname) = 0">
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(' ``[link beast.ref.', declname, ' ', declname, ']``')"/>
<xsl:value-of select="type"/>
<!--<xsl:value-of select="concat(' ``[link beast.ref.', declname, ' ', declname, ']``')"/>-->
</xsl:otherwise>
</xsl:choose>
<xsl:if test="count(defval) > 0">
@@ -1557,6 +1467,10 @@
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="' &amp;&amp;' = substring(type, string-length(type) - 2)">
<xsl:value-of select="substring(type, 1, string-length(type) - 3)"/>
<xsl:text>&amp;&amp;</xsl:text>
</xsl:when>
<xsl:when test="' &amp;' = substring(type, string-length(type) - 1)">
<xsl:value-of select="substring(type, 1, string-length(type) - 2)"/>
<xsl:text>&amp;</xsl:text>

View File

@@ -0,0 +1,30 @@
# Part of Beast
GroupSources(include/beast)
GroupSources(examples)
add_executable (http-crawl
${BEAST_INCLUDES}
http_crawl.cpp
urls_large_data.cpp
)
add_executable (http-server
${BEAST_INCLUDES}
http_server.cpp
)
add_executable (http-example
${BEAST_INCLUDES}
http_example.cpp
)
add_executable (websocket-echo
${BEAST_INCLUDES}
websocket_echo.cpp
)
add_executable (websocket-example
${BEAST_INCLUDES}
websocket_example.cpp
)

View File

@@ -8,28 +8,23 @@
import os ;
exe http_crawl :
../src/beast_http_nodejs_parser.cpp
http_crawl.cpp
urls_large_data.cpp
;
exe http_server :
../src/beast_http_nodejs_parser.cpp
http_server.cpp
;
exe wsproto_echo :
../src/beast_http_nodejs_parser.cpp
wsproto_echo.cpp
;
exe http_example :
../src/beast_http_nodejs_parser.cpp
http_example.cpp
;
exe websocket_echo :
websocket_echo.cpp
;
exe websocket_example :
../src/beast_http_nodejs_parser.cpp
websocket_example.cpp
;

View File

@@ -80,7 +80,8 @@ struct file_body
operator()(resume_context&&, error_code&, Write&& write)
{
buf_len_ = std::min(size_ - offset_, sizeof(buf_));
fread(buf_, 1, sizeof(buf_), file_);
auto const nread = fread(buf_, 1, sizeof(buf_), file_);
(void)nread;
offset_ += buf_len_;
write(boost::asio::buffer(buf_, buf_len_));
return offset_ >= size_;

View File

@@ -23,7 +23,6 @@
#include <beast/bind_handler.hpp>
#include <beast/handler_alloc.hpp>
#include <beast/http/read.hpp>
#include <beast/http/type_check.hpp>
#include <beast/http/write.hpp>
#include <cassert>

View File

@@ -32,18 +32,11 @@ sig_wait()
boost::asio::io_service ios;
boost::asio::signal_set signals(
ios, SIGINT, SIGTERM);
std::mutex m;
bool stop = false;
std::condition_variable cv;
signals.async_wait(
[&](boost::system::error_code const&, int)
{
std::lock_guard<std::mutex> lock(m);
stop = true;
cv.notify_one();
});
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return stop; });
ios.run();
}
#endif

View File

@@ -17,8 +17,8 @@
*/
//==============================================================================
#ifndef BEAST_WSPROTO_ASYNC_ECHO_PEER_H_INCLUDED
#define BEAST_WSPROTO_ASYNC_ECHO_PEER_H_INCLUDED
#ifndef BEAST_WEBSOCKET_ASYNC_ECHO_PEER_H_INCLUDED
#define BEAST_WEBSOCKET_ASYNC_ECHO_PEER_H_INCLUDED
#include <beast/placeholders.hpp>
#include <beast/streambuf.hpp>

View File

@@ -17,8 +17,8 @@
*/
//==============================================================================
#include "wsproto_async_echo_peer.h"
#include "wsproto_sync_echo_peer.h"
#include "websocket_async_echo_peer.h"
#include "websocket_sync_echo_peer.h"
#include "sig_wait.h"
int main()

View File

@@ -16,13 +16,12 @@
namespace beast {
/** Completion helper for implementing the extensible asynchronous model.
/** Helper for customizing the return type of asynchronous initiation functions.
This class template is used to transform caller-provided
completion tokens in calls to asynchronous initiation
functions. The transformation allows customization of
the return type of the initiating function, and the type
of the final handler.
This class template is used to transform caller-provided completion
tokens in calls to asynchronous initiation functions. The transformation
allows customization of the return type of the initiating function, and the
function signature of the final handler.
@tparam CompletionToken A CompletionHandler, or a user defined type
with specializations for customizing the return type (for example,
@@ -30,14 +29,16 @@ namespace beast {
@tparam Signature The callable signature of the final completion handler.
Usage:
Example:
@code
...
template<class CompletionToken>
typename async_completion<CompletionToken, Signature>::result_type
typename async_completion<CompletionToken,
void(boost::system::error_code)>::result_type
async_initfn(..., CompletionToken&& token)
{
async_completion<CompletionToken, Signature> completion(token);
async_completion<CompletionToken,
void(boost::system::error_code)> completion(token);
...
return completion.result.get();
}
@@ -49,19 +50,19 @@ namespace beast {
template <class CompletionToken, class Signature>
struct async_completion
{
/** The type of the final handler.
/** The type of the final handler called by the asynchronous initiation function.
Objects of this type will be callable with the
specified signature.
Objects of this type will be callable with the specified signature.
*/
using handler_type =
typename boost::asio::handler_type<
CompletionToken, Signature>::type;
/// The type of the value returned by the asynchronous initiation function.
using result_type = typename
boost::asio::async_result<handler_type>::type;
/** Construct the completion helper.
/** Construct the helper.
@param token The completion token. Copies will be made as
required. If `CompletionToken` is movable, it may also be moved.
@@ -74,10 +75,10 @@ struct async_completion
"Handler requirements not met");
}
/** The final completion handler, callable with the specified signature. */
/// The final completion handler, callable with the specified signature.
handler_type handler;
/** The return value of the asynchronous initiation function. */
/// The return value of the asynchronous initiation function.
boost::asio::async_result<handler_type> result;
};

View File

@@ -36,6 +36,7 @@ class basic_streambuf
#endif
{
public:
/// The type of allocator used.
using allocator_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<std::uint8_t>;
@@ -73,10 +74,20 @@ private:
size_type out_end_ = 0; // output end offset in list_.back()
public:
#if GENERATING_DOCS
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = implementation_defined;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = implementation_defined;
#else
class const_buffers_type;
class mutable_buffers_type;
#endif
/// Destructor.
~basic_streambuf();
@@ -175,7 +186,7 @@ public:
template<class OtherAlloc>
basic_streambuf& operator=(basic_streambuf<OtherAlloc> const& other);
/** Construct a stream buffer.
/** Default constructor.
@param alloc_size The size of buffer to allocate. This is a soft
limit, calls to prepare for buffers exceeding this size will allocate
@@ -258,60 +269,6 @@ private:
debug_check() const;
};
/// The type used to represent the input sequence as a list of buffers.
template<class Allocator>
class basic_streambuf<Allocator>::const_buffers_type
{
basic_streambuf const* sb_ = nullptr;
friend class basic_streambuf;
explicit
const_buffers_type(basic_streambuf const& sb);
public:
using value_type = boost::asio::const_buffer;
class const_iterator;
const_buffers_type() = default;
const_buffers_type(const_buffers_type const&) = default;
const_buffers_type& operator=(const_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
};
/// The type used to represent the output sequence as a list of buffers.
template<class Allocator>
class basic_streambuf<Allocator>::mutable_buffers_type
{
basic_streambuf const* sb_;
friend class basic_streambuf;
explicit
mutable_buffers_type(basic_streambuf const& sb);
public:
using value_type = mutable_buffer;
class const_iterator;
mutable_buffers_type() = default;
mutable_buffers_type(mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(mutable_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
};
/** Format output to a stream buffer.
@param streambuf The streambuf to write to.
@@ -322,7 +279,7 @@ public:
*/
template<class Alloc, class T>
basic_streambuf<Alloc>&
operator<<(basic_streambuf<Alloc>& buf, T const& t);
operator<<(basic_streambuf<Alloc>& streambuf, T const& t);
/** Convert the entire basic_streambuf to a string.

View File

@@ -121,7 +121,7 @@ public:
Example:
@code
template<class AsyncReadStream, ReadHandler>
template<class AsyncReadStream, class ReadHandler>
void
do_cancel(AsyncReadStream& stream, ReadHandler&& handler)
{

View File

@@ -9,11 +9,6 @@
#define BEAST_BUFFERS_ADAPTER_HPP
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <array>
#include <cstring>
#include <iterator>
#include <stdexcept>
#include <type_traits>
namespace beast {
@@ -70,19 +65,30 @@ private:
}
public:
#if GENERATING_DOCS
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = implementation_defined;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = implementation_defined;
#else
class const_buffers_type;
class mutable_buffers_type;
// Move constructor.
#endif
/// Move constructor.
buffers_adapter(buffers_adapter&& other);
// Copy constructor.
/// Copy constructor.
buffers_adapter(buffers_adapter const& other);
// Move assignment.
/// Move assignment.
buffers_adapter& operator=(buffers_adapter&& other);
// Copy assignment.
/// Copy assignment.
buffers_adapter& operator=(buffers_adapter const&);
/** Construct a buffers adapter.
@@ -129,508 +135,8 @@ public:
consume(std::size_t n);
};
//------------------------------------------------------------------------------
/// The type used to represent the input sequence as a list of buffers.
template<class Buffers>
class buffers_adapter<Buffers>::const_buffers_type
{
buffers_adapter const* ba_;
public:
using value_type = boost::asio::const_buffer;
class const_iterator;
const_buffers_type() = default;
const_buffers_type(
const_buffers_type const&) = default;
const_buffers_type& operator=(
const_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class buffers_adapter;
const_buffers_type(buffers_adapter const& ba)
: ba_(&ba)
{
}
};
template<class Buffers>
class buffers_adapter<Buffers>::const_buffers_type::const_iterator
{
iter_type it_;
buffers_adapter const* ba_;
public:
using value_type = boost::asio::const_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return ba_ == other.ba_ &&
it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return value_type{buffer_cast<void const*>(*it_),
(ba_->out_ == ba_->bs_.end() ||
it_ != ba_->out_) ? buffer_size(*it_) : ba_->out_pos_} +
(it_ == ba_->begin_ ? ba_->in_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class const_buffers_type;
const_iterator(buffers_adapter const& ba,
iter_type iter)
: it_(iter)
, ba_(&ba)
{
}
};
template<class Buffers>
inline
auto
buffers_adapter<Buffers>::const_buffers_type::begin() const ->
const_iterator
{
return const_iterator{*ba_, ba_->begin_};
}
template<class Buffers>
inline
auto
buffers_adapter<Buffers>::const_buffers_type::end() const ->
const_iterator
{
return const_iterator{*ba_, ba_->out_ ==
ba_->end_ ? ba_->end_ : std::next(ba_->out_)};
}
//------------------------------------------------------------------------------
/// The type used to represent the output sequence as a list of buffers.
template<class Buffers>
class buffers_adapter<Buffers>::mutable_buffers_type
{
buffers_adapter const* ba_;
public:
using value_type = boost::asio::mutable_buffer;
class const_iterator;
mutable_buffers_type() = default;
mutable_buffers_type(
mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(
mutable_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class buffers_adapter;
mutable_buffers_type(
buffers_adapter const& ba)
: ba_(&ba)
{
}
};
template<class Buffers>
class buffers_adapter<Buffers>::mutable_buffers_type::const_iterator
{
iter_type it_;
buffers_adapter const* ba_;
public:
using value_type = boost::asio::mutable_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return ba_ == other.ba_ &&
it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return value_type{buffer_cast<void*>(*it_),
it_ == std::prev(ba_->end_) ?
ba_->out_end_ : buffer_size(*it_)} +
(it_ == ba_->out_ ? ba_->out_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class mutable_buffers_type;
const_iterator(buffers_adapter const& ba,
iter_type iter)
: it_(iter)
, ba_(&ba)
{
}
};
template<class Buffers>
inline
auto
buffers_adapter<Buffers>::mutable_buffers_type::begin() const ->
const_iterator
{
return const_iterator{*ba_, ba_->out_};
}
template<class Buffers>
inline
auto
buffers_adapter<Buffers>::mutable_buffers_type::end() const ->
const_iterator
{
return const_iterator{*ba_, ba_->end_};
}
//------------------------------------------------------------------------------
template<class Buffers>
buffers_adapter<Buffers>::buffers_adapter(
buffers_adapter&& other)
: buffers_adapter(std::move(other),
std::distance<iter_type>(other.bs_.begin(), other.begin_),
std::distance<iter_type>(other.bs_.begin(), other.out_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class Buffers>
buffers_adapter<Buffers>::buffers_adapter(
buffers_adapter const& other)
: buffers_adapter(other,
std::distance<iter_type>(other.bs_.begin(), other.begin_),
std::distance<iter_type>(other.bs_.begin(), other.out_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class Buffers>
auto
buffers_adapter<Buffers>::operator=(
buffers_adapter&& other) -> buffers_adapter&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
auto const nout = std::distance<iter_type>(
other.bs_.begin(), other.out_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = std::move(other.bs_);
begin_ = std::next(bs_.begin(), nbegin);
out_ = std::next(bs_.begin(), nout);
end_ = std::next(bs_.begin(), nend);
max_size_ = other.max_size_;
in_pos_ = other.in_pos_;
in_size_ = other.in_size_;
out_pos_ = other.out_pos_;
out_end_ = other.out_end_;
return *this;
}
template<class Buffers>
auto
buffers_adapter<Buffers>::operator=(
buffers_adapter const& other) -> buffers_adapter&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
auto const nout = std::distance<iter_type>(
other.bs_.begin(), other.out_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = other.bs_;
begin_ = std::next(bs_.begin(), nbegin);
out_ = std::next(bs_.begin(), nout);
end_ = std::next(bs_.begin(), nend);
max_size_ = other.max_size_;
in_pos_ = other.in_pos_;
in_size_ = other.in_size_;
out_pos_ = other.out_pos_;
out_end_ = other.out_end_;
return *this;
}
template<class Buffers>
buffers_adapter<Buffers>::buffers_adapter(
Buffers const& bs)
: bs_(bs)
, begin_(bs_.begin())
, out_(bs_.begin())
, end_(bs_.begin())
, max_size_(boost::asio::buffer_size(bs_))
{
}
template<class Buffers>
auto
buffers_adapter<Buffers>::prepare(std::size_t n) ->
mutable_buffers_type
{
using boost::asio::buffer_size;
static_assert(is_mutable,
"Operation not valid for ConstBufferSequence");
end_ = out_;
if(end_ != bs_.end())
{
auto size = buffer_size(*end_) - out_pos_;
if(n > size)
{
n -= size;
while(++end_ != bs_.end())
{
size = buffer_size(*end_);
if(n < size)
{
out_end_ = n;
n = 0;
++end_;
break;
}
n -= size;
out_end_ = size;
}
}
else
{
++end_;
out_end_ = out_pos_ + n;
n = 0;
}
}
if(n > 0)
throw std::length_error(
"no space in buffers_adapter");
return mutable_buffers_type{*this};
}
template<class Buffers>
void
buffers_adapter<Buffers>::commit(std::size_t n)
{
using boost::asio::buffer_size;
static_assert(is_mutable,
"Operation not valid for ConstBufferSequence");
if(out_ == end_)
return;
auto const last = std::prev(end_);
while(out_ != last)
{
auto const avail =
buffer_size(*out_) - out_pos_;
if(n < avail)
{
out_pos_ += n;
in_size_ += n;
max_size_ -= n;
return;
}
++out_;
n -= avail;
out_pos_ = 0;
in_size_ += avail;
max_size_ -= avail;
}
n = std::min(n, out_end_ - out_pos_);
out_pos_ += n;
in_size_ += n;
max_size_ -= n;
if(out_pos_ == buffer_size(*out_))
{
++out_;
out_pos_ = 0;
out_end_ = 0;
}
}
template<class Buffers>
inline
auto
buffers_adapter<Buffers>::data() const ->
const_buffers_type
{
return const_buffers_type{*this};
}
template<class Buffers>
void
buffers_adapter<Buffers>::consume(std::size_t n)
{
for(;;)
{
if(begin_ != out_)
{
auto const avail =
buffer_size(*begin_) - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
break;
}
n -= avail;
in_size_ -= avail;
in_pos_ = 0;
++begin_;
}
else
{
auto const avail = out_pos_ - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
}
else
{
in_size_ -= avail;
if(out_pos_ != out_end_||
out_ != std::prev(bs_.end()))
{
in_pos_ = out_pos_;
}
else
{
// Use the whole buffer now.
in_pos_ = 0;
out_pos_ = 0;
out_end_ = 0;
}
}
break;
}
}
}
} // beast
#include <beast/impl/buffers_adapter.ipp>
#endif

View File

@@ -14,6 +14,11 @@
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)

View File

@@ -8,7 +8,6 @@
#ifndef BEAST_CONSUMING_BUFFERS_HPP
#define BEAST_CONSUMING_BUFFERS_HPP
#include <beast/type_check.hpp>
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <iterator>
@@ -64,8 +63,15 @@ public:
/// The type for each element in the list of buffers.
using value_type = ValueType;
#if GENERATING_DOCS
/// A bidirectional iterator type that may be used to read elements.
using const_iterator = implementation_defined;
#else
class const_iterator;
#endif
/// Move constructor.
consuming_buffers(consuming_buffers&&);
@@ -104,194 +110,13 @@ public:
consume(std::size_t n);
};
/// A bidirectional iterator type that may be used to read elements.
template<class Buffers, class ValueType>
class consuming_buffers<Buffers, ValueType>::const_iterator
{
friend class consuming_buffers<Buffers, ValueType>;
using iter_type =
typename Buffers::const_iterator;
iter_type it_;
consuming_buffers const* b_;
public:
using value_type =
typename std::iterator_traits<iter_type>::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return b_ == other.b_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
if(it_ == b_->begin_)
return *it_ + b_->skip_;
return *it_;
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
const_iterator(consuming_buffers const& b,
iter_type it)
: it_(it)
, b_(&b)
{
}
};
template<class Buffers, class ValueType>
consuming_buffers<Buffers, ValueType>::
consuming_buffers(consuming_buffers&& other)
: consuming_buffers(std::move(other),
std::distance<iter_type>(
other.bs_.begin(), other.begin_))
{
}
template<class Buffers, class ValueType>
consuming_buffers<Buffers, ValueType>::
consuming_buffers(consuming_buffers const& other)
: consuming_buffers(other,
std::distance<iter_type>(
other.bs_.begin(), other.begin_))
{
}
template<class Buffers, class ValueType>
auto
consuming_buffers<Buffers, ValueType>::
operator=(consuming_buffers&& other) ->
consuming_buffers&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
bs_ = std::move(other.bs_);
begin_ = std::next(bs_.begin(), nbegin);
skip_ = other.skip_;
return *this;
}
template<class Buffers, class ValueType>
auto
consuming_buffers<Buffers, ValueType>::
operator=(consuming_buffers const& other) ->
consuming_buffers&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
bs_ = other.bs_;
begin_ = std::next(bs_.begin(), nbegin);
skip_ = other.skip_;
return *this;
}
template<class Buffers, class ValueType>
consuming_buffers<Buffers, ValueType>::
consuming_buffers(Buffers const& bs)
: bs_(bs)
, begin_(bs_.begin())
{
}
template<class Buffers, class ValueType>
auto
consuming_buffers<Buffers, ValueType>::begin() const ->
const_iterator
{
return const_iterator{*this, begin_};
}
template<class Buffers, class ValueType>
auto
consuming_buffers<Buffers, ValueType>::end() const ->
const_iterator
{
return const_iterator{*this, bs_.end()};
}
template<class Buffers, class ValueType>
void
consuming_buffers<Buffers, ValueType>::consume(std::size_t n)
{
using boost::asio::buffer_size;
for(;n > 0 && begin_ != bs_.end(); ++begin_)
{
auto const len =
buffer_size(*begin_) - skip_;
if(n < len)
{
skip_ += n;
break;
}
n -= len;
skip_ = 0;
}
}
/// Returns a consumed buffer
template<class Buffers>
consuming_buffers<Buffers, typename Buffers::value_type>
consumed_buffers(Buffers const& bs, std::size_t n)
{
consuming_buffers<Buffers> cb(bs);
cb.consume(n);
return cb;
}
consumed_buffers(Buffers const& bs, std::size_t n);
} // beast
#include <beast/impl/consuming_buffers.ipp>
#endif

View File

@@ -0,0 +1,146 @@
//
// 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_WRITE_STREAMBUF_HPP
#define BEAST_DETAIL_WRITE_STREAMBUF_HPP
#include <boost/asio/buffer.hpp>
#include <boost/lexical_cast.hpp>
#include <utility>
namespace beast {
namespace detail {
// detects string literals.
template<class T>
struct is_string_literal : std::integral_constant<bool,
! std::is_same<T, typename std::remove_extent<T>::type>::value &&
std::is_same<char, typename std::remove_extent<T>::type>::value>
{
};
// `true` if a call to boost::asio::buffer(T const&) is possible
// note: we exclude string literals because boost::asio::buffer()
// will include the null terminator, which we don't want.
template<class T>
class is_BufferConvertible
{
template<class U, class R = decltype(
boost::asio::buffer(std::declval<U const&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
static bool const value = type::value &&
! is_string_literal<T>::value;
};
template<class Streambuf>
inline
void
write_streambuf(Streambuf&)
{
}
template<class Streambuf>
void
write_streambuf(Streambuf& streambuf,
boost::asio::const_buffer const& buffer)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
streambuf.commit(buffer_copy(
streambuf.prepare(buffer_size(buffer)),
buffer));
}
template<class Streambuf>
void
write_streambuf(Streambuf& streambuf,
boost::asio::mutable_buffer const& buffer)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
streambuf.commit(buffer_copy(
streambuf.prepare(buffer_size(buffer)),
buffer));
}
template<class Streambuf, class T>
typename std::enable_if<
is_BufferConvertible<T>::value &&
! std::is_convertible<T, boost::asio::const_buffer>::value &&
! std::is_convertible<T, boost::asio::mutable_buffer>::value
>::type
write_streambuf(Streambuf& streambuf, T const& t)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
auto const buffers = boost::asio::buffer(t);
streambuf.commit(buffer_copy(
streambuf.prepare(buffer_size(buffers)),
buffers));
}
template<class Streambuf, class Buffers>
typename std::enable_if<
is_ConstBufferSequence<Buffers>::value &&
! is_BufferConvertible<Buffers>::value &&
! std::is_convertible<Buffers, boost::asio::const_buffer>::value &&
! std::is_convertible<Buffers, boost::asio::mutable_buffer>::value
>::type
write_streambuf(Streambuf& streambuf, Buffers const& buffers)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
streambuf.commit(buffer_copy(
streambuf.prepare(buffer_size(buffers)),
buffers));
}
template<class Streambuf, std::size_t N>
void
write_streambuf(Streambuf& streambuf, const char (&s)[N])
{
using boost::asio::buffer_copy;
streambuf.commit(buffer_copy(
streambuf.prepare(N - 1),
boost::asio::buffer(s, N - 1)));
}
template<class Streambuf, class T>
typename std::enable_if<
! is_string_literal<T>::value &&
! is_ConstBufferSequence<T>::value &&
! is_BufferConvertible<T>::value &&
! std::is_convertible<T, boost::asio::const_buffer>::value &&
! std::is_convertible<T, boost::asio::mutable_buffer>::value
>::type
write_streambuf(Streambuf& streambuf, T const& t)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
auto const s = boost::lexical_cast<std::string>(t);
streambuf.commit(buffer_copy(
streambuf.prepare(s.size()), buffer(s)));
}
template<class Streambuf, class T0, class T1, class... TN>
void
write_streambuf(Streambuf& streambuf, T0&& t0, T1&& t1, TN... tn)
{
write_streambuf(streambuf, std::forward<T0>(t0));
write_streambuf(streambuf, std::forward<T1>(t1));
write_streambuf(streambuf, std::forward<TN>(tn)...);
}
} // detail
} // beast
#endif

View File

@@ -22,12 +22,21 @@ namespace beast {
/** An allocator that uses handler customizations.
This allocator uses the handler customizations `asio_handler_allocate`
and `asio_handler_deallocate` to manage memory.
and `asio_handler_deallocate` to manage memory. It meets the requirements
of `Allocator` and can be used anywhere a `std::allocator` is
accepted.
@tparam T The type of object
@tparam T The type of objects allocated by the allocator.
@tparam Handler The type of handler.
@note Allocated memory is only valid until the handler is called. The
caller is still responsible for freeing memory.
*/
#if GENERATING_DOCS
template <class T, class Handler>
class handler_alloc;
#else
template <class T, class Handler>
class handler_alloc
{
@@ -132,6 +141,7 @@ public:
return !(lhs == rhs);
}
};
#endif
} // beast

View File

@@ -15,6 +15,7 @@
#include <beast/http/error.hpp>
#include <beast/http/headers.hpp>
#include <beast/http/message.hpp>
#include <beast/http/parse_error.hpp>
#include <beast/http/parser.hpp>
#include <beast/http/read.hpp>
#include <beast/http/reason.hpp>
@@ -22,7 +23,6 @@
#include <beast/http/rfc2616.hpp>
#include <beast/http/streambuf_body.hpp>
#include <beast/http/string_body.hpp>
#include <beast/http/type_check.hpp>
#include <beast/http/write.hpp>
#endif

View File

@@ -8,7 +8,6 @@
#ifndef BEAST_HTTP_BASIC_HEADERS_HPP
#define BEAST_HTTP_BASIC_HEADERS_HPP
#include <beast/http/detail/writes.hpp>
#include <beast/type_check.hpp>
#include <beast/detail/ci_char_traits.hpp>
#include <beast/detail/empty_base_optimization.hpp>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,203 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
#define BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
#include <boost/system/error_code.hpp>
#include <boost/utility/string_ref.hpp>
#include <array>
#include <cstdint>
namespace beast {
namespace http {
namespace detail {
// '0'...'9'
inline
bool
is_digit(char c)
{
return c >= '0' && c <= '9';
}
inline
bool
is_token(char c)
{
/* token = 1*<any CHAR except CTLs or separators>
CHAR = <any US-ASCII character (octets 0 - 127)>
sep = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
*/
static std::array<char, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
}};
return tab[static_cast<std::uint8_t>(c)] != 0;
}
inline
bool
is_text(char c)
{
// TEXT = <any OCTET except CTLs, but including LWS>
static std::array<char, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
}};
return tab[static_cast<std::uint8_t>(c)] != 0;
}
// converts to lower case,
// returns 0 if not a valid token char
//
inline
char
to_field_char(char c)
{
/* token = 1*<any CHAR except CTLs or separators>
CHAR = <any US-ASCII character (octets 0 - 127)>
sep = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
*/
static std::array<char, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, '!', 0, '#', '$', '%', '&', '\'', 0, 0, '*', '+', 0, '-', '.', 0,
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0
}};
return tab[static_cast<std::uint8_t>(c)];
}
// converts to lower case,
// returns 0 if not a valid text char
//
inline
char
to_value_char(char c)
{
// TEXT = <any OCTET except CTLs, but including LWS>
static std::array<std::uint8_t, 256> constexpr tab = {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
}};
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
}
inline
std::uint8_t
unhex(char c)
{
static std::array<std::int8_t, 256> constexpr tab = {{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 112
}};
return tab[static_cast<std::uint8_t>(c)];
};
template<class = void>
struct parser_str_t
{
static char constexpr close[6] = "close";
static char constexpr chunked[8] = "chunked";
static char constexpr keep_alive[11] = "keep-alive";
static char constexpr upgrade[8] = "upgrade";
static char constexpr connection[11] = "connection";
static char constexpr content_length[15] = "content-length";
static char constexpr proxy_connection[17] = "proxy-connection";
static char constexpr transfer_encoding[18] = "transfer-encoding";
};
template<class _>
char constexpr
parser_str_t<_>::close[6];
template<class _>
char constexpr
parser_str_t<_>::chunked[8];
template<class _>
char constexpr
parser_str_t<_>::keep_alive[11];
template<class _>
char constexpr
parser_str_t<_>::upgrade[8];
template<class _>
char constexpr
parser_str_t<_>::connection[11];
template<class _>
char constexpr
parser_str_t<_>::content_length[15];
template<class _>
char constexpr
parser_str_t<_>::proxy_connection[17];
template<class _>
char constexpr
parser_str_t<_>::transfer_encoding[18];
using parser_str = parser_str_t<>;
} // detail
} // http
} // beast
#endif

View File

@@ -1,71 +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_ERROR_HPP
#define BEAST_HTTP_DETAIL_ERROR_HPP
#include <beast/http/impl/http_parser.h>
#include <boost/system/error_code.hpp>
namespace beast {
namespace http {
namespace detail {
class message_category
: public boost::system::error_category
{
public:
const char*
name() const noexcept override
{
return "http error";
}
std::string
message(int ev) const override
{
return http_errno_description(
static_cast<http_errno>(ev));
}
boost::system::error_condition
default_error_condition(int ev) const noexcept override
{
return boost::system::error_condition{ev, *this};
}
bool
equivalent(int ev,
boost::system::error_condition const& condition
) const noexcept override
{
return condition.value() == ev &&
&condition.category() == this;
}
bool
equivalent(boost::system::error_code const& error,
int ev) const noexcept override
{
return error.value() == ev &&
&error.category() == this;
}
};
template<class = void>
boost::system::error_code
make_error(int http_errno)
{
static message_category const mc{};
return boost::system::error_code{http_errno, mc};
}
} // detail
} // http
} // beast
#endif

View File

@@ -10,6 +10,7 @@
#include <beast/http/error.hpp>
#include <beast/streambuf.hpp>
#include <beast/write_streambuf.hpp>
namespace beast {
namespace http {
@@ -84,7 +85,7 @@ struct write_preparation
msg.write_firstline(sb);
write_fields(sb, h);
detail::write(sb, "\r\n");
beast::write(sb, "\r\n");
}
private:

View File

@@ -1,66 +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_WRITES_HPP
#define BEAST_HTTP_DETAIL_WRITES_HPP
#include <beast/type_check.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/utility/string_ref.hpp>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
namespace detail {
template<class Streambuf, class T,
class = typename std::enable_if<
is_Streambuf<Streambuf>::value>::type>
void
write(Streambuf& streambuf, T&& t)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
auto const& s =
boost::lexical_cast<std::string>(
std::forward<T>(t));
streambuf.commit(buffer_copy(
streambuf.prepare(s.size()), buffer(s)));
}
template<class Streambuf, std::size_t N,
class = typename std::enable_if< (N>0) &&
is_Streambuf<Streambuf>::value>::type>
void
write(Streambuf& streambuf, char const(&s)[N])
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
streambuf.commit(buffer_copy(
streambuf.prepare(N), buffer(s, N-1)));
}
template<class Streambuf,
class = typename std::enable_if<
is_Streambuf<Streambuf>::value>::type>
void
write(Streambuf& streambuf, boost::string_ref const& s)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
streambuf.commit(buffer_copy(
streambuf.prepare(s.size()),
buffer(s.data(), s.size())));
}
} // detail
} // http
} // beast
#endif

View File

@@ -22,9 +22,16 @@ namespace http {
*/
struct empty_body
{
struct value_type
{
};
#if GENERATING_DOCS
/// The type of the `message::body` member
using value_type = void;
#else
struct value_type {};
#endif
#if GENERATING_DOCS
private:
#endif
struct reader
{

View File

@@ -8,7 +8,6 @@
#ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#include <beast/http/detail/writes.hpp>
#include <beast/type_check.hpp>
namespace beast {

File diff suppressed because it is too large Load Diff

View File

@@ -9,11 +9,11 @@
#define BEAST_HTTP_IMPL_MESSAGE_IPP
#include <beast/http/chunk_encode.hpp>
#include <beast/http/type_check.hpp>
#include <beast/http/detail/writes.hpp>
#include <beast/http/detail/write_preparation.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>
@@ -26,16 +26,12 @@ template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message()
{
static_assert(is_Body<Body>::value,
"Body requirements not met");
}
template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message(request_params params)
{
static_assert(is_Body<Body>::value,
"Body requirements not met");
static_assert(isRequest, "message is not a request");
this->method = params.method;
this->url = std::move(params.url);
@@ -46,8 +42,6 @@ template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message(response_params params)
{
static_assert(is_Body<Body>::value,
"Body requirements not met");
static_assert(! isRequest, "message is not a response");
this->status = params.status;
this->reason = std::move(params.reason);
@@ -61,23 +55,23 @@ message<isRequest, Body, Headers>::
write_firstline(Streambuf& streambuf,
std::true_type) const
{
detail::write(streambuf, to_string(this->method));
detail::write(streambuf, " ");
detail::write(streambuf, this->url);
write(streambuf, to_string(this->method));
write(streambuf, " ");
write(streambuf, this->url);
switch(version)
{
case 10:
detail::write(streambuf, " HTTP/1.0\r\n");
write(streambuf, " HTTP/1.0\r\n");
break;
case 11:
detail::write(streambuf, " HTTP/1.1\r\n");
write(streambuf, " HTTP/1.1\r\n");
break;
default:
detail::write(streambuf, " HTTP/");
detail::write(streambuf, version / 10);
detail::write(streambuf, ".");
detail::write(streambuf, version % 10);
detail::write(streambuf, "\r\n");
write(streambuf, " HTTP/");
write(streambuf, version / 10);
write(streambuf, ".");
write(streambuf, version % 10);
write(streambuf, "\r\n");
break;
}
}
@@ -92,23 +86,23 @@ write_firstline(Streambuf& streambuf,
switch(version)
{
case 10:
detail::write(streambuf, "HTTP/1.0 ");
write(streambuf, "HTTP/1.0 ");
break;
case 11:
detail::write(streambuf, "HTTP/1.1 ");
write(streambuf, "HTTP/1.1 ");
break;
default:
detail::write(streambuf, " HTTP/");
detail::write(streambuf, version / 10);
detail::write(streambuf, ".");
detail::write(streambuf, version % 10);
detail::write(streambuf, " ");
write(streambuf, " HTTP/");
write(streambuf, version / 10);
write(streambuf, ".");
write(streambuf, version % 10);
write(streambuf, " ");
break;
}
detail::write(streambuf, this->status);
detail::write(streambuf, " ");
detail::write(streambuf, this->reason);
detail::write(streambuf, "\r\n");
write(streambuf, this->status);
write(streambuf, " ");
write(streambuf, this->reason);
write(streambuf, "\r\n");
}
namespace detail {
@@ -160,8 +154,6 @@ std::ostream&
operator<<(std::ostream& os,
message<isRequest, Body, Headers> const& msg)
{
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
error_code ec;
detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec);
@@ -282,10 +274,10 @@ write_fields(Streambuf& streambuf, FieldSequence const& fields)
// "FieldSequence requirements not met");
for(auto const& field : fields)
{
detail::write(streambuf, field.name());
detail::write(streambuf, ": ");
detail::write(streambuf, field.value());
detail::write(streambuf, "\r\n");
write(streambuf, field.name());
write(streambuf, ": ");
write(streambuf, field.value());
write(streambuf, "\r\n");
}
}

View File

@@ -8,7 +8,6 @@
#ifndef BEAST_HTTP_IMPL_READ_IPP_HPP
#define BEAST_HTTP_IMPL_READ_IPP_HPP
#include <beast/http/type_check.hpp>
#include <beast/bind_handler.hpp>
#include <beast/handler_alloc.hpp>
#include <cassert>
@@ -39,6 +38,7 @@ class read_op
message_type& m;
parser_type p;
Handler h;
bool started = false;
bool cont;
int state = 0;
@@ -130,6 +130,8 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
bind_handler(std::move(*this), ec, 0));
return;
}
if(used > 0)
d.started = true;
d.sb.consume(used);
if(d.p.complete())
{
@@ -157,7 +159,7 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
{
if(ec == boost::asio::error::eof)
{
if(! d.p.started())
if(! d.started)
{
// call handler
d.state = 99;
@@ -219,9 +221,8 @@ read(SyncReadStream& stream, Streambuf& streambuf,
"SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
parser<isRequest, Body, Headers> p;
bool started = false;
for(;;)
{
auto used =
@@ -229,6 +230,8 @@ read(SyncReadStream& stream, Streambuf& streambuf,
if(ec)
return;
streambuf.consume(used);
if(used > 0)
started = true;
if(p.complete())
{
m = p.release();
@@ -241,7 +244,7 @@ read(SyncReadStream& stream, Streambuf& streambuf,
return;
if(ec == boost::asio::error::eof)
{
if(! p.started())
if(! started)
return;
// Caller will see eof on next read.
ec = {};
@@ -268,8 +271,6 @@ async_read(AsyncReadStream& stream, Streambuf& streambuf,
"AsyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
beast::async_completion<ReadHandler,
void(error_code)> completion(handler);
detail::read_op<AsyncReadStream, Streambuf,

View File

@@ -10,8 +10,6 @@
#include <beast/http/chunk_encode.hpp>
#include <beast/http/resume_context.hpp>
#include <beast/http/type_check.hpp>
#include <beast/http/detail/writes.hpp>
#include <beast/http/detail/write_preparation.hpp>
#include <beast/buffer_cat.hpp>
#include <beast/bind_handler.hpp>
@@ -265,9 +263,9 @@ operator()(error_code ec, std::size_t, bool again)
case 4:
// VFALCO Unfortunately the current interface to the
// Writer concept prevents us from using coalescing the
// Writer concept prevents us from coalescing the
// final body chunk with the final chunk delimiter.
//
//
// write final chunk
d.state = 5;
boost::asio::async_write(d.s,
@@ -359,8 +357,6 @@ write(SyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg,
boost::system::error_code& ec)
{
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec);
if(ec)
@@ -447,8 +443,6 @@ async_write(AsyncWriteStream& stream,
static_assert(
is_AsyncWriteStream<AsyncWriteStream>::value,
"AsyncWriteStream requirements not met");
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
beast::async_completion<WriteHandler,
void(error_code)> completion(handler);
detail::write_op<AsyncWriteStream, decltype(completion.handler),

View File

@@ -35,6 +35,8 @@ struct response_fields
} // detail
#if ! GENERATING_DOCS
struct request_params
{
http::method_t method;
@@ -49,6 +51,8 @@ struct response_params
int version;
};
#endif
/** A HTTP message.
A message can be a request or response, depending on the `isRequest`
@@ -110,7 +114,7 @@ struct message
/// Diagnostics only
template<bool, class, class>
friend
friend
std::ostream&
operator<<(std::ostream& os,
message const& m);

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_PARSE_ERROR_HPP
#define BEAST_HTTP_PARSE_ERROR_HPP
#include <beast/http/error.hpp>
#include <boost/system/error_code.hpp>
namespace beast {
namespace http {
enum class parse_error
{
connection_closed,
bad_method,
bad_uri,
bad_version,
bad_crlf,
bad_request,
bad_status_code,
bad_status,
bad_field,
bad_value,
bad_content_length,
illegal_content_length,
bad_on_headers_rv,
invalid_chunk_size,
short_read
};
class parse_error_category : public boost::system::error_category
{
public:
const char*
name() const noexcept override
{
return "http";
}
std::string
message(int ev) const override
{
switch(static_cast<parse_error>(ev))
{
case parse_error::connection_closed:
return "data after Connection close";
case parse_error::bad_method:
return "bad method";
case parse_error::bad_uri:
return "bad Request-URI";
case parse_error::bad_version:
return "bad HTTP-Version";
case parse_error::bad_crlf:
return "missing CRLF";
case parse_error::bad_request:
return "bad Request-Line";
case parse_error::bad_status_code:
return "bad Status-Code";
case parse_error::bad_status:
return "bad Status-Line";
case parse_error::bad_field:
return "bad field token";
case parse_error::bad_value:
return "bad field-value";
case parse_error::bad_content_length:
return "bad Content-Length";
case parse_error::illegal_content_length:
return "illegal Content-Length with chunked Transfer-Encoding";
case parse_error::bad_on_headers_rv:
return "on_headers returned an unknown value";
case parse_error::invalid_chunk_size:
return "invalid chunk size";
case parse_error::short_read:
return "unexpected end of data";
default:
return "beast::http::parser error";
}
}
boost::system::error_condition
default_error_condition(int ev) const noexcept override
{
return boost::system::error_condition(ev, *this);
}
bool
equivalent(int ev,
boost::system::error_condition const& condition
) const noexcept override
{
return condition.value() == ev &&
&condition.category() == this;
}
bool
equivalent(error_code const& error, int ev) const noexcept override
{
return error.value() == ev &&
&error.category() == this;
}
};
inline
boost::system::error_category const&
get_parse_error_category()
{
static parse_error_category const cat{};
return cat;
}
inline
boost::system::error_code
make_error_code(parse_error ev)
{
return error_code(static_cast<int>(ev),
get_parse_error_category());
}
} // http
} // beast
namespace boost {
namespace system {
template<>
struct is_error_code_enum<beast::http::parse_error>
{
static bool const value = true;
};
} // system
} // boost
#endif

View File

@@ -13,43 +13,51 @@
#include <beast/http/message.hpp>
#include <boost/optional.hpp>
#include <functional>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/** A HTTP parser.
namespace detail {
struct parser_request
{
std::string method_;
std::string uri_;
};
struct parser_response
{
std::string reason_;
};
} // detail
The parser may only be used once.
*/
template<bool isRequest, class Body, class Headers>
class parser
: public basic_parser<parser<isRequest, Body, Headers>>
: 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_;
bool started_ = false;
public:
parser(parser&&) = default;
parser()
: http::basic_parser<parser>(isRequest)
, r_(m_)
: r_(m_)
{
}
/// Returns `true` if at least one byte has been processed
bool
started()
{
return started_;
}
message_type
release()
{
@@ -57,94 +65,165 @@ public:
}
private:
friend class http::basic_parser<parser>;
friend class basic_parser<isRequest, parser>;
void
on_start()
void flush()
{
started_ = true;
if(! value_.empty())
{
rfc2616::trim_right_in_place(value_);
// VFALCO could std::move
m_.headers.insert(field_, value_);
field_.clear();
value_.clear();
}
}
void
on_field(std::string const& field, std::string const& value)
void on_method(boost::string_ref const& s, error_code&)
{
m_.headers.insert(field, value);
this->method_.append(s.data(), s.size());
}
void
on_headers_complete(error_code&)
void on_uri(boost::string_ref const& s, error_code&)
{
// vFALCO TODO Decode the Content-Length and
// Transfer-Encoding, see if we can reserve the buffer.
//
// r_.reserve(content_length)
this->uri_.append(s.data(), s.size());
}
bool
on_request(http::method_t method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
void on_reason(boost::string_ref const& s, error_code&)
{
m_.method = method;
m_.url = url;
m_.version = major * 10 + minor;
return true;
this->reason_.append(s.data(), s.size());
}
bool
on_request(http::method_t, std::string const&,
int, int, bool, bool,
std::false_type)
void on_field(boost::string_ref const& s, error_code&)
{
return true;
flush();
field_.append(s.data(), s.size());
}
bool
on_request(http::method_t method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade)
void on_value(boost::string_ref const& s, error_code&)
{
return on_request(method, url,
major, minor, keep_alive, upgrade,
typename message_type::is_request{});
value_.append(s.data(), s.size());
}
bool
on_response(int status, std::string const& reason,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
void set(std::true_type)
{
m_.status = status;
m_.reason = reason;
m_.version = major * 10 + minor;
// VFALCO TODO return expect_body_
return true;
}
bool
on_response(int, std::string const&, int, int, bool, bool,
std::false_type)
{
return true;
// 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_);
}
bool
on_response(int status, std::string const& reason,
int major, int minor, bool keep_alive, bool upgrade)
void set(std::false_type)
{
return on_response(
status, reason, major, minor, keep_alive, upgrade,
std::integral_constant<bool, ! message_type::is_request::value>{});
m_.status = this->status_code();
m_.reason = this->reason_;
}
void
on_body(void const* data,
std::size_t size, error_code& ec)
int on_headers(error_code&)
{
r_.write(data, size, ec);
flush();
m_.version = 10 * this->http_major() + this->http_minor();
return 0;
}
void
on_complete()
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&)
{
}
};

View File

@@ -11,7 +11,6 @@
#include <beast/async_completion.hpp>
#include <beast/http/error.hpp>
#include <beast/http/parser.hpp>
#include <beast/http/type_check.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/system/error_code.hpp>

View File

@@ -21,6 +21,8 @@
namespace beast {
#if ! GENERATING_DOCS
/** Routines for performing RFC2616 compliance.
RFC2616:
Hypertext Transfer Protocol -- HTTP/1.1
@@ -454,6 +456,8 @@ token_in_list(boost::string_ref const& value,
} // rfc2616
#endif
} // beast
#endif

View File

@@ -0,0 +1,23 @@
//
// 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_RFC7230_HPP
#define BEAST_HTTP_RFC7230_HPP
#include <array>
#include <cstdint>
namespace beast {
namespace rfc7230 {
} // rfc7230
} // beast
#endif

View File

@@ -23,8 +23,13 @@ namespace http {
template<class Streambuf>
struct basic_streambuf_body
{
/// The type of the `message::body` member
using value_type = Streambuf;
#if GENERATING_DOCS
private:
#endif
class reader
{
value_type& sb_;

View File

@@ -23,8 +23,13 @@ namespace http {
*/
struct string_body
{
/// The type of the `message::body` member
using value_type = std::string;
#if GENERATING_DOCS
private:
#endif
class reader
{
value_type& s_;

View File

@@ -1,49 +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_TYPE_CHECK_HPP
#define BEAST_HTTP_TYPE_CHECK_HPP
#include <type_traits>
namespace beast {
namespace http {
/// Evaluates to std::true_type if `T` models Body
template<class T>
struct is_Body : std::true_type
{
};
/// Evalulates to std::true_type if Body has a reader
template<class T>
struct is_ReadableBody : std::true_type
{
};
/// Evalulates to std::true_type if Body has a writer
template<class T>
struct is_WritableBody : std::true_type
{
};
/// Evaluates to std::true_type if `T` models HTTPMessage
template<class T>
struct is_HTTPMessage : std::false_type
{
};
/// Evaluates to std::true_type if `HTTPMessage` is a request
template<class HTTPMessage>
struct is_HTTPRequest : std::true_type
{
};
} // http
} // beast
#endif

View File

@@ -115,6 +115,59 @@ public:
}
};
template<class Allocator>
class basic_streambuf<Allocator>::const_buffers_type
{
basic_streambuf const* sb_ = nullptr;
friend class basic_streambuf;
explicit
const_buffers_type(basic_streambuf const& sb);
public:
/// Why?
using value_type = boost::asio::const_buffer;
class const_iterator;
const_buffers_type() = default;
const_buffers_type(const_buffers_type const&) = default;
const_buffers_type& operator=(const_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
};
template<class Allocator>
class basic_streambuf<Allocator>::mutable_buffers_type
{
basic_streambuf const* sb_;
friend class basic_streambuf;
explicit
mutable_buffers_type(basic_streambuf const& sb);
public:
using value_type = mutable_buffer;
class const_iterator;
mutable_buffers_type() = default;
mutable_buffers_type(mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(mutable_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
};
//------------------------------------------------------------------------------
template<class Allocator>
@@ -798,16 +851,16 @@ basic_streambuf<Allocator>::debug_check() const
template<class Alloc, class T>
basic_streambuf<Alloc>&
operator<<(basic_streambuf<Alloc>& buf, T const& t)
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();
buf.commit(buffer_copy(buf.prepare(s.size()),
boost::asio::buffer(s)));
return buf;
streambuf.commit(buffer_copy(
streambuf.prepare(s.size()), buffer(s)));
return streambuf;
}
//------------------------------------------------------------------------------

View File

@@ -0,0 +1,520 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_BUFFERS_ADAPTER_IPP
#define BEAST_IMPL_BUFFERS_ADAPTER_IPP
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <stdexcept>
#include <type_traits>
namespace beast {
template<class Buffers>
class buffers_adapter<Buffers>::const_buffers_type
{
buffers_adapter const* ba_;
public:
using value_type = boost::asio::const_buffer;
class const_iterator;
const_buffers_type() = default;
const_buffers_type(
const_buffers_type const&) = default;
const_buffers_type& operator=(
const_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class buffers_adapter;
const_buffers_type(buffers_adapter const& ba)
: ba_(&ba)
{
}
};
template<class Buffers>
class buffers_adapter<Buffers>::const_buffers_type::const_iterator
{
iter_type it_;
buffers_adapter const* ba_;
public:
using value_type = boost::asio::const_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return ba_ == other.ba_ &&
it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return value_type{buffer_cast<void const*>(*it_),
(ba_->out_ == ba_->bs_.end() ||
it_ != ba_->out_) ? buffer_size(*it_) : ba_->out_pos_} +
(it_ == ba_->begin_ ? ba_->in_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class const_buffers_type;
const_iterator(buffers_adapter const& ba,
iter_type iter)
: it_(iter)
, ba_(&ba)
{
}
};
template<class Buffers>
inline
auto
buffers_adapter<Buffers>::const_buffers_type::begin() const ->
const_iterator
{
return const_iterator{*ba_, ba_->begin_};
}
template<class Buffers>
inline
auto
buffers_adapter<Buffers>::const_buffers_type::end() const ->
const_iterator
{
return const_iterator{*ba_, ba_->out_ ==
ba_->end_ ? ba_->end_ : std::next(ba_->out_)};
}
//------------------------------------------------------------------------------
template<class Buffers>
class buffers_adapter<Buffers>::mutable_buffers_type
{
buffers_adapter const* ba_;
public:
using value_type = boost::asio::mutable_buffer;
class const_iterator;
mutable_buffers_type() = default;
mutable_buffers_type(
mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(
mutable_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class buffers_adapter;
mutable_buffers_type(
buffers_adapter const& ba)
: ba_(&ba)
{
}
};
template<class Buffers>
class buffers_adapter<Buffers>::mutable_buffers_type::const_iterator
{
iter_type it_;
buffers_adapter const* ba_;
public:
using value_type = boost::asio::mutable_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return ba_ == other.ba_ &&
it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return value_type{buffer_cast<void*>(*it_),
it_ == std::prev(ba_->end_) ?
ba_->out_end_ : buffer_size(*it_)} +
(it_ == ba_->out_ ? ba_->out_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class mutable_buffers_type;
const_iterator(buffers_adapter const& ba,
iter_type iter)
: it_(iter)
, ba_(&ba)
{
}
};
template<class Buffers>
inline
auto
buffers_adapter<Buffers>::mutable_buffers_type::begin() const ->
const_iterator
{
return const_iterator{*ba_, ba_->out_};
}
template<class Buffers>
inline
auto
buffers_adapter<Buffers>::mutable_buffers_type::end() const ->
const_iterator
{
return const_iterator{*ba_, ba_->end_};
}
//------------------------------------------------------------------------------
template<class Buffers>
buffers_adapter<Buffers>::buffers_adapter(
buffers_adapter&& other)
: buffers_adapter(std::move(other),
std::distance<iter_type>(other.bs_.begin(), other.begin_),
std::distance<iter_type>(other.bs_.begin(), other.out_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class Buffers>
buffers_adapter<Buffers>::buffers_adapter(
buffers_adapter const& other)
: buffers_adapter(other,
std::distance<iter_type>(other.bs_.begin(), other.begin_),
std::distance<iter_type>(other.bs_.begin(), other.out_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class Buffers>
auto
buffers_adapter<Buffers>::operator=(
buffers_adapter&& other) -> buffers_adapter&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
auto const nout = std::distance<iter_type>(
other.bs_.begin(), other.out_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = std::move(other.bs_);
begin_ = std::next(bs_.begin(), nbegin);
out_ = std::next(bs_.begin(), nout);
end_ = std::next(bs_.begin(), nend);
max_size_ = other.max_size_;
in_pos_ = other.in_pos_;
in_size_ = other.in_size_;
out_pos_ = other.out_pos_;
out_end_ = other.out_end_;
return *this;
}
template<class Buffers>
auto
buffers_adapter<Buffers>::operator=(
buffers_adapter const& other) -> buffers_adapter&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
auto const nout = std::distance<iter_type>(
other.bs_.begin(), other.out_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = other.bs_;
begin_ = std::next(bs_.begin(), nbegin);
out_ = std::next(bs_.begin(), nout);
end_ = std::next(bs_.begin(), nend);
max_size_ = other.max_size_;
in_pos_ = other.in_pos_;
in_size_ = other.in_size_;
out_pos_ = other.out_pos_;
out_end_ = other.out_end_;
return *this;
}
template<class Buffers>
buffers_adapter<Buffers>::buffers_adapter(
Buffers const& bs)
: bs_(bs)
, begin_(bs_.begin())
, out_(bs_.begin())
, end_(bs_.begin())
, max_size_(boost::asio::buffer_size(bs_))
{
}
template<class Buffers>
auto
buffers_adapter<Buffers>::prepare(std::size_t n) ->
mutable_buffers_type
{
using boost::asio::buffer_size;
static_assert(is_mutable,
"Operation not valid for ConstBufferSequence");
end_ = out_;
if(end_ != bs_.end())
{
auto size = buffer_size(*end_) - out_pos_;
if(n > size)
{
n -= size;
while(++end_ != bs_.end())
{
size = buffer_size(*end_);
if(n < size)
{
out_end_ = n;
n = 0;
++end_;
break;
}
n -= size;
out_end_ = size;
}
}
else
{
++end_;
out_end_ = out_pos_ + n;
n = 0;
}
}
if(n > 0)
throw std::length_error(
"no space in buffers_adapter");
return mutable_buffers_type{*this};
}
template<class Buffers>
void
buffers_adapter<Buffers>::commit(std::size_t n)
{
using boost::asio::buffer_size;
static_assert(is_mutable,
"Operation not valid for ConstBufferSequence");
if(out_ == end_)
return;
auto const last = std::prev(end_);
while(out_ != last)
{
auto const avail =
buffer_size(*out_) - out_pos_;
if(n < avail)
{
out_pos_ += n;
in_size_ += n;
max_size_ -= n;
return;
}
++out_;
n -= avail;
out_pos_ = 0;
in_size_ += avail;
max_size_ -= avail;
}
n = std::min(n, out_end_ - out_pos_);
out_pos_ += n;
in_size_ += n;
max_size_ -= n;
if(out_pos_ == buffer_size(*out_))
{
++out_;
out_pos_ = 0;
out_end_ = 0;
}
}
template<class Buffers>
inline
auto
buffers_adapter<Buffers>::data() const ->
const_buffers_type
{
return const_buffers_type{*this};
}
template<class Buffers>
void
buffers_adapter<Buffers>::consume(std::size_t n)
{
for(;;)
{
if(begin_ != out_)
{
auto const avail =
buffer_size(*begin_) - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
break;
}
n -= avail;
in_size_ -= avail;
in_pos_ = 0;
++begin_;
}
else
{
auto const avail = out_pos_ - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
}
else
{
in_size_ -= avail;
if(out_pos_ != out_end_||
out_ != std::prev(bs_.end()))
{
in_pos_ = out_pos_;
}
else
{
// Use the whole buffer now.
in_pos_ = 0;
out_pos_ = 0;
out_end_ = 0;
}
}
break;
}
}
}
} // beast
#endif

View File

@@ -0,0 +1,211 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_CONSUMING_BUFFERS_IPP
#define BEAST_IMPL_CONSUMING_BUFFERS_IPP
#include <beast/type_check.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <type_traits>
#include <utility>
namespace beast {
template<class Buffers, class ValueType>
class consuming_buffers<Buffers, ValueType>::const_iterator
{
friend class consuming_buffers<Buffers, ValueType>;
using iter_type =
typename Buffers::const_iterator;
iter_type it_;
consuming_buffers const* b_;
public:
using value_type =
typename std::iterator_traits<iter_type>::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return b_ == other.b_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
if(it_ == b_->begin_)
return *it_ + b_->skip_;
return *it_;
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
const_iterator(consuming_buffers const& b,
iter_type it)
: it_(it)
, b_(&b)
{
}
};
template<class Buffers, class ValueType>
consuming_buffers<Buffers, ValueType>::
consuming_buffers(consuming_buffers&& other)
: consuming_buffers(std::move(other),
std::distance<iter_type>(
other.bs_.begin(), other.begin_))
{
}
template<class Buffers, class ValueType>
consuming_buffers<Buffers, ValueType>::
consuming_buffers(consuming_buffers const& other)
: consuming_buffers(other,
std::distance<iter_type>(
other.bs_.begin(), other.begin_))
{
}
template<class Buffers, class ValueType>
auto
consuming_buffers<Buffers, ValueType>::
operator=(consuming_buffers&& other) ->
consuming_buffers&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
bs_ = std::move(other.bs_);
begin_ = std::next(bs_.begin(), nbegin);
skip_ = other.skip_;
return *this;
}
template<class Buffers, class ValueType>
auto
consuming_buffers<Buffers, ValueType>::
operator=(consuming_buffers const& other) ->
consuming_buffers&
{
auto const nbegin = std::distance<iter_type>(
other.bs_.begin(), other.begin_);
bs_ = other.bs_;
begin_ = std::next(bs_.begin(), nbegin);
skip_ = other.skip_;
return *this;
}
template<class Buffers, class ValueType>
consuming_buffers<Buffers, ValueType>::
consuming_buffers(Buffers const& bs)
: bs_(bs)
, begin_(bs_.begin())
{
static_assert(is_BufferSequence<Buffers, ValueType>::value,
"BufferSequence requirements not met");
}
template<class Buffers, class ValueType>
auto
consuming_buffers<Buffers, ValueType>::begin() const ->
const_iterator
{
return const_iterator{*this, begin_};
}
template<class Buffers, class ValueType>
auto
consuming_buffers<Buffers, ValueType>::end() const ->
const_iterator
{
return const_iterator{*this, bs_.end()};
}
template<class Buffers, class ValueType>
void
consuming_buffers<Buffers, ValueType>::consume(std::size_t n)
{
using boost::asio::buffer_size;
for(;n > 0 && begin_ != bs_.end(); ++begin_)
{
auto const len =
buffer_size(*begin_) - skip_;
if(n < len)
{
skip_ += n;
break;
}
n -= len;
skip_ = 0;
}
}
template<class Buffers>
consuming_buffers<Buffers, typename Buffers::value_type>
consumed_buffers(Buffers const& bs, std::size_t n)
{
consuming_buffers<Buffers> cb(bs);
cb.consume(n);
return cb;
}
} // beast
#endif

View File

@@ -0,0 +1,215 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_PREPARE_BUFFERS_IPP
#define BEAST_IMPL_PREPARE_BUFFERS_IPP
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <stdexcept>
#include <utility>
namespace beast {
template<class BufferSequence>
void
prepared_buffers<BufferSequence>::
setup(std::size_t n)
{
for(end_ = bs_.begin(); end_ != bs_.end(); ++end_)
{
auto const len =
boost::asio::buffer_size(*end_);
if(n <= len)
{
size_ = n;
back_ = end_++;
return;
}
n -= len;
}
size_ = 0;
back_ = end_;
}
template<class BufferSequence>
class prepared_buffers<BufferSequence>::const_iterator
{
friend class prepared_buffers<BufferSequence>;
using iter_type =
typename BufferSequence::const_iterator;
prepared_buffers const* b_;
typename BufferSequence::const_iterator it_;
public:
using value_type =
typename std::iterator_traits<iter_type>::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return b_ == other.b_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
if(it_ == b_->back_)
return prepare_buffer(b_->size_, *it_);
return *it_;
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
const_iterator(prepared_buffers const& b,
bool at_end)
: b_(&b)
, it_(at_end ? b.end_ : b.bs_.begin())
{
}
};
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(prepared_buffers&& other)
: prepared_buffers(std::move(other),
std::distance<iter_type>(other.bs_.begin(), other.back_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(prepared_buffers const& other)
: prepared_buffers(other,
std::distance<iter_type>(other.bs_.begin(), other.back_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::
operator=(prepared_buffers&& other) ->
prepared_buffers&
{
auto const nback = std::distance<iter_type>(
other.bs_.begin(), other.back_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = std::move(other.bs_);
back_ = std::next(bs_.begin(), nback);
end_ = std::next(bs_.begin(), nend);
size_ = other.size_;
return *this;
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::
operator=(prepared_buffers const& other) ->
prepared_buffers&
{
auto const nback = std::distance<iter_type>(
other.bs_.begin(), other.back_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = other.bs_;
back_ = std::next(bs_.begin(), nback);
end_ = std::next(bs_.begin(), nend);
size_ = other.size_;
return *this;
}
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(std::size_t n, BufferSequence const& bs)
: bs_(bs)
{
setup(n);
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::begin() const ->
const_iterator
{
return const_iterator{*this, false};
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::end() const ->
const_iterator
{
return const_iterator{*this, true};
}
template<class BufferSequence>
inline
prepared_buffers<BufferSequence>
prepare_buffers(std::size_t n, BufferSequence const& buffers)
{
return prepared_buffers<BufferSequence>(n, buffers);
}
} // beast
#endif

View File

@@ -0,0 +1,304 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_STATIC_STREAMBUF_IPP
#define BEAST_IMPL_STATIC_STREAMBUF_IPP
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <stdexcept>
namespace beast {
class static_streambuf::const_buffers_type
{
std::size_t n_;
std::uint8_t const* p_;
public:
using value_type = boost::asio::const_buffer;
class const_iterator;
const_buffers_type() = default;
const_buffers_type(
const_buffers_type const&) = default;
const_buffers_type& operator=(
const_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class static_streambuf;
const_buffers_type(
std::uint8_t const* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
class static_streambuf::const_buffers_type::const_iterator
{
std::size_t n_;
std::uint8_t const* p_;
public:
using value_type = boost::asio::const_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return p_ == other.p_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return value_type{p_, n_};
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
p_ += n_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
p_ -= n_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class const_buffers_type;
const_iterator(
std::uint8_t const* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
inline
auto
static_streambuf::const_buffers_type::begin() const ->
const_iterator
{
return const_iterator{p_, n_};
}
inline
auto
static_streambuf::const_buffers_type::end() const ->
const_iterator
{
return const_iterator{p_ + n_, n_};
}
//------------------------------------------------------------------------------
class static_streambuf::mutable_buffers_type
{
std::size_t n_;
std::uint8_t* p_;
public:
using value_type = boost::asio::mutable_buffer;
class const_iterator;
mutable_buffers_type() = default;
mutable_buffers_type(
mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(
mutable_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class static_streambuf;
mutable_buffers_type(
std::uint8_t* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
class static_streambuf::mutable_buffers_type::const_iterator
{
std::size_t n_;
std::uint8_t* p_;
public:
using value_type = boost::asio::mutable_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return p_ == other.p_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return value_type{p_, n_};
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
p_ += n_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
p_ -= n_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class mutable_buffers_type;
const_iterator(std::uint8_t* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
inline
auto
static_streambuf::mutable_buffers_type::begin() const ->
const_iterator
{
return const_iterator{p_, n_};
}
inline
auto
static_streambuf::mutable_buffers_type::end() const ->
const_iterator
{
return const_iterator{p_ + n_, n_};
}
//------------------------------------------------------------------------------
inline
auto
static_streambuf::prepare(std::size_t n) ->
mutable_buffers_type
{
if(n > static_cast<std::size_t>(end_ - out_))
throw std::length_error("no space in streambuf");
last_ = out_ + n;
return mutable_buffers_type{out_, n};
}
inline
auto
static_streambuf::data() const ->
const_buffers_type
{
return const_buffers_type{in_,
static_cast<std::size_t>(out_ - in_)};
}
} // beast
#endif

View File

@@ -85,8 +85,15 @@ public:
using value_type =
typename std::iterator_traits<iter_type>::value_type;
#if GENERATING_DOCS
/// A bidirectional iterator type that may be used to read elements.
using const_iterator = implementation_defined;
#else
class const_iterator;
#endif
/// Move constructor.
prepared_buffers(prepared_buffers&&);
@@ -120,190 +127,9 @@ public:
private:
void
setup(std::size_t n)
{
for(end_ = bs_.begin(); end_ != bs_.end(); ++end_)
{
auto const len =
boost::asio::buffer_size(*end_);
if(n <= len)
{
size_ = n;
back_ = end_++;
return;
}
n -= len;
}
size_ = 0;
back_ = end_;
}
setup(std::size_t n);
};
/// A bidirectional iterator type that may be used to read elements.
template<class BufferSequence>
class prepared_buffers<BufferSequence>::const_iterator
{
friend class prepared_buffers<BufferSequence>;
using iter_type =
typename BufferSequence::const_iterator;
prepared_buffers const* b_;
typename BufferSequence::const_iterator it_;
public:
using value_type =
typename std::iterator_traits<iter_type>::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return b_ == other.b_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
if(it_ == b_->back_)
return prepare_buffer(b_->size_, *it_);
return *it_;
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
const_iterator(prepared_buffers const& b,
bool at_end)
: b_(&b)
, it_(at_end ? b.end_ : b.bs_.begin())
{
}
};
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(prepared_buffers&& other)
: prepared_buffers(std::move(other),
std::distance<iter_type>(other.bs_.begin(), other.back_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(prepared_buffers const& other)
: prepared_buffers(other,
std::distance<iter_type>(other.bs_.begin(), other.back_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::
operator=(prepared_buffers&& other) ->
prepared_buffers&
{
auto const nback = std::distance<iter_type>(
other.bs_.begin(), other.back_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = std::move(other.bs_);
back_ = std::next(bs_.begin(), nback);
end_ = std::next(bs_.begin(), nend);
size_ = other.size_;
return *this;
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::
operator=(prepared_buffers const& other) ->
prepared_buffers&
{
auto const nback = std::distance<iter_type>(
other.bs_.begin(), other.back_);
auto const nend = std::distance<iter_type>(
other.bs_.begin(), other.end_);
bs_ = other.bs_;
back_ = std::next(bs_.begin(), nback);
end_ = std::next(bs_.begin(), nend);
size_ = other.size_;
return *this;
}
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(std::size_t n, BufferSequence const& bs)
: bs_(bs)
{
setup(n);
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::begin() const ->
const_iterator
{
return const_iterator{*this, false};
}
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::end() const ->
const_iterator
{
return const_iterator{*this, true};
}
//------------------------------------------------------------------------------
/** Return a trimmed, wrapped buffer sequence.
@@ -321,13 +147,11 @@ prepared_buffers<BufferSequence>::end() const ->
will be made, but ownership of the underlying memory is not transferred.
*/
template<class BufferSequence>
inline
prepared_buffers<BufferSequence>
prepare_buffers(std::size_t n, BufferSequence const& buffers)
{
return prepared_buffers<BufferSequence>(n, buffers);
}
prepare_buffers(std::size_t n, BufferSequence const& buffers);
} // beast
#include <beast/impl/prepare_buffers.ipp>
#endif

View File

@@ -8,12 +8,10 @@
#ifndef BEAST_STATIC_STREAMBUF_HPP
#define BEAST_STATIC_STREAMBUF_HPP
#include <boost/asio/buffer.hpp>
#include <boost/utility/base_from_member.hpp>
#include <algorithm>
#include <array>
#include <cstring>
#include <iterator>
#include <stdexcept>
namespace beast {
@@ -40,18 +38,23 @@ protected:
std::uint8_t* end_;
public:
#if GENERATING_DOCS
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = implementation_defined;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = implementation_defined;
#else
class const_buffers_type;
class mutable_buffers_type;
#if GENERATING_DOCS
private:
#endif
static_streambuf(
static_streambuf const& other) noexcept = delete;
static_streambuf& operator=(
static_streambuf const&) noexcept = delete;
#if GENERATING_DOCS
public:
#endif
/// Returns the largest size output sequence possible.
@@ -116,293 +119,6 @@ protected:
//------------------------------------------------------------------------------
/// The type used to represent the input sequence as a list of buffers.
class static_streambuf::const_buffers_type
{
std::size_t n_;
std::uint8_t const* p_;
public:
using value_type = boost::asio::const_buffer;
class const_iterator;
const_buffers_type() = default;
const_buffers_type(
const_buffers_type const&) = default;
const_buffers_type& operator=(
const_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class static_streambuf;
const_buffers_type(
std::uint8_t const* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
class static_streambuf::const_buffers_type::const_iterator
{
std::size_t n_;
std::uint8_t const* p_;
public:
using value_type = boost::asio::const_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return p_ == other.p_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return value_type{p_, n_};
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
p_ += n_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
p_ -= n_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class const_buffers_type;
const_iterator(
std::uint8_t const* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
inline
auto
static_streambuf::const_buffers_type::begin() const ->
const_iterator
{
return const_iterator{p_, n_};
}
inline
auto
static_streambuf::const_buffers_type::end() const ->
const_iterator
{
return const_iterator{p_ + n_, n_};
}
//------------------------------------------------------------------------------
/// The type used to represent the output sequence as a list of buffers.
class static_streambuf::mutable_buffers_type
{
std::size_t n_;
std::uint8_t* p_;
public:
using value_type = boost::asio::mutable_buffer;
class const_iterator;
mutable_buffers_type() = default;
mutable_buffers_type(
mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(
mutable_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class static_streambuf;
mutable_buffers_type(
std::uint8_t* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
class static_streambuf::mutable_buffers_type::const_iterator
{
std::size_t n_;
std::uint8_t* p_;
public:
using value_type = boost::asio::mutable_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return p_ == other.p_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return value_type{p_, n_};
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
p_ += n_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
p_ -= n_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class mutable_buffers_type;
const_iterator(std::uint8_t* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
inline
auto
static_streambuf::mutable_buffers_type::begin() const ->
const_iterator
{
return const_iterator{p_, n_};
}
inline
auto
static_streambuf::mutable_buffers_type::end() const ->
const_iterator
{
return const_iterator{p_ + n_, n_};
}
//------------------------------------------------------------------------------
inline
auto
static_streambuf::prepare(std::size_t n) ->
mutable_buffers_type
{
if(n > static_cast<std::size_t>(end_ - out_))
throw std::length_error("no space in streambuf");
last_ = out_ + n;
return mutable_buffers_type{out_, n};
}
inline
auto
static_streambuf::data() const ->
const_buffers_type
{
return const_buffers_type{in_,
static_cast<std::size_t>(out_ - in_)};
}
//------------------------------------------------------------------------------
/** A `Streambuf` with a fixed size internal buffer.
@tparam N The number of bytes in the internal buffer.
@@ -456,4 +172,6 @@ public:
} // beast
#include <beast/impl/static_streambuf.ipp>
#endif

View File

@@ -0,0 +1,21 @@
//
// 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_VERSION_HPP
#define BEAST_VERSION_HPP
// follows http://semver.org
// BEAST_VERSION % 100 is the patch level
// BEAST_VERSION / 100 % 1000 is the minor version
// BEAST_VERSION / 100000 is the major version
//
#define BEAST_VERSION 100000
#define BEAST_VERSION_STRING "1.0.0-b2"
#endif

View File

@@ -0,0 +1,60 @@
//
// 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_WEBSOCKET_DETAIL_ENDIAN_HPP
#define BEAST_WEBSOCKET_DETAIL_ENDIAN_HPP
#include <cstdint>
namespace beast {
namespace websocket {
namespace detail {
inline
std::uint16_t
big_uint16_to_native(void const* buf)
{
auto const p = reinterpret_cast<
std::uint8_t const*>(buf);
return (p[0]<<8) + p[1];
}
inline
std::uint64_t
big_uint64_to_native(void const* buf)
{
auto const p = reinterpret_cast<
std::uint8_t const*>(buf);
return
(static_cast<std::uint64_t>(p[0])<<56) +
(static_cast<std::uint64_t>(p[1])<<48) +
(static_cast<std::uint64_t>(p[2])<<40) +
(static_cast<std::uint64_t>(p[3])<<32) +
(static_cast<std::uint64_t>(p[4])<<24) +
(static_cast<std::uint64_t>(p[5])<<16) +
(static_cast<std::uint64_t>(p[6])<< 8) +
p[7];
}
inline
std::uint32_t
little_uint32_to_native(void const* buf)
{
auto const p = reinterpret_cast<
std::uint8_t const*>(buf);
return
p[0] +
(static_cast<std::uint64_t>(p[1])<< 8) +
(static_cast<std::uint64_t>(p[2])<<16) +
(static_cast<std::uint64_t>(p[3])<<24);
}
} // detail
} // websocket
} // beast
#endif

View File

@@ -10,6 +10,7 @@
#include <beast/websocket/rfc6455.hpp>
#include <beast/websocket/static_string.hpp>
#include <beast/websocket/detail/endian.hpp>
#include <beast/websocket/detail/utf8_checker.hpp>
#include <beast/consuming_buffers.hpp>
#include <beast/static_streambuf.hpp>
@@ -244,8 +245,13 @@ read_fh2(frame_header& fh, Streambuf& sb,
std::uint8_t b[2];
assert(buffer_size(sb.data()) >= sizeof(b));
sb.consume(buffer_copy(buffer(b), sb.data()));
#if 0
// Causes strict-aliasing warning in gcc
fh.len = reinterpret_cast<
big_uint16_buf_t const*>(&b[0])->value();
#else
fh.len = big_uint16_to_native(&b[0]);
#endif
// length not canonical
if(fh.len < 126)
{
@@ -259,8 +265,13 @@ read_fh2(frame_header& fh, Streambuf& sb,
std::uint8_t b[8];
assert(buffer_size(sb.data()) >= sizeof(b));
sb.consume(buffer_copy(buffer(b), sb.data()));
#if 0
// Causes strict-aliasing warning in gcc
fh.len = reinterpret_cast<
big_uint64_buf_t const*>(&b[0])->value();
#else
fh.len = big_uint64_to_native(&b[0]);
#endif
// length not canonical
if(fh.len < 65536)
{
@@ -275,8 +286,13 @@ read_fh2(frame_header& fh, Streambuf& sb,
std::uint8_t b[4];
assert(buffer_size(sb.data()) >= sizeof(b));
sb.consume(buffer_copy(buffer(b), sb.data()));
#if 0
// Causes strict-aliasing warning in gcc
fh.key = reinterpret_cast<
little_uint32_buf_t const*>(&b[0])->value();
#else
fh.key = little_uint32_to_native(&b[0]);
#endif
}
code = close_code::none;
}
@@ -327,9 +343,15 @@ read(close_reason& cr,
{
std::uint8_t b[2];
buffer_copy(buffer(b), cb);
#if 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);
n -= 2;
if(! is_valid(cr.code))

View File

@@ -68,10 +68,9 @@ class invokable
void operator()(){}
};
using buf_type = std::uint8_t[
sizeof(holder<exemplar>)];
using buf_type = char[sizeof(holder<exemplar>)];
bool b_ = false;
base* base_ = nullptr;
alignas(holder<exemplar>) buf_type buf_;
public:
@@ -81,7 +80,7 @@ public:
// Engaged invokables must be invoked before
// destruction otherwise the io_service
// invariants are broken w.r.t completions.
assert(! b_);
assert(! base_);
}
#endif
@@ -90,12 +89,12 @@ public:
invokable& operator=(invokable const&) = delete;
invokable(invokable&& other)
: b_(other.b_)
{
if(other.b_)
if(other.base_)
{
other.get().move(buf_);
other.b_ = false;
base_ = reinterpret_cast<base*>(&buf_[0]);
other.base_->move(buf_);
other.base_ = nullptr;
}
}
@@ -105,13 +104,13 @@ public:
// Engaged invokables must be invoked before
// assignment otherwise the io_service
// invariants are broken w.r.t completions.
assert(! b_);
assert(! base_);
if(other.b_)
if(other.base_)
{
b_ = true;
other.get().move(buf_);
other.b_ = false;
base_ = reinterpret_cast<base*>(&buf_[0]);
other.base_->move(buf_);
other.base_ = nullptr;
}
return *this;
}
@@ -123,19 +122,13 @@ public:
void
maybe_invoke()
{
if(b_)
if(base_)
{
b_ = false;
get()();
auto const basep = base_;
base_ = nullptr;
(*basep)();
}
}
private:
base&
get()
{
return *reinterpret_cast<base*>(&buf_[0]);
}
};
template<class F>
@@ -144,9 +137,9 @@ invokable::emplace(F&& f)
{
static_assert(sizeof(buf_type) >= sizeof(holder<F>),
"buffer too small");
assert(! b_);
assert(! base_);
::new(buf_) holder<F>(std::forward<F>(f));
b_ = true;
base_ = reinterpret_cast<base*>(&buf_[0]);
}
} // detail

View File

@@ -13,6 +13,7 @@
namespace beast {
namespace websocket {
/// The type of error used by functions and completion handlers.
using error_code = boost::system::error_code;
/// Error values

View File

@@ -0,0 +1,63 @@
//
// 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_WRITE_STREAMBUF_HPP
#define BEAST_WRITE_STREAMBUF_HPP
#include <beast/type_check.hpp>
#include <beast/detail/write_streambuf.hpp>
#include <type_traits>
#include <utility>
namespace beast {
/** Write to a Streambuf.
This function appends the serialized representation of each provided
argument into the stream buffer. It is capable of converting the
following types of arguments:
@li `boost::asio::const_buffer`
@li `boost::asio::mutable_buffer`
@li A type for which the call to `boost::asio::buffer()` is defined
@li A type meeting the requirements of `ConstBufferSequence`
@li A type meeting the requirements of `MutableBufferSequence`
For all types not listed above, the function will invoke
`boost::lexical_cast` on the argument in an attempt to convert to
a string, which is then appended to the stream buffer.
When this function serializes numbers, it converts them to
their text representation as if by a call to `std::to_string`.
@param streambuf The stream buffer to write to.
@param args A list of one or more arguments to write.
@throws unspecified Any exceptions thrown by `boost::lexical_cast`.
@note This function participates in overload resolution only if
the `streambuf` parameter meets the requirements of Streambuf.
*/
template<class Streambuf, class... Args>
#if GENERATING_DOCS
void
#else
typename std::enable_if<is_Streambuf<Streambuf>::value>::type
#endif
write(Streambuf& streambuf, Args&&... args)
{
detail::write_streambuf(streambuf, std::forward<Args>(args)...);
}
} // beast
#endif

View File

@@ -0,0 +1,82 @@
# Part of Beast
GroupSources(include/beast)
GroupSources(test)
set(CORE_TESTS_SRCS
main.cpp
async_completion.cpp
basic_streambuf.cpp
bind_handler.cpp
buffer_cat.cpp
buffers_adapter.cpp
buffers_debug.cpp
consuming_buffers.cpp
handler_alloc.cpp
placeholders.cpp
prepare_buffers.cpp
static_streambuf.cpp
streambuf.cpp
streambuf_readstream.cpp
type_check.cpp
detail/base64.cpp
detail/empty_base_optimization.cpp
)
add_executable (core-tests
${BEAST_INCLUDES}
${CORE_TESTS_SRCS}
)
set(HTTP_TESTS_SRCS
main.cpp
http/basic_headers.cpp
http/basic_parser.cpp
http/chunk_encode.cpp
http/empty_body.cpp
http/error.cpp
http/headers.cpp
http/message.cpp
http/method.cpp
http/parse_error.cpp
http/parser.cpp
http/read.cpp
http/reason.cpp
http/resume_context.cpp
http/rfc2616.cpp
http/rfc7230.cpp
http/streambuf_body.cpp
http/string_body.cpp
http/write.cpp
)
add_executable (http-tests
${BEAST_INCLUDES}
${HTTP_TESTS_SRCS}
)
set(WEBSOCKET_TESTS_SRCS
main.cpp
websocket/error.cpp
websocket/option.cpp
websocket/rfc6455.cpp
websocket/static_string.cpp
websocket/teardown.cpp
websocket/utf8_checker.cpp
)
add_executable (websocket-tests
${BEAST_INCLUDES}
${WEBSOCKET_TESTS_SRCS}
)
set(PARSER_BENCH_SRCS
main.cpp
http/nodejs_parser.cpp
http/parser_bench.cpp
)
add_executable (parser-bench
${BEAST_INCLUDES}
${PARSER_BENCH_SRCS}
)

View File

@@ -7,29 +7,7 @@
import os ;
unit-test http_tests :
main.cpp
../src/beast_http_nodejs_parser.cpp
http/basic_headers.cpp
http/basic_parser.cpp
http/chunk_encode.cpp
http/empty_body.cpp
http/error.cpp
http/headers.cpp
http/message.cpp
http/method.cpp
http/parser.cpp
http/read.cpp
http/reason.cpp
http/resume_context.cpp
http/rfc2616.cpp
http/streambuf_body.cpp
http/string_body.cpp
http/type_check.cpp
http/write.cpp
;
unit-test core_tests :
unit-test core-tests :
main.cpp
async_completion.cpp
basic_streambuf.cpp
@@ -48,3 +26,41 @@ unit-test core_tests :
detail/base64.cpp
detail/empty_base_optimization.cpp
;
unit-test http-tests :
main.cpp
http/basic_headers.cpp
http/basic_parser.cpp
http/chunk_encode.cpp
http/empty_body.cpp
http/error.cpp
http/headers.cpp
http/message.cpp
http/method.cpp
http/parse_error.cpp
http/parser.cpp
http/read.cpp
http/reason.cpp
http/resume_context.cpp
http/rfc2616.cpp
http/rfc7230.cpp
http/streambuf_body.cpp
http/string_body.cpp
http/write.cpp
;
unit-test websocket-tests :
main.cpp
websocket/error.cpp
websocket/option.cpp
websocket/rfc6455.cpp
websocket/static_string.cpp
websocket/teardown.cpp
websocket/utf8_checker.cpp
;
unit-test parser-bench :
main.cpp
http/nodejs_parser.cpp
http/parser_bench.cpp
;

View File

@@ -295,7 +295,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE(basic_streambuf,asio,beast);
BEAST_DEFINE_TESTSUITE(basic_streambuf,core,beast);
} // test
} // beast

View File

@@ -31,7 +31,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE(bind_handler,asio,beast);
BEAST_DEFINE_TESTSUITE(bind_handler,core,beast);
} // test
} // beast

View File

@@ -75,7 +75,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE(buffer_cat,asio,beast);
BEAST_DEFINE_TESTSUITE(buffer_cat,core,beast);
} // test
} // beast

View File

@@ -116,7 +116,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE(buffers_adapter,asio,beast);
BEAST_DEFINE_TESTSUITE(buffers_adapter,core,beast);
} // test
} // beast

View File

@@ -47,7 +47,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE(base64,crypto,beast);
BEAST_DEFINE_TESTSUITE(base64,detail,beast);
} // test
} // beast

View File

@@ -103,7 +103,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE(empty_base_optimization,utility,beast);
BEAST_DEFINE_TESTSUITE(empty_base_optimization,detail,beast);
} // test
} // beast

View File

@@ -7,3 +7,630 @@
// Test that header file is self-contained.
#include <beast/http/basic_parser.hpp>
#include "message_fuzz.hpp"
#include <beast/streambuf.hpp>
#include <beast/buffers_debug.hpp>
#include <beast/write_streambuf.hpp>
#include <beast/http/error.hpp>
#include <beast/http/rfc2616.hpp>
#include <beast/detail/ci_char_traits.hpp>
#include <beast/detail/unit_test/suite.hpp>
#include <boost/utility/string_ref.hpp>
#include <cassert>
#include <climits>
#include <map>
#include <new>
#include <random>
#include <type_traits>
namespace beast {
namespace http {
class basic_parser_test : public beast::detail::unit_test::suite
{
std::mt19937 rng_;
public:
struct cb_req_checker
{
bool method = false;
bool uri = false;
bool request = false;
};
struct cb_res_checker
{
bool reason = false;
bool response = false;
};
template<bool isRequest>
struct cb_checker
: public basic_parser<isRequest, cb_checker<isRequest>>
, std::conditional<isRequest,
cb_req_checker, cb_res_checker>::type
{
bool field = false;
bool value = false;
bool headers = false;
bool body = false;
bool complete = false;
private:
friend class basic_parser<isRequest, cb_checker<isRequest>>;
void on_method(boost::string_ref const&, error_code&)
{
this->method = true;
}
void on_uri(boost::string_ref const&, error_code&)
{
this->uri = true;
}
void on_reason(boost::string_ref const&, error_code&)
{
this->reason = true;
}
void on_request(error_code&)
{
this->request = true;
}
void on_response(error_code&)
{
this->response = true;
}
void on_field(boost::string_ref const&, error_code&)
{
field = true;
}
void on_value(boost::string_ref const&, error_code&)
{
value = true;
}
int on_headers(error_code&)
{
headers = true;
return 0;
}
void on_body(boost::string_ref const&, error_code&)
{
body = true;
}
void on_complete(error_code&)
{
complete = true;
}
};
//--------------------------------------------------------------------------
static
std::string
escaped_string(boost::string_ref const& s)
{
std::string out;
out.reserve(s.size());
char const* p = s.data();
while(p != s.end())
{
if(*p == '\r')
out.append("\\r");
else if(*p == '\n')
out.append("\\n");
else if (*p == '\t')
out.append("\\t");
else
out.append(p, 1);
++p;
}
return out;
}
template<bool isRequest>
struct null_parser : basic_parser<isRequest, null_parser<isRequest>>
{
};
template<bool isRequest>
class test_parser :
public basic_parser<isRequest, test_parser<isRequest>>
{
std::string field_;
std::string value_;
void check()
{
if(! value_.empty())
{
rfc2616::trim_right_in_place(value_);
fields.emplace(field_, value_);
field_.clear();
value_.clear();
}
}
public:
std::map<std::string, std::string,
beast::detail::ci_less> fields;
std::string body;
void on_field(boost::string_ref const& s, error_code&)
{
check();
field_.append(s.data(), s.size());
}
void on_value(boost::string_ref const& s, error_code&)
{
value_.append(s.data(), s.size());
}
int on_headers(error_code&)
{
check();
return 0;
}
void on_body(boost::string_ref const& s, error_code&)
{
body.append(s.data(), s.size());
}
};
void
testCallbacks()
{
{
cb_checker<true> p;
error_code ec;
std::string const s =
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(s.data(), s.size(), ec);
if( expect(! ec))
{
expect(p.method);
expect(p.uri);
expect(p.request);
expect(p.field);
expect(p.value);
expect(p.headers);
expect(p.body);
expect(p.complete);
}
}
{
cb_checker<false> p;
error_code ec;
std::string const s =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(s.data(), s.size(), ec);
if( expect(! ec))
{
expect(p.reason);
expect(p.response);
expect(p.field);
expect(p.value);
expect(p.headers);
expect(p.body);
expect(p.complete);
}
}
}
// Parse the entire input buffer as a valid message,
// then parse in two pieces of all possible lengths.
//
template<class Parser, class F>
void
parse(boost::string_ref const& m, F&& f)
{
{
error_code ec;
Parser p;
p.write(m.data(), m.size(), ec);
if(expect(p.complete()))
if(expect(! ec, ec.message()))
f(p);
}
for(std::size_t i = 1; i < m.size() - 1; ++i)
{
error_code ec;
Parser p;
p.write(&m[0], i, ec);
if(! expect(! ec, ec.message()))
continue;
if(p.complete())
{
f(p);
}
else
{
p.write(&m[i], m.size() - i, ec);
if(! expect(! ec, ec.message()))
continue;
expect(p.complete());
f(p);
}
}
}
// Parse with an expected error code
//
template<bool isRequest>
void
parse_ev(boost::string_ref const& m, parse_error ev)
{
{
error_code ec;
null_parser<isRequest> p;
p.write(m.data(), m.size(), ec);
if(expect(! p.complete()))
expect(ec == ev, ec.message());
}
for(std::size_t i = 1; i < m.size() - 1; ++i)
{
error_code ec;
null_parser<isRequest> p;
p.write(&m[0], i, ec);
if(! expect(! p.complete()))
continue;
if(ec)
{
expect(ec == ev, ec.message());
continue;
}
p.write(&m[i], m.size() - i, ec);
if(! expect(! p.complete()))
continue;
if(! expect(ec == ev, ec.message()))
continue;
}
}
//--------------------------------------------------------------------------
template<class UInt = std::size_t>
UInt
rand(std::size_t n)
{
return static_cast<UInt>(
std::uniform_int_distribution<
std::size_t>{0, n-1}(rng_));
}
//--------------------------------------------------------------------------
// Parse a valid message with expected version
//
template<bool isRequest>
void
version(boost::string_ref const& m,
unsigned major, unsigned minor)
{
parse<null_parser<isRequest>>(m,
[&](null_parser<isRequest> const& p)
{
expect(p.http_major() == major);
expect(p.http_minor() == minor);
});
}
// Parse a valid message with expected flags mask
//
void
checkf(boost::string_ref const& m, std::uint8_t mask)
{
parse<null_parser<true>>(m,
[&](null_parser<true> const& p)
{
expect(p.flags() & mask);
});
}
void
testVersion()
{
version<true>("GET / HTTP/0.0\r\n\r\n", 0, 0);
version<true>("GET / HTTP/0.1\r\n\r\n", 0, 1);
version<true>("GET / HTTP/0.9\r\n\r\n", 0, 9);
version<true>("GET / HTTP/1.0\r\n\r\n", 1, 0);
version<true>("GET / HTTP/1.1\r\n\r\n", 1, 1);
version<true>("GET / HTTP/9.9\r\n\r\n", 9, 9);
version<true>("GET / HTTP/999.999\r\n\r\n", 999, 999);
parse_ev<true>("GET / HTTP/1000.0\r\n\r\n", parse_error::bad_version);
parse_ev<true>("GET / HTTP/0.1000\r\n\r\n", parse_error::bad_version);
parse_ev<true>("GET / HTTP/99999999999999999999.0\r\n\r\n", parse_error::bad_version);
parse_ev<true>("GET / HTTP/0.99999999999999999999\r\n\r\n", parse_error::bad_version);
}
void
testConnection(std::string const& token,
std::uint8_t flag)
{
checkf("GET / HTTP/1.1\r\nConnection:" + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: " + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection:\t" + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: \t" + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: " + token + " \r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: " + token + "\t\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: " + token + " \t\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: " + token + "\t \r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: \r\n" " " + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection:\t\r\n" " " + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: \r\n" "\t" + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection:\t\r\n" "\t" + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: X," + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: X, " + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: X,\t" + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: X,\t " + token + "\r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: X," + token + " \r\n\r\n", flag);
checkf("GET / HTTP/1.1\r\nConnection: X," + token + "\t\r\n\r\n", flag);
}
void
testContentLength()
{
std::size_t const length = 0;
std::string const length_s =
std::to_string(length);
checkf("GET / HTTP/1.1\r\nContent-Length:"+ length_s + "\r\n\r\n", parse_flag::contentlength);
checkf("GET / HTTP/1.1\r\nContent-Length: "+ length_s + "\r\n\r\n", parse_flag::contentlength);
checkf("GET / HTTP/1.1\r\nContent-Length:\t"+ length_s + "\r\n\r\n", parse_flag::contentlength);
checkf("GET / HTTP/1.1\r\nContent-Length: \t"+ length_s + "\r\n\r\n", parse_flag::contentlength);
checkf("GET / HTTP/1.1\r\nContent-Length: "+ length_s + " \r\n\r\n", parse_flag::contentlength);
checkf("GET / HTTP/1.1\r\nContent-Length: "+ length_s + "\t\r\n\r\n", parse_flag::contentlength);
checkf("GET / HTTP/1.1\r\nContent-Length: "+ length_s + " \t\r\n\r\n", parse_flag::contentlength);
checkf("GET / HTTP/1.1\r\nContent-Length: "+ length_s + "\t \r\n\r\n", parse_flag::contentlength);
checkf("GET / HTTP/1.1\r\nContent-Length: \r\n" " "+ length_s + "\r\n\r\n", parse_flag::contentlength);
checkf("GET / HTTP/1.1\r\nContent-Length:\t\r\n" " "+ length_s + "\r\n\r\n", parse_flag::contentlength);
checkf("GET / HTTP/1.1\r\nContent-Length: \r\n" "\t"+ length_s + "\r\n\r\n", parse_flag::contentlength);
checkf("GET / HTTP/1.1\r\nContent-Length:\t\r\n" "\t"+ length_s + "\r\n\r\n", parse_flag::contentlength);
}
void
testTransferEncoding()
{
checkf("GET / HTTP/1.1\r\nTransfer-Encoding:chunked\r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding:\tchunked\r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding: \tchunked\r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding: chunked \r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding: chunked\t\r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding: chunked \t\r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding: chunked\t \r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding: \r\n" " chunked\r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding:\t\r\n" " chunked\r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding: \r\n" "\tchunked\r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding:\t\r\n" "\tchunked\r\n\r\n0\r\n\r\n", parse_flag::chunked );
}
void
testFlags()
{
testConnection("keep-alive",
parse_flag::connection_keep_alive);
testConnection("close",
parse_flag::connection_close);
testConnection("upgrade",
parse_flag::connection_upgrade);
testContentLength();
testTransferEncoding();
checkf(
"GET / HTTP/1.1\r\n"
"Upgrade: x\r\n"
"\r\n",
parse_flag::upgrade
);
parse_ev<true>(
"GET / HTTP/1.1\r\n"
"Transfer-Encoding:chunked\r\n"
"Content-Length: 0\r\n"
"\r\n", parse_error::illegal_content_length);
}
void
testUpgrade()
{
null_parser<true> p;
boost::string_ref s =
"GET / HTTP/1.1\r\nConnection: upgrade\r\nUpgrade: WebSocket\r\n\r\n";
error_code ec;
p.write(s.data(), s.size(), ec);
if(! expect(! ec, ec.message()))
return;
expect(p.complete());
expect(p.upgrade());
}
void testBad()
{
parse_ev<true>(" ", parse_error::bad_method);
parse_ev<true>(" G", parse_error::bad_method);
parse_ev<true>("G:", parse_error::bad_request);
parse_ev<true>("GET /", parse_error::bad_uri);
parse_ev<true>("GET / X", parse_error::bad_version);
parse_ev<true>("GET / HX", parse_error::bad_version);
parse_ev<true>("GET / HTTX", parse_error::bad_version);
parse_ev<true>("GET / HTTPX", parse_error::bad_version);
parse_ev<true>("GET / HTTP/.", parse_error::bad_version);
parse_ev<true>("GET / HTTP/1000", parse_error::bad_version);
parse_ev<true>("GET / HTTP/1. ", parse_error::bad_version);
parse_ev<true>("GET / HTTP/1.1000", parse_error::bad_version);
parse_ev<true>("GET / HTTP/1.1\r ", parse_error::bad_crlf);
parse_ev<true>("GET / HTTP/1.1\r\nf :", parse_error::bad_field);
}
void
testRandomReq(std::size_t N)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
message_fuzz mg;
for(std::size_t i = 0; i < N; ++i)
{
std::string s;
{
streambuf sb;
mg.request(sb);
s.reserve(buffer_size(sb.data()));
for(auto const& b : sb.data())
s.append(buffer_cast<char const*>(b),
buffer_size(b));
}
null_parser<true> p;
for(std::size_t j = 1; j < s.size() - 1; ++j)
{
error_code ec;
p.write(&s[0], j, ec);
if(! expect(! ec, ec.message()))
{
log << escaped_string(s);
break;
}
if(! p.complete())
{
p.write(&s[j], s.size() - j, ec);
if(! expect(! ec, ec.message()))
{
log << escaped_string(s);
break;
}
}
if(! expect(p.complete()))
break;
if(! p.keep_alive())
{
p.~null_parser();
new(&p) null_parser<true>{};
}
}
}
}
void
testRandomResp(std::size_t N)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
message_fuzz mg;
for(std::size_t i = 0; i < N; ++i)
{
std::string s;
{
streambuf sb;
mg.response(sb);
s.reserve(buffer_size(sb.data()));
for(auto const& b : sb.data())
s.append(buffer_cast<char const*>(b),
buffer_size(b));
}
null_parser<false> p;
for(std::size_t j = 1; j < s.size() - 1; ++j)
{
error_code ec;
p.write(&s[0], j, ec);
if(! expect(! ec, ec.message()))
{
log << escaped_string(s);
break;
}
if(! p.complete())
{
p.write(&s[j], s.size() - j, ec);
if(! expect(! ec, ec.message()))
{
log << escaped_string(s);
break;
}
}
if(! expect(p.complete()))
break;
if(! p.keep_alive())
{
p.~null_parser();
new(&p) null_parser<false>{};
}
}
}
}
void testBody()
{
auto match =
[&](std::string const& body)
{
return
[&](test_parser<true> const& p)
{
expect(p.body == body);
};
};
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nContent-Length: 1\r\n\r\n123", match("1"));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nContent-Length: 3\r\n\r\n123", match("123"));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nContent-Length: 0\r\n\r\n", match(""));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
"1\r\n"
"a\r\n"
"0\r\n"
"\r\n", match("a"));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
"2\r\n"
"ab\r\n"
"0\r\n"
"\r\n", match("ab"));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
"2\r\n"
"ab\r\n"
"1\r\n"
"c\r\n"
"0\r\n"
"\r\n", match("abc"));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
"10\r\n"
"1234567890123456\r\n"
"0\r\n"
"\r\n", match("1234567890123456"));
}
void run() override
{
testCallbacks();
testVersion();
testFlags();
testUpgrade();
testBad();
testRandomReq(100);
testRandomResp(100);
testBody();
}
};
BEAST_DEFINE_TESTSUITE(basic_parser,http,beast);
} // http
} // beast

View File

@@ -0,0 +1,562 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP
#define BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP
#include <beast/http/detail/basic_parser.hpp>
#include <beast/write_streambuf.hpp>
#include <cstdint>
#include <random>
#include <string>
namespace beast {
namespace http {
template<class = void>
std::string
escaped_string(boost::string_ref const& s)
{
std::string out;
out.reserve(s.size());
char const* p = s.data();
while(p != s.end())
{
if(*p == '\r')
out.append("\\r");
else if(*p == '\n')
out.append("\\n");
else if (*p == '\t')
out.append("\\t");
else
out.append(p, 1);
++p;
}
return out;
}
// Produces random HTTP messages
//
template<class = void>
class message_fuzz_t
{
std::mt19937 rng_;
static
std::string
to_hex(std::size_t v)
{
if(! v)
return "0";
std::string s;
while(v > 0)
{
s.insert(s.begin(),
"0123456789abcdef"[v&0xf]);
v >>= 4;
}
return s;
}
public:
template<class UInt = std::size_t>
UInt
rand(std::size_t n)
{
return static_cast<UInt>(
std::uniform_int_distribution<
std::size_t>{0, n-1}(rng_));
}
std::string
method()
{
#if 0
// All IANA registered methods
static char const* const list[39] = {
"ACL", "BASELINE-CONTROL", "BIND", "CHECKIN", "CHECKOUT", "CONNECT",
"COPY", "DELETE", "GET", "HEAD", "LABEL", "LINK", "LOCK", "MERGE",
"MKACTIVITY", "MKCALENDAR", "MKCOL", "MKREDIRECTREF", "MKWORKSPACE",
"MOVE", "OPTIONS", "ORDERPATCH", "PATCH", "POST", "PRI", "PROPFIND",
"PROPPATCH", "PUT", "REBIND", "REPORT", "SEARCH", "TRACE", "UNBIND",
"UNCHECKOUT", "UNLINK", "UNLOCK", "UPDATE", "UPDATEREDIRECTREF",
"VERSION-CONTROL"
};
return list[rand(39)];
#else
// methods parsed by nodejs-http-parser
static char const* const list[33] = {
"ACL", "BIND", "CHECKOUT", "CONNECT", "COPY", "DELETE", "HEAD", "GET",
"LINK", "LOCK", "MERGE", "MKCOL", "MKCALENDAR", "MKACTIVITY", "M-SEARCH",
"MOVE", "NOTIFY", "OPTIONS", "PATCH", "POST", "PROPFIND", "PROPPATCH",
"PURGE", "PUT", "REBIND", "REPORT", "SEARCH", "SUBSCRIBE", "TRACE",
"UNBIND", "UNLINK", "UNLOCK", "UNSUBSCRIBE"
};
return list[rand(33)];
#endif
}
std::string
scheme()
{
static char const* const list[241] = {
"aaa", "aaas", "about", "acap", "acct", "acr", "adiumxtra", "afp", "afs",
"aim", "appdata", "apt", "attachment", "aw", "barion", "beshare", "bitcoin",
"blob", "bolo", "callto", "cap", "chrome", "chrome-extension", "cid",
"coap", "coaps", "com-eventbrite-attendee", "content", "crid", "cvs",
"data", "dav", "dict", "dis", "dlna-playcontainer", "dlna-playsingle",
"dns", "dntp", "dtn", "dvb", "ed2k", "example", "facetime", "fax", "feed",
"feedready", "file", "filesystem", "finger", "fish", "ftp", "geo", "gg",
"git", "gizmoproject", "go", "gopher", "gtalk", "h323", "ham", "hcp",
"http", "https", "iax", "icap", "icon", "im", "imap", "info", "iotdisco",
"ipn", "ipp", "ipps", "irc", "irc6", "ircs", "iris", "iris.beep",
"iris.lwz", "iris.xpc", "iris.xpcs", "isostore", "itms", "jabber", "jar",
"jms", "keyparc", "lastfm", "ldap", "ldaps", "magnet", "mailserver",
"mailto", "maps", "market", "message", "mid", "mms",
"modem", "ms-access", "ms-drive-to", "ms-enrollment", "ms-excel",
"ms-getoffice", "ms-help", "ms-infopath", "ms-media-stream-id", "ms-project",
"ms-powerpoint", "ms-publisher", "ms-search-repair",
"ms-secondary-screen-controller", "ms-secondary-screen-setup",
"ms-settings", "ms-settings-airplanemode", "ms-settings-bluetooth",
"ms-settings-camera", "ms-settings-cellular", "ms-settings-cloudstorage",
"ms-settings-emailandaccounts", "ms-settings-language", "ms-settings-location",
"ms-settings-lock", "ms-settings-nfctransactions", "ms-settings-notifications",
"ms-settings-power", "ms-settings-privacy", "ms-settings-proximity",
"ms-settings-screenrotation", "ms-settings-wifi", "ms-settings-workplace",
"ms-spd", "ms-transit-to", "ms-visio", "ms-walk-to", "ms-word", "msnim",
"msrp", "msrps", "mtqp", "mumble", "mupdate", "mvn", "news", "nfs", "ni",
"nih", "nntp", "notes", "oid", "opaquelocktoken", "pack", "palm", "paparazzi",
"pkcs11", "platform", "pop", "pres", "prospero", "proxy", "psyc", "query",
"redis", "rediss", "reload", "res", "resource", "rmi", "rsync", "rtmfp",
"rtmp", "rtsp", "rtsps", "rtspu", "secondlife", "service", "session", "sftp",
"sgn", "shttp", "sieve", "sip", "sips", "skype", "smb", "sms", "smtp",
"snews", "snmp", "soap.beep", "soap.beeps", "soldat", "spotify", "ssh",
"steam", "stun", "stuns", "submit", "svn", "tag", "teamspeak", "tel",
"teliaeid", "telnet", "tftp", "things", "thismessage", "tip", "tn3270",
"tool", "turn", "turns", "tv", "udp", "unreal", "urn", "ut2004", "v-event",
"vemmi", "ventrilo", "videotex", "vnc", "view-source", "wais", "webcal",
"wpid", "ws", "wss", "wtai", "wyciwyg", "xcon", "xcon-userid", "xfire",
"xmlrpc.beep", "xmlrpc.beeps", "xmpp", "xri", "ymsgr", "z39.50", "z39.50r",
"z39.50s:"
};
return list[rand(241)];
}
std::string
pchar()
{
if(rand(4))
return std::string(1,
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
":@&=+$,"[rand(69)]);
std::string s = "%";
s += "0123456789abcdef"[rand(16)];
s += "0123456789abcdef"[rand(16)];
return s;
}
char
uric()
{
return 'a';
}
char
uric_no_slash()
{
return 'a';
}
std::string
param()
{
std::string s;
while(rand(2))
s += pchar();
return s;
}
std::string
query()
{
std::string s;
while(rand(2))
s += uric();
return s;
}
std::string
userinfo()
{
std::string s;
while(rand(2))
s += "a";
return s;
}
/* authority = server | reg_name
reg_name = 1*( unreserved | escaped | "$" | "," |
";" | ":" | "@" | "&" | "=" | "+" )
server = [ [ userinfo "@" ] hostport ]
userinfo = *( unreserved | escaped |
";" | ":" | "&" | "=" | "+" | "$" | "," )
hostport = host [ ":" port ]
host = hostname | IPv4address
hostname = *( domainlabel "." ) toplabel [ "." ]
domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
toplabel = alpha | alpha *( alphanum | "-" ) alphanum
IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
port = *digit
*/
std::string
server()
{
std::string s;
if(rand(2))
s += userinfo() + "@";
return s;
}
std::string
reg_name()
{
std::string s;
s = "a";
while(rand(2))
s += "a";
return s;
}
std::string
authority()
{
if(rand(2))
return server();
return reg_name();
}
std::string
opaque_part()
{
std::string s;
s += uric_no_slash();
while(rand(2))
s += uric();
return s;
}
/* abs_path = "/" path_segments
path_segments = segment *( "/" segment )
segment = *pchar *( ";" param )
param = *pchar
pchar = unreserved | escaped |
":" | "@" | "&" | "=" | "+" | "$" | ","
*/
std::string
abs_path()
{
std::string s = "/";
for(;;)
{
while(rand(2))
s += pchar();
while(rand(2))
s += ";" + param();
if(rand(2))
break;
s.append("/");
}
return s;
}
/* net_path = "//" authority [ abs_path ]
*/
std::string
net_path()
{
std::string s = "//";
s += authority();
if(rand(2))
s += abs_path();
return s;
}
/* absoluteURI = scheme ":" ( hier_part | opaque_part )
scheme = alpha *( alpha | digit | "+" | "-" | "." )
hier_part = ( net_path | abs_path ) [ "?" query ]
abs_path = "/" path_segments
query = *uric
opaque_part = uric_no_slash *uric
*/
std::string
abs_uri()
{
std::string s;
s = scheme() + ":";
if(rand(2))
{
if(rand(2))
s += net_path();
else
s += abs_path();
if(rand(2))
s += "?" + query();
}
else
{
s += opaque_part();
}
return s;
}
std::string
uri()
{
//switch(rand(4))
switch(1)
{
case 0: return abs_uri();
case 1: return abs_path();
case 2: return authority();
default:
case 3: break;
}
return "*";
}
std::string
token()
{
static char constexpr valid[78] =
"!#$%&\'*+-." "0123456789" "ABCDEFGHIJ" "KLMNOPQRST"
"UVWXYZ^_`a" "bcdefghijk" "lmnopqrstu" "vwxyz|~";
std::string s;
s.append(1, valid[rand(77)]);
while(rand(4))
s.append(1, valid[rand(77)]);
return s;
}
#if 0
std::string
uri()
{
static char constexpr alpha[63] =
"0123456789" "ABCDEFGHIJ" "KLMNOPQRST"
"UVWXYZabcd" "efghijklmn" "opqrstuvwx" "yz";
std::string s;
s = "/";
while(rand(4))
s.append(1, alpha[rand(62)]);
return s;
}
#endif
std::string
field()
{ static char const* const list[289] =
{
"A-IM",
"Accept", "Accept-Additions", "Accept-Charset", "Accept-Datetime", "Accept-Encoding",
"Accept-Features", "Accept-Language", "Accept-Patch", "Accept-Ranges", "Age", "Allow",
"ALPN", "Also-Control", "Alt-Svc", "Alt-Used", "Alternate-Recipient", "Alternates",
"Apply-To-Redirect-Ref", "Approved", "Archive", "Archived-At", "Article-Names",
"Article-Updates", "Authentication-Info", "Authentication-Results", "Authorization",
"Auto-Submitted", "Autoforwarded", "Autosubmitted", "Base", "Bcc", "Body", "C-Ext",
"C-Man", "C-Opt", "C-PEP", "C-PEP-Info", "Cache-Control",
"CalDAV-Timezones", "Cc", "Close", "Comments", /*"Connection",*/ "Content-Alternative",
"Content-Base", "Content-Description", "Content-Disposition", "Content-Duration",
"Content-Encoding", "Content-features", "Content-ID", "Content-Identifier",
"Content-Language", /*"Content-Length",*/ "Content-Location", "Content-MD5",
"Content-Range", "Content-Return", "Content-Script-Type", "Content-Style-Type",
"Content-Transfer-Encoding", "Content-Type", "Content-Version", "Control", "Conversion",
"Conversion-With-Loss", "Cookie", "Cookie2", "DASL", "DAV", "DL-Expansion-History", "Date",
"Date-Received", "Default-Style", "Deferred-Delivery", "Delivery-Date", "Delta-Base",
"Depth", "Derived-From", "Destination", "Differential-ID", "Digest",
"Discarded-X400-IPMS-Extensions", "Discarded-X400-MTS-Extensions", "Disclose-Recipients",
"Disposition-Notification-Options", "Disposition-Notification-To", "Distribution",
"DKIM-Signature", "Downgraded-Bcc", "Downgraded-Cc", "Downgraded-Disposition-Notification-To",
"Downgraded-Final-Recipient", "Downgraded-From", "Downgraded-In-Reply-To",
"Downgraded-Mail-From", "Downgraded-Message-Id", "Downgraded-Original-Recipient",
"Downgraded-Rcpt-To", "Downgraded-References", "Downgraded-Reply-To", "Downgraded-Resent-Bcc",
"Downgraded-Resent-Cc", "Downgraded-Resent-From", "Downgraded-Resent-Reply-To",
"Downgraded-Resent-Sender", "Downgraded-Resent-To", "Downgraded-Return-Path",
"Downgraded-Sender", "Downgraded-To", "Encoding", "Encrypted", "ETag", "Expect",
"Expires", "Expiry-Date", "Ext", "Followup-To", "Forwarded", "From",
"Generate-Delivery-Report", "GetProfile", "Hobareg", "Host", "HTTP2-Settings", "IM", "If",
"If-Match", "If-Modified-Since", "If-None-Match", "If-Range", "If-Schedule-Tag-Match",
"If-Unmodified-Since", "Importance", "In-Reply-To", "Incomplete-Copy", "Injection-Date",
"Injection-Info", "Keep-Alive", "Keywords", "Label", "Language", "Last-Modified",
"Latest-Delivery-Time", "Lines", "Link", "List-Archive", "List-Help", "List-ID",
"List-Owner", "List-Post", "List-Subscribe", "List-Unsubscribe", "Location", "Lock-Token",
"Man", "Max-Forwards", "Memento-Datetime", "Message-Context", "Message-ID", "Message-Type",
"Meter", "MIME-Version", "MMHS-Exempted-Address", "MMHS-Extended-Authorisation-Info",
"MMHS-Subject-Indicator-Codes", "MMHS-Handling-Instructions", "MMHS-Message-Instructions",
"MMHS-Codress-Message-Indicator", "MMHS-Originator-Reference", "MMHS-Primary-Precedence",
"MMHS-Copy-Precedence", "MMHS-Message-Type", "MMHS-Other-Recipients-Indicator-To",
"MMHS-Other-Recipients-Indicator-CC", "MMHS-Acp127-Message-Identifier", "MMHS-Originator-PLAD",
"MT-Priority", "Negotiate", "Newsgroups", "NNTP-Posting-Date", "NNTP-Posting-Host",
"Obsoletes", "Opt", "Ordering-Type", "Organization", "Origin",
"Original-Encoded-Information-Types", "Original-From", "Original-Message-ID",
"Original-Recipient", "Original-Sender", "Originator-Return-Address", "Original-Subject",
"Overwrite", "P3P", "Path", "PEP", "PICS-Label", "Pep-Info", "Position", "Posting-Version",
"Pragma", "Prefer", "Preference-Applied", "Prevent-NonDelivery-Report", "Priority",
"ProfileObject", "Protocol", "Protocol-Info", "Protocol-Query", "Protocol-Request",
"Proxy-Authenticate", "Proxy-Authentication-Info", "Proxy-Authorization", "Proxy-Features",
"Proxy-Instruction", "Public", "Public-Key-Pins", "Public-Key-Pins-Report-Only", "Range",
"Received", "Received-SPF", "Redirect-Ref", "References", "Referer", "Relay-Version",
"Reply-By", "Reply-To", "Require-Recipient-Valid-Since", "Resent-Bcc", "Resent-Cc",
"Resent-Date", "Resent-From", "Resent-Message-ID", "Resent-Reply-To", "Resent-Sender",
"Resent-To", "Retry-After", "Return-Path", "Safe", "Schedule-Reply", "Schedule-Tag",
"Sec-WebSocket-Accept", "Sec-WebSocket-Extensions", "Sec-WebSocket-Key",
"Sec-WebSocket-Protocol", "Sec-WebSocket-Version", "Security-Scheme", "See-Also", "Sender",
"Sensitivity", "Server", "Set-Cookie", "Set-Cookie2",
"SetProfile", "SLUG", "SoapAction", "Solicitation", "Status-URI", "Strict-Transport-Security",
"Subject", "Summary", "Supersedes", "Surrogate-Capability", "Surrogate-Control", "TCN",
"TE", "Timeout", "To", "Trailer", /*"Transfer-Encoding",*/ "URI", /*"Upgrade",*/ "User-Agent",
"Variant-Vary", "Vary", "VBR-Info", "Via", "WWW-Authenticate", "Want-Digest", "Warning",
"X400-Content-Identifier", "X400-Content-Return", "X400-Content-Type", "X400-MTS-Identifier",
"X400-Originator", "X400-Received", "X400-Recipients", "X400-Trace", "X-Frame-Options", "Xref"
};
return list[rand(289)];
}
std::string
text()
{
std::string s;
while(rand(3))
{
for(;;)
{
char c = rand<char>(256);
if(detail::is_text(c))
{
s.append(1, c);
break;
}
}
}
return s;
}
std::string
value()
{
std::string s;
while(rand(3))
{
if(rand(5))
{
s.append(text());
}
else
{
// LWS
if(! rand(4))
s.append("\r\n");
s.append(1, rand(2) ? ' ' : '\t');
while(rand(2))
s.append(1, rand(2) ? ' ' : '\t');
}
}
return s;
}
template<class Streambuf>
void
headers(Streambuf& sb)
{
while(rand(6))
{
write(sb, field());
write(sb, rand(4) ? ": " : ":");
write(sb, value());
write(sb, "\r\n");
}
}
template<class Streambuf>
void
body(Streambuf& sb)
{
if(! rand(4))
{
write(sb, "Content-Length: 0\r\n\r\n");
return;
}
if(rand(2))
{
auto const len = rand(500);
write(sb, "Content-Length: ", len, "\r\n\r\n");
for(auto const& b : sb.prepare(len))
{
auto p = boost::asio::buffer_cast<char*>(b);
auto n = boost::asio::buffer_size(b);
while(n--)
*p++ = static_cast<char>(32 + rand(26+26+10+6));
}
sb.commit(len);
}
else
{
auto len = rand(500);
write(sb, "Transfer-Encoding: chunked\r\n\r\n");
while(len > 0)
{
auto n = std::min(1 + rand(300), len);
len -= n;
write(sb, to_hex(n), "\r\n");
for(auto const& b : sb.prepare(n))
{
auto p = boost::asio::buffer_cast<char*>(b);
auto m = boost::asio::buffer_size(b);
while(m--)
*p++ = static_cast<char>(32 + rand(26+26+10+6));
}
sb.commit(n);
write(sb, "\r\n");
}
write(sb, "0\r\n\r\n");
}
}
template<class Streambuf>
void
request(Streambuf& sb)
{
write(sb, method(), " ", uri(), " HTTP/1.1\r\n");
headers(sb);
body(sb);
}
template<class Streambuf>
void
response(Streambuf& sb)
{
write(sb, "HTTP/1.");
write(sb, rand(2) ? "0" : "1");
write(sb, " ", 100 + rand(401), " ");
write(sb, token());
write(sb, "\r\n");
headers(sb);
body(sb);
write(sb, "\r\n");
}
};
using message_fuzz = message_fuzz_t<>;
} // http
} // beast
#endif

View File

@@ -21,7 +21,7 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "../../include/beast/http/impl/http_parser.h"
#include "http_parser.h"
#include <assert.h>
#include <stddef.h>
#include <ctype.h>

View File

@@ -10,7 +10,7 @@
# pragma warning (disable: 4127) // conditional expression is constant
# pragma warning (disable: 4244) // integer conversion, possible loss of data
#endif
#include "http-parser/http_parser.c"
#include "nodejs-parser/http_parser.c"
#ifdef _MSC_VER
# pragma warning (pop)
#endif

View File

@@ -0,0 +1,881 @@
//
// 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_NODEJS_PARSER_HPP
#define BEAST_HTTP_NODEJS_PARSER_HPP
#include "nodejs-parser/http_parser.h"
#include <beast/http/method.hpp>
#include <beast/http/basic_parser.hpp>
#include <beast/http/rfc2616.hpp>
#include <beast/type_check.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/system/error_code.hpp>
#include <cstdint>
#include <string>
#include <type_traits>
namespace beast {
namespace http {
namespace detail {
class nodejs_message_category
: public boost::system::error_category
{
public:
const char*
name() const noexcept override
{
return "nodejs-http-error";
}
std::string
message(int ev) const override
{
return http_errno_description(
static_cast<http_errno>(ev));
}
boost::system::error_condition
default_error_condition(int ev) const noexcept override
{
return boost::system::error_condition{ev, *this};
}
bool
equivalent(int ev,
boost::system::error_condition const& condition
) const noexcept override
{
return condition.value() == ev &&
&condition.category() == this;
}
bool
equivalent(boost::system::error_code const& error,
int ev) const noexcept override
{
return error.value() == ev &&
&error.category() == this;
}
};
template<class = void>
boost::system::error_code
make_nodejs_error(int http_errno)
{
static nodejs_message_category const mc{};
return boost::system::error_code{http_errno, mc};
}
inline
beast::http::method_t
convert_http_method(http_method m)
{
using namespace beast;
switch (m)
{
case HTTP_DELETE: return http::method_t::http_delete;
case HTTP_GET: return http::method_t::http_get;
case HTTP_HEAD: return http::method_t::http_head;
case HTTP_POST: return http::method_t::http_post;
case HTTP_PUT: return http::method_t::http_put;
// pathological
case HTTP_CONNECT: return http::method_t::http_connect;
case HTTP_OPTIONS: return http::method_t::http_options;
case HTTP_TRACE: return http::method_t::http_trace;
// webdav
case HTTP_COPY: return http::method_t::http_copy;
case HTTP_LOCK: return http::method_t::http_lock;
case HTTP_MKCOL: return http::method_t::http_mkcol;
case HTTP_MOVE: return http::method_t::http_move;
case HTTP_PROPFIND: return http::method_t::http_propfind;
case HTTP_PROPPATCH: return http::method_t::http_proppatch;
case HTTP_SEARCH: return http::method_t::http_search;
case HTTP_UNLOCK: return http::method_t::http_unlock;
case HTTP_BIND: return http::method_t::http_bind;
case HTTP_REBIND: return http::method_t::http_rebind;
case HTTP_UNBIND: return http::method_t::http_unbind;
case HTTP_ACL: return http::method_t::http_acl;
// subversion
case HTTP_REPORT: return http::method_t::http_report;
case HTTP_MKACTIVITY: return http::method_t::http_mkactivity;
case HTTP_CHECKOUT: return http::method_t::http_checkout;
case HTTP_MERGE: return http::method_t::http_merge;
// upnp
case HTTP_MSEARCH: return http::method_t::http_msearch;
case HTTP_NOTIFY: return http::method_t::http_notify;
case HTTP_SUBSCRIBE: return http::method_t::http_subscribe;
case HTTP_UNSUBSCRIBE: return http::method_t::http_unsubscribe;
// RFC-5789
case HTTP_PATCH: return http::method_t::http_patch;
case HTTP_PURGE: return http::method_t::http_purge;
// CalDav
case HTTP_MKCALENDAR: return http::method_t::http_mkcalendar;
// RFC-2068, section 19.6.1.2
case HTTP_LINK: return http::method_t::http_link;
case HTTP_UNLINK: return http::method_t::http_unlink;
};
return http::method_t::http_get;
}
} // detail
template<class Derived>
class nodejs_basic_parser
{
http_parser state_;
boost::system::error_code* ec_;
bool complete_ = false;
std::string url_;
std::string status_;
std::string field_;
std::string value_;
public:
using error_code = boost::system::error_code;
nodejs_basic_parser(nodejs_basic_parser&& other);
nodejs_basic_parser&
operator=(nodejs_basic_parser&& other);
nodejs_basic_parser(nodejs_basic_parser const& other);
nodejs_basic_parser& operator=(nodejs_basic_parser const& other);
explicit
nodejs_basic_parser(bool request) noexcept;
bool
complete() const noexcept
{
return complete_;
}
std::size_t
write(void const* data, std::size_t size)
{
error_code ec;
auto const used = write(data, size, ec);
if(ec)
throw boost::system::system_error{ec};
return used;
}
std::size_t
write(void const* data, std::size_t size,
error_code& ec);
template<class ConstBufferSequence>
std::size_t
write(ConstBufferSequence const& buffers)
{
error_code ec;
auto const used = write(buffers, ec);
if(ec)
throw boost::system::system_error{ec};
return used;
}
template<class ConstBufferSequence>
std::size_t
write(ConstBufferSequence const& buffers,
error_code& ec);
void
write_eof()
{
error_code ec;
write_eof(ec);
if(ec)
throw boost::system::system_error{ec};
}
void
write_eof(error_code& ec);
private:
Derived&
impl()
{
return *static_cast<Derived*>(this);
}
template<class C>
class has_on_start_t
{
template<class T, class R =
decltype(std::declval<T>().on_start(), std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_start =
std::integral_constant<bool, has_on_start_t<C>::value>;
void
call_on_start(std::true_type)
{
impl().on_start();
}
void
call_on_start(std::false_type)
{
}
template<class C>
class has_on_field_t
{
template<class T, class R =
decltype(std::declval<T>().on_field(
std::declval<std::string const&>(),
std::declval<std::string const&>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_field =
std::integral_constant<bool, has_on_field_t<C>::value>;
void
call_on_field(std::string const& field,
std::string const& value, std::true_type)
{
impl().on_field(field, value);
}
void
call_on_field(std::string const&, std::string const&,
std::false_type)
{
}
template<class C>
class has_on_headers_complete_t
{
template<class T, class R =
decltype(std::declval<T>().on_headers_complete(
std::declval<error_code&>()), std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_headers_complete =
std::integral_constant<bool, has_on_headers_complete_t<C>::value>;
void
call_on_headers_complete(error_code& ec, std::true_type)
{
impl().on_headers_complete(ec);
}
void
call_on_headers_complete(error_code&, std::false_type)
{
}
template<class C>
class has_on_request_t
{
template<class T, class R =
decltype(std::declval<T>().on_request(
std::declval<method_t>(), std::declval<std::string>(),
std::declval<int>(), std::declval<int>(),
std::declval<bool>(), std::declval<bool>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_request =
std::integral_constant<bool, has_on_request_t<C>::value>;
void
call_on_request(method_t method, std::string url,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
{
impl().on_request(
method, url, major, minor, keep_alive, upgrade);
}
void
call_on_request(method_t, std::string, int, int, bool, bool,
std::false_type)
{
}
template<class C>
class has_on_response_t
{
template<class T, class R =
decltype(std::declval<T>().on_response(
std::declval<int>(), std::declval<std::string>,
std::declval<int>(), std::declval<int>(),
std::declval<bool>(), std::declval<bool>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
#if 0
using type = decltype(check<C>(0));
#else
// VFALCO Trait seems broken for http::parser
using type = std::true_type;
#endif
public:
static bool const value = type::value;
};
template<class C>
using has_on_response =
std::integral_constant<bool, has_on_response_t<C>::value>;
bool
call_on_response(int status, std::string text,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
{
return impl().on_response(
status, text, major, minor, keep_alive, upgrade);
}
bool
call_on_response(int, std::string, int, int, bool, bool,
std::false_type)
{
// VFALCO Certainly incorrect
return true;
}
template<class C>
class has_on_body_t
{
template<class T, class R =
decltype(std::declval<T>().on_body(
std::declval<void const*>(), std::declval<std::size_t>(),
std::declval<error_code&>()), std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_body =
std::integral_constant<bool, has_on_body_t<C>::value>;
void
call_on_body(void const* data, std::size_t bytes,
error_code& ec, std::true_type)
{
impl().on_body(data, bytes, ec);
}
void
call_on_body(void const*, std::size_t,
error_code&, std::false_type)
{
}
template<class C>
class has_on_complete_t
{
template<class T, class R =
decltype(std::declval<T>().on_complete(), std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_complete =
std::integral_constant<bool, has_on_complete_t<C>::value>;
void
call_on_complete(std::true_type)
{
impl().on_complete();
}
void
call_on_complete(std::false_type)
{
}
void
check_header();
static int cb_message_start(http_parser*);
static int cb_url(http_parser*, char const*, std::size_t);
static int cb_status(http_parser*, char const*, std::size_t);
static int cb_header_field(http_parser*, char const*, std::size_t);
static int cb_header_value(http_parser*, char const*, std::size_t);
static int cb_headers_complete(http_parser*);
static int cb_body(http_parser*, char const*, std::size_t);
static int cb_message_complete(http_parser*);
static int cb_chunk_header(http_parser*);
static int cb_chunk_complete(http_parser*);
struct hooks_t : http_parser_settings
{
hooks_t()
{
http_parser_settings_init(this);
on_message_begin = &nodejs_basic_parser::cb_message_start;
on_url = &nodejs_basic_parser::cb_url;
on_status = &nodejs_basic_parser::cb_status;
on_header_field = &nodejs_basic_parser::cb_header_field;
on_header_value = &nodejs_basic_parser::cb_header_value;
on_headers_complete = &nodejs_basic_parser::cb_headers_complete;
on_body = &nodejs_basic_parser::cb_body;
on_message_complete = &nodejs_basic_parser::cb_message_complete;
on_chunk_header = &nodejs_basic_parser::cb_chunk_header;
on_chunk_complete = &nodejs_basic_parser::cb_chunk_complete;
}
};
static
http_parser_settings const*
hooks();
};
template<class Derived>
template<class ConstBufferSequence>
std::size_t
nodejs_basic_parser<Derived>::write(
ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::size_t bytes_used = 0;
for (auto const& buffer : buffers)
{
auto const n = write(
buffer_cast<void const*>(buffer),
buffer_size(buffer), ec);
if(ec)
return 0;
bytes_used += n;
if(complete())
break;
}
return bytes_used;
}
template<class Derived>
http_parser_settings const*
nodejs_basic_parser<Derived>::hooks()
{
static hooks_t const h;
return &h;
}
template<class Derived>
nodejs_basic_parser<Derived>::
nodejs_basic_parser(nodejs_basic_parser&& other)
{
state_ = other.state_;
state_.data = this;
complete_ = other.complete_;
url_ = std::move(other.url_);
status_ = std::move(other.status_);
field_ = std::move(other.field_);
value_ = std::move(other.value_);
}
template<class Derived>
auto
nodejs_basic_parser<Derived>::operator=(nodejs_basic_parser&& other) ->
nodejs_basic_parser&
{
state_ = other.state_;
state_.data = this;
complete_ = other.complete_;
url_ = std::move(other.url_);
status_ = std::move(other.status_);
field_ = std::move(other.field_);
value_ = std::move(other.value_);
return *this;
}
template<class Derived>
nodejs_basic_parser<Derived>::
nodejs_basic_parser(nodejs_basic_parser const& other)
{
state_ = other.state_;
state_.data = this;
complete_ = other.complete_;
url_ = other.url_;
status_ = other.status_;
field_ = other.field_;
value_ = other.value_;
}
template<class Derived>
auto
nodejs_basic_parser<Derived>::
operator=(nodejs_basic_parser const& other) ->
nodejs_basic_parser&
{
state_ = other.state_;
state_.data = this;
complete_ = other.complete_;
url_ = other.url_;
status_ = other.status_;
field_ = other.field_;
value_ = other.value_;
return *this;
}
template<class Derived>
nodejs_basic_parser<Derived>::nodejs_basic_parser(bool request) noexcept
{
state_.data = this;
http_parser_init(&state_, request
? http_parser_type::HTTP_REQUEST
: http_parser_type::HTTP_RESPONSE);
}
template<class Derived>
std::size_t
nodejs_basic_parser<Derived>::write(void const* data,
std::size_t size, error_code& ec)
{
ec_ = &ec;
auto const n = http_parser_execute(
&state_, hooks(),
static_cast<const char*>(data), size);
if(! ec)
ec = detail::make_nodejs_error(
static_cast<int>(state_.http_errno));
if(ec)
return 0;
return n;
}
template<class Derived>
void
nodejs_basic_parser<Derived>::write_eof(error_code& ec)
{
ec_ = &ec;
http_parser_execute(
&state_, hooks(), nullptr, 0);
if(! ec)
ec = detail::make_nodejs_error(
static_cast<int>(state_.http_errno));
}
template<class Derived>
void
nodejs_basic_parser<Derived>::check_header()
{
if (! value_.empty())
{
rfc2616::trim_right_in_place(value_);
call_on_field(field_, value_,
has_on_field<Derived>{});
field_.clear();
value_.clear();
}
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_message_start(http_parser* p)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.complete_ = false;
t.url_.clear();
t.status_.clear();
t.field_.clear();
t.value_.clear();
t.call_on_start(has_on_start<Derived>{});
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_url(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.url_.append(in, bytes);
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_status(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.status_.append(in, bytes);
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_header_field(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.check_header();
t.field_.append(in, bytes);
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_header_value(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.value_.append(in, bytes);
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_headers_complete(http_parser* p)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.check_header();
t.call_on_headers_complete(*t.ec_,
has_on_headers_complete<Derived>{});
if(*t.ec_)
return 1;
bool const keep_alive =
http_should_keep_alive(p) != 0;
if(p->type == http_parser_type::HTTP_REQUEST)
{
t.call_on_request(detail::convert_http_method(
http_method(p->method)), t.url_,
p->http_major, p->http_minor, keep_alive,
p->upgrade, has_on_request<Derived>{});
return 0;
}
return t.call_on_response(p->status_code, t.status_,
p->http_major, p->http_minor, keep_alive,
p->upgrade, has_on_response<Derived>{}) ? 0 : 1;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_body(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.call_on_body(in, bytes, *t.ec_, has_on_body<Derived>{});
return *t.ec_ ? 1 : 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_message_complete(http_parser* p)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.complete_ = true;
t.call_on_complete(has_on_complete<Derived>{});
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_chunk_header(http_parser*)
{
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_chunk_complete(http_parser*)
{
return 0;
}
} // http
} // beast
#include <beast/http/error.hpp>
#include <beast/http/message.hpp>
#include <boost/optional.hpp>
#include <functional>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/** A HTTP parser.
The parser may only be used once.
*/
template<bool isRequest, class Body, class Headers>
class nodejs_parser
: public nodejs_basic_parser<nodejs_parser<isRequest, Body, Headers>>
{
using message_type =
message<isRequest, Body, Headers>;
message_type m_;
typename message_type::body_type::reader r_;
bool started_ = false;
public:
nodejs_parser(nodejs_parser&&) = default;
nodejs_parser()
: http::nodejs_basic_parser<nodejs_parser>(isRequest)
, r_(m_)
{
}
/// Returns `true` if at least one byte has been processed
bool
started()
{
return started_;
}
message_type
release()
{
return std::move(m_);
}
private:
friend class http::nodejs_basic_parser<nodejs_parser>;
void
on_start()
{
started_ = true;
}
void
on_field(std::string const& field, std::string const& value)
{
m_.headers.insert(field, value);
}
void
on_headers_complete(error_code&)
{
// vFALCO TODO Decode the Content-Length and
// Transfer-Encoding, see if we can reserve the buffer.
//
// r_.reserve(content_length)
}
bool
on_request(http::method_t method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
{
m_.method = method;
m_.url = url;
m_.version = major * 10 + minor;
return true;
}
bool
on_request(http::method_t, std::string const&,
int, int, bool, bool,
std::false_type)
{
return true;
}
bool
on_request(http::method_t method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade)
{
return on_request(method, url,
major, minor, keep_alive, upgrade,
typename message_type::is_request{});
}
bool
on_response(int status, std::string const& reason,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
{
m_.status = status;
m_.reason = reason;
m_.version = major * 10 + minor;
// VFALCO TODO return expect_body_
return true;
}
bool
on_response(int, std::string const&, int, int, bool, bool,
std::false_type)
{
return true;
}
bool
on_response(int status, std::string const& reason,
int major, int minor, bool keep_alive, bool upgrade)
{
return on_response(
status, reason, major, minor, keep_alive, upgrade,
std::integral_constant<bool, ! message_type::is_request::value>{});
}
void
on_body(void const* data,
std::size_t size, error_code& ec)
{
r_.write(data, size, ec);
}
void
on_complete()
{
}
};
} // http
} // beast
#endif

View File

@@ -0,0 +1,9 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/http/parse_error.hpp>

View File

@@ -7,3 +7,63 @@
// Test that header file is self-contained.
#include <beast/http/parser.hpp>
#include <beast/http/headers.hpp>
#include <beast/http/string_body.hpp>
#include <beast/detail/unit_test/suite.hpp>
namespace beast {
namespace http {
class parser_test : public beast::detail::unit_test::suite
{
public:
void run() override
{
{
error_code ec;
parser<true, string_body,
basic_headers<std::allocator<char>>> p;
std::string const s =
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(s.data(), s.size(), ec);
expect(! ec);
expect(p.complete());
auto m = p.release();
expect(m.method == method_t::http_get);
expect(m.url == "/");
expect(m.version == 11);
expect(m.headers["User-Agent"] == "test");
expect(m.body == "*");
}
{
error_code ec;
parser<false, string_body,
basic_headers<std::allocator<char>>> p;
std::string const s =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(s.data(), s.size(), ec);
expect(! ec);
expect(p.complete());
auto m = p.release();
expect(m.status == 200);
expect(m.reason == "OK");
expect(m.version == 11);
expect(m.headers["Server"] == "test");
expect(m.body == "*");
}
}
};
BEAST_DEFINE_TESTSUITE(parser,http,beast);
} // http
} // beast

View File

@@ -0,0 +1,155 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "nodejs_parser.hpp"
#include "message_fuzz.hpp"
#include <beast/http.hpp>
#include <beast/streambuf.hpp>
#include <beast/buffers_debug.hpp>
#include <beast/detail/unit_test/suite.hpp>
#include <chrono>
#include <iostream>
#include <vector>
namespace beast {
namespace http {
class parser_bench_test : public beast::detail::unit_test::suite
{
public:
static std::size_t constexpr N = 2000;
using corpus = std::vector<streambuf>;
corpus creq_;
corpus cres_;
std::size_t size_ = 0;
parser_bench_test()
{
creq_ = build_corpus(N/2, std::true_type{});
cres_ = build_corpus(N/2, std::false_type{});
}
corpus
build_corpus(std::size_t n, std::true_type)
{
corpus v;
v.resize(N);
message_fuzz mg;
for(std::size_t i = 0; i < n; ++i)
{
mg.request(v[i]);
size_ += v[i].size();
}
return v;
}
corpus
build_corpus(std::size_t n, std::false_type)
{
corpus v;
v.resize(N);
message_fuzz mg;
for(std::size_t i = 0; i < n; ++i)
{
mg.response(v[i]);
size_ += v[i].size();
}
return v;
}
template<class Parser>
void
testParser(std::size_t repeat, corpus const& v)
{
while(repeat--)
for(auto const& sb : v)
{
Parser p;
error_code ec;
p.write(sb.data(), ec);
if(! expect(! ec, ec.message()))
log << debug::buffers_to_string(
sb.data()) << std::endl;
}
}
template<class Function>
void
timedTest(std::size_t repeat, std::string const& name, Function&& f)
{
using namespace std::chrono;
using clock_type = std::chrono::high_resolution_clock;
log << name;
for(std::size_t trial = 1; trial <= repeat; ++trial)
{
auto const t0 = clock_type::now();
f();
auto const elapsed = clock_type::now() - t0;
log <<
"Trial " << trial << ": " <<
duration_cast<milliseconds>(elapsed).count() << " ms";
}
}
template<bool isRequest>
struct null_parser : basic_parser<isRequest, null_parser<isRequest>>
{
};
void
testSpeed()
{
static std::size_t constexpr Trials = 3;
static std::size_t constexpr Repeat = 50;
log << "sizeof(request parser) == " <<
sizeof(basic_parser<true, null_parser<true>>);
log << "sizeof(response parser) == " <<
sizeof(basic_parser<false, null_parser<true>>);
testcase << "Parser speed test, " <<
((Repeat * size_ + 512) / 1024) << "KB in " <<
(Repeat * (creq_.size() + cres_.size())) << " messages";
timedTest(Trials, "nodejs_parser",
[&]
{
testParser<nodejs_parser<
true, streambuf_body, http_headers>>(
Repeat, creq_);
testParser<nodejs_parser<
false, streambuf_body, http_headers>>(
Repeat, cres_);
});
timedTest(Trials, "http::basic_parser",
[&]
{
testParser<parser<
true, streambuf_body, http_headers>>(
Repeat, creq_);
testParser<parser<
false, streambuf_body, http_headers>>(
Repeat, cres_);
});
pass();
}
void run() override
{
pass();
testSpeed();
}
};
BEAST_DEFINE_TESTSUITE(parser_bench,http,beast);
} // http
} // beast

View File

@@ -6,4 +6,4 @@
//
// Test that header file is self-contained.
#include <beast/http/type_check.hpp>
#include <beast/http/rfc7230.hpp>

View File

@@ -0,0 +1,9 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/websocket/error.hpp>

View File

@@ -0,0 +1,9 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/websocket/option.hpp>

View File

@@ -0,0 +1,9 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/websocket/rfc6455.hpp>

View File

@@ -0,0 +1,9 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/websocket/static_string.hpp>

View File

@@ -0,0 +1,9 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/websocket/teardown.hpp>

View File

@@ -0,0 +1,264 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/websocket/detail/utf8_checker.hpp>
#include <beast/streambuf.hpp>
#include <beast/detail/unit_test/suite.hpp>
#include <array>
namespace beast {
namespace websocket {
class utf8_checker_test : public beast::detail::unit_test::suite
{
public:
void
testOneByteSequence()
{
detail::utf8_checker utf8;
std::array<std::uint8_t, 256> const buf =
([]()
{
std::array<std::uint8_t, 256> values;
std::uint8_t i = 0;
for (auto& c : values)
c = i++;
return values;
})();
// Valid range 0-127
expect(utf8.write(buf.data(), 128));
expect(utf8.finish());
// Invalid range 128-193
for (auto it = std::next(buf.begin(), 128);
it != std::next(buf.begin(), 194); ++it)
expect(! utf8.write(&(*it), 1));
// Invalid range 245-255
for (auto it = std::next(buf.begin(), 245);
it != buf.end(); ++it)
expect(! utf8.write(&(*it), 1));
}
void
testTwoByteSequence()
{
detail::utf8_checker utf8;
std::uint8_t buf[2];
for(auto i = 194; i <= 223; ++i)
{
// First byte valid range 194-223
buf[0] = static_cast<std::uint8_t>(i);
for (auto j = 128; j <= 191; ++j)
{
// Second byte valid range 128-191
buf[1] = static_cast<std::uint8_t>(j);
expect(utf8.write(buf, 2));
expect(utf8.finish());
}
for (auto j = 0; j <= 127; ++j)
{
// Second byte invalid range 0-127
buf[1] = static_cast<std::uint8_t>(j);
expect(! utf8.write(buf, 2));
}
for (auto j = 192; j <= 255; ++j)
{
// Second byte invalid range 192-255
buf[1] = static_cast<std::uint8_t>(j);
expect(! utf8.write(buf, 2));
}
}
}
void
testThreeByteSequence()
{
detail::utf8_checker utf8;
std::uint8_t buf[3];
for (auto i = 224; i <= 239; ++i)
{
// First byte valid range 224-239
buf[0] = static_cast<std::uint8_t>(i);
std::int32_t const b = (i == 224 ? 160 : 128);
std::int32_t const e = (i == 237 ? 159 : 191);
for (auto j = b; j <= e; ++j)
{
// Second byte valid range 128-191 or 160-191 or 128-159
buf[1] = static_cast<std::uint8_t>(j);
for (auto k = 128; k <= 191; ++k)
{
// Third byte valid range 128-191
buf[2] = static_cast<std::uint8_t>(k);
expect(utf8.write(buf, 3));
expect(utf8.finish());
}
for (auto k = 0; k <= 127; ++k)
{
// Third byte invalid range 0-127
buf[2] = static_cast<std::uint8_t>(k);
expect(! utf8.write(buf, 3));
}
for (auto k = 192; k <= 255; ++k)
{
// Third byte invalid range 192-255
buf[2] = static_cast<std::uint8_t>(k);
expect(! utf8.write(buf, 3));
}
}
for (auto j = 0; j < b; ++j)
{
// Second byte invalid range 0-127 or 0-159
buf[1] = static_cast<std::uint8_t>(j);
expect(! utf8.write(buf, 3));
}
for (auto j = e + 1; j <= 255; ++j)
{
// Second byte invalid range 160-255 or 192-255
buf[1] = static_cast<std::uint8_t>(j);
expect(! utf8.write(buf, 3));
}
}
}
void
testFourByteSequence()
{
detail::utf8_checker utf8;
std::uint8_t buf[4];
for (auto i = 240; i <= 244; ++i)
{
// First byte valid range 240-244
buf[0] = static_cast<std::uint8_t>(i);
std::int32_t const b = (i == 240 ? 144 : 128);
std::int32_t const e = (i == 244 ? 143 : 191);
for (auto j = b; j <= e; ++j)
{
// Second byte valid range 128-191 or 144-191 or 128-143
buf[1] = static_cast<std::uint8_t>(j);
for (auto k = 128; k <= 191; ++k)
{
// Third byte valid range 128-191
buf[2] = static_cast<std::uint8_t>(k);
for (auto n = 128; n <= 191; ++n)
{
// Fourth byte valid range 128-191
buf[3] = static_cast<std::uint8_t>(n);
expect(utf8.write(buf, 4));
expect(utf8.finish());
}
for (auto n = 0; n <= 127; ++n)
{
// Fourth byte invalid range 0-127
buf[3] = static_cast<std::uint8_t>(n);
expect(! utf8.write(buf, 4));
}
for (auto n = 192; n <= 255; ++n)
{
// Fourth byte invalid range 192-255
buf[3] = static_cast<std::uint8_t>(n);
expect(! utf8.write(buf, 4));
}
}
for (auto k = 0; k <= 127; ++k)
{
// Third byte invalid range 0-127
buf[2] = static_cast<std::uint8_t>(k);
expect(! utf8.write(buf, 4));
}
for (auto k = 192; k <= 255; ++k)
{
// Third byte invalid range 192-255
buf[2] = static_cast<std::uint8_t>(k);
expect(! utf8.write(buf, 4));
}
}
for (auto j = 0; j < b; ++j)
{
// Second byte invalid range 0-127 or 0-143
buf[1] = static_cast<std::uint8_t>(j);
expect(! utf8.write(buf, 3));
}
for (auto j = e + 1; j <= 255; ++j)
{
// Second byte invalid range 144-255 or 192-255
buf[1] = static_cast<std::uint8_t>(j);
expect(! utf8.write(buf, 3));
}
}
}
void
testWithStreamBuffer()
{
using namespace boost::asio;
// Valid UTF8 encoded text
std::vector<std::vector<std::uint8_t>> const data{
{0x48,0x65,0x69,0x7A,0xC3,0xB6,0x6C,0x72,0xC3,0xBC,0x63,0x6B,
0x73,0x74,0x6F,0xC3,0x9F,0x61,0x62,0x64,0xC3,0xA4,0x6D,0x70,
0x66,0x75,0x6E,0x67},
{0xCE,0x93,0xCE,0xB1,0xCE,0xB6,0xCE,0xAD,0xCE,0xB5,0xCF,0x82,
0x20,0xCE,0xBA,0xCE,0xB1,0xE1,0xBD,0xB6,0x20,0xCE,0xBC,0xCF,
0x85,0xCF,0x81,0xCF,0x84,0xCE,0xB9,0xE1,0xBD,0xB2,0xCF,0x82,
0x20,0xCE,0xB4,0xE1,0xBD,0xB2,0xCE,0xBD,0x20,0xCE,0xB8,0xE1,
0xBD,0xB0,0x20,0xCE,0xB2,0xCF,0x81,0xE1,0xBF,0xB6,0x20,0xCF,
0x80,0xCE,0xB9,0xE1,0xBD,0xB0,0x20,0xCF,0x83,0xCF,0x84,0xE1,
0xBD,0xB8,0x20,0xCF,0x87,0xCF,0x81,0xCF,0x85,0xCF,0x83,0xCE,
0xB1,0xCF,0x86,0xE1,0xBD,0xB6,0x20,0xCE,0xBE,0xCE,0xAD,0xCF,
0x86,0xCF,0x89,0xCF,0x84,0xCE,0xBF},
{0xC3,0x81,0x72,0x76,0xC3,0xAD,0x7A,0x74,0xC5,0xB1,0x72,0xC5,
0x91,0x20,0x74,0xC3,0xBC,0x6B,0xC3,0xB6,0x72,0x66,0xC3,0xBA,
0x72,0xC3,0xB3,0x67,0xC3,0xA9,0x70}
};
detail::utf8_checker utf8;
for(auto const& s : data)
{
beast::streambuf sb(
s.size() / 4); // Force split across blocks
sb.commit(buffer_copy(
sb.prepare(s.size()),
const_buffer(s.data(), s.size())));
expect(utf8.write(sb.data()));
expect(utf8.finish());
}
}
void run() override
{
testOneByteSequence();
testTwoByteSequence();
testThreeByteSequence();
testFourByteSequence();
testWithStreamBuffer();
}
};
BEAST_DEFINE_TESTSUITE(utf8_checker,websocket,beast);
} // websocket
} // beast

View File

@@ -0,0 +1,35 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/write_streambuf.hpp>
#include <beast/streambuf.hpp>
#include <beast/detail/unit_test/suite.hpp>
namespace beast {
class write_streambuf_test : public beast::detail::unit_test::suite
{
public:
void run() override
{
streambuf sb;
std::string s;
write(sb, boost::asio::const_buffer{"", 0});
write(sb, boost::asio::mutable_buffer{nullptr, 0});
write(sb, boost::asio::null_buffers{});
write(sb, boost::asio::const_buffers_1{"", 0});
write(sb, boost::asio::mutable_buffers_1{nullptr, 0});
write(sb, s);
write(sb, 23);
pass();
}
};
BEAST_DEFINE_TESTSUITE(write_streambuf,core,beast);
} // beast