mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-04 11:15:56 +00:00
Merge subtree Beast 1.0.0-b2:
Merge commit '6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99'
This commit is contained in:
41
src/beast/CMakeLists.txt
Normal file
41
src/beast/CMakeLists.txt
Normal 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()
|
||||
@@ -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>.
|
||||
:
|
||||
|
||||
@@ -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->()
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
[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]
</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>] 

</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>]
</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']) < 0">
|
||||
<xsl:text>[heading Private Member Functions]
</xsl:text>
|
||||
<xsl:text>[table
 [[Name][Description]]
</xsl:text>
|
||||
<xsl:for-each select="sectiondef[@kind='private-func']/memberdef" mode="class-table">
|
||||
@@ -988,7 +985,7 @@
|
||||
</xsl:for-each>
|
||||
<xsl:text>]
</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']) < 0">
|
||||
<xsl:text>[heading Private Data Members]
</xsl:text>
|
||||
<xsl:text>[table
 [[Name][Description]]
</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>```
</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="' &&' = substring(type, string-length(type) - 2)">
|
||||
<xsl:value-of select="substring(type, 1, string-length(type) - 3)"/>
|
||||
<xsl:text>&&</xsl:text>
|
||||
</xsl:when>
|
||||
<xsl:when test="' &' = substring(type, string-length(type) - 1)">
|
||||
<xsl:value-of select="substring(type, 1, string-length(type) - 2)"/>
|
||||
<xsl:text>&</xsl:text>
|
||||
|
||||
30
src/beast/examples/CMakeLists.txt
Normal file
30
src/beast/examples/CMakeLists.txt
Normal 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
|
||||
)
|
||||
@@ -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
|
||||
;
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ public:
|
||||
|
||||
Example:
|
||||
@code
|
||||
template<class AsyncReadStream, ReadHandler>
|
||||
template<class AsyncReadStream, class ReadHandler>
|
||||
void
|
||||
do_cancel(AsyncReadStream& stream, ReadHandler&& handler)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
146
src/beast/include/beast/detail/write_streambuf.hpp
Normal file
146
src/beast/include/beast/detail/write_streambuf.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
203
src/beast/include/beast/http/detail/basic_parser.hpp
Normal file
203
src/beast/include/beast/http/detail/basic_parser.hpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
157
src/beast/include/beast/http/parse_error.hpp
Normal file
157
src/beast/include/beast/http/parse_error.hpp
Normal 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
|
||||
@@ -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&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
23
src/beast/include/beast/http/rfc7230.hpp
Normal file
23
src/beast/include/beast/http/rfc7230.hpp
Normal 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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
520
src/beast/include/beast/impl/buffers_adapter.ipp
Normal file
520
src/beast/include/beast/impl/buffers_adapter.ipp
Normal 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
|
||||
211
src/beast/include/beast/impl/consuming_buffers.ipp
Normal file
211
src/beast/include/beast/impl/consuming_buffers.ipp
Normal 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
|
||||
215
src/beast/include/beast/impl/prepare_buffers.ipp
Normal file
215
src/beast/include/beast/impl/prepare_buffers.ipp
Normal 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
|
||||
304
src/beast/include/beast/impl/static_streambuf.ipp
Normal file
304
src/beast/include/beast/impl/static_streambuf.ipp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
21
src/beast/include/beast/version.hpp
Normal file
21
src/beast/include/beast/version.hpp
Normal 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
|
||||
60
src/beast/include/beast/websocket/detail/endian.hpp
Normal file
60
src/beast/include/beast/websocket/detail/endian.hpp
Normal 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
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
63
src/beast/include/beast/write_streambuf.hpp
Normal file
63
src/beast/include/beast/write_streambuf.hpp
Normal 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
|
||||
82
src/beast/test/CMakeLists.txt
Normal file
82
src/beast/test/CMakeLists.txt
Normal 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}
|
||||
)
|
||||
@@ -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
|
||||
;
|
||||
|
||||
@@ -295,7 +295,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(basic_streambuf,asio,beast);
|
||||
BEAST_DEFINE_TESTSUITE(basic_streambuf,core,beast);
|
||||
|
||||
} // test
|
||||
} // beast
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(bind_handler,asio,beast);
|
||||
BEAST_DEFINE_TESTSUITE(bind_handler,core,beast);
|
||||
|
||||
} // test
|
||||
} // beast
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(buffer_cat,asio,beast);
|
||||
BEAST_DEFINE_TESTSUITE(buffer_cat,core,beast);
|
||||
|
||||
} // test
|
||||
} // beast
|
||||
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(buffers_adapter,asio,beast);
|
||||
BEAST_DEFINE_TESTSUITE(buffers_adapter,core,beast);
|
||||
|
||||
} // test
|
||||
} // beast
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(base64,crypto,beast);
|
||||
BEAST_DEFINE_TESTSUITE(base64,detail,beast);
|
||||
|
||||
} // test
|
||||
} // beast
|
||||
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(empty_base_optimization,utility,beast);
|
||||
BEAST_DEFINE_TESTSUITE(empty_base_optimization,detail,beast);
|
||||
|
||||
} // test
|
||||
} // beast
|
||||
|
||||
@@ -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
|
||||
|
||||
562
src/beast/test/http/message_fuzz.hpp
Normal file
562
src/beast/test/http/message_fuzz.hpp
Normal 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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
881
src/beast/test/http/nodejs_parser.hpp
Normal file
881
src/beast/test/http/nodejs_parser.hpp
Normal 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
|
||||
9
src/beast/test/http/parse_error.cpp
Normal file
9
src/beast/test/http/parse_error.cpp
Normal 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>
|
||||
@@ -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
|
||||
|
||||
155
src/beast/test/http/parser_bench.cpp
Normal file
155
src/beast/test/http/parser_bench.cpp
Normal 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
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/type_check.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
9
src/beast/test/websocket/error.cpp
Normal file
9
src/beast/test/websocket/error.cpp
Normal 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>
|
||||
9
src/beast/test/websocket/option.cpp
Normal file
9
src/beast/test/websocket/option.cpp
Normal 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>
|
||||
9
src/beast/test/websocket/rfc6455.cpp
Normal file
9
src/beast/test/websocket/rfc6455.cpp
Normal 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>
|
||||
9
src/beast/test/websocket/static_string.cpp
Normal file
9
src/beast/test/websocket/static_string.cpp
Normal 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>
|
||||
9
src/beast/test/websocket/teardown.cpp
Normal file
9
src/beast/test/websocket/teardown.cpp
Normal 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>
|
||||
264
src/beast/test/websocket/utf8_checker.cpp
Normal file
264
src/beast/test/websocket/utf8_checker.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// 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
|
||||
35
src/beast/test/write_streambuf.cpp
Normal file
35
src/beast/test/write_streambuf.cpp
Normal 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
|
||||
Reference in New Issue
Block a user