mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-27 06:25:51 +00:00
New header-only basic_parser:
The basic_parser is rewritten to be header-only. The nodejs parser is removed from the include subtree and placed into the test directory. Other changes: * Parser specific error codes in parse_error.hpp * Add parser-bench performance testing, nodejs vs beast * New random message generator for fuzz tests * Test for header-only parser using random message generator * Augmented some existing message tests to check more cases
This commit is contained in:
5
Jamroot
5
Jamroot
@@ -60,8 +60,11 @@ project beast
|
|||||||
<threading>multi
|
<threading>multi
|
||||||
<link>static
|
<link>static
|
||||||
<runtime-link>shared
|
<runtime-link>shared
|
||||||
|
<debug-symbols>on
|
||||||
<toolset>gcc:<cxxflags>-std=c++11
|
<toolset>gcc:<cxxflags>-std=c++11
|
||||||
<toolset>clang:<cxxflags>-std=c++11
|
<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>_XOPEN_SOURCE=600
|
||||||
<os>LINUX:<define>_GNU_SOURCE=1
|
<os>LINUX:<define>_GNU_SOURCE=1
|
||||||
<os>SOLARIS:<define>_XOPEN_SOURCE=500
|
<os>SOLARIS:<define>_XOPEN_SOURCE=500
|
||||||
@@ -78,8 +81,6 @@ project beast
|
|||||||
<os>HPUX:<library>ipv6
|
<os>HPUX:<library>ipv6
|
||||||
<os>QNXNTO:<library>socket
|
<os>QNXNTO:<library>socket
|
||||||
<os>HAIKU:<library>network
|
<os>HAIKU:<library>network
|
||||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
|
|
||||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
|
|
||||||
: usage-requirements
|
: usage-requirements
|
||||||
<include>.
|
<include>.
|
||||||
:
|
:
|
||||||
|
|||||||
7
TODO.txt
7
TODO.txt
@@ -22,3 +22,10 @@
|
|||||||
* Figure out why namespace rfc2616 is included in docs
|
* Figure out why namespace rfc2616 is included in docs
|
||||||
(currently disabled via GENERATING_DOCS macro)
|
(currently disabled via GENERATING_DOCS macro)
|
||||||
* Include Example program listings in the docs
|
* 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
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ INPUT = \
|
|||||||
../include/beast/streambuf_readstream.hpp \
|
../include/beast/streambuf_readstream.hpp \
|
||||||
../include/beast/type_check.hpp \
|
../include/beast/type_check.hpp \
|
||||||
../include/beast/websocket.hpp \
|
../include/beast/websocket.hpp \
|
||||||
|
../include/beast/write_streambuf.hpp \
|
||||||
../include/beast/http/basic_headers.hpp \
|
../include/beast/http/basic_headers.hpp \
|
||||||
../include/beast/http/basic_parser.hpp \
|
../include/beast/http/basic_parser.hpp \
|
||||||
../include/beast/http/chunk_encode.hpp \
|
../include/beast/http/chunk_encode.hpp \
|
||||||
@@ -128,6 +129,7 @@ INPUT = \
|
|||||||
../include/beast/http/headers.hpp \
|
../include/beast/http/headers.hpp \
|
||||||
../include/beast/http/message.hpp \
|
../include/beast/http/message.hpp \
|
||||||
../include/beast/http/method.hpp \
|
../include/beast/http/method.hpp \
|
||||||
|
../include/beast/http/parse_error.hpp \
|
||||||
../include/beast/http/parser.hpp \
|
../include/beast/http/parser.hpp \
|
||||||
../include/beast/http/read.hpp \
|
../include/beast/http/read.hpp \
|
||||||
../include/beast/http/resume_context.hpp \
|
../include/beast/http/resume_context.hpp \
|
||||||
|
|||||||
@@ -80,9 +80,9 @@ Beast requires:
|
|||||||
|
|
||||||
[note Tested compilers: msvc-14+, gcc 5+, clang 3.6+]
|
[note Tested compilers: msvc-14+, gcc 5+, clang 3.6+]
|
||||||
|
|
||||||
Most of the library is header-only; however, the HTTP parser used is written
|
The library is [*header-only]. It is not necessary to add any .cpp files,
|
||||||
in C. To link an application that uses Beast, it is necessary to add a single
|
or to edit your existing build script or project file except to provide
|
||||||
.cpp file from beast into your project's build script.
|
that the include/ directory for beast is searched for include files.
|
||||||
|
|
||||||
[endsect]
|
[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
|
and run. Source code and build scripts for these programs may be found
|
||||||
in the examples directory.
|
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:
|
Use HTTP to request the root page from a website and print the response:
|
||||||
```
|
```
|
||||||
#include <beast/http.hpp>
|
#include <beast/http.hpp>
|
||||||
|
|||||||
@@ -33,9 +33,16 @@ 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
|
interface of Boost.Asio, it is logical for Beast.HTTP to use Boost.Asio as its
|
||||||
network transport.
|
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
|
[note The documentation which follows assumes familiarity with
|
||||||
both Boost.Asio and the HTTP protocol specification described in
|
both Boost.Asio and the HTTP protocol specification described in
|
||||||
[@https://tools.ietf.org/html/rfc2616 rfc2616] ]
|
[@https://tools.ietf.org/html/rfc7230 rfc7230] ]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
|
|||||||
@@ -8,28 +8,23 @@
|
|||||||
import os ;
|
import os ;
|
||||||
|
|
||||||
exe http_crawl :
|
exe http_crawl :
|
||||||
../src/beast_http_nodejs_parser.cpp
|
|
||||||
http_crawl.cpp
|
http_crawl.cpp
|
||||||
urls_large_data.cpp
|
urls_large_data.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
exe http_server :
|
exe http_server :
|
||||||
../src/beast_http_nodejs_parser.cpp
|
|
||||||
http_server.cpp
|
http_server.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
exe wsproto_echo :
|
exe websocket_echo :
|
||||||
../src/beast_http_nodejs_parser.cpp
|
websocket_echo.cpp
|
||||||
wsproto_echo.cpp
|
|
||||||
;
|
;
|
||||||
|
|
||||||
exe http_example :
|
exe http_example :
|
||||||
../src/beast_http_nodejs_parser.cpp
|
|
||||||
http_example.cpp
|
http_example.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
exe websocket_example :
|
exe websocket_example :
|
||||||
../src/beast_http_nodejs_parser.cpp
|
|
||||||
websocket_example.cpp
|
websocket_example.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#ifndef BEAST_WSPROTO_ASYNC_ECHO_PEER_H_INCLUDED
|
#ifndef BEAST_WEBSOCKET_ASYNC_ECHO_PEER_H_INCLUDED
|
||||||
#define BEAST_WSPROTO_ASYNC_ECHO_PEER_H_INCLUDED
|
#define BEAST_WEBSOCKET_ASYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
|
||||||
#include <beast/placeholders.hpp>
|
#include <beast/placeholders.hpp>
|
||||||
#include <beast/streambuf.hpp>
|
#include <beast/streambuf.hpp>
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include "wsproto_async_echo_peer.h"
|
#include "websocket_async_echo_peer.h"
|
||||||
#include "wsproto_sync_echo_peer.h"
|
#include "websocket_sync_echo_peer.h"
|
||||||
#include "sig_wait.h"
|
#include "sig_wait.h"
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <beast/http/error.hpp>
|
#include <beast/http/error.hpp>
|
||||||
#include <beast/http/headers.hpp>
|
#include <beast/http/headers.hpp>
|
||||||
#include <beast/http/message.hpp>
|
#include <beast/http/message.hpp>
|
||||||
|
#include <beast/http/parse_error.hpp>
|
||||||
#include <beast/http/parser.hpp>
|
#include <beast/http/parser.hpp>
|
||||||
#include <beast/http/read.hpp>
|
#include <beast/http/read.hpp>
|
||||||
#include <beast/http/reason.hpp>
|
#include <beast/http/reason.hpp>
|
||||||
|
|||||||
@@ -8,150 +8,355 @@
|
|||||||
#ifndef BEAST_HTTP_BASIC_PARSER_HPP
|
#ifndef BEAST_HTTP_BASIC_PARSER_HPP
|
||||||
#define BEAST_HTTP_BASIC_PARSER_HPP
|
#define BEAST_HTTP_BASIC_PARSER_HPP
|
||||||
|
|
||||||
#include <beast/http/method.hpp>
|
#include <beast/http/message.hpp>
|
||||||
#include <beast/http/impl/http_parser.h>
|
#include <beast/http/parse_error.hpp>
|
||||||
|
#include <beast/http/rfc7230.hpp>
|
||||||
|
#include <beast/http/detail/basic_parser.hpp>
|
||||||
#include <beast/type_check.hpp>
|
#include <beast/type_check.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <beast/detail/ci_char_traits.hpp>
|
||||||
#include <boost/system/error_code.hpp>
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
#include <climits>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
|
namespace parse_flag {
|
||||||
|
enum values
|
||||||
|
{
|
||||||
|
chunked = 1 << 0,
|
||||||
|
connection_keep_alive = 1 << 1,
|
||||||
|
connection_close = 1 << 2,
|
||||||
|
connection_upgrade = 1 << 3,
|
||||||
|
trailing = 1 << 4,
|
||||||
|
upgrade = 1 << 5,
|
||||||
|
skipbody = 1 << 6,
|
||||||
|
contentlength = 1 << 7
|
||||||
|
};
|
||||||
|
} // parse_flag
|
||||||
|
|
||||||
/** Parser for producing HTTP requests and responses.
|
/** Parser for producing HTTP requests and responses.
|
||||||
|
|
||||||
Callbacks:
|
During parsing, callbacks will be made to the derived class
|
||||||
|
if those members are present (detected through SFINAE). The
|
||||||
|
signatures which can be present in the derived class are:<br>
|
||||||
|
|
||||||
If a is an object of type Derived, and the call expression is
|
@li `void on_method(boost::string_ref const&, error_code& ec)`
|
||||||
valid then the stated effects will take place:
|
|
||||||
|
|
||||||
a.on_start()
|
Called for each piece of the Request-Method
|
||||||
|
|
||||||
Called once when a new message begins.
|
@li `void on_uri(boost::string_ref const&, error_code& ec)`
|
||||||
|
|
||||||
a.on_field(std::string field, std::string value)
|
Called for each piece of the Request-URI
|
||||||
|
|
||||||
Called for each field
|
@li `void on_reason(boost::string_ref const&, error_code& ec)`
|
||||||
|
|
||||||
a.on_headers_complete(error_code&)
|
Called for each piece of the reason-phrase
|
||||||
|
|
||||||
Called when all the header fields have been received, but
|
@li `void on_request(error_code& ec)`
|
||||||
before any part of the body if any is received.
|
|
||||||
|
|
||||||
a.on_request(method_t method, std::string url,
|
Called after the entire Request-Line has been parsed successfully.
|
||||||
int major, int minor, bool keep_alive, bool upgrade)
|
|
||||||
|
|
||||||
Called for requests when all the headers have been received.
|
@li `void on_response(error_code& ec)`
|
||||||
This will precede any content body.
|
|
||||||
|
|
||||||
When keep_alive is false:
|
Called after the entire Response-Line has been parsed successfully.
|
||||||
* Server roles respond with a "Connection: close" header.
|
|
||||||
* Client roles close the connection.
|
|
||||||
|
|
||||||
a.on_response(int status, std::string text,
|
@li `void on_field(boost::string_ref const&, error_code& ec)`
|
||||||
int major, int minor, bool keep_alive,
|
|
||||||
bool upgrade)
|
|
||||||
|
|
||||||
Called for responses when all the headers have been received.
|
Called for each piece of the current header field.
|
||||||
This will precede any content body.
|
|
||||||
|
|
||||||
When keep_alive is `false`:
|
@li `void on_value(boost::string_ref const&, error_code& ec)`
|
||||||
* Client roles close the connection.
|
|
||||||
* Server roles respond with a "Connection: close" header.
|
|
||||||
|
|
||||||
This function should return `true` if upgrade is false and
|
Called for each piece of the current header value.
|
||||||
a content body is expected. When upgrade is true, no
|
|
||||||
content-body is expected, and the return value is ignored.
|
|
||||||
|
|
||||||
a.on_body(void const* data, std::size_t bytes, error_code&)
|
@li `int on_headers(error_code& ec)`
|
||||||
|
|
||||||
Called zero or more times for the content body. Any transfer
|
Called when all the headers have been parsed successfully.
|
||||||
encoding is already decoded in the memory pointed to by data.
|
|
||||||
|
|
||||||
a.on_complete()
|
@li `void on_body(boost::string_ref const&, error_code& ec)`
|
||||||
|
|
||||||
Called when parsing completes successfully.
|
Called for each piece of the body. If the headers indicated
|
||||||
|
chunked encoding, the chunk encoding is removed from the
|
||||||
|
buffer before being passed to the callback.
|
||||||
|
|
||||||
|
@li `void on_complete(error_code& ec)`
|
||||||
|
|
||||||
|
Called when the entire message has been parsed successfully.
|
||||||
|
At this point, basic_parser::complete() returns `true`, and
|
||||||
|
the parser is ready to parse another message if keep_alive()
|
||||||
|
would return `true`.
|
||||||
|
|
||||||
|
The return value of `on_headers` is special, it controls whether
|
||||||
|
or not the parser should expect a body. These are the return values:
|
||||||
|
|
||||||
|
@li *0* The parser should expect a body
|
||||||
|
|
||||||
|
@li *1* The parser should skip the body. For example, this is
|
||||||
|
used when sending a response to a HEAD request.
|
||||||
|
|
||||||
|
@li *2* The parser should skip ths body, this is an
|
||||||
|
upgrade to a different protocol.
|
||||||
|
|
||||||
The parser uses traits to determine if the callback is possible.
|
The parser uses traits to determine if the callback is possible.
|
||||||
If the Derived type omits the callbacks, they are simply skipped
|
If the Derived type omits one or more callbacks, they are simply
|
||||||
with no compilation error.
|
skipped with no compilation error. The default behavior of on_body
|
||||||
|
when the derived class does not provide the member, is to specify that
|
||||||
|
the body should not be skipped.
|
||||||
|
|
||||||
|
If a callback sets an error, parsing stops at the current octet
|
||||||
|
and the error is returned to the caller.
|
||||||
*/
|
*/
|
||||||
/*
|
template<bool isRequest, class Derived>
|
||||||
VFALCO TODO is_call_possible, enable_if_t on Derived calls
|
|
||||||
use boost::string_ref instead of std::string
|
|
||||||
*/
|
|
||||||
template<class Derived>
|
|
||||||
class basic_parser
|
class basic_parser
|
||||||
{
|
{
|
||||||
http_parser state_;
|
private:
|
||||||
boost::system::error_code* ec_;
|
using self = basic_parser;
|
||||||
bool complete_ = false;
|
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
||||||
std::string url_;
|
|
||||||
std::string status_;
|
static std::uint64_t constexpr no_content_length =
|
||||||
std::string field_;
|
std::numeric_limits<std::uint64_t>::max();
|
||||||
std::string value_;
|
|
||||||
|
enum state : std::uint8_t
|
||||||
|
{
|
||||||
|
s_closed = 1,
|
||||||
|
|
||||||
|
s_req_start,
|
||||||
|
s_req_method_start,
|
||||||
|
s_req_method,
|
||||||
|
s_req_space_before_url,
|
||||||
|
s_req_url_start,
|
||||||
|
s_req_url,
|
||||||
|
s_req_http_start,
|
||||||
|
s_req_http_H,
|
||||||
|
s_req_http_HT,
|
||||||
|
s_req_http_HTT,
|
||||||
|
s_req_http_HTTP,
|
||||||
|
s_req_major_start,
|
||||||
|
s_req_major,
|
||||||
|
s_req_minor_start,
|
||||||
|
s_req_minor,
|
||||||
|
s_req_line_end,
|
||||||
|
|
||||||
|
s_res_start,
|
||||||
|
s_res_H,
|
||||||
|
s_res_HT,
|
||||||
|
s_res_HTT,
|
||||||
|
s_res_HTTP,
|
||||||
|
s_res_major_start,
|
||||||
|
s_res_major,
|
||||||
|
s_res_minor_start,
|
||||||
|
s_res_minor,
|
||||||
|
s_res_status_code_start,
|
||||||
|
s_res_status_code,
|
||||||
|
s_res_status_start,
|
||||||
|
s_res_status,
|
||||||
|
s_res_line_almost_done,
|
||||||
|
s_res_line_done,
|
||||||
|
|
||||||
|
s_header_field_start,
|
||||||
|
s_header_field,
|
||||||
|
s_header_value_start,
|
||||||
|
s_header_value_discard_lWs0,
|
||||||
|
s_header_value_discard_ws0,
|
||||||
|
s_header_value_almost_done0,
|
||||||
|
s_header_value_text_start,
|
||||||
|
s_header_value_discard_lWs,
|
||||||
|
s_header_value_discard_ws,
|
||||||
|
s_header_value_text,
|
||||||
|
s_header_value_almost_done,
|
||||||
|
|
||||||
|
s_headers_almost_done,
|
||||||
|
s_headers_done,
|
||||||
|
|
||||||
|
s_chunk_size_start,
|
||||||
|
s_chunk_size,
|
||||||
|
s_chunk_parameters,
|
||||||
|
s_chunk_size_almost_done,
|
||||||
|
|
||||||
|
// states below do not count towards
|
||||||
|
// the limit on the size of the message
|
||||||
|
|
||||||
|
s_body_identity0,
|
||||||
|
s_body_identity,
|
||||||
|
s_body_identity_eof0,
|
||||||
|
s_body_identity_eof,
|
||||||
|
|
||||||
|
s_chunk_data_start,
|
||||||
|
s_chunk_data,
|
||||||
|
s_chunk_data_almost_done,
|
||||||
|
s_chunk_data_done,
|
||||||
|
|
||||||
|
s_complete,
|
||||||
|
s_restart
|
||||||
|
};
|
||||||
|
|
||||||
|
enum field_state : std::uint8_t
|
||||||
|
{
|
||||||
|
h_general = 0,
|
||||||
|
h_C,
|
||||||
|
h_CO,
|
||||||
|
h_CON,
|
||||||
|
|
||||||
|
h_matching_connection,
|
||||||
|
h_matching_proxy_connection,
|
||||||
|
h_matching_content_length,
|
||||||
|
h_matching_transfer_encoding,
|
||||||
|
h_matching_upgrade,
|
||||||
|
|
||||||
|
h_connection,
|
||||||
|
h_content_length,
|
||||||
|
h_transfer_encoding,
|
||||||
|
h_upgrade,
|
||||||
|
|
||||||
|
h_matching_transfer_encoding_chunked,
|
||||||
|
h_matching_connection_token_start,
|
||||||
|
h_matching_connection_keep_alive,
|
||||||
|
h_matching_connection_close,
|
||||||
|
h_matching_connection_upgrade,
|
||||||
|
h_matching_connection_token,
|
||||||
|
|
||||||
|
h_transfer_encoding_chunked,
|
||||||
|
h_connection_keep_alive,
|
||||||
|
h_connection_close,
|
||||||
|
h_connection_upgrade,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::uint64_t content_length_;
|
||||||
|
std::uint64_t nread_;
|
||||||
|
pmf_t cb_;
|
||||||
|
state s_ : 8;
|
||||||
|
unsigned flags_ : 8;
|
||||||
|
unsigned fs_ : 8;
|
||||||
|
unsigned pos_ : 8; // position in field state
|
||||||
|
unsigned http_major_ : 16;
|
||||||
|
unsigned http_minor_ : 16;
|
||||||
|
unsigned status_code_ : 16;
|
||||||
|
bool upgrade_ : 1; // true if parser exited for upgrade
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using error_code = boost::system::error_code;
|
/// Copy constructor.
|
||||||
|
basic_parser(basic_parser const&) = default;
|
||||||
|
|
||||||
/** Move constructor.
|
/// Copy assignment.
|
||||||
|
basic_parser& operator=(basic_parser const&) = default;
|
||||||
|
|
||||||
The state of the moved-from object is undefined,
|
/// Constructor
|
||||||
but safe to destroy.
|
basic_parser()
|
||||||
*/
|
{
|
||||||
basic_parser(basic_parser&& other);
|
init(std::integral_constant<bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
/** Move assignment.
|
/// Returns internal flags associated with the parser.
|
||||||
|
unsigned
|
||||||
|
flags() const
|
||||||
|
{
|
||||||
|
return flags_;
|
||||||
|
}
|
||||||
|
|
||||||
The state of the moved-from object is undefined,
|
/** Returns `true` if the message end is indicated by eof.
|
||||||
but safe to destroy.
|
|
||||||
*/
|
|
||||||
basic_parser&
|
|
||||||
operator=(basic_parser&& other);
|
|
||||||
|
|
||||||
/** Copy constructor. */
|
This function returns true if the semantics of the message require
|
||||||
basic_parser(basic_parser const& other);
|
that the end of the message is signaled by an end of file. For
|
||||||
|
example, if the message is a HTTP/1.0 message and the Content-Length
|
||||||
|
is unspecified, the end of the message is indicated by an end of file.
|
||||||
|
|
||||||
/** Copy assignment. */
|
@return `true` if write_eof must be used to indicate the message end.
|
||||||
basic_parser& operator=(basic_parser const& other);
|
|
||||||
|
|
||||||
/** Construct the parser.
|
|
||||||
|
|
||||||
@param request If `true`, the parser is setup for a request.
|
|
||||||
*/
|
|
||||||
explicit
|
|
||||||
basic_parser(bool request) noexcept;
|
|
||||||
|
|
||||||
/** Returns `true` if parsing is complete.
|
|
||||||
|
|
||||||
This is only defined when no errors have been returned.
|
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
complete() const noexcept
|
needs_eof() const
|
||||||
{
|
{
|
||||||
return complete_;
|
return needs_eof(
|
||||||
|
std::integral_constant<bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the major HTTP version number.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
* Returns 1 for HTTP/1.1
|
||||||
|
* Returns 1 for HTTP/1.0
|
||||||
|
|
||||||
|
@return The HTTP major version number.
|
||||||
|
*/
|
||||||
|
unsigned
|
||||||
|
http_major() const
|
||||||
|
{
|
||||||
|
return http_major_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the minor HTTP version number.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
* Returns 1 for HTTP/1.1
|
||||||
|
* Returns 0 for HTTP/1.0
|
||||||
|
|
||||||
|
@return The HTTP minor version number.
|
||||||
|
*/
|
||||||
|
unsigned
|
||||||
|
http_minor() const
|
||||||
|
{
|
||||||
|
return http_minor_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if the message is an upgrade message.
|
||||||
|
|
||||||
|
A value of `true` indicates that the parser has successfully
|
||||||
|
completed parsing a HTTP upgrade message.
|
||||||
|
|
||||||
|
@return `true` if the message is an upgrade message.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
upgrade() const
|
||||||
|
{
|
||||||
|
return upgrade_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the numeric HTTP Status-Code of a response.
|
||||||
|
|
||||||
|
@return The Status-Code.
|
||||||
|
*/
|
||||||
|
unsigned
|
||||||
|
status_code() const
|
||||||
|
{
|
||||||
|
return status_code_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if the connection should be kept open.
|
||||||
|
|
||||||
|
@note This function is only valid to call when the parser
|
||||||
|
is complete.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
keep_alive() const;
|
||||||
|
|
||||||
|
/** Returns `true` if the parse has completed succesfully.
|
||||||
|
|
||||||
|
When the parse has completed successfully, and the semantics
|
||||||
|
of the parsed message indicate that the connection is still
|
||||||
|
active, a subsequent call to `write` will begin parsing a
|
||||||
|
new message.
|
||||||
|
|
||||||
|
@return `true` If the parsing has completed successfully.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
complete() const
|
||||||
|
{
|
||||||
|
return s_ == s_restart;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Write data to the parser.
|
/** Write data to the parser.
|
||||||
|
|
||||||
@param data A pointer to a buffer representing the input sequence.
|
@param buffers An object meeting the requirements of
|
||||||
@param size The number of bytes in the buffer pointed to by data.
|
ConstBufferSequence that represents the input sequence.
|
||||||
|
|
||||||
@throws boost::system::system_error Thrown on failure.
|
@param ec Set to the error, if any error occurred.
|
||||||
|
|
||||||
@return The number of bytes consumed in the input sequence.
|
@return The number of bytes consumed in the input sequence.
|
||||||
*/
|
*/
|
||||||
|
template<class ConstBufferSequence>
|
||||||
std::size_t
|
std::size_t
|
||||||
write(void const* data, std::size_t size)
|
write(ConstBufferSequence const& buffers, error_code& ec);
|
||||||
{
|
|
||||||
error_code ec;
|
|
||||||
auto const used = write(data, size, ec);
|
|
||||||
if(ec)
|
|
||||||
throw boost::system::system_error{ec};
|
|
||||||
return used;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Write data to the parser.
|
/** Write data to the parser.
|
||||||
|
|
||||||
@@ -162,41 +367,7 @@ public:
|
|||||||
@return The number of bytes consumed in the input sequence.
|
@return The number of bytes consumed in the input sequence.
|
||||||
*/
|
*/
|
||||||
std::size_t
|
std::size_t
|
||||||
write(void const* data, std::size_t size,
|
write(void const* data, std::size_t size, error_code& ec);
|
||||||
error_code& ec);
|
|
||||||
|
|
||||||
/** Write data to the parser.
|
|
||||||
|
|
||||||
@param buffers An object meeting the requirements of
|
|
||||||
ConstBufferSequence that represents the input sequence.
|
|
||||||
|
|
||||||
@throws boost::system::system_error Thrown on failure.
|
|
||||||
|
|
||||||
@return The number of bytes consumed in the input sequence.
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Write data to the parser.
|
|
||||||
|
|
||||||
@param buffers An object meeting the requirements of
|
|
||||||
ConstBufferSequence that represents the input sequence.
|
|
||||||
@param ec Set to the error, if any error occurred.
|
|
||||||
|
|
||||||
@return The number of bytes consumed in the input sequence.
|
|
||||||
*/
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
write(ConstBufferSequence const& buffers,
|
|
||||||
error_code& ec);
|
|
||||||
|
|
||||||
/** Called to indicate the end of file.
|
/** Called to indicate the end of file.
|
||||||
|
|
||||||
@@ -210,26 +381,6 @@ public:
|
|||||||
@throws boost::system::system_error Thrown on failure.
|
@throws boost::system::system_error Thrown on failure.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
write_eof()
|
|
||||||
{
|
|
||||||
error_code ec;
|
|
||||||
write_eof(ec);
|
|
||||||
if(ec)
|
|
||||||
throw boost::system::system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Called to indicate the end of file.
|
|
||||||
|
|
||||||
HTTP needs to know where the end of the stream is. For example,
|
|
||||||
sometimes servers send responses without Content-Length and
|
|
||||||
expect the client to consume input (for the body) until EOF.
|
|
||||||
Callbacks and errors will still be processed as usual.
|
|
||||||
|
|
||||||
@note This is typically called when a socket read returns eof.
|
|
||||||
|
|
||||||
@param ec Set to the error, if any error occurred.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
write_eof(error_code& ec);
|
write_eof(error_code& ec);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -239,299 +390,182 @@ private:
|
|||||||
return *static_cast<Derived*>(this);
|
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
|
void
|
||||||
call_on_start(std::true_type)
|
init(std::true_type)
|
||||||
{
|
{
|
||||||
impl().on_start();
|
s_ = s_req_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
call_on_start(std::false_type)
|
init(std::false_type)
|
||||||
{
|
{
|
||||||
}
|
s_ = s_res_start;
|
||||||
|
|
||||||
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
|
bool
|
||||||
call_on_response(int, std::string, int, int, bool, bool,
|
needs_eof(std::true_type) const;
|
||||||
std::false_type)
|
|
||||||
|
bool
|
||||||
|
needs_eof(std::false_type) const;
|
||||||
|
|
||||||
|
void call_on_method(error_code& ec,
|
||||||
|
boost::string_ref const& s, std::true_type)
|
||||||
{
|
{
|
||||||
// VFALCO Certainly incorrect
|
impl().on_method(s, ec);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class C>
|
void call_on_method(error_code&,
|
||||||
class has_on_body_t
|
boost::string_ref const&, std::false_type)
|
||||||
{
|
|
||||||
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>
|
void call_on_method(error_code& ec,
|
||||||
class has_on_complete_t
|
boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
template<class T, class R =
|
call_on_method(ec, s, std::integral_constant<bool,
|
||||||
decltype(std::declval<T>().on_complete(), std::true_type{})>
|
isRequest && detail::has_on_method<Derived>::value>{});
|
||||||
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
|
void call_on_uri(error_code& ec,
|
||||||
call_on_complete(std::false_type)
|
boost::string_ref const& s, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_uri(s, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_uri(error_code&,
|
||||||
|
boost::string_ref const&, std::false_type)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void call_on_uri(error_code& ec, boost::string_ref const& s)
|
||||||
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()
|
call_on_uri(ec, s, std::integral_constant<bool,
|
||||||
{
|
isRequest && detail::has_on_uri<Derived>::value>{});
|
||||||
http_parser_settings_init(this);
|
|
||||||
on_message_begin = &basic_parser::cb_message_start;
|
|
||||||
on_url = &basic_parser::cb_url;
|
|
||||||
on_status = &basic_parser::cb_status;
|
|
||||||
on_header_field = &basic_parser::cb_header_field;
|
|
||||||
on_header_value = &basic_parser::cb_header_value;
|
|
||||||
on_headers_complete = &basic_parser::cb_headers_complete;
|
|
||||||
on_body = &basic_parser::cb_body;
|
|
||||||
on_message_complete = &basic_parser::cb_message_complete;
|
|
||||||
on_chunk_header = &basic_parser::cb_chunk_header;
|
|
||||||
on_chunk_complete = &basic_parser::cb_chunk_complete;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
static
|
void call_on_reason(error_code& ec,
|
||||||
http_parser_settings const*
|
boost::string_ref const& s, std::true_type)
|
||||||
hooks();
|
{
|
||||||
};
|
impl().on_reason(s, ec);
|
||||||
|
}
|
||||||
|
|
||||||
template<class Derived>
|
void call_on_reason(error_code&,
|
||||||
template<class ConstBufferSequence>
|
boost::string_ref const&, std::false_type)
|
||||||
std::size_t
|
|
||||||
basic_parser<Derived>::write(
|
|
||||||
ConstBufferSequence const& buffers, error_code& ec)
|
|
||||||
{
|
{
|
||||||
static_assert(beast::is_ConstBufferSequence<
|
}
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
void call_on_reason(error_code& ec, boost::string_ref const& s)
|
||||||
using boost::asio::buffer_cast;
|
{
|
||||||
using boost::asio::buffer_size;
|
call_on_reason(ec, s, std::integral_constant<bool,
|
||||||
std::size_t bytes_used = 0;
|
! isRequest && detail::has_on_reason<Derived>::value>{});
|
||||||
for (auto const& buffer : buffers)
|
}
|
||||||
|
|
||||||
|
void call_on_request(error_code& ec, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_request(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_request(error_code&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_request(error_code& ec)
|
||||||
|
{
|
||||||
|
call_on_request(ec, detail::has_on_request<Derived>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_response(error_code& ec, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_response(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_response(error_code&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_response(error_code& ec)
|
||||||
|
{
|
||||||
|
call_on_response(ec, detail::has_on_response<Derived>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_field(error_code& ec,
|
||||||
|
boost::string_ref const& s, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_field(s, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_field(error_code&,
|
||||||
|
boost::string_ref const&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_field(error_code& ec, boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
call_on_field(ec, s, detail::has_on_field<Derived>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_value(error_code& ec,
|
||||||
|
boost::string_ref const& s, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_value(s, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_value(error_code&,
|
||||||
|
boost::string_ref const&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_value(error_code& ec, boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
call_on_value(ec, s, detail::has_on_value<Derived>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
int call_on_headers(error_code& ec, std::true_type)
|
||||||
|
{
|
||||||
|
return impl().on_headers(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
int call_on_headers(error_code& ec, std::false_type)
|
||||||
{
|
{
|
||||||
auto const n = write(
|
|
||||||
buffer_cast<void const*>(buffer),
|
|
||||||
buffer_size(buffer), ec);
|
|
||||||
if(ec)
|
|
||||||
return 0;
|
return 0;
|
||||||
bytes_used += n;
|
|
||||||
if(complete())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return bytes_used;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Derived>
|
int call_on_headers(error_code& ec)
|
||||||
http_parser_settings const*
|
|
||||||
basic_parser<Derived>::hooks()
|
|
||||||
{
|
{
|
||||||
static hooks_t const h;
|
return call_on_headers(ec, detail::has_on_headers<Derived>{});
|
||||||
return &h;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void call_on_body(error_code& ec,
|
||||||
|
boost::string_ref const& s, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_body(s, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_body(error_code&,
|
||||||
|
boost::string_ref const&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_body(error_code& ec, boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
call_on_body(ec, s, detail::has_on_body<Derived>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_complete(error_code& ec, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_complete(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_complete(error_code&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_on_complete(error_code& ec)
|
||||||
|
{
|
||||||
|
call_on_complete(ec, detail::has_on_complete<Derived>{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // http
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
|||||||
388
include/beast/http/detail/basic_parser.hpp
Normal file
388
include/beast/http/detail/basic_parser.hpp
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
//
|
||||||
|
// 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<>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_method_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_method(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
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_method =
|
||||||
|
std::integral_constant<bool, has_on_method_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_uri_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_uri(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
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_uri =
|
||||||
|
std::integral_constant<bool, has_on_uri_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_reason_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_reason(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
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_reason =
|
||||||
|
std::integral_constant<bool, has_on_reason_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_request_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_request(
|
||||||
|
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_request =
|
||||||
|
std::integral_constant<bool, has_on_request_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_response_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_response(
|
||||||
|
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_response =
|
||||||
|
std::integral_constant<bool, has_on_response_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_field_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_uri(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
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_field =
|
||||||
|
std::integral_constant<bool, has_on_field_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_value_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_uri(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
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_value =
|
||||||
|
std::integral_constant<bool, has_on_value_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_headers_t
|
||||||
|
{
|
||||||
|
template<class T, class R = std::is_same<int,
|
||||||
|
decltype(std::declval<T>().on_headers(
|
||||||
|
std::declval<error_code&>()))>>
|
||||||
|
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 =
|
||||||
|
std::integral_constant<bool, has_on_headers_t<C>::value>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_body_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_body(
|
||||||
|
std::declval<boost::string_ref const&>(),
|
||||||
|
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>;
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_complete_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_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_complete =
|
||||||
|
std::integral_constant<bool, has_on_complete_t<C>::value>;
|
||||||
|
|
||||||
|
} // 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
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,7 @@ class read_op
|
|||||||
message_type& m;
|
message_type& m;
|
||||||
parser_type p;
|
parser_type p;
|
||||||
Handler h;
|
Handler h;
|
||||||
|
bool started = false;
|
||||||
bool cont;
|
bool cont;
|
||||||
int state = 0;
|
int state = 0;
|
||||||
|
|
||||||
@@ -129,6 +130,8 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
|
|||||||
bind_handler(std::move(*this), ec, 0));
|
bind_handler(std::move(*this), ec, 0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(used > 0)
|
||||||
|
d.started = true;
|
||||||
d.sb.consume(used);
|
d.sb.consume(used);
|
||||||
if(d.p.complete())
|
if(d.p.complete())
|
||||||
{
|
{
|
||||||
@@ -156,7 +159,7 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
|
|||||||
{
|
{
|
||||||
if(ec == boost::asio::error::eof)
|
if(ec == boost::asio::error::eof)
|
||||||
{
|
{
|
||||||
if(! d.p.started())
|
if(! d.started)
|
||||||
{
|
{
|
||||||
// call handler
|
// call handler
|
||||||
d.state = 99;
|
d.state = 99;
|
||||||
@@ -219,6 +222,7 @@ read(SyncReadStream& stream, Streambuf& streambuf,
|
|||||||
static_assert(is_Streambuf<Streambuf>::value,
|
static_assert(is_Streambuf<Streambuf>::value,
|
||||||
"Streambuf requirements not met");
|
"Streambuf requirements not met");
|
||||||
parser<isRequest, Body, Headers> p;
|
parser<isRequest, Body, Headers> p;
|
||||||
|
bool started = false;
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
auto used =
|
auto used =
|
||||||
@@ -226,6 +230,8 @@ read(SyncReadStream& stream, Streambuf& streambuf,
|
|||||||
if(ec)
|
if(ec)
|
||||||
return;
|
return;
|
||||||
streambuf.consume(used);
|
streambuf.consume(used);
|
||||||
|
if(used > 0)
|
||||||
|
started = true;
|
||||||
if(p.complete())
|
if(p.complete())
|
||||||
{
|
{
|
||||||
m = p.release();
|
m = p.release();
|
||||||
@@ -238,7 +244,7 @@ read(SyncReadStream& stream, Streambuf& streambuf,
|
|||||||
return;
|
return;
|
||||||
if(ec == boost::asio::error::eof)
|
if(ec == boost::asio::error::eof)
|
||||||
{
|
{
|
||||||
if(! p.started())
|
if(! started)
|
||||||
return;
|
return;
|
||||||
// Caller will see eof on next read.
|
// Caller will see eof on next read.
|
||||||
ec = {};
|
ec = {};
|
||||||
|
|||||||
157
include/beast/http/parse_error.hpp
Normal file
157
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 <beast/http/message.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
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>
|
template<bool isRequest, class Body, class Headers>
|
||||||
class parser
|
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 =
|
using message_type =
|
||||||
message<isRequest, Body, Headers>;
|
message<isRequest, Body, Headers>;
|
||||||
|
|
||||||
|
std::string field_;
|
||||||
|
std::string value_;
|
||||||
message_type m_;
|
message_type m_;
|
||||||
typename message_type::body_type::reader r_;
|
typename message_type::body_type::reader r_;
|
||||||
bool started_ = false;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
parser(parser&&) = default;
|
parser(parser&&) = default;
|
||||||
|
|
||||||
parser()
|
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
|
message_type
|
||||||
release()
|
release()
|
||||||
{
|
{
|
||||||
@@ -57,94 +65,164 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class http::basic_parser<parser>;
|
friend class basic_parser<isRequest, parser>;
|
||||||
|
|
||||||
void
|
void flush()
|
||||||
on_start()
|
|
||||||
{
|
{
|
||||||
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
|
void on_method(boost::string_ref const& s, error_code&)
|
||||||
on_field(std::string const& field, std::string const& value)
|
|
||||||
{
|
{
|
||||||
m_.headers.insert(field, value);
|
this->method_.append(s.data(), s.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void on_uri(boost::string_ref const& s, error_code&)
|
||||||
on_headers_complete(error_code&)
|
|
||||||
{
|
{
|
||||||
// vFALCO TODO Decode the Content-Length and
|
this->uri_.append(s.data(), s.size());
|
||||||
// Transfer-Encoding, see if we can reserve the buffer.
|
|
||||||
//
|
|
||||||
// r_.reserve(content_length)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void on_reason(boost::string_ref const& s, error_code&)
|
||||||
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;
|
this->reason_.append(s.data(), s.size());
|
||||||
m_.url = url;
|
}
|
||||||
m_.version = major * 10 + minor;
|
|
||||||
|
void on_field(boost::string_ref const& s, error_code&)
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
field_.append(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_value(boost::string_ref const& s, error_code&)
|
||||||
|
{
|
||||||
|
value_.append(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(std::true_type)
|
||||||
|
{
|
||||||
|
// VFALCO This is terrible for setting method
|
||||||
|
auto m =
|
||||||
|
[&](char const* s, method_t m)
|
||||||
|
{
|
||||||
|
if(this->method_ == s)
|
||||||
|
{
|
||||||
|
m_.method = m;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
bool
|
};
|
||||||
on_request(http::method_t, std::string const&,
|
while(false)
|
||||||
int, int, bool, bool,
|
|
||||||
std::false_type)
|
|
||||||
{
|
{
|
||||||
return true;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
m_.url = std::move(this->uri_);
|
||||||
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
|
void set(std::false_type)
|
||||||
on_response(int status, std::string const& reason,
|
|
||||||
int major, int minor, bool keep_alive, bool upgrade,
|
|
||||||
std::true_type)
|
|
||||||
{
|
{
|
||||||
m_.status = status;
|
m_.status = this->status_code();
|
||||||
m_.reason = reason;
|
m_.reason = this->reason_;
|
||||||
m_.version = major * 10 + minor;
|
|
||||||
// VFALCO TODO return expect_body_
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
int on_headers(error_code&)
|
||||||
on_response(int, std::string const&, int, int, bool, bool,
|
|
||||||
std::false_type)
|
|
||||||
{
|
{
|
||||||
return true;
|
flush();
|
||||||
|
m_.version = 10 * this->http_major() + this->http_minor();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void on_request(error_code& ec)
|
||||||
on_response(int status, std::string const& reason,
|
|
||||||
int major, int minor, bool keep_alive, bool upgrade)
|
|
||||||
{
|
{
|
||||||
return on_response(
|
set(std::integral_constant<
|
||||||
status, reason, major, minor, keep_alive, upgrade,
|
bool, isRequest>{});
|
||||||
std::integral_constant<bool, ! message_type::is_request::value>{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void on_response(error_code& ec)
|
||||||
on_body(void const* data,
|
|
||||||
std::size_t size, error_code& ec)
|
|
||||||
{
|
{
|
||||||
r_.write(data, size, ec);
|
set(std::integral_constant<
|
||||||
|
bool, isRequest>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void on_body(boost::string_ref const& s, error_code& ec)
|
||||||
on_complete()
|
{
|
||||||
|
r_.write(s.data(), s.size(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_complete(error_code&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
23
include/beast/http/rfc7230.hpp
Normal file
23
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
|
||||||
|
|
||||||
@@ -21,11 +21,15 @@ namespace beast {
|
|||||||
argument into the stream buffer. It is capable of converting the
|
argument into the stream buffer. It is capable of converting the
|
||||||
following types of arguments:
|
following types of arguments:
|
||||||
|
|
||||||
* `boost::asio::const_buffer`
|
@li `boost::asio::const_buffer`
|
||||||
* `boost::asio::mutable_buffer`
|
|
||||||
* A type for which the call to `boost::asio::buffer()` is defined
|
@li `boost::asio::mutable_buffer`
|
||||||
* A type meeting the requirements of `ConstBufferSequence`
|
|
||||||
* A type meeting the requirements of `MutableBufferSequence`
|
@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
|
For all types not listed above, the function will invoke
|
||||||
`boost::lexical_cast` on the argument in an attempt to convert to
|
`boost::lexical_cast` on the argument in an attempt to convert to
|
||||||
@@ -38,7 +42,7 @@ namespace beast {
|
|||||||
|
|
||||||
@param args A list of one or more arguments to write.
|
@param args A list of one or more arguments to write.
|
||||||
|
|
||||||
@throws Any exceptions thrown by `boost::lexical_cast`.
|
@throws unspecified Any exceptions thrown by `boost::lexical_cast`.
|
||||||
|
|
||||||
@note This function participates in overload resolution only if
|
@note This function participates in overload resolution only if
|
||||||
the `streambuf` parameter meets the requirements of Streambuf.
|
the `streambuf` parameter meets the requirements of Streambuf.
|
||||||
|
|||||||
15
test/Jamfile
15
test/Jamfile
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import os ;
|
import os ;
|
||||||
|
|
||||||
unit-test core_tests :
|
unit-test core-tests :
|
||||||
main.cpp
|
main.cpp
|
||||||
async_completion.cpp
|
async_completion.cpp
|
||||||
basic_streambuf.cpp
|
basic_streambuf.cpp
|
||||||
@@ -27,9 +27,8 @@ unit-test core_tests :
|
|||||||
detail/empty_base_optimization.cpp
|
detail/empty_base_optimization.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
unit-test http_tests :
|
unit-test http-tests :
|
||||||
main.cpp
|
main.cpp
|
||||||
../src/beast_http_nodejs_parser.cpp
|
|
||||||
http/basic_headers.cpp
|
http/basic_headers.cpp
|
||||||
http/basic_parser.cpp
|
http/basic_parser.cpp
|
||||||
http/chunk_encode.cpp
|
http/chunk_encode.cpp
|
||||||
@@ -38,17 +37,19 @@ unit-test http_tests :
|
|||||||
http/headers.cpp
|
http/headers.cpp
|
||||||
http/message.cpp
|
http/message.cpp
|
||||||
http/method.cpp
|
http/method.cpp
|
||||||
|
http/parse_error.cpp
|
||||||
http/parser.cpp
|
http/parser.cpp
|
||||||
http/read.cpp
|
http/read.cpp
|
||||||
http/reason.cpp
|
http/reason.cpp
|
||||||
http/resume_context.cpp
|
http/resume_context.cpp
|
||||||
http/rfc2616.cpp
|
http/rfc2616.cpp
|
||||||
|
http/rfc7230.cpp
|
||||||
http/streambuf_body.cpp
|
http/streambuf_body.cpp
|
||||||
http/string_body.cpp
|
http/string_body.cpp
|
||||||
http/write.cpp
|
http/write.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
unit-test websocket_tests :
|
unit-test websocket-tests :
|
||||||
main.cpp
|
main.cpp
|
||||||
websocket/error.cpp
|
websocket/error.cpp
|
||||||
websocket/option.cpp
|
websocket/option.cpp
|
||||||
@@ -56,3 +57,9 @@ unit-test websocket_tests :
|
|||||||
websocket/static_string.cpp
|
websocket/static_string.cpp
|
||||||
websocket/teardown.cpp
|
websocket/teardown.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
|
exe parser-bench :
|
||||||
|
main.cpp
|
||||||
|
http/nodejs_parser.cpp
|
||||||
|
http/parser_bench.cpp
|
||||||
|
;
|
||||||
|
|||||||
@@ -7,3 +7,507 @@
|
|||||||
|
|
||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <beast/http/basic_parser.hpp>
|
#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:
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
testVersion();
|
||||||
|
testFlags();
|
||||||
|
testUpgrade();
|
||||||
|
testBad();
|
||||||
|
testRandomReq(100);
|
||||||
|
testRandomResp(100);
|
||||||
|
testBody();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(basic_parser,http,beast);
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|||||||
562
test/http/message_fuzz.hpp
Normal file
562
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
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "../../include/beast/http/impl/http_parser.h"
|
#include "http_parser.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
# pragma warning (disable: 4127) // conditional expression is constant
|
# pragma warning (disable: 4127) // conditional expression is constant
|
||||||
# pragma warning (disable: 4244) // integer conversion, possible loss of data
|
# pragma warning (disable: 4244) // integer conversion, possible loss of data
|
||||||
#endif
|
#endif
|
||||||
#include "http-parser/http_parser.c"
|
#include "nodejs-parser/http_parser.c"
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# pragma warning (pop)
|
# pragma warning (pop)
|
||||||
#endif
|
#endif
|
||||||
881
test/http/nodejs_parser.hpp
Normal file
881
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
test/http/parse_error.cpp
Normal file
9
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>
|
||||||
157
test/http/parser_bench.cpp
Normal file
157
test/http/parser_bench.cpp
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)
|
||||||
|
//
|
||||||
|
|
||||||
|
#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]);
|
||||||
|
//log << debug::buffers_to_string(v[i].data()) << "\r";
|
||||||
|
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]);
|
||||||
|
//log << debug::buffers_to_string(v[i].data()) << "\r";
|
||||||
|
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
|
||||||
|
|
||||||
9
test/http/rfc7230.cpp
Normal file
9
test/http/rfc7230.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/rfc7230.hpp>
|
||||||
Reference in New Issue
Block a user