Beast.HTTP:

New classes are introduced to represent HTTP messages and their
associated bodies. The parser interface is reworked to use CRTP,
error codes, and trait checks.

New classes:

* basic_headers

  Models field/value pairs in a HTTP message.

* message

  Models a HTTP message, body behavior defined by template argument.
  Parsed message carries metadata generated during parsing.

* parser

  Produces parsed messages.

* empty_body, string_body, basic_streambuf_body

  Classes used to represent content bodies in various ways.

New functions:

* read, async_read, write, async_write

  Read and write HTTP messages on a socket.

New concepts:

* Body: Represents the HTTP Content-Body.
* Field: A HTTP header field.
* FieldSequence: A forward sequence of fields.
* Reader: Parses a Body from a stream of bytes.
* Writer: Serializes a Body to buffers.

basic_parser changes:

* add write methods which throw exceptions instead
* error_code passed via parameter instead of return value
* fold private member calls into existing callbacks
* basic_parser uses CRTP instead of virtual members
* add documentation on Derived requirements for CRTP

impl/http-parser changes:

* joyent renamed to nodejs to reflect upstream changes
This commit is contained in:
Vinnie Falco
2016-03-11 06:40:37 -05:00
parent f25b448a49
commit bcbe22c780
88 changed files with 6843 additions and 1867 deletions

View File

@@ -246,6 +246,8 @@
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\asio\buffers_adapter.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\asio\buffers_debug.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\asio\consuming_buffers.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\asio\handler_alloc.h">
@@ -361,62 +363,88 @@
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\hash\xxhasher.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\basic_headers.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\basic_parser.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\body.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\chunk_encode.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\detail\error.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\detail\writes.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\detail\write_preparation.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\empty_body.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\error.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\fields.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\headers.h">
</ClInclude>
<ClCompile Include="..\..\src\beast\beast\http\HTTP.unity.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\impl\basic_parser.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<None Include="..\..\src\beast\beast\http\impl\basic_headers.ipp">
</None>
<None Include="..\..\src\beast\beast\http\impl\basic_parser.ipp">
</None>
<ClCompile Include="..\..\src\beast\beast\http\impl\http-parser\http_parser.c">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\http\impl\http-parser\http_parser.h">
</ClInclude>
<ClCompile Include="..\..\src\beast\beast\http\impl\joyent_parser.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\http\impl\joyent_parser.h">
</ClInclude>
<ClCompile Include="..\..\src\beast\beast\http\impl\method.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\impl\URL.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<None Include="..\..\src\beast\beast\http\impl\message.ipp">
</None>
<None Include="..\..\src\beast\beast\http\impl\read.ipp">
</None>
<None Include="..\..\src\beast\beast\http\impl\write.ipp">
</None>
<ClInclude Include="..\..\src\beast\beast\http\message.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\method.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\parser.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\read.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\reason.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\resume_context.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\rfc2616.h">
</ClInclude>
<ClCompile Include="..\..\src\beast\beast\http\tests\chunked_encoder.test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
<ClCompile Include="..\..\src\beast\beast\http\src\beast_http_nodejs_parser.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\tests\parser.test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\tests\rfc2616.test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\tests\URL.test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\tests\urls_large_data.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\http\tests\urls_large_data.h">
<ClInclude Include="..\..\src\beast\beast\http\src\nodejs_parser.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\URL.h">
<ClCompile Include="..\..\src\beast\beast\http\src\test\beast_http_chunked_encoder_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\src\test\beast_http_message_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\src\test\beast_http_parser_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\src\test\beast_http_rfc2616_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\http\streambuf_body.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\string_body.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\type_check.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\write.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\is_call_possible.h">
</ClInclude>
@@ -454,6 +482,10 @@
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\unity\beast_hash_unity.cpp">
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\unity\beast_http_unity.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\unity\beast_streams_unity.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
@@ -1728,6 +1760,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\beast\net\impl\URL.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\beast\net\IPAddress.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\beast\net\IPAddressConversion.h">
@@ -1738,10 +1774,16 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\beast\net\IPEndpoint.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\beast\net\tests\beast_http_URL_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\beast\net\tests\IPEndpoint.test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\beast\net\URL.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\beast\nudb.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\beast\nudb\api.h">

View File

@@ -49,14 +49,20 @@
<Filter Include="beast\http">
<UniqueIdentifier>{7138D215-DA65-98D5-EF7D-C9896685201E}</UniqueIdentifier>
</Filter>
<Filter Include="beast\http\detail">
<UniqueIdentifier>{3E84AA4C-CB48-99F0-EB35-5603FF633A51}</UniqueIdentifier>
</Filter>
<Filter Include="beast\http\impl">
<UniqueIdentifier>{932F732F-F09E-5C50-C8A1-D62342CCAA1F}</UniqueIdentifier>
</Filter>
<Filter Include="beast\http\impl\http-parser">
<UniqueIdentifier>{59E40DE8-464F-35ED-5F22-E7B52FDE3ECD}</UniqueIdentifier>
</Filter>
<Filter Include="beast\http\tests">
<UniqueIdentifier>{7FFB535D-F2F1-0B27-4C2F-28A6BAFDE4ED}</UniqueIdentifier>
<Filter Include="beast\http\src">
<UniqueIdentifier>{CCA07C08-BF91-95E2-4980-7D56A618C37E}</UniqueIdentifier>
</Filter>
<Filter Include="beast\http\src\test">
<UniqueIdentifier>{5F7F2E0C-4CDE-B1E1-1668-E06768D6FFD3}</UniqueIdentifier>
</Filter>
<Filter Include="beast\streams">
<UniqueIdentifier>{7944CEAB-F8F9-1E2B-CD03-B3D9F78B20E9}</UniqueIdentifier>
@@ -552,6 +558,9 @@
<ClInclude Include="..\..\src\beast\beast\asio\buffers_adapter.h">
<Filter>beast\asio</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\asio\buffers_debug.h">
<Filter>beast\asio</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\asio\consuming_buffers.h">
<Filter>beast\asio</Filter>
</ClInclude>
@@ -699,6 +708,12 @@
<ClInclude Include="..\..\src\beast\beast\hash\xxhasher.h">
<Filter>beast\hash</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http.h">
<Filter>beast</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\basic_headers.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\basic_parser.h">
<Filter>beast\http</Filter>
</ClInclude>
@@ -708,33 +723,48 @@
<ClInclude Include="..\..\src\beast\beast\http\chunk_encode.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\detail\error.h">
<Filter>beast\http\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\detail\writes.h">
<Filter>beast\http\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\detail\write_preparation.h">
<Filter>beast\http\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\empty_body.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\error.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\fields.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\headers.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClCompile Include="..\..\src\beast\beast\http\HTTP.unity.cpp">
<Filter>beast\http</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\impl\basic_parser.cpp">
<None Include="..\..\src\beast\beast\http\impl\basic_headers.ipp">
<Filter>beast\http\impl</Filter>
</ClCompile>
</None>
<None Include="..\..\src\beast\beast\http\impl\basic_parser.ipp">
<Filter>beast\http\impl</Filter>
</None>
<ClCompile Include="..\..\src\beast\beast\http\impl\http-parser\http_parser.c">
<Filter>beast\http\impl\http-parser</Filter>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\http\impl\http-parser\http_parser.h">
<Filter>beast\http\impl\http-parser</Filter>
</ClInclude>
<ClCompile Include="..\..\src\beast\beast\http\impl\joyent_parser.cpp">
<None Include="..\..\src\beast\beast\http\impl\message.ipp">
<Filter>beast\http\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\http\impl\joyent_parser.h">
</None>
<None Include="..\..\src\beast\beast\http\impl\read.ipp">
<Filter>beast\http\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\beast\beast\http\impl\method.cpp">
</None>
<None Include="..\..\src\beast\beast\http\impl\write.ipp">
<Filter>beast\http\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\impl\URL.cpp">
<Filter>beast\http\impl</Filter>
</ClCompile>
</None>
<ClInclude Include="..\..\src\beast\beast\http\message.h">
<Filter>beast\http</Filter>
</ClInclude>
@@ -744,28 +774,46 @@
<ClInclude Include="..\..\src\beast\beast\http\parser.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\read.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\reason.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\resume_context.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\rfc2616.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClCompile Include="..\..\src\beast\beast\http\tests\chunked_encoder.test.cpp">
<Filter>beast\http\tests</Filter>
<ClCompile Include="..\..\src\beast\beast\http\src\beast_http_nodejs_parser.cpp">
<Filter>beast\http\src</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\tests\parser.test.cpp">
<Filter>beast\http\tests</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\tests\rfc2616.test.cpp">
<Filter>beast\http\tests</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\tests\URL.test.cpp">
<Filter>beast\http\tests</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\tests\urls_large_data.cpp">
<Filter>beast\http\tests</Filter>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\http\tests\urls_large_data.h">
<Filter>beast\http\tests</Filter>
<ClInclude Include="..\..\src\beast\beast\http\src\nodejs_parser.h">
<Filter>beast\http\src</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\URL.h">
<ClCompile Include="..\..\src\beast\beast\http\src\test\beast_http_chunked_encoder_test.cpp">
<Filter>beast\http\src\test</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\src\test\beast_http_message_test.cpp">
<Filter>beast\http\src\test</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\src\test\beast_http_parser_test.cpp">
<Filter>beast\http\src\test</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\http\src\test\beast_http_rfc2616_test.cpp">
<Filter>beast\http\src\test</Filter>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\http\streambuf_body.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\string_body.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\type_check.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\http\write.h">
<Filter>beast\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\is_call_possible.h">
@@ -807,6 +855,9 @@
<ClCompile Include="..\..\src\beast\beast\unity\beast_hash_unity.cpp">
<Filter>beast\unity</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\unity\beast_http_unity.cpp">
<Filter>beast\unity</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\unity\beast_streams_unity.cpp">
<Filter>beast\unity</Filter>
</ClCompile>
@@ -2211,6 +2262,9 @@
<ClCompile Include="..\..\src\ripple\beast\net\impl\IPEndpoint.cpp">
<Filter>ripple\beast\net\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\beast\net\impl\URL.cpp">
<Filter>ripple\beast\net\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\beast\net\IPAddress.h">
<Filter>ripple\beast\net</Filter>
</ClInclude>
@@ -2226,9 +2280,15 @@
<ClInclude Include="..\..\src\ripple\beast\net\IPEndpoint.h">
<Filter>ripple\beast\net</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\beast\net\tests\beast_http_URL_test.cpp">
<Filter>ripple\beast\net\tests</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\beast\net\tests\IPEndpoint.test.cpp">
<Filter>ripple\beast\net\tests</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\beast\net\URL.h">
<Filter>ripple\beast\net</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\beast\nudb.h">
<Filter>ripple\beast</Filter>
</ClInclude>

View File

@@ -904,6 +904,7 @@ def get_classic_sources(toolchain):
append_sources(result, *list_sources('src/beast/beast/asio/src', '.cpp'))
append_sources(result, *list_sources('src/beast/beast/clock', '.cpp'))
append_sources(result, *list_sources('src/beast/beast/crypto', '.cpp'))
append_sources(result, *list_sources('src/beast/beast/http/src', '.cpp'))
append_sources(result, *list_sources('src/beast/beast/streams', '.cpp'))
append_sources(result, *list_sources('src/beast/beast/test', '.cpp'))
append_sources(result, *list_sources('src/ripple/beast/container', '.cpp'))
@@ -953,6 +954,7 @@ def get_unity_sources(toolchain):
'src/beast/beast/unity/beast_asio_unity.cpp',
'src/beast/beast/unity/beast_clock_unity.cpp',
'src/beast/beast/unity/beast_crypto_unity.cpp',
'src/beast/beast/unity/beast_http_unity.cpp',
'src/beast/beast/unity/beast_streams_unity.cpp',
'src/beast/beast/unity/beast_test_unity.cpp',
'src/ripple/beast/unity/beast_container_unity.cpp',

View File

@@ -41,6 +41,10 @@ project beast
<include>.
#<use>/boost//headers
<library>/boost/system//boost_system
<library>/boost/filesystem//boost_filesystem
<library>/boost/program_options//boost_program_options
# <library>ssl
# <library>crypto
<define>BOOST_ALL_NO_LIB=1
<threading>multi
<link>static
@@ -61,6 +65,8 @@ project beast
<os>HPUX:<library>ipv6
<os>QNXNTO:<library>socket
<os>HAIKU:<library>network
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
: usage-requirements
<include>.
:

1
src/beast/README.md Normal file
View File

@@ -0,0 +1 @@
# Beast: A C++ Library

View File

@@ -1 +0,0 @@
# Beast: An amazing cross platform library

View File

@@ -20,6 +20,7 @@
#ifndef BEAST_UTILITY_CI_CHAR_TRAITS_H_INCLUDED
#define BEAST_UTILITY_CI_CHAR_TRAITS_H_INCLUDED
#include <boost/utility/string_ref.hpp>
#include <algorithm>
#include <type_traits>
#include <cctype>
@@ -34,16 +35,15 @@ struct ci_less
{
static bool const is_transparent = true;
template <class String>
bool
operator() (String const& lhs, String const& rhs) const
operator()(boost::string_ref const& lhs,
boost::string_ref const& rhs) const noexcept
{
using std::begin;
using std::end;
using char_type = typename String::value_type;
return std::lexicographical_compare (
return std::lexicographical_compare(
begin(lhs), end(lhs), begin(rhs), end(rhs),
[] (char_type lhs, char_type rhs)
[](char lhs, char rhs)
{
return std::tolower(lhs) < std::tolower(rhs);
}

39
src/beast/beast/http.h Normal file
View File

@@ -0,0 +1,39 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_H_INCLUDED
#define BEAST_HTTP_H_INCLUDED
#include <beast/http/basic_parser.h>
#include <beast/http/chunk_encode.h>
#include <beast/http/empty_body.h>
#include <beast/http/error.h>
#include <beast/http/fields.h>
#include <beast/http/headers.h>
#include <beast/http/message.h>
#include <beast/http/parser.h>
#include <beast/http/read.h>
#include <beast/http/reason.h>
#include <beast/http/resume_context.h>
#include <beast/http/streambuf_body.h>
#include <beast/http/string_body.h>
#include <beast/http/type_check.h>
#include <beast/http/write.h>
#endif

View File

@@ -0,0 +1,447 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_BASIC_HEADERS_H_INCLUDED
#define BEAST_HTTP_BASIC_HEADERS_H_INCLUDED
#include <beast/http/detail/writes.h>
#include <beast/asio/type_check.h>
#include <beast/ci_char_traits.h>
#include <beast/empty_base_optimization.h>
#include <boost/intrusive/list.hpp>
#include <boost/intrusive/set.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/utility/string_ref.hpp>
#include <algorithm>
#include <cctype>
#include <memory>
#include <string>
#include <beast/cxx17/type_traits.h> // <type_traits>
#include <utility>
namespace beast {
namespace http {
template<class Allocator>
class basic_headers;
namespace detail {
class basic_headers_base
{
public:
struct value_type
{
std::string first;
std::string second;
value_type(boost::string_ref const& name_,
boost::string_ref const& value_)
: first(name_)
, second(value_)
{
}
boost::string_ref
name() const
{
return first;
}
boost::string_ref
value() const
{
return second;
}
};
protected:
template<class Allocator>
friend class beast::http::basic_headers;
struct element
: boost::intrusive::set_base_hook <
boost::intrusive::link_mode <
boost::intrusive::normal_link>>
, boost::intrusive::list_base_hook <
boost::intrusive::link_mode <
boost::intrusive::normal_link>>
{
value_type data;
element(boost::string_ref const& name,
boost::string_ref const& value)
: data(name, value)
{
}
};
struct less : private ci_less
{
template<class String>
bool
operator()(String const& lhs, element const& rhs) const
{
return ci_less::operator()(lhs, rhs.data.first);
}
template<class String>
bool
operator()(element const& lhs, String const& rhs) const
{
return ci_less::operator()(lhs.data.first, rhs);
}
bool
operator()(element const& lhs, element const& rhs) const
{
return beast::ci_less::operator()(
lhs.data.first, rhs.data.first);
}
};
using list_t = typename boost::intrusive::make_list<
element, boost::intrusive::constant_time_size<false>>::type;
using set_t = typename boost::intrusive::make_set<
element, boost::intrusive::constant_time_size<true>,
boost::intrusive::compare<less>>::type;
// data
set_t set_;
list_t list_;
basic_headers_base(set_t&& set, list_t&& list)
: set_(std::move(set))
, list_(std::move(list))
{
}
public:
class const_iterator;
using iterator = const_iterator;
basic_headers_base() = default;
/// Returns an iterator to the beginning of the field sequence.
iterator
begin() const;
/// Returns an iterator to the end of the field sequence.
iterator
end() const;
/// Returns an iterator to the beginning of the field sequence.
iterator
cbegin() const;
/// Returns an iterator to the end of the field sequence.
iterator
cend() const;
};
//------------------------------------------------------------------------------
class basic_headers_base::const_iterator
{
using iter_type = list_t::const_iterator;
iter_type it_;
template<class Allocator>
friend class beast::http::basic_headers;
friend class basic_headers_base;
const_iterator(iter_type it)
: it_(it)
{
}
public:
using value_type =
typename basic_headers_base::value_type;
using pointer = value_type const*;
using reference = value_type const&;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return it_->data;
}
pointer
operator->() const
{
return &**this;
}
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
};
} // detail
//------------------------------------------------------------------------------
/** Container to store HTTP headers.
Meets the requirements of `FieldSequence`.
*/
template<class Allocator>
class basic_headers
#if ! GENERATING_DOCS
: private empty_base_optimization<
typename std::allocator_traits<Allocator>::
template rebind_alloc<
detail::basic_headers_base::element>>
, public detail::basic_headers_base
#endif
{
using alloc_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<
detail::basic_headers_base::element>;
using alloc_traits =
std::allocator_traits<alloc_type>;
using size_type =
typename std::allocator_traits<Allocator>::size_type;
void
delete_all();
void
move_assign(basic_headers&, std::false_type);
void
move_assign(basic_headers&, std::true_type);
void
copy_assign(basic_headers const&, std::false_type);
void
copy_assign(basic_headers const&, std::true_type);
template<class FieldSequence>
void
copy_from(FieldSequence const& fs)
{
for(auto const& e : fs)
insert(e.first, e.second);
}
public:
/// The type of allocator used.
using allocator_type = Allocator;
/// Default constructor.
basic_headers() = default;
/// Destructor
~basic_headers();
/** Construct the headers.
@param alloc The allocator to use.
*/
explicit
basic_headers(Allocator const& alloc);
/** Move constructor.
The moved-from object becomes an empty field sequence.
@param other The object to move from.
*/
basic_headers(basic_headers&& other);
/** Move assignment.
The moved-from object becomes an empty field sequence.
@param other The object to move from.
*/
basic_headers& operator=(basic_headers&& other);
/// Copy constructor.
basic_headers(basic_headers const&);
/// Copy assignment.
basic_headers& operator=(basic_headers const&);
/// Copy constructor.
template<class OtherAlloc>
basic_headers(basic_headers<OtherAlloc> const&);
/// Copy assignment.
template<class OtherAlloc>
basic_headers& operator=(basic_headers<OtherAlloc> const&);
/// Construct from a field sequence.
template<class FwdIt>
basic_headers(FwdIt first, FwdIt last);
/// Returns `true` if the field sequence contains no elements.
bool
empty() const
{
return set_.empty();
}
/// Returns the number of elements in the field sequence.
std::size_t
size() const
{
return set_.size();
}
/** Returns `true` if the specified field exists. */
bool
exists(boost::string_ref const& name) const
{
return set_.find(name, less{}) != set_.end();
}
/** Returns an iterator to the case-insensitive matching header. */
iterator
find(boost::string_ref const& name) const;
/** Returns the value for a case-insensitive matching header, or "" */
boost::string_ref
operator[](boost::string_ref const& name) const;
/** Clear the contents of the basic_headers. */
void
clear() noexcept;
/** Remove a field.
@return The number of fields removed.
*/
std::size_t
erase(boost::string_ref const& name);
/** Insert a field value.
If a field value already exists the new value will be
extended as per RFC2616 Section 4.2.
*/
// VFALCO TODO Consider allowing rvalue references for std::move?
void
insert(boost::string_ref const& name,
boost::string_ref const& value);
/** Insert a field value.
If a field value already exists the new value will be
extended as per RFC2616 Section 4.2.
*/
template<class T,
class = std::enable_if_t<
! std::is_constructible<boost::string_ref, T>::value>>
void
insert(boost::string_ref name, T const& value)
{
insert(name,
boost::lexical_cast<std::string>(value));
}
/** Replace a field value.
The current field value, if any, is removed. Then the
specified value is inserted as if by insert(field, value).
*/
void
replace(boost::string_ref const& name,
boost::string_ref const& value);
/** Replace a field value.
The current field value, if any, is removed. Then the
specified value is inserted as if by insert(field, value).
*/
template<class T,
class = std::enable_if_t<
! std::is_constructible<boost::string_ref, T>::value>>
void
replace(boost::string_ref const& name, T const& value)
{
replace(name,
boost::lexical_cast<std::string>(value));
}
};
} // http
} // beast
#include <beast/http/impl/basic_headers.ipp>
#endif

View File

@@ -21,66 +21,84 @@
#define BEAST_HTTP_BASIC_PARSER_H_INCLUDED
#include <beast/http/method.h>
#include <beast/http/src/nodejs_parser.h>
#include <beast/asio/type_check.h>
#include <boost/asio/buffer.hpp>
#include <boost/system/error_code.hpp>
#include <array>
#include <cstdint>
#include <memory>
#include <string>
#include <beast/cxx17/type_traits.h> // <type_traits>
namespace beast {
namespace joyent {
struct http_parser;
};
namespace http {
/** Parser for producing HTTP requests and responses.
Callbacks:
If a is an object of type Derived, and the call expression is
valid then the stated effects will take place:
a.on_start()
Called once when a new message begins.
a.on_field(std::string field, std::string value)
Called for each field
a.on_headers_complete(error_code&)
Called when all the header fields have been received, but
before any part of the body if any is received.
a.on_request(method_t method, std::string url,
int major, int minor, bool keep_alive, bool upgrade)
Called for requests when all the headers have been received.
This will precede any content body.
When keep_alive is false:
* Server roles respond with a "Connection: close" header.
* Client roles close the connection.
a.on_response(int status, std::string text,
int major, int minor, bool keep_alive,
bool upgrade)
Called for responses when all the headers have been received.
This will precede any content body.
When keep_alive is `false`:
* Client roles close the connection.
* Server roles respond with a "Connection: close" header.
This function should return `true` if upgrade is false and
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&)
Called zero or more times for the content body. Any transfer
encoding is already decoded in the memory pointed to by data.
a.on_complete()
Called when parsing completes successfully.
The parser uses traits to determine if the callback is possible.
If the Derived type omits the callbacks, they are simply skipped
with no compilation error.
*/
/*
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
{
private:
// These structures must exactly match the
// declarations in joyent http_parser.h include
//
struct state_t
{
unsigned int type : 2;
unsigned int flags : 6;
unsigned int state : 8;
unsigned int header_state : 8;
unsigned int index : 8;
std::uint32_t nread;
std::uint64_t content_length;
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16;
unsigned int method : 8;
unsigned int http_errno : 7;
unsigned int upgrade : 1;
void *data;
};
using data_cb_t = int (*) (
state_t*, const char *at, size_t length);
using cb_t = int (*) (state_t*);
struct hooks_t
{
cb_t on_message_begin;
data_cb_t on_url;
data_cb_t on_status;
data_cb_t on_header_field;
data_cb_t on_header_value;
cb_t on_headers_complete;
data_cb_t on_body;
cb_t on_message_complete;
cb_t on_chunk_header;
cb_t on_chunk_complete;
};
char state_ [sizeof(state_t)];
char hooks_ [sizeof(hooks_t)];
http_parser state_;
boost::system::error_code* ec_;
bool complete_ = false;
std::string url_;
std::string status_;
@@ -90,20 +108,36 @@ private:
public:
using error_code = boost::system::error_code;
virtual
~basic_parser() = default;
/** Move constructor.
The state of the moved-from object is undefined,
but safe to destroy.
*/
basic_parser(basic_parser&& other);
/** Move assignment.
The state of the moved-from object is undefined,
but safe to destroy.
*/
basic_parser&
operator=(basic_parser&& other);
/** Copy constructor. */
basic_parser(basic_parser const& other);
/** Copy assignment. */
basic_parser& operator=(basic_parser const& other);
/** Construct the parser.
If `request` is `true` this sets up the parser to
process an HTTP request.
@param request If `true`, the parser is setup for a request.
*/
explicit
basic_parser (bool request) noexcept;
basic_parser&
operator= (basic_parser&& other);
basic_parser(bool request) noexcept;
/** Returns `true` if parsing is complete.
This is only defined when no errors have been returned.
*/
bool
@@ -113,153 +147,406 @@ public:
}
/** Write data to the parser.
@param data A buffer containing the data to write
@param bytes The size of the buffer pointed to by data.
@return A pair with bool success, and the number of bytes consumed.
*/
std::pair <error_code, std::size_t>
write (void const* data, std::size_t bytes);
/** Write a set of buffer data to the parser.
The return value includes the error code if any,
and the number of bytes consumed in the input sequence.
@param buffers The buffers to write. These must meet the
requirements of ConstBufferSequence.
@return A pair with bool success, and the number of bytes consumed.
@param data A pointer to a buffer representing the input sequence.
@param size The number of bytes in the buffer pointed to by data.
@throws boost::system::system_error Thrown on failure.
@return The number of bytes consumed in the input sequence.
*/
template <class ConstBufferSequence>
std::pair <error_code, std::size_t>
write (ConstBufferSequence const& buffers);
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;
}
/** Write data to the parser.
@param data A pointer to a buffer representing the input sequence.
@param size The number of bytes in the buffer pointed to by data.
@param ec Set to the error, if any error occurred.
@return The number of bytes consumed in the input sequence.
*/
std::size_t
write(void const* data, std::size_t size,
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.
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.
@return `true` if the message is complete.
@throws boost::system::system_error Thrown on failure.
*/
error_code
write_eof();
protected:
/** Called once when a new message begins. */
virtual
void
on_start() = 0;
write_eof()
{
error_code ec;
write_eof(ec);
if(ec)
throw boost::system::system_error{ec};
}
/** Called for each header field. */
virtual
void
on_field (std::string const& field, std::string const& value) = 0;
/** Called to indicate the end of file.
/** Called for requests when all the headers have been received.
This will precede any content body.
When keep_alive is false:
* Server roles respond with a "Connection: close" header.
* Client roles close the connection.
When upgrade is true, no content-body is expected, and the
return value is ignored.
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.
@param method The HTTP method specified in the request line
@param major The HTTP major version number
@param minor The HTTP minor version number
@param url The URL specified in the request line
@param keep_alive `false` if this is the last message.
@param upgrade `true` if the Upgrade header is specified
@return `true` If upgrade is false and a content body is expected.
@note This is typically called when a socket read returns eof.
@param ec Set to the error, if any error occurred.
*/
virtual
bool
on_request (method_t method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade) = 0;
/** Called for responses when all the headers have been received.
This will precede any content body.
When keep_alive is `false`:
* Client roles close the connection.
* Server roles respond with a "Connection: close" header.
When upgrade is true, no content-body is expected, and the
return value is ignored.
@param status The numerical HTTP status code in the response line
@param text The status text in the response line
@param major The HTTP major version number
@param minor The HTTP minor version number
@param keep_alive `false` if this is the last message.
@param upgrade `true` if the Upgrade header is specified
@return `true` If upgrade is false and a content body is expected.
*/
virtual
bool
on_response (int status, std::string const& text,
int major, int minor, bool keep_alive, bool upgrade) = 0;
/** Called zero or more times for the content body.
Any transfer encoding is already decoded in the
memory pointed to by data.
@param data A memory block containing the next decoded
chunk of the content body.
@param bytes The number of bytes pointed to by data.
*/
virtual
void
on_body (void const* data, std::size_t bytes) = 0;
/** Called once when the message is complete. */
virtual
void
on_complete() = 0;
write_eof(error_code& ec);
private:
void check_header();
Derived&
impl()
{
return *static_cast<Derived*>(this);
}
int do_message_start ();
int do_url (char const* in, std::size_t bytes);
int do_status (char const* in, std::size_t bytes);
int do_header_field (char const* in, std::size_t bytes);
int do_header_value (char const* in, std::size_t bytes);
int do_headers_complete ();
int do_body (char const* in, std::size_t bytes);
int do_message_complete ();
int do_chunk_header();
int do_chunk_complete();
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::bool_constant<has_on_start_t<C>::value>;
static int cb_message_start (joyent::http_parser*);
static int cb_url (joyent::http_parser*, char const*, std::size_t);
static int cb_status (joyent::http_parser*, char const*, std::size_t);
static int cb_header_field (joyent::http_parser*, char const*, std::size_t);
static int cb_header_value (joyent::http_parser*, char const*, std::size_t);
static int cb_headers_complete (joyent::http_parser*);
static int cb_body (joyent::http_parser*, char const*, std::size_t);
static int cb_message_complete (joyent::http_parser*);
static int cb_chunk_header (joyent::http_parser*);
static int cb_chunk_complete (joyent::http_parser*);
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::bool_constant<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::bool_constant<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::bool_constant<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::bool_constant<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::bool_constant<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::bool_constant<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 = &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
http_parser_settings const*
hooks();
};
template <class ConstBufferSequence>
auto
basic_parser::write (ConstBufferSequence const& buffers) ->
std::pair <error_code, std::size_t>
template<class Derived>
template<class ConstBufferSequence>
std::size_t
basic_parser<Derived>::write(
ConstBufferSequence const& buffers, error_code& ec)
{
std::pair <error_code, std::size_t> result ({}, 0);
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)
{
std::size_t bytes_consumed;
std::tie (result.first, bytes_consumed) =
write (boost::asio::buffer_cast <void const*> (buffer),
boost::asio::buffer_size (buffer));
if (result.first)
break;
result.second += bytes_consumed;
if (complete())
auto const n = write(
buffer_cast<void const*>(buffer),
buffer_size(buffer), ec);
if(ec)
return 0;
bytes_used += n;
if(complete())
break;
}
return result;
return bytes_used;
}
template<class Derived>
http_parser_settings const*
basic_parser<Derived>::hooks()
{
static hooks_t const h;
return &h;
}
} // http
} // beast
#include <beast/http/impl/basic_parser.ipp>
#endif

View File

@@ -29,7 +29,7 @@
#include <string>
namespace beast {
namespace http {
namespace deprecated_http {
/** Container for the HTTP content-body. */
class body
@@ -144,7 +144,7 @@ body::data() const
return buf_->data();
}
} // http
} // deprecated_http
} // beast
#endif

View File

@@ -148,8 +148,8 @@ chunk_encoded_buffers<Buffers>::to_hex(
template <class Buffers>
chunk_encoded_buffers<Buffers>::const_iterator::const_iterator()
: where_(Where::end)
, buffers_(nullptr)
: buffers_(nullptr)
, where_(Where::end)
{
}
@@ -193,7 +193,7 @@ chunk_encoded_buffers<Buffers>::const_iterator::operator--() ->
const_iterator&
{
assert(buffers_);
assert(where_ != Where::begin);
assert(where_ != Where::head);
if (where_ == Where::end)
where_ = Where::input;
else if (iter_ != buffers_->buffers_.begin())
@@ -247,37 +247,44 @@ chunk_encoded_buffers<Buffers>::const_iterator::const_iterator(
{
}
}
} // detail
/** Returns a chunk-encoded BufferSequence.
See:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
@tparam Buffers A type meeting the requirements of BufferSequence.
@param buffers The input buffer sequence.
@param final_chunk `true` If this should include a final-chunk.
@return A chunk-encoded ConstBufferSeqeunce representing the input.
@return A chunk-encoded ConstBufferSequence representing the input.
*/
/** @{ */
template <class Buffers>
detail::chunk_encoded_buffers<Buffers>
chunk_encode (Buffers const& buffers,
template<class ConstBufferSequence>
#if GENERATING_DOCS
implementation_defined
#else
detail::chunk_encoded_buffers<ConstBufferSequence>
#endif
chunk_encode(ConstBufferSequence const& buffers,
bool final_chunk = false)
{
return detail::chunk_encoded_buffers<
Buffers>(buffers, final_chunk);
ConstBufferSequence>{buffers, final_chunk};
}
// Returns a chunked encoding final chunk.
/// Returns a chunked encoding final chunk.
inline
#if GENERATING_DOCS
implementation_defined
#else
boost::asio::const_buffers_1
#endif
chunk_encode_final()
{
return boost::asio::const_buffers_1(
"0\r\n\r\n", 5);
}
/** @} */
} // http
} // beast

View File

@@ -0,0 +1,83 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_DETAIL_ERROR_H_INCLUDED
#define BEAST_HTTP_DETAIL_ERROR_H_INCLUDED
#include <beast/http/src/nodejs_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>
auto
make_error(int http_errno)
{
static message_category const mc{};
return boost::system::error_code{http_errno, mc};
}
} // detail
} // http
} // beast
#endif

View File

@@ -0,0 +1,133 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_WRITE_PREPARATION_H_INCLUDED
#define BEAST_HTTP_WRITE_PREPARATION_H_INCLUDED
#include <beast/asio/streambuf.h>
namespace beast {
namespace http {
namespace detail {
template<class T>
class has_content_length_value
{
template<class U, class R = typename std::is_convertible<
decltype(std::declval<U>().content_length()),
std::size_t>>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
// `true` if `T` meets the requirements.
static bool const value = type::value;
};
// Determines if the writer can provide the content length
template<class T>
using has_content_length =
std::integral_constant<bool,
has_content_length_value<T>::value>;
template<bool isRequest, class Body, class Headers>
struct write_preparation
{
using headers_type =
basic_headers<std::allocator<char>>;
message<isRequest, Body, Headers> const& msg;
typename Body::writer w;
streambuf sb;
bool chunked;
bool close;
explicit
write_preparation(
message<isRequest, Body, Headers> const& msg_)
: msg(msg_)
, w(msg)
{
}
void
init(error_code& ec)
{
w.init(ec);
if(ec)
return;
// VFALCO TODO This implementation requires making a
// copy of the headers, we can do better.
// VFALCO Should we be using handler_alloc?
headers_type h(msg.headers.begin(), msg.headers.end());
set_content_length(h, has_content_length<
typename Body::writer>{});
// VFALCO TODO Keep-Alive
if(close)
{
if(msg.version >= 11)
h.insert("Connection", "close");
}
else
{
if(msg.version < 11)
h.insert("Connection", "keep-alive");
}
msg.write_firstline(sb);
write_fields(sb, h);
detail::write(sb, "\r\n");
}
private:
void
set_content_length(headers_type& h,
std::true_type)
{
close = false;
chunked = false;
h.insert("Content-Length", w.content_length());
}
void
set_content_length(headers_type& h,
std::false_type)
{
if(msg.version >= 11)
{
close = false;
chunked = true;
h.insert("Transfer-Encoding", "chunked");
}
else
{
close = true;
chunked = false;
}
}
};
} // detail
} // http
} // beast
#endif

View File

@@ -0,0 +1,76 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_WRITES_H_INCLUDED
#define BEAST_HTTP_WRITES_H_INCLUDED
#include <beast/asio/type_check.h>
#include <boost/lexical_cast.hpp>
#include <boost/utility/string_ref.hpp>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
namespace detail {
template<class Streambuf, class T,
class = std::enable_if_t<is_Streambuf<Streambuf>::value>>
void
write(Streambuf& streambuf, T&& t)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
auto const& s =
boost::lexical_cast<std::string>(
std::forward<T>(t));
streambuf.commit(buffer_copy(
streambuf.prepare(s.size()), buffer(s)));
}
template<class Streambuf, std::size_t N,
class = std::enable_if_t< (N>0) &&
is_Streambuf<Streambuf>::value>>
void
write(Streambuf& streambuf, char const(&s)[N])
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
streambuf.commit(buffer_copy(
streambuf.prepare(N), buffer(s, N-1)));
}
template<class Streambuf,
class = std::enable_if_t<is_Streambuf<Streambuf>::value>>
void
write(Streambuf& streambuf, boost::string_ref const& s)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
streambuf.commit(buffer_copy(
streambuf.prepare(s.size()),
buffer(s.data(), s.size())));
}
} // detail
} // http
} // beast
#endif

View File

@@ -0,0 +1,87 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_EMPTY_BODY_H_INCLUDED
#define BEAST_HTTP_EMPTY_BODY_H_INCLUDED
#include <beast/http/error.h>
#include <beast/http/message.h>
#include <beast/asio/streambuf.h>
#include <boost/asio/buffer.hpp>
#include <memory>
#include <string>
namespace beast {
namespace http {
/** An empty content-body.
*/
struct empty_body
{
struct value_type
{
};
struct reader
{
template<bool isRequest, class Allocator>
explicit
reader(message<isRequest, empty_body, Allocator>&)
{
}
void
write(void const*, std::size_t, error_code&)
{
}
};
struct writer
{
template<bool isRequest, class Allocator>
explicit
writer(message<isRequest, empty_body, Allocator> const& m)
{
}
void
init(error_code& ec)
{
}
std::size_t
content_length() const
{
return 0;
}
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
write(boost::asio::null_buffers{});
return true;
}
};
};
} // http
} // beast
#endif

View File

@@ -17,18 +17,17 @@
*/
//==============================================================================
#if BEAST_INCLUDE_BEASTCONFIG
#include <BeastConfig.h>
#ifndef BEAST_HTTP_ERROR_H_INCLUDED
#define BEAST_HTTP_ERROR_H_INCLUDED
#include <boost/system/error_code.hpp>
namespace beast {
namespace http {
using error_code = boost::system::error_code;
} // http
} // beast
#endif
#include <beast/http/impl/basic_parser.cpp>
#include <beast/http/impl/joyent_parser.cpp>
#include <beast/http/impl/method.cpp>
#include <beast/http/impl/URL.cpp>
#include <beast/http/tests/chunked_encoder.test.cpp>
#include <beast/http/tests/parser.test.cpp>
#include <beast/http/tests/rfc2616.test.cpp>
#include <beast/http/tests/URL.test.cpp>
#include <beast/http/tests/urls_large_data.cpp>

View File

@@ -0,0 +1,133 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_FIELDS_H_INCLUDED
#define BEAST_HTTP_FIELDS_H_INCLUDED
#include <array>
namespace beast {
namespace http {
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmissing-braces"
#endif // defined(__clang__)
template<class = void>
auto const&
common_fields()
{
// Must be sorted
static std::array<char const*, 82> constexpr h{
"Accept"
,"Accept-Charset"
,"Accept-Datetime"
,"Accept-Encoding"
,"Accept-Language"
,"Accept-Ranges"
,"Access-Control-Allow-Credentials"
,"Access-Control-Allow-Headers"
,"Access-Control-Allow-Methods"
,"Access-Control-Allow-Origin"
,"Access-Control-Expose-Headers"
,"Access-Control-Max-Age"
,"Access-Control-Request-Headers"
,"Access-Control-Request-Method"
,"Age"
,"Allow"
,"Authorization"
,"Cache-Control"
,"Connection"
,"Content-Disposition"
,"Content-Encoding"
,"Content-Language"
,"Content-Length"
,"Content-Location"
,"Content-MD5"
,"Content-Range"
,"Content-Type"
,"Cookie"
,"DNT"
,"Date"
,"ETag"
,"Expect"
,"Expires"
,"From"
,"Front-End-Https"
,"Host"
,"If-Match"
,"If-Modified-Since"
,"If-None-Match"
,"If-Range"
,"If-Unmodified-Since"
,"Keep-Alive"
,"Last-Modified"
,"Link"
,"Location"
,"Max-Forwards"
,"Origin"
,"P3P"
,"Pragma"
,"Proxy-Authenticate"
,"Proxy-Authorization"
,"Proxy-Connection"
,"Range"
,"Referer"
,"Refresh"
,"Retry-After"
,"Server"
,"Set-Cookie"
,"Strict-Transport-Security"
,"TE"
,"Timestamp"
,"Trailer"
,"Transfer-Encoding"
,"Upgrade"
,"User-Agent"
,"VIP"
,"Vary"
,"Via"
,"WWW-Authenticate"
,"Warning"
,"X-Accel-Redirect"
,"X-Content-Security-Policy-Report-Only"
,"X-Content-Type-Options"
,"X-Forwarded-For"
,"X-Forwarded-Proto"
,"X-Frame-Options"
,"X-Powered-By"
,"X-Real-IP"
,"X-Requested-With"
,"X-UA-Compatible"
,"X-Wap-Profile"
,"X-XSS-Protection"
};
return h;
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif // defined(__clang__)
} // http
} // beast
#endif

View File

@@ -20,390 +20,17 @@
#ifndef BEAST_HTTP_HEADERS_H_INCLUDED
#define BEAST_HTTP_HEADERS_H_INCLUDED
#include <beast/ci_char_traits.h>
#include <boost/intrusive/list.hpp>
#include <boost/intrusive/set.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <algorithm>
#include <cctype>
#include <map>
#include <ostream>
#include <string>
#include <utility>
#include <beast/http/basic_headers.h>
#include <memory>
namespace beast {
namespace http {
/** Holds a collection of HTTP headers. */
class headers
{
public:
using value_type = std::pair<std::string, std::string>;
template<class Allocator>
using headers = basic_headers<Allocator>;
private:
struct element
: boost::intrusive::set_base_hook <
boost::intrusive::link_mode <
boost::intrusive::normal_link>>
, boost::intrusive::list_base_hook <
boost::intrusive::link_mode <
boost::intrusive::normal_link>>
{
template <class = void>
element (std::string const& f, std::string const& v);
value_type data;
};
struct less : private beast::ci_less
{
template <class String>
bool
operator() (String const& lhs, element const& rhs) const;
template <class String>
bool
operator() (element const& lhs, String const& rhs) const;
bool
operator() (element const& lhs, element const& rhs) const;
};
struct transform
: public std::unary_function <element, value_type>
{
value_type const&
operator() (element const& e) const
{
return e.data;
}
};
using list_t = boost::intrusive::make_list <element,
boost::intrusive::constant_time_size <false>
>::type;
using set_t = boost::intrusive::make_set <element,
boost::intrusive::constant_time_size <true>,
boost::intrusive::compare<less>
>::type;
list_t list_;
set_t set_;
public:
using iterator = boost::transform_iterator <transform,
list_t::const_iterator>;
using const_iterator = iterator;
~headers()
{
clear();
}
headers() = default;
headers (headers&& other);
headers& operator= (headers&& other);
headers (headers const& other);
headers& operator= (headers const& other);
/** Returns an iterator to headers in order of appearance. */
/** @{ */
iterator
begin() const;
iterator
end() const;
iterator
cbegin() const;
iterator
cend() const;
/** @} */
/** Returns an iterator to the case-insensitive matching header. */
template <class = void>
iterator
find (std::string const& field) const;
/** Returns the value for a case-insensitive matching header, or "" */
template <class = void>
std::string const&
operator[] (std::string const& field) const;
/** Clear the contents of the headers. */
template <class = void>
void
clear() noexcept;
/** Remove a field.
@return The number of fields removed.
*/
template <class = void>
std::size_t
erase (std::string const& field);
/** Append a field value.
If a field value already exists the new value will be
extended as per RFC2616 Section 4.2.
*/
// VFALCO TODO Consider allowing rvalue references for std::move
template <class = void>
void
append (std::string const& field, std::string const& value);
};
template <class = void>
std::string
to_string (headers const& h);
// HACK!
template <class = void>
std::map <std::string, std::string>
build_map (headers const& h);
//------------------------------------------------------------------------------
template <class>
headers::element::element (
std::string const& f, std::string const& v)
{
data.first = f;
data.second = v;
}
template <class String>
bool
headers::less::operator() (
String const& lhs, element const& rhs) const
{
return beast::ci_less::operator() (lhs, rhs.data.first);
}
template <class String>
bool
headers::less::operator() (
element const& lhs, String const& rhs) const
{
return beast::ci_less::operator() (lhs.data.first, rhs);
}
inline
bool
headers::less::operator() (
element const& lhs, element const& rhs) const
{
return beast::ci_less::operator() (lhs.data.first, rhs.data.first);
}
//------------------------------------------------------------------------------
inline
headers::headers (headers&& other)
: list_ (std::move (other.list_))
, set_ (std::move (other.set_))
{
other.list_.clear();
other.set_.clear();
}
inline
headers&
headers::operator= (headers&& other)
{
list_ = std::move(other.list_);
set_ = std::move(other.set_);
other.list_.clear();
other.set_.clear();
return *this;
}
inline
headers::headers (headers const& other)
{
for (auto const& e : other.list_)
append (e.data.first, e.data.second);
}
inline
headers&
headers::operator= (headers const& other)
{
clear();
for (auto const& e : other.list_)
append (e.data.first, e.data.second);
return *this;
}
inline
headers::iterator
headers::begin() const
{
return {list_.cbegin(), transform{}};
}
inline
headers::iterator
headers::end() const
{
return {list_.cend(), transform{}};
}
inline
headers::iterator
headers::cbegin() const
{
return {list_.cbegin(), transform{}};
}
inline
headers::iterator
headers::cend() const
{
return {list_.cend(), transform{}};
}
template <class>
headers::iterator
headers::find (std::string const& field) const
{
auto const iter (set_.find (field, less{}));
if (iter == set_.end())
return {list_.end(), transform{}};
return {list_.iterator_to (*iter), transform{}};
}
template <class>
std::string const&
headers::operator[] (std::string const& field) const
{
static std::string none;
auto const found (find (field));
if (found == end())
return none;
return found->second;
}
template <class>
void
headers::clear() noexcept
{
for (auto iter (list_.begin()); iter != list_.end();)
delete &(*iter++);
}
template <class>
std::size_t
headers::erase (std::string const& field)
{
auto const iter = set_.find(field, less{});
if (iter == set_.end())
return 0;
element& e = *iter;
set_.erase(set_.iterator_to(e));
list_.erase(list_.iterator_to(e));
delete &e;
return 1;
}
template <class>
void
headers::append (std::string const& field,
std::string const& value)
{
set_t::insert_commit_data d;
auto const result (set_.insert_check (field, less{}, d));
if (result.second)
{
element* const p = new element (field, value);
list_.push_back (*p);
set_.insert_commit (*p, d);
return;
}
// If field already exists, append comma
// separated value as per RFC2616 section 4.2
auto& cur (result.first->data.second);
cur.reserve (cur.size() + 1 + value.size());
cur.append (1, ',');
cur.append (value);
}
//------------------------------------------------------------------------------
template <class Streambuf>
void
write (Streambuf& stream, std::string const& s)
{
stream.commit (boost::asio::buffer_copy (
stream.prepare (s.size()), boost::asio::buffer(s)));
}
template <class Streambuf>
void
write (Streambuf& stream, char const* s)
{
auto const len (::strlen(s));
stream.commit (boost::asio::buffer_copy (
stream.prepare (len), boost::asio::buffer (s, len)));
}
template <class Streambuf>
void
write (Streambuf& stream, headers const& h)
{
for (auto const& _ : h)
{
write (stream, _.first);
write (stream, ": ");
write (stream, _.second);
write (stream, "\r\n");
}
}
template <class>
std::string
to_string (headers const& h)
{
std::string s;
std::size_t n (0);
for (auto const& e : h)
n += e.first.size() + 2 + e.second.size() + 2;
s.reserve (n);
for (auto const& e : h)
{
s.append (e.first);
s.append (": ");
s.append (e.second);
s.append ("\r\n");
}
return s;
}
inline
std::ostream&
operator<< (std::ostream& s, headers const& h)
{
s << to_string(h);
return s;
}
template <class>
std::map <std::string, std::string>
build_map (headers const& h)
{
std::map <std::string, std::string> c;
for (auto const& e : h)
{
auto key (e.first);
// TODO Replace with safe C++14 version
std::transform (key.begin(), key.end(), key.begin(), ::tolower);
c [key] = e.second;
}
return c;
}
using http_headers =
basic_headers<std::allocator<char>>;
} // http
} // beast

View File

@@ -0,0 +1,312 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_BASIC_HEADERS_IPP_INCLUDED
#define BEAST_HTTP_BASIC_HEADERS_IPP_INCLUDED
#include <beast/http/detail/writes.h>
#include <beast/asio/type_check.h>
namespace beast {
namespace http {
namespace detail {
inline
auto
basic_headers_base::begin() const ->
const_iterator
{
return list_.cbegin();
}
inline
auto
basic_headers_base::end() const ->
const_iterator
{
return list_.cend();
}
inline
auto
basic_headers_base::cbegin() const ->
const_iterator
{
return list_.cbegin();
}
inline
auto
basic_headers_base::cend() const ->
const_iterator
{
return list_.cend();
}
} // detail
//------------------------------------------------------------------------------
template<class Allocator>
void
basic_headers<Allocator>::
delete_all()
{
for(auto it = list_.begin(); it != list_.end();)
{
auto& e = *it++;
e.~element();
alloc_traits::deallocate(
this->member(), &e, 1);
}
}
template<class Allocator>
inline
void
basic_headers<Allocator>::
move_assign(basic_headers& other, std::false_type)
{
if(this->member() != other.member())
{
copy_from(other);
other.clear();
}
else
{
set_ = std::move(other.set_);
list_ = std::move(other.list_);
}
}
template<class Allocator>
inline
void
basic_headers<Allocator>::
move_assign(basic_headers& other, std::true_type)
{
this->member() = std::move(other.member());
set_ = std::move(other.set_);
list_ = std::move(other.list_);
}
template<class Allocator>
inline
void
basic_headers<Allocator>::
copy_assign(basic_headers const& other, std::false_type)
{
copy_from(other);
}
template<class Allocator>
inline
void
basic_headers<Allocator>::
copy_assign(basic_headers const& other, std::true_type)
{
this->member() = other.member();
copy_from(other);
}
//------------------------------------------------------------------------------
template<class Allocator>
basic_headers<Allocator>::
~basic_headers()
{
delete_all();
}
template<class Allocator>
basic_headers<Allocator>::
basic_headers(Allocator const& alloc)
: empty_base_optimization<
alloc_type>(alloc)
{
}
template<class Allocator>
basic_headers<Allocator>::
basic_headers(basic_headers&& other)
: empty_base_optimization<alloc_type>(
std::move(other.member()))
, detail::basic_headers_base(
std::move(other.set_), std::move(other.list_))
{
other.list_.clear();
other.set_.clear();
}
template<class Allocator>
auto
basic_headers<Allocator>::
operator=(basic_headers&& other) ->
basic_headers&
{
if(this == &other)
return *this;
clear();
move_assign(other, std::integral_constant<bool,
alloc_traits::propagate_on_container_move_assignment::value>{});
return *this;
}
template<class Allocator>
basic_headers<Allocator>::
basic_headers(basic_headers const& other)
: basic_headers(alloc_traits::
select_on_container_copy_construction(other.member()))
{
copy_from(other);
}
template<class Allocator>
auto
basic_headers<Allocator>::
operator=(basic_headers const& other) ->
basic_headers&
{
clear();
copy_assign(other, std::integral_constant<bool,
alloc_traits::propagate_on_container_copy_assignment::value>{});
return *this;
}
template<class Allocator>
template<class OtherAlloc>
basic_headers<Allocator>::
basic_headers(basic_headers<OtherAlloc> const& other)
{
copy_from(other);
}
template<class Allocator>
template<class OtherAlloc>
auto
basic_headers<Allocator>::
operator=(basic_headers<OtherAlloc> const& other) ->
basic_headers&
{
clear();
copy_from(other);
return *this;
}
template<class Allocator>
template<class FwdIt>
basic_headers<Allocator>::
basic_headers(FwdIt first, FwdIt last)
{
for(;first != last; ++first)
insert(first->name(), first->value());
}
template<class Allocator>
auto
basic_headers<Allocator>::
find(boost::string_ref const& name) const ->
iterator
{
auto const it = set_.find(name, less{});
if(it == set_.end())
return list_.end();
return list_.iterator_to(*it);
}
template<class Allocator>
boost::string_ref
basic_headers<Allocator>::
operator[](boost::string_ref const& name) const
{
// VFALCO This none object looks sketchy
static boost::string_ref const none;
auto const it = find(name);
if(it == end())
return none;
return it->second;
}
template<class Allocator>
void
basic_headers<Allocator>::
clear() noexcept
{
delete_all();
list_.clear();
set_.clear();
}
template<class Allocator>
std::size_t
basic_headers<Allocator>::
erase(boost::string_ref const& name)
{
auto const it = set_.find(name, less{});
if(it == set_.end())
return 0;
auto& e = *it;
set_.erase(set_.iterator_to(e));
list_.erase(list_.iterator_to(e));
alloc_traits::deallocate(this->member(), &e, 1);
return 1;
}
template<class Allocator>
void
basic_headers<Allocator>::
insert(boost::string_ref const& name,
boost::string_ref const& value)
{
typename set_t::insert_commit_data d;
auto const result =
set_.insert_check(name, less{}, d);
if (result.second)
{
auto const p = alloc_traits::allocate(
this->member(), 1);
alloc_traits::construct(
this->member(), p, name, value);
list_.push_back(*p);
set_.insert_commit(*p, d);
return;
}
// If field already exists, insert comma
// separated value as per RFC2616 section 4.2
auto& cur = result.first->data.second;
cur.reserve(cur.size() + 1 + value.size());
cur.append(1, ',');
cur.append(value.data(), value.size());
}
template<class Allocator>
void
basic_headers<Allocator>::
replace(boost::string_ref const& name,
boost::string_ref const& value)
{
erase(name);
insert(name, value);
}
} // http
} // beast
#endif

View File

@@ -1,327 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <beast/http/basic_parser.h>
#include <beast/http/impl/joyent_parser.h>
#include <beast/http/rfc2616.h>
#include <boost/system/error_code.hpp>
namespace beast {
namespace http {
boost::system::error_category const&
message_category() noexcept
{
class message_category_t : public boost::system::error_category
{
public:
const char*
name() const noexcept override
{
return "http::message";
}
std::string
message (int ev) const override
{
return joyent::http_errno_description (
static_cast<joyent::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;
}
};
static message_category_t cat;
return cat;
}
//------------------------------------------------------------------------------
basic_parser::basic_parser (bool request) noexcept
{
static_assert (sizeof(joyent::http_parser) == sizeof(state_t),
"state_t size must match http_parser size");
static_assert (sizeof(joyent::http_parser_settings) == sizeof(hooks_t),
"hooks_t size must match http_parser_settings size");
auto s (reinterpret_cast <joyent::http_parser*> (&state_));
s->data = this;
auto h (reinterpret_cast <joyent::http_parser_settings*> (&hooks_));
h->on_message_begin = &basic_parser::cb_message_start;
h->on_url = &basic_parser::cb_url;
h->on_status = &basic_parser::cb_status;
h->on_header_field = &basic_parser::cb_header_field;
h->on_header_value = &basic_parser::cb_header_value;
h->on_headers_complete = &basic_parser::cb_headers_complete;
h->on_body = &basic_parser::cb_body;
h->on_message_complete = &basic_parser::cb_message_complete;
h->on_chunk_header = &basic_parser::cb_chunk_header;
h->on_chunk_complete = &basic_parser::cb_chunk_complete;
joyent::http_parser_init (s, request
? joyent::http_parser_type::HTTP_REQUEST
: joyent::http_parser_type::HTTP_RESPONSE);
}
basic_parser&
basic_parser::operator= (basic_parser&& other)
{
*reinterpret_cast<joyent::http_parser*>(&state_) =
*reinterpret_cast<joyent::http_parser*>(&other.state_);
reinterpret_cast<joyent::http_parser*>(&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;
}
auto
basic_parser::write (void const* data, std::size_t bytes) ->
std::pair <error_code, std::size_t>
{
std::pair <error_code, std::size_t> result ({}, 0);
auto s (reinterpret_cast <joyent::http_parser*> (&state_));
auto h (reinterpret_cast <joyent::http_parser_settings const*> (&hooks_));
result.second = joyent::http_parser_execute (s, h,
static_cast <const char*> (data), bytes);
result.first = error_code{static_cast<int>(s->http_errno),
message_category()};
return result;
}
auto
basic_parser::write_eof() ->
error_code
{
auto s (reinterpret_cast <joyent::http_parser*> (&state_));
auto h (reinterpret_cast <joyent::http_parser_settings const*> (&hooks_));
joyent::http_parser_execute (s, h, nullptr, 0);
return error_code{static_cast<int>(s->http_errno),
message_category()};
}
//------------------------------------------------------------------------------
void
basic_parser::check_header()
{
if (! value_.empty())
{
rfc2616::trim_right_in_place (value_);
on_field (field_, value_);
field_.clear();
value_.clear();
}
}
int
basic_parser::do_message_start ()
{
complete_ = false;
url_.clear();
status_.clear();
field_.clear();
value_.clear();
on_start();
return 0;
}
int
basic_parser::do_url (char const* in, std::size_t bytes)
{
url_.append (static_cast <char const*> (in), bytes);
return 0;
}
int
basic_parser::do_status (char const* in, std::size_t bytes)
{
status_.append (static_cast <char const*> (in), bytes);
return 0;
}
int
basic_parser::do_header_field (char const* in, std::size_t bytes)
{
check_header();
field_.append (static_cast <char const*> (in), bytes);
return 0;
}
int
basic_parser::do_header_value (char const* in, std::size_t bytes)
{
value_.append (static_cast <char const*> (in), bytes);
return 0;
}
/* Called when all the headers are complete but before
the content body, if present.
Returning 1 from here tells the joyent parser
that the message has no body (e.g. a HEAD request).
*/
int
basic_parser::do_headers_complete()
{
check_header();
auto const p (reinterpret_cast <joyent::http_parser const*> (&state_));
bool const keep_alive (joyent::http_should_keep_alive (p) != 0);
if (p->type == joyent::http_parser_type::HTTP_REQUEST)
return on_request (joyent::convert_http_method (
joyent::http_method(p->method)), url_,
p->http_major, p->http_minor, keep_alive, p->upgrade) ? 0 : 1;
return on_response (p->status_code, status_,
p->http_major, p->http_minor, keep_alive, p->upgrade) ? 0 : 1;
}
/* Called repeatedly for the content body. The passed buffer
has already had the transfer-encoding removed.
*/
int
basic_parser::do_body (char const* in, std::size_t bytes)
{
on_body (in, bytes);
return 0;
}
/* Called when the both the headers and content body (if any) are complete. */
int
basic_parser::do_message_complete ()
{
complete_ = true;
on_complete();
return 0;
}
int
basic_parser::do_chunk_header()
{
return 0;
}
int
basic_parser::do_chunk_complete()
{
return 0;
}
//------------------------------------------------------------------------------
int
basic_parser::cb_message_start (joyent::http_parser* p)
{
return reinterpret_cast <basic_parser*> (
p->data)->do_message_start();
}
int
basic_parser::cb_url (joyent::http_parser* p,
char const* in, std::size_t bytes)
{
return reinterpret_cast <basic_parser*> (
p->data)->do_url (in, bytes);
}
int
basic_parser::cb_status (joyent::http_parser* p,
char const* in, std::size_t bytes)
{
return reinterpret_cast <basic_parser*> (
p->data)->do_status (in, bytes);
}
int
basic_parser::cb_header_field (joyent::http_parser* p,
char const* in, std::size_t bytes)
{
return reinterpret_cast <basic_parser*> (
p->data)->do_header_field (in, bytes);
}
int
basic_parser::cb_header_value (joyent::http_parser* p,
char const* in, std::size_t bytes)
{
return reinterpret_cast <basic_parser*> (
p->data)->do_header_value (in, bytes);
}
int
basic_parser::cb_headers_complete (joyent::http_parser* p)
{
return reinterpret_cast <basic_parser*> (
p->data)->do_headers_complete();
}
int
basic_parser::cb_body (joyent::http_parser* p,
char const* in, std::size_t bytes)
{
return reinterpret_cast <basic_parser*> (
p->data)->do_body (
in, bytes);
}
int
basic_parser::cb_message_complete (joyent::http_parser* p)
{
return reinterpret_cast <basic_parser*> (
p->data)->do_message_complete();
}
int
basic_parser::cb_chunk_header (joyent::http_parser* p)
{
return reinterpret_cast <basic_parser*> (
p->data)->do_chunk_header();
}
int
basic_parser::cb_chunk_complete (joyent::http_parser* p)
{
return reinterpret_cast <basic_parser*> (
p->data)->do_chunk_complete();
}
} // http
} // beast

View File

@@ -0,0 +1,260 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <beast/http/src/nodejs_parser.h>
#include <beast/http/rfc2616.h>
#include <beast/http/detail/error.h>
namespace beast {
namespace http {
template<class Derived>
basic_parser<Derived>::
basic_parser(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
basic_parser<Derived>::operator=(basic_parser&& other) ->
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>
basic_parser<Derived>::
basic_parser(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
basic_parser<Derived>::
operator=(basic_parser const& other) ->
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>
basic_parser<Derived>::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
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_error(
static_cast<int>(state_.http_errno));
if(ec)
return 0;
return n;
}
template<class Derived>
void
basic_parser<Derived>::write_eof(error_code& ec)
{
ec_ = &ec;
http_parser_execute(
&state_, hooks(), nullptr, 0);
if(! ec)
ec = detail::make_error(
static_cast<int>(state_.http_errno));
}
template<class Derived>
void
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
basic_parser<Derived>::cb_message_start(http_parser* p)
{
auto& t = *reinterpret_cast<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
basic_parser<Derived>::cb_url(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<basic_parser*>(p->data);
t.url_.append(in, bytes);
return 0;
}
template<class Derived>
int
basic_parser<Derived>::cb_status(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<basic_parser*>(p->data);
t.status_.append(in, bytes);
return 0;
}
template<class Derived>
int
basic_parser<Derived>::cb_header_field(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<basic_parser*>(p->data);
t.check_header();
t.field_.append(in, bytes);
return 0;
}
template<class Derived>
int
basic_parser<Derived>::cb_header_value(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<basic_parser*>(p->data);
t.value_.append(in, bytes);
return 0;
}
/* Called when all the headers are complete but before
the content body, if present.
Returning 1 from here tells the nodejs parser
that the message has no body (e.g. a HEAD request).
*/
template<class Derived>
int
basic_parser<Derived>::cb_headers_complete(http_parser* p)
{
auto& t = *reinterpret_cast<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(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;
}
// Called repeatedly for the content body,
// after any transfer-encoding is applied.
template<class Derived>
int
basic_parser<Derived>::cb_body(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<basic_parser*>(p->data);
t.call_on_body(in, bytes, *t.ec_, has_on_body<Derived>{});
return *t.ec_ ? 1 : 0;
}
// Called when the both the headers
// and content body (if any) are complete.
template<class Derived>
int
basic_parser<Derived>::cb_message_complete(http_parser* p)
{
auto& t = *reinterpret_cast<basic_parser*>(p->data);
t.complete_ = true;
t.call_on_complete(has_on_complete<Derived>{});
return 0;
}
template<class Derived>
int
basic_parser<Derived>::cb_chunk_header(http_parser*)
{
return 0;
}
template<class Derived>
int
basic_parser<Derived>::cb_chunk_complete(http_parser*)
{
return 0;
}
} // http
} // beast

View File

@@ -1,128 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <beast/http/basic_url.h>
#include <beast/http/impl/joyent_parser.h>
namespace beast {
namespace http {
namespace detail {
void
basic_url_base::parse_impl (string_ref s, boost::system::error_code& ec)
{
joyent::http_parser_url p;
value_type const* const data (s.data());
int const error (joyent::http_parser_parse_url (
data, s.size(), false, &p));
if (error)
{
ec = boost::system::error_code (
boost::system::errc::invalid_argument,
boost::system::generic_category());
return;
}
if ((p.field_set & (1<<joyent::UF_SCHEMA)) != 0)
{
m_scheme = string_ref (
data + p.field_data [joyent::UF_SCHEMA].off,
p.field_data [joyent::UF_SCHEMA].len);
}
else
{
m_scheme = string_ref {};
}
if ((p.field_set & (1<<joyent::UF_HOST)) != 0)
{
m_host = string_ref (
data + p.field_data [joyent::UF_HOST].off,
p.field_data [joyent::UF_HOST].len);
}
else
{
m_host = string_ref {};
}
if ((p.field_set & (1<<joyent::UF_PORT)) != 0)
{
m_port = p.port;
m_port_string = string_ref (
data + p.field_data [joyent::UF_PORT].off,
p.field_data [joyent::UF_PORT].len);
}
else
{
m_port = 0;
m_port_string = string_ref {};
}
if ((p.field_set & (1<<joyent::UF_PATH)) != 0)
{
m_path = string_ref (
data + p.field_data [joyent::UF_PATH].off,
p.field_data [joyent::UF_PATH].len);
}
else
{
m_path = string_ref {};
}
if ((p.field_set & (1<<joyent::UF_QUERY)) != 0)
{
m_query = string_ref (
data + p.field_data [joyent::UF_QUERY].off,
p.field_data [joyent::UF_QUERY].len);
}
else
{
m_query = string_ref {};
}
if ((p.field_set & (1<<joyent::UF_FRAGMENT)) != 0)
{
m_fragment = string_ref (
data + p.field_data [joyent::UF_FRAGMENT].off,
p.field_data [joyent::UF_FRAGMENT].len);
}
else
{
m_fragment = string_ref {};
}
if ((p.field_set & (1<<joyent::UF_USERINFO)) != 0)
{
m_userinfo = string_ref (
data + p.field_data [joyent::UF_USERINFO].off,
p.field_data [joyent::UF_USERINFO].len);
}
else
{
m_userinfo = string_ref {};
}
}
} // detail
} // http
} // beast

View File

@@ -0,0 +1,320 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_MESSAGE_IPP_INCLUDED
#define BEAST_HTTP_MESSAGE_IPP_INCLUDED
#include <beast/http/chunk_encode.h>
#include <beast/http/type_check.h>
#include <beast/http/detail/writes.h>
#include <beast/http/detail/write_preparation.h>
#include <beast/http/rfc2616.h>
#include <boost/asio/buffer.hpp>
#include <condition_variable>
#include <mutex>
namespace beast {
namespace http {
template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message()
{
static_assert(is_Body<Body>::value,
"Body requirements not met");
}
template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message(request_params params)
{
static_assert(is_Body<Body>::value,
"Body requirements not met");
static_assert(isRequest, "message is not a request");
this->method = params.method;
this->url = std::move(params.url);
version = params.version;
}
template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message(response_params params)
{
static_assert(is_Body<Body>::value,
"Body requirements not met");
static_assert(! isRequest, "message is not a response");
this->status = params.status;
this->reason = std::move(params.reason);
version = params.version;
}
template<bool isRequest, class Body, class Headers>
template<class Streambuf>
void
message<isRequest, Body, Headers>::
write_firstline(Streambuf& streambuf,
std::true_type) const
{
detail::write(streambuf, to_string(this->method));
detail::write(streambuf, " ");
detail::write(streambuf, this->url);
switch(version)
{
case 10:
detail::write(streambuf, " HTTP/1.0\r\n");
break;
case 11:
detail::write(streambuf, " HTTP/1.1\r\n");
break;
default:
detail::write(streambuf, " HTTP/");
detail::write(streambuf, version / 10);
detail::write(streambuf, ".");
detail::write(streambuf, version % 10);
detail::write(streambuf, "\r\n");
break;
}
}
template<bool isRequest, class Body, class Headers>
template<class Streambuf>
void
message<isRequest, Body, Headers>::
write_firstline(Streambuf& streambuf,
std::false_type) const
{
switch(version)
{
case 10:
detail::write(streambuf, "HTTP/1.0 ");
break;
case 11:
detail::write(streambuf, "HTTP/1.1 ");
break;
default:
detail::write(streambuf, " HTTP/");
detail::write(streambuf, version / 10);
detail::write(streambuf, ".");
detail::write(streambuf, version % 10);
detail::write(streambuf, " ");
break;
}
detail::write(streambuf, this->status);
detail::write(streambuf, " ");
detail::write(streambuf, this->reason);
detail::write(streambuf, "\r\n");
}
namespace detail {
template<class ConstBufferSequence>
std::string
buffers_to_string(ConstBufferSequence const& buffers)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(buffers));
for(auto const& b : buffers)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
return s;
}
} // detail
// Diagnostic output only
template<bool isRequest, class Body, class Headers>
std::ostream&
operator<<(std::ostream& os,
message<isRequest, Body, Headers> const& msg)
{
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
error_code ec;
detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec);
if(ec)
return os;
std::mutex m;
std::condition_variable cv;
bool ready = false;
resume_context resume{
[&]
{
std::lock_guard<std::mutex> lock(m);
ready = true;
cv.notify_one();
}};
auto copy = resume;
os << detail::buffers_to_string(wp.sb.data());
wp.sb.consume(wp.sb.size());
auto writef =
[&os, &wp](auto const& buffers)
{
if(wp.chunked)
os << detail::buffers_to_string(
chunk_encode(buffers));
else
os << detail::buffers_to_string(
buffers);
};
for(;;)
{
{
auto result = wp.w(std::move(copy), ec, writef);
if(ec)
return os;
if(result)
break;
if(boost::indeterminate(result))
{
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
}
wp.sb.consume(wp.sb.size());
for(;;)
{
auto result = wp.w(std::move(copy), ec, writef);
if(ec)
return os;
if(result)
break;
if(boost::indeterminate(result))
{
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
}
}
if(wp.chunked)
{
// VFALCO Unfortunately the current interface to the
// Writer concept prevents us from using coalescing the
// final body chunk with the final chunk delimiter.
//
// write final chunk
os << detail::buffers_to_string(chunk_encode_final());
if(ec)
return os;
}
os << std::endl;
return os;
}
//------------------------------------------------------------------------------
template<bool isRequest, class Body, class Headers>
void
set_connection(bool keep_alive,
message<isRequest, Body, Headers>& req)
{
if(req.version >= 11)
{
if(! keep_alive)
req.headers.replace("Connection", "close");
else
req.headers.erase("Connection");
}
else
{
if(keep_alive)
req.headers.replace("Connection", "keep-alive");
else
req.headers.erase("Connection");
}
}
template<class Body, class Headers,
class OtherBody, class OtherAllocator>
void
set_connection(bool keep_alive,
message<false, Body, Headers>& resp,
message<true, OtherBody, OtherAllocator> const& req)
{
if(req.version >= 11)
{
if(rfc2616::token_in_list(req["Connection"], "close"))
keep_alive = false;
}
else
{
if(! rfc2616::token_in_list(req["Connection"], "keep-alive"))
keep_alive = false;
}
set_connection(keep_alive, resp);
}
template<class Streambuf, class FieldSequence>
void
write_fields(Streambuf& streambuf, FieldSequence const& fields)
{
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
//static_assert(is_FieldSequence<FieldSequence>::value,
// "FieldSequence requirements not met");
for(auto const& field : fields)
{
detail::write(streambuf, field.name());
detail::write(streambuf, ": ");
detail::write(streambuf, field.value());
detail::write(streambuf, "\r\n");
}
}
template<bool isRequest, class Body, class Headers>
bool
is_keep_alive(message<isRequest, Body, Headers> const& msg)
{
if(msg.version >= 11)
{
if(rfc2616::token_in_list(
msg.headers["Connection"], "close"))
return false;
return true;
}
if(rfc2616::token_in_list(
msg.headers["Connection"], "keep-alive"))
return true;
return false;
}
template<bool isRequest, class Body, class Headers>
bool
is_upgrade(message<isRequest, Body, Headers> const& msg)
{
if(msg.version < 11)
return false;
if(rfc2616::token_in_list(
msg.headers["Connection"], "upgrade"))
return true;
return false;
}
} // http
} // beast
#include <beast/http/impl/message.ipp>
#endif

View File

@@ -0,0 +1,297 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_READ_IPP_H_INCLUDED
#define BEAST_HTTP_READ_IPP_H_INCLUDED
#include <beast/http/type_check.h>
#include <beast/asio/async_completion.h>
#include <beast/asio/bind_handler.h>
#include <beast/asio/handler_alloc.h>
#include <cassert>
namespace beast {
namespace http {
namespace detail {
template<class Stream, class Streambuf,
bool isRequest, class Body, class Headers,
class Handler>
class read_op
{
using alloc_type =
handler_alloc<char, Handler>;
using parser_type =
parser<isRequest, Body, Headers>;
using message_type =
message<isRequest, Body, Headers>;
struct data
{
Stream& s;
Streambuf& sb;
message_type& m;
parser_type p;
Handler h;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, Stream& s_,
Streambuf& sb_, message_type& m_)
: s(s_)
, sb(sb_)
, m(m_)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
read_op(read_op&&) = default;
read_op(read_op const&) = default;
template<class DeducedHandler, class... Args>
read_op(DeducedHandler&& h, Stream&s, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), s,
std::forward<Args>(args)...))
{
(*this)(error_code{}, 0, false);
}
void
operator()(error_code ec,
std::size_t bytes_transferred, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, read_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, read_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(read_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, read_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class Stream, class Streambuf,
bool isRequest, class Body, class Headers,
class Handler>
void
read_op<Stream, Streambuf, isRequest, Body, Headers, Handler>::
operator()(error_code ec, std::size_t bytes_transferred, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(d.state != 99)
{
switch(d.state)
{
case 0:
{
auto const used =
d.p.write(d.sb.data(), ec);
if(ec)
{
// call handler
d.state = 99;
d.s.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
return;
}
d.sb.consume(used);
if(d.p.complete())
{
// call handler
d.state = 99;
d.m = d.p.release();
d.s.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
return;
}
d.state = 1;
break;
}
case 1:
// read
d.state = 2;
d.s.async_read_some(d.sb.prepare(
read_size_helper(d.sb, 65536)),
std::move(*this));
return;
// got data
case 2:
{
if(ec == boost::asio::error::eof)
{
if(! d.p.started())
{
// call handler
d.state = 99;
break;
}
// Caller will see eof on next read.
ec = {};
d.p.write_eof(ec);
if(! ec)
{
assert(d.p.complete());
d.m = d.p.release();
}
// call handler
d.state = 99;
break;
}
if(ec)
{
// call handler
d.state = 99;
break;
}
d.sb.commit(bytes_transferred);
d.sb.consume(d.p.write(d.sb.data(), ec));
if(ec)
{
// call handler
d.state = 99;
break;
}
if(d.p.complete())
{
// call handler
d.state = 99;
d.m = d.p.release();
break;
}
d.state = 1;
break;
}
}
}
d.h(ec);
}
} // detail
//------------------------------------------------------------------------------
template<class SyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers>
void
read(SyncReadStream& stream, Streambuf& streambuf,
message<isRequest, Body, Headers>& m,
error_code& ec)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
parser<isRequest, Body, Headers> p;
for(;;)
{
auto used =
p.write(streambuf.data(), ec);
if(ec)
return;
streambuf.consume(used);
if(p.complete())
{
m = p.release();
break;
}
streambuf.commit(stream.read_some(
streambuf.prepare(read_size_helper(
streambuf, 65536)), ec));
if(ec && ec != boost::asio::error::eof)
return;
if(ec == boost::asio::error::eof)
{
if(! p.started())
return;
// Caller will see eof on next read.
ec = {};
p.write_eof(ec);
if(ec)
return;
assert(p.complete());
m = p.release();
break;
}
}
}
template<class AsyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers,
class CompletionToken>
auto
async_read(AsyncReadStream& stream, Streambuf& streambuf,
message<isRequest, Body, Headers>& m,
CompletionToken&& token)
{
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
beast::async_completion<CompletionToken,
void(error_code)> completion(token);
detail::read_op<AsyncReadStream, Streambuf,
isRequest, Body, Headers, decltype(
completion.handler)>{completion.handler,
stream, streambuf, m};
return completion.result.get();
}
} // http
} // beast
#endif

View File

@@ -0,0 +1,396 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_WRITE_IPP_INCLUDED
#define BEAST_HTTP_WRITE_IPP_INCLUDED
#include <beast/http/chunk_encode.h>
#include <beast/http/resume_context.h>
#include <beast/http/type_check.h>
#include <beast/http/detail/writes.h>
#include <beast/http/detail/write_preparation.h>
#include <beast/asio/append_buffers.h>
#include <beast/asio/async_completion.h>
#include <beast/asio/bind_handler.h>
#include <beast/asio/handler_alloc.h>
#include <beast/asio/streambuf.h>
#include <beast/asio/type_check.h>
#include <boost/asio/write.hpp>
#include <boost/logic/tribool.hpp>
#include <condition_variable>
#include <mutex>
#include <beast/cxx17/type_traits.h> // <type_traits>
namespace beast {
namespace http {
namespace detail {
template<class Stream, class Handler,
bool isRequest, class Body, class Headers>
class write_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
Stream& s;
// VFALCO How do we use handler_alloc in write_preparation?
write_preparation<
isRequest, Body, Headers> wp;
Handler h;
resume_context resume;
resume_context copy;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, Stream& s_,
message<isRequest, Body, Headers> const& m_)
: s(s_)
, wp(m_)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
write_op(write_op&&) = default;
write_op(write_op const&) = default;
template<class DeducedHandler, class... Args>
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), s,
std::forward<Args>(args)...))
{
auto& d = *d_;
d.resume = {
[self = *this]() mutable
{
self.d_->cont = false;
auto& ios = self.d_->s.get_io_service();
ios.dispatch(bind_handler(std::move(self),
error_code{}, 0, false));
}};
d.copy = d.resume;
(*this)(error_code{}, 0, false);
}
explicit
write_op(std::shared_ptr<data> d)
: d_(std::move(d))
{
}
void
operator()(error_code ec,
std::size_t bytes_transferred, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(write_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, write_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class Stream, class Handler,
bool isRequest, class Body, class Headers>
void
write_op<Stream, Handler, isRequest, Body, Headers>::
operator()(error_code ec, std::size_t, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
{
d.wp.init(ec);
if(ec)
{
// call handler
d.state = 99;
d.s.get_io_service().post(bind_handler(
std::move(*this), ec, 0, false));
return;
}
d.state = 1;
break;
}
case 1:
{
auto const result = d.wp.w(std::move(d.copy), ec,
[&](auto const& buffers)
{
// write headers and body
if(d.wp.chunked)
boost::asio::async_write(d.s,
append_buffers(d.wp.sb.data(),
chunk_encode(buffers)),
std::move(*this));
else
boost::asio::async_write(d.s,
append_buffers(d.wp.sb.data(),
buffers), std::move(*this));
});
if(ec)
{
// call handler
d.state = 99;
d.s.get_io_service().post(bind_handler(
std::move(*this), ec, false));
return;
}
if(boost::indeterminate(result))
{
// suspend
d.copy = d.resume;
return;
}
if(result)
d.state = d.wp.chunked ? 4 : 5;
else
d.state = 2;
return;
}
// sent headers and body
case 2:
d.wp.sb.consume(d.wp.sb.size());
d.state = 3;
break;
case 3:
{
auto const result = d.wp.w(std::move(d.copy), ec,
[&](auto const& buffers)
{
// write body
if(d.wp.chunked)
boost::asio::async_write(d.s,
chunk_encode(buffers),
std::move(*this));
else
boost::asio::async_write(d.s,
buffers, std::move(*this));
});
if(ec)
{
// call handler
d.state = 99;
break;
}
if(boost::indeterminate(result))
{
// suspend
d.copy = d.resume;
return;
}
if(result)
d.state = d.wp.chunked ? 4 : 5;
else
d.state = 2;
return;
}
case 4:
// VFALCO Unfortunately the current interface to the
// Writer concept prevents us from using coalescing the
// final body chunk with the final chunk delimiter.
//
// write final chunk
d.state = 5;
boost::asio::async_write(d.s,
chunk_encode_final(), std::move(*this));
return;
case 5:
if(d.wp.close)
{
// VFALCO TODO Decide on an error code
ec = boost::asio::error::eof;
}
d.state = 99;
break;
}
}
d.h(ec);
d.resume = {};
d.copy = {};
}
} // detail
//------------------------------------------------------------------------------
template<class SyncWriteStream,
bool isRequest, class Body, class Headers>
void
write(SyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg,
boost::system::error_code& ec)
{
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec);
if(ec)
return;
std::mutex m;
std::condition_variable cv;
bool ready = false;
resume_context resume{
[&]
{
std::lock_guard<std::mutex> lock(m);
ready = true;
cv.notify_one();
}};
auto copy = resume;
for(;;)
{
{
auto result = wp.w(std::move(copy), ec,
[&](auto const& buffers)
{
// write headers and body
if(wp.chunked)
boost::asio::write(stream, append_buffers(
wp.sb.data(), chunk_encode(buffers)), ec);
else
boost::asio::write(stream, append_buffers(
wp.sb.data(), buffers), ec);
});
if(ec)
return;
if(result)
break;
if(boost::indeterminate(result))
{
boost::asio::write(stream, wp.sb.data(), ec);
if(ec)
return;
wp.sb.consume(wp.sb.size());
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
}
wp.sb.consume(wp.sb.size());
for(;;)
{
auto result = wp.w(std::move(copy), ec,
[&](auto const& buffers)
{
// write body
if(wp.chunked)
boost::asio::write(stream,
chunk_encode(buffers), ec);
else
boost::asio::write(stream, buffers, ec);
});
if(ec)
return;
if(result)
break;
if(boost::indeterminate(result))
{
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
}
}
if(wp.chunked)
{
// VFALCO Unfortunately the current interface to the
// Writer concept prevents us from using coalescing the
// final body chunk with the final chunk delimiter.
//
// write final chunk
boost::asio::write(stream, chunk_encode_final(), ec);
if(ec)
return;
}
if(wp.close)
{
// VFALCO TODO Decide on an error code
ec = boost::asio::error::eof;
}
}
template<class AsyncWriteStream,
bool isRequest, class Body, class Headers,
class CompletionToken>
auto
async_write(AsyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg,
CompletionToken&& token)
{
static_assert(
is_AsyncWriteStream<AsyncWriteStream>::value,
"AsyncWriteStream requirements not met");
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
beast::async_completion<CompletionToken,
void(error_code)> completion(token);
detail::write_op<AsyncWriteStream, decltype(completion.handler),
isRequest, Body, Headers>{completion.handler, stream, msg};
return completion.result.get();
}
} // http
} // beast
#endif

View File

@@ -20,12 +20,171 @@
#ifndef BEAST_HTTP_MESSAGE_H_INCLUDED
#define BEAST_HTTP_MESSAGE_H_INCLUDED
#include <beast/http/basic_parser.h>
#include <beast/http/headers.h>
#include <beast/http/method.h>
#include <beast/asio/buffers_debug.h>
#include <beast/asio/type_check.h>
#include <memory>
#include <ostream>
#include <string>
namespace beast {
namespace http {
namespace detail {
struct request_fields
{
http::method_t method;
std::string url;
};
struct response_fields
{
int status;
std::string reason;
};
} // detail
struct request_params
{
http::method_t method;
std::string url;
int version;
};
struct response_params
{
int status;
std::string reason;
int version;
};
/** A HTTP message.
A message can be a request or response, depending on the `isRequest`
template argument value. Requests and responses have different types,
so functions may be overloaded on them if desired.
The `Body` template argument type determines the model used
to read or write the content body of the message.
@tparam isRequest `true` if this is a request.
@tparam Body A type meeting the requirements of Body.
@tparam Headers A type meeting the requirements of Headers.
*/
template<bool isRequest, class Body, class Headers>
struct message
: std::conditional_t<isRequest,
detail::request_fields, detail::response_fields>
{
/** The trait type characterizing the body.
The body member will be of type body_type::value_type.
*/
using body_type = Body;
using headers_type = Headers;
using is_request =
std::integral_constant<bool, isRequest>;
int version; // 10 or 11
headers_type headers;
typename Body::value_type body;
message();
message(message&&) = default;
message(message const&) = default;
message& operator=(message&&) = default;
message& operator=(message const&) = default;
/** Construct a HTTP request.
*/
explicit
message(request_params params);
/** Construct a HTTP response.
*/
explicit
message(response_params params);
/// Serialize the request or response line to a Streambuf.
template<class Streambuf>
void
write_firstline(Streambuf& streambuf) const
{
write_firstline(streambuf,
std::integral_constant<bool, isRequest>{});
}
/// Diagnostics only
template<bool, class, class>
friend
std::ostream&
operator<<(std::ostream& os,
message const& m);
private:
template<class Streambuf>
void
write_firstline(Streambuf& streambuf,
std::true_type) const;
template<class Streambuf>
void
write_firstline(Streambuf& streambuf,
std::false_type) const;
};
#if ! GENERATING_DOCS
/// A typical HTTP request
template<class Body,
class Headers = basic_headers<std::allocator<char>>>
using request = message<true, Body, Headers>;
/// A typical HTTP response
template<class Body,
class Headers = basic_headers<std::allocator<char>>>
using response = message<false, Body, Headers>;
#endif
// For diagnostic output only
template<bool isRequest, class Body, class Headers>
std::ostream&
operator<<(std::ostream& os,
message<isRequest, Body, Headers> const& m);
/// Write a FieldSequence to a Streambuf.
template<class Streambuf, class FieldSequence>
void
write_fields(Streambuf& streambuf, FieldSequence const& fields);
/// Returns `true` if a message indicates a keep alive
template<bool isRequest, class Body, class Headers>
bool
is_keep_alive(message<isRequest, Body, Headers> const& msg);
/// Returns `true` if a message indicates a HTTP Upgrade request or response
template<bool isRequest, class Body, class Headers>
bool
is_upgrade(message<isRequest, Body, Headers> const& msg);
} // http
} // beast
#include <beast/http/impl/message.ipp>
//------------------------------------------------------------------------------
#include <beast/http/method.h>
#include <beast/http/headers.h>
#include <beast/ci_char_traits.h>
#include <boost/intrusive/list.hpp>
#include <boost/intrusive/set.hpp>
#include <beast/http/detail/writes.h>
#include <algorithm>
#include <cctype>
#include <ostream>
@@ -34,7 +193,7 @@
#include <utility>
namespace beast {
namespace http {
namespace deprecated_http {
inline
std::pair<int, int>
@@ -70,17 +229,16 @@ private:
public:
~message() = default;
message (message const&) = delete;
message& operator= (message const&) = delete;
message (message const&) = default;
message (message&& other) = default;
message& operator= (message const&) = default;
message& operator= (message&& other) = default;
template <class = void>
message();
message (message&& other) = default;
message& operator= (message&& other) = default;
// Memberspace
beast::http::headers headers;
beast::http::headers<std::allocator<char>> headers;
bool
request() const
@@ -221,49 +379,31 @@ write (Streambuf& stream, message const& m)
{
if (m.request())
{
write (stream, to_string(m.method()));
write (stream, " ");
write (stream, m.url());
write (stream, " HTTP/");
write (stream, std::to_string(m.version().first));
write (stream, ".");
write (stream, std::to_string(m.version().second));
http::detail::write (stream, to_string(m.method()));
http::detail::write (stream, " ");
http::detail::write (stream, m.url());
http::detail::write (stream, " HTTP/");
http::detail::write (stream, std::to_string(m.version().first));
http::detail::write (stream, ".");
http::detail::write (stream, std::to_string(m.version().second));
}
else
{
write (stream, "HTTP/");
write (stream, std::to_string(m.version().first));
write (stream, ".");
write (stream, std::to_string(m.version().second));
write (stream, " ");
write (stream, std::to_string(m.status()));
write (stream, " ");
write (stream, m.reason());
http::detail::write (stream, "HTTP/");
http::detail::write (stream, std::to_string(m.version().first));
http::detail::write (stream, ".");
http::detail::write (stream, std::to_string(m.version().second));
http::detail::write (stream, " ");
http::detail::write (stream, std::to_string(m.status()));
http::detail::write (stream, " ");
http::detail::write (stream, m.reason());
}
write (stream, "\r\n");
write(stream, m.headers);
write (stream, "\r\n");
http::detail::write (stream, "\r\n");
write_fields(stream, m.headers);
http::detail::write (stream, "\r\n");
}
template <class = void>
std::string
to_string (message const& m)
{
std::stringstream ss;
if (m.request())
ss << to_string(m.method()) << " " << m.url() << " HTTP/" <<
std::to_string(m.version().first) << "." <<
std::to_string(m.version().second) << "\r\n";
else
ss << "HTTP/" << std::to_string(m.version().first) << "." <<
std::to_string(m.version().second) << " " <<
std::to_string(m.status()) << " " << m.reason() << "\r\n";
ss << to_string(m.headers);
ss << "\r\n";
return ss.str();
}
} // http
} // deprecated_http
} // beast
#endif

View File

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

View File

@@ -20,6 +20,157 @@
#ifndef BEAST_HTTP_PARSER_H_INCLUDED
#define BEAST_HTTP_PARSER_H_INCLUDED
#include <beast/http/basic_parser.h>
#include <beast/http/error.h>
#include <beast/http/message.h>
#include <boost/optional.hpp>
#include <functional>
#include <beast/cxx17/type_traits.h> // <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 parser
: public basic_parser<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:
parser(parser&&) = default;
parser()
: http::basic_parser<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::basic_parser<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
//------------------------------------------------------------------------------
//
// LEGACY
//
#include <beast/http/basic_parser.h>
#include <beast/http/message.h>
#include <beast/http/body.h>
#include <functional>
@@ -27,167 +178,105 @@
#include <utility>
namespace beast {
namespace http {
namespace deprecated_http {
/** Parser for HTTP messages.
The result is stored in a message object.
*/
class parser : public beast::http::basic_parser
class parser
: public beast::http::basic_parser<parser>
{
private:
std::reference_wrapper <message> message_;
// friend class basic_parser<parser>;
message& m_;
std::function<void(void const*, std::size_t)> write_body_;
public:
parser(parser&&) = default;
parser(parser const&) = delete;
parser& operator=(parser&&) = delete;
parser& operator=(parser const&) = delete;
/** Construct a parser for HTTP request or response.
The headers plus request or status line are stored in message.
The content-body, if any, is passed as a series of calls to
the write_body function. Transfer encodings are applied before
any data is passed to the write_body function.
*/
parser (std::function<void(void const*, std::size_t)> write_body,
parser(std::function<void(void const*, std::size_t)> write_body,
message& m, bool request)
: beast::http::basic_parser (request)
, message_(m)
: basic_parser(request)
, m_(m)
, write_body_(std::move(write_body))
{
message_.get().request(request);
m_.request(request);
}
parser (message& m, body& b, bool request)
: beast::http::basic_parser (request)
, message_(m)
parser(message& m, body& b, bool request)
: basic_parser(request)
, m_(m)
{
write_body_ = [&b](void const* data, std::size_t size)
{
b.write(data, size);
};
message_.get().request(request);
m_.request(request);
}
parser& operator= (parser&& other) = default;
private:
template <class = void>
//private:
void
do_start ();
template <class = void>
bool
do_request (method_t method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade);
template <class = void>
bool
do_response (int status, std::string const& text,
int major, int minor, bool keep_alive, bool upgrade);
template <class = void>
void
do_field (std::string const& field, std::string const& value);
template <class = void>
void
do_body (void const* data, std::size_t bytes);
template <class = void>
void
do_complete();
void
on_start () override
on_start()
{
}
void
on_headers_complete(error_code&)
{
do_start();
}
bool
on_request (method_t method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade) override
on_request(http::method_t method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade)
{
return do_request (method, url, major, minor, keep_alive, upgrade);
m_.method(method);
m_.url(url);
m_.version(major, minor);
m_.keep_alive(keep_alive);
m_.upgrade(upgrade);
return true;
}
bool
on_response (int status, std::string const& text,
int major, int minor, bool keep_alive, bool upgrade) override
on_response(int status, std::string const& text,
int major, int minor, bool keep_alive, bool upgrade)
{
return do_response (status, text, major, minor, keep_alive, upgrade);
m_.status(status);
m_.reason(text);
m_.version(major, minor);
m_.keep_alive(keep_alive);
m_.upgrade(upgrade);
return true;
}
void
on_field (std::string const& field, std::string const& value) override
on_field(std::string const& field, std::string const& value)
{
do_field (field, value);
m_.headers.insert(field, value);
}
void
on_body (void const* data, std::size_t bytes) override
on_body(void const* data, std::size_t bytes, error_code&)
{
do_body (data, bytes);
write_body_(data, bytes);
}
void
on_complete() override
on_complete()
{
do_complete();
}
};
//------------------------------------------------------------------------------
template <class>
void
parser::do_start()
{
}
template <class>
bool
parser::do_request (method_t method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade)
{
message_.get().method (method);
message_.get().url (url);
message_.get().version (major, minor);
message_.get().keep_alive (keep_alive);
message_.get().upgrade (upgrade);
return true;
}
template <class>
bool
parser::do_response (int status, std::string const& text,
int major, int minor, bool keep_alive, bool upgrade)
{
message_.get().status (status);
message_.get().reason (text);
message_.get().version (major, minor);
message_.get().keep_alive (keep_alive);
message_.get().upgrade (upgrade);
return true;
}
template <class>
void
parser::do_field (std::string const& field, std::string const& value)
{
message_.get().headers.append (field, value);
}
template <class>
void
parser::do_body (void const* data, std::size_t bytes)
{
write_body_(data, bytes);
}
template <class>
void
parser::do_complete()
{
}
} // http
} // deprecated_http
} // beast
#endif

119
src/beast/beast/http/read.h Normal file
View File

@@ -0,0 +1,119 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_READ_H_INCLUDED
#define BEAST_HTTP_READ_H_INCLUDED
#include <beast/http/error.h>
#include <beast/http/parser.h>
#include <beast/http/type_check.h>
#include <boost/asio/buffer.hpp>
#include <boost/system/error_code.hpp>
namespace beast {
namespace http {
/** Read a HTTP message from a stream.
@param stream The stream to read the message from.
@param streambuf A Streambuf used to hold unread bytes. The
implementation may read past the end of the message. The extra
bytes are stored here, to be presented in a subsequent call to
read.
@param msg An object used to store the read message. Any
contents will be overwritten.
@throws boost::system::system_error on failure.
*/
template<class SyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers>
void
read(SyncReadStream& stream, Streambuf& streambuf,
message<isRequest, Body, Headers>& msg)
{
error_code ec;
read(stream, streambuf, msg, ec);
if(ec)
throw boost::system::system_error{ec};
}
/** Read a HTTP message from a stream.
@param stream The stream to read the message from.
@param streambuf A Streambuf used to hold unread bytes. The
implementation may read past the end of the message. The extra
bytes are stored here, to be presented in a subsequent call to
read.
@param msg An object used to store the read message. Any
contents will be overwritten.
@param ec Set to the error, if any occurred.
*/
template<class SyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers>
void
read(SyncReadStream& stream, Streambuf& streambuf,
message<isRequest, Body, Headers>& msg,
error_code& ec);
/** Start reading a HTTP message from a stream asynchronously.
@param stream The stream to read the message from.
@param streambuf A Streambuf used to hold unread bytes. The
implementation may read past the end of the message. The extra
bytes are stored here, to be presented in a subsequent call to
async_read.
@param msg An object used to store the read message. Any
contents will be overwritten.
@param token The handler to be called when the request completes.
Copies will be made of the handler as required. The equivalent
function signature of the handler must be:
@code void handler(
error_code const& error // result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using boost::asio::io_service::post().
*/
template<class AsyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers,
class CompletionToken>
#if GENERATING_DOCS
void_or_deduced
#else
auto
#endif
async_read(AsyncReadStream& stream, Streambuf& streambuf,
message<isRequest, Body, Headers>& msg,
CompletionToken&& token);
} // http
} // beast
#include <beast/http/impl/read.ipp>
#endif

View File

@@ -1,75 +1,32 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013: Vinnie Falco <vinnie.falco@gmail.com>
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use: copy: modify: and/or distribute this software for any
purpose with or without fee is hereby granted: provided that the above
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL : DIRECT: INDIRECT: OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE: DATA OR PROFITS: WHETHER IN AN
ACTION OF CONTRACT: NEGLIGENCE OR OTHER TORTIOUS ACTION: ARISING OUT OF
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <beast/http/method.h>
#include <cassert>
#ifndef BEAST_HTTP_REASON_H_INCLUDED
#define BEAST_HTTP_REASON_H_INCLUDED
namespace beast {
namespace http {
std::string
to_string (method_t m)
{
switch(m)
{
case method_t::http_delete: return "DELETE";
case method_t::http_get: return "GET";
case method_t::http_head: return "HEAD";
case method_t::http_post: return "POST";
case method_t::http_put: return "PUT";
case method_t::http_connect: return "CONNECT";
case method_t::http_options: return "OPTIONS";
case method_t::http_trace: return "TRACE";
case method_t::http_copy: return "COPY";
case method_t::http_lock: return "LOCK";
case method_t::http_mkcol: return "MKCOL";
case method_t::http_move: return "MOVE";
case method_t::http_propfind: return "PROPFIND";
case method_t::http_proppatch: return "PROPPATCH";
case method_t::http_search: return "SEARCH";
case method_t::http_unlock: return "UNLOCK";
case method_t::http_report: return "REPORT";
case method_t::http_mkactivity: return "MKACTIVITY";
case method_t::http_checkout: return "CHECKOUT";
case method_t::http_merge: return "MERGE";
case method_t::http_msearch: return "MSEARCH";
case method_t::http_notify: return "NOTIFY";
case method_t::http_subscribe: return "SUBSCRIBE";
case method_t::http_unsubscribe: return "UNSUBSCRIBE";
case method_t::http_patch: return "PATCH";
case method_t::http_purge: return "PURGE";
default:
assert(false);
break;
};
return "GET";
}
std::string
status_text (int status)
/** Returns the text for a known status code integer. */
template<class = void>
char const*
reason_string(int status)
{
switch(status)
{
@@ -88,7 +45,6 @@ status_text (int status)
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
//case 306: return "<reserved>";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
@@ -114,11 +70,15 @@ status_text (int status)
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
case 306: return "<reserved>";
default:
break;
}
return "Unknown HTTP status";
return "<unknown-status>";
}
}
}
} // http
} // beast
#endif

View File

@@ -17,47 +17,30 @@
*/
//==============================================================================
#ifndef BEAST_HTTP_DETAIL_HEADER_TRAITS_H_INCLUDED
#define BEAST_HTTP_DETAIL_HEADER_TRAITS_H_INCLUDED
#ifndef BEAST_HTTP_RESUME_CONTEXT_H_INCLUDED
#define BEAST_HTTP_RESUME_CONTEXT_H_INCLUDED
#include <beast/utility/ci_char_traits.h>
#include <boost/utility/string_ref.hpp>
#include <memory>
#include <string>
#include <functional>
namespace beast {
namespace http {
namespace detail {
// Utilities for dealing with HTTP headers
/** A functor that resumes a write operation.
template <class Allocator = std::allocator <char>>
using basic_field_string =
std::basic_string <char, ci_char_traits, Allocator>;
An rvalue reference to an object of this type is provided by the
write implementation to the `writer` associated with the body of
a message being sent.
using field_string = basic_field_string <>;
using field_string_ref = boost::basic_string_ref <char, ci_char_traits>;
/** Returns `true` if two header fields are the same.
The comparison is case-insensitive.
If it is desired that the `writer` suspend the write operation (for
example, to wait until data is ready), it can take ownership of
the resume context using a move. Then, it returns `boost::indeterminate`
to indicate that the write operation should suspend. Later, the calling
code invokes the resume function and the write operation continues
from where it left off.
*/
template <class Alloc1, class Alloc2>
inline
bool field_eq (
std::basic_string <char, std::char_traits <char>, Alloc1> const& s1,
std::basic_string <char, std::char_traits <char>, Alloc2> const& s2)
{
return field_string_ref (s1.c_str(), s1.size()) ==
field_string_ref (s2.c_str(), s2.size());
}
using resume_context = std::function<void(void)>;
/** Returns the string with leading and trailing LWS removed. */
}
}
}
} // http
} // beast
#endif

View File

@@ -20,10 +20,14 @@
#ifndef BEAST_HTTP_RFC2616_H_INCLUDED
#define BEAST_HTTP_RFC2616_H_INCLUDED
#include <boost/regex.hpp>
#include <boost/range/algorithm/equal.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/utility/string_ref.hpp>
#include <algorithm>
#include <cctype>
#include <string>
#include <iterator>
#include <tuple> // for std::tie, remove ASAP
#include <utility>
#include <vector>
@@ -36,20 +40,34 @@ namespace beast {
*/
namespace rfc2616 {
namespace detail {
struct ci_equal_pred
{
bool operator()(char c1, char c2)
{
// VFALCO TODO Use a table lookup here
return std::tolower(c1) == std::tolower(c2);
}
};
} // detail
/** Returns `true` if `c` is linear white space.
This excludes the CRLF sequence allowed for line continuations.
*/
template <class CharT>
inline
bool
is_lws (CharT c)
is_lws(char c)
{
return c == ' ' || c == '\t';
}
/** Returns `true` if `c` is any whitespace character. */
template <class CharT>
inline
bool
is_white (CharT c)
is_white(char c)
{
switch (c)
{
@@ -61,18 +79,19 @@ is_white (CharT c)
}
/** Returns `true` if `c` is a control character. */
template <class CharT>
inline
bool
is_ctl (CharT c)
is_control(char c)
{
return c <= 31 || c >= 127;
}
/** Returns `true` if `c` is a separator. */
template <class CharT>
inline
bool
is_sep (CharT c)
is_separator(char c)
{
// VFALCO Could use a static table
switch (c)
{
case '(': case ')': case '<': case '>': case '@':
@@ -83,12 +102,20 @@ is_sep (CharT c)
return false;
}
/** Returns `true` if `c` is a character. */
inline
bool
is_char(char c)
{
return c >= 0 && c <= 127;
}
template <class FwdIter>
FwdIter
trim_left (FwdIter first, FwdIter last)
{
return std::find_if_not (first, last,
&is_white <typename FwdIter::value_type>);
is_white);
}
template <class FwdIter>
@@ -166,8 +193,9 @@ trim (std::string const& s)
*/
template <class FwdIt,
class Result = std::vector<
std::basic_string<typename FwdIt::value_type>>,
class Char>
std::basic_string<typename
std::iterator_traits<FwdIt>::value_type>>,
class Char>
Result
split(FwdIt first, FwdIt last, Char delim)
{
@@ -239,7 +267,8 @@ split(FwdIt first, FwdIt last, Char delim)
template <class FwdIt,
class Result = std::vector<
std::basic_string<typename FwdIt::value_type>>>
std::basic_string<typename std::iterator_traits<
FwdIt>::value_type>>>
Result
split_commas(FwdIt first, FwdIt last)
{
@@ -248,11 +277,194 @@ split_commas(FwdIt first, FwdIt last)
template <class Result = std::vector<std::string>>
Result
split_commas(std::string const& s)
split_commas(boost::string_ref const& s)
{
return split_commas(s.begin(), s.end());
}
//------------------------------------------------------------------------------
/** Iterates through a comma separated list.
Meets the requirements of ForwardIterator.
List defined in rfc2616 2.1.
@note Values returned may contain backslash escapes.
*/
class list_iterator
{
using iter_type = boost::string_ref::const_iterator;
iter_type it_;
iter_type end_;
boost::string_ref value_;
public:
using value_type = boost::string_ref;
using pointer = value_type const*;
using reference = value_type const&;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::forward_iterator_tag;
list_iterator(iter_type begin, iter_type end)
: it_(begin)
, end_(end)
{
if(it_ != end_)
increment();
}
bool
operator==(list_iterator const& other) const
{
return other.it_ == it_ && other.end_ == end_
&& other.value_.size() == value_.size();
}
bool
operator!=(list_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return value_;
}
pointer
operator->() const
{
return &*(*this);
}
list_iterator&
operator++()
{
increment();
return *this;
}
list_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
private:
template<class = void>
void
increment();
};
template<class>
void
list_iterator::increment()
{
value_.clear();
while(it_ != end_)
{
if(*it_ == '"')
{
// quoted-string
++it_;
if(it_ == end_)
return;
if(*it_ != '"')
{
auto start = it_;
for(;;)
{
++it_;
if(it_ == end_)
{
value_ = boost::string_ref(
&*start, std::distance(start, it_));
return;
}
if(*it_ == '"')
{
value_ = boost::string_ref(
&*start, std::distance(start, it_));
++it_;
return;
}
}
}
++it_;
}
else if(*it_ == ',')
{
it_++;
continue;
}
else if(is_lws(*it_))
{
++it_;
continue;
}
else
{
auto start = it_;
for(;;)
{
++it_;
if(it_ == end_ ||
*it_ == ',' ||
is_lws(*it_))
{
value_ = boost::string_ref(
&*start, std::distance(start, it_));
return;
}
}
}
}
}
/** Returns true if two strings are equal.
A case-insensitive comparison is used.
*/
inline
bool
ci_equal(boost::string_ref s1, boost::string_ref s2)
{
return boost::range::equal(s1, s2,
detail::ci_equal_pred{});
}
/** Returns a range representing the list. */
inline
auto
make_list(boost::string_ref const& field)
{
return boost::iterator_range<list_iterator>{
list_iterator{field.begin(), field.end()},
list_iterator{field.end(), field.end()}};
}
/** Returns true if the specified token exists in the list.
A case-insensitive comparison is used.
*/
template<class = void>
bool
token_in_list(boost::string_ref const& value,
boost::string_ref const& token)
{
for(auto const& item : make_list(value))
if(ci_equal(item, token))
return true;
return false;
}
} // rfc2616
} // beast

View File

@@ -17,13 +17,10 @@
*/
//==============================================================================
#include <beast/http/impl/joyent_parser.h>
#include <beast/http/src/nodejs_parser.h>
#include <beast/http/method.h>
#include <boost/system/error_code.hpp>
namespace beast {
namespace joyent {
#ifdef _MSC_VER
# pragma warning (push)
# pragma warning (disable: 4127) // conditional expression is constant
@@ -34,33 +31,10 @@ namespace joyent {
# pragma warning (pop)
#endif
}
}
namespace boost {
namespace system {
template <>
struct is_error_code_enum <beast::joyent::http_errno>
: std::true_type
{
};
template <>
struct is_error_condition_enum <beast::joyent::http_errno>
: std::true_type
{
};
}
}
namespace beast {
namespace joyent {
http::method_t
convert_http_method (joyent::http_method m)
beast::http::method_t
convert_http_method(http_method m)
{
using namespace beast;
switch (m)
{
case HTTP_DELETE: return http::method_t::http_delete;
@@ -114,58 +88,3 @@ convert_http_method (joyent::http_method m)
return http::method_t::http_get;
}
boost::system::error_code
convert_http_errno (joyent::http_errno err)
{
class http_error_category_t
: public boost::system::error_category
{
private:
using error_code = boost::system::error_code;
using error_condition = boost::system::error_condition;
public:
char const*
name() const noexcept override
{
return "http_errno";
}
std::string
message (int ev) const override
{
return joyent::http_errno_name (
joyent::http_errno (ev));
}
error_condition
default_error_condition (int ev) const noexcept override
{
return error_condition (ev, *this);
}
bool
equivalent (int code, error_condition const& condition
) const noexcept override
{
return default_error_condition (code) == condition;
}
bool
equivalent (error_code const& code, int condition
) const noexcept override
{
return *this == code.category() &&
code.value() == condition;
}
};
static http_error_category_t http_error_category;
return boost::system::error_code (
err, http_error_category);
}
}
}

View File

@@ -17,28 +17,29 @@
*/
//==============================================================================
#ifndef BEAST_HTTP_JOYENT_PARSER_H_INCLUDED
#define BEAST_HTTP_JOYENT_PARSER_H_INCLUDED
#ifndef BEAST_HTTP_NODEJS_PARSER_H_INCLUDED
#define BEAST_HTTP_NODEJS_PARSER_H_INCLUDED
#include <beast/http/method.h>
// TODO Use <system_error>
#include <beast/http/impl/http-parser/http_parser.h>
#include <boost/system/error_code.hpp>
// Wraps the C-language joyent http parser header in a namespace
beast::http::method_t
convert_http_method(http_method m);
namespace beast {
namespace joyent {
#include <beast/http/impl/http-parser/http_parser.h>
http::method_t
convert_http_method (joyent::http_method m);
boost::system::error_code
convert_http_errno (joyent::http_errno err);
}
}
namespace boost {
namespace system {
template<>
struct is_error_code_enum<http_errno>
: std::true_type
{
};
template<>
struct is_error_condition_enum<http_errno>
: std::true_type
{
};
} // system
} // boost
#endif

View File

@@ -0,0 +1,186 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <BeastConfig.h>
#include <beast/unit_test/suite.h>
#include <beast/unit_test/thread.h>
#include <beast/asio/buffers_debug.h>
#include <beast/asio/placeholders.h>
#include <beast/asio/streambuf.h>
#include <beast/http.h>
#include <boost/asio.hpp>
namespace beast {
namespace http {
namespace test {
class sync_echo_http_server
{
public:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
private:
unit_test::suite& suite_;
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
unit_test::thread thread_;
public:
sync_echo_http_server(
endpoint_type ep, unit_test::suite& suite)
: suite_(suite)
, sock_(ios_)
, acceptor_(ios_)
{
error_code ec;
acceptor_.open(ep.protocol(), ec);
maybe_throw(ec, "open");
acceptor_.bind(ep, ec);
maybe_throw(ec, "bind");
acceptor_.listen(
boost::asio::socket_base::max_connections, ec);
maybe_throw(ec, "listen");
acceptor_.async_accept(sock_,
std::bind(&sync_echo_http_server::on_accept, this,
beast::asio::placeholders::error));
thread_ = unit_test::thread(suite_,
[&]
{
ios_.run();
});
}
~sync_echo_http_server()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
thread_.join();
}
private:
void
fail(error_code ec, std::string what)
{
suite_.log <<
what << ": " << ec.message();
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec &&
ec != boost::asio::error::operation_aborted)
{
fail(ec, what);
throw ec;
}
}
void
on_accept(error_code ec)
{
if(ec == boost::asio::error::operation_aborted)
return;
maybe_throw(ec, "accept");
std::thread{&sync_echo_http_server::do_client, this,
std::move(sock_), boost::asio::io_service::work{
sock_.get_io_service()}}.detach();
acceptor_.async_accept(sock_,
std::bind(&sync_echo_http_server::on_accept, this,
beast::asio::placeholders::error));
}
void
do_client(socket_type sock, boost::asio::io_service::work)
{
error_code ec;
streambuf rb;
for(;;)
{
request<string_body> req;
read(sock, rb, req, ec);
if(ec)
break;
response<string_body> resp(
{100, "OK", req.version});
resp.body = "Completed successfully.";
write(sock, resp, ec);
if(ec)
break;
}
}
};
class http_message_test : public unit_test::suite
{
public:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
void
syncEcho(endpoint_type ep)
{
boost::asio::io_service ios;
socket_type sock(ios);
sock.connect(ep);
streambuf rb;
{
request<string_body> req(
{beast::http::method_t::http_get, "/", 11});
req.body = "Beast.HTTP";
req.headers.replace("Host",
ep.address().to_string() + ":" +
std::to_string(ep.port()));
write(sock, req);
}
{
response<string_body> m;
read(sock, rb, m);
}
}
void
testAsio()
{
endpoint_type ep{
address_type::from_string("127.0.0.1"), 6000};
sync_echo_http_server s(ep, *this);
syncEcho(ep);
}
void run() override
{
testAsio();
pass();
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(http_message,http,beast);
} // test
} // http
} // beast

View File

@@ -23,46 +23,30 @@
#include <utility>
namespace beast {
namespace http {
namespace deprecated_http {
namespace test {
class message_test : public beast::unit_test::suite
class parser_test : public beast::unit_test::suite
{
public:
std::pair <message, bool>
request (std::string const& text)
message
request(std::string const& text)
{
message m;
body b;
parser p (m, b, true);
auto result (p.write (boost::asio::buffer(text)));
message m;
parser p(m, b, true);
auto const used =
p.write(boost::asio::buffer(text));
expect(used == text.size());
p.write_eof();
return std::make_pair (std::move(m), result.first);
}
void
dump()
{
auto const result = request (
"GET / HTTP/1.1\r\n"
//"Connection: Upgrade\r\n"
//"Upgrade: Ripple\r\n"
"Field: \t Value \t \r\n"
"Blib: Continu\r\n"
" ation\r\n"
"Field: Hey\r\n"
"Content-Length: 1\r\n"
"\r\n"
"x"
);
log << result.first.headers;
log << "|" << result.first.headers["Field"] << "|";
return m;
}
void
test_headers()
{
headers h;
h.append("Field", "Value");
beast::http::headers<std::allocator<char>> h;
h.insert("Field", "Value");
expect (h.erase("Field") == 1);
}
@@ -76,14 +60,14 @@ public:
"GET / HTTP/1.1\r\n"
"\r\n"
;
message m;
body b;
message m;
parser p (m, b, true);
auto result (p.write (boost::asio::buffer(text)));
expect (! result.first);
auto result2 (p.write_eof());
expect (! result2);
expect (p.complete());
auto const used = p.write(
boost::asio::buffer(text));
expect(used == text.size());
p.write_eof();
expect(p.complete());
}
{
@@ -92,17 +76,19 @@ public:
"GET\r\n"
"\r\n"
;
message m;
body b;
parser p (m, b, true);
auto result = p.write (boost::asio::buffer(text));
if (expect (result.first))
expect (result.first.message() == "invalid HTTP method");
message m;
parser p(m, b, true);
boost::system::error_code ec;
p.write(boost::asio::buffer(text), ec);
if(expect(ec))
expect(ec.message() == "invalid HTTP method");
}
}
};
BEAST_DEFINE_TESTSUITE(message,http,beast);
BEAST_DEFINE_TESTSUITE(parser,http,beast);
} // http
} // test
} // deprecated_http
} // beast

View File

@@ -0,0 +1,126 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <beast/http/rfc2616.h>
#include <beast/unit_test/suite.h>
#include <string>
#include <vector>
namespace beast {
namespace rfc2616 {
namespace test {
class rfc2616_test : public beast::unit_test::suite
{
public:
void
checkSplit(std::string const& s,
std::vector <std::string> const& expected)
{
auto const parsed = split_commas(s.begin(), s.end());
expect (parsed == expected);
}
void testSplit()
{
checkSplit("", {});
checkSplit(" ", {});
checkSplit(" ", {});
checkSplit("\t", {});
checkSplit(" \t ", {});
checkSplit(",", {});
checkSplit(",,", {});
checkSplit(" ,", {});
checkSplit(" , ,", {});
checkSplit("x", {"x"});
checkSplit(" x", {"x"});
checkSplit(" \t x", {"x"});
checkSplit("x ", {"x"});
checkSplit("x \t", {"x"});
checkSplit(" \t x \t ", {"x"});
checkSplit("\"\"", {});
checkSplit(" \"\"", {});
checkSplit("\"\" ", {});
checkSplit("\"x\"", {"x"});
checkSplit("\" \"", {" "});
checkSplit("\" x\"", {" x"});
checkSplit("\"x \"", {"x "});
checkSplit("\" x \"", {" x "});
checkSplit("\"\tx \"", {"\tx "});
checkSplit("x,y", {"x", "y"});
checkSplit("x ,\ty ", {"x", "y"});
checkSplit("x, y, z", {"x","y","z"});
checkSplit("x, \"y\", z", {"x","y","z"});
checkSplit(",,x,,\"y\",,", {"x","y"});
}
void
checkIter(std::string const& s,
std::vector<std::string> const& expected)
{
std::vector<std::string> got;
for(auto const& v : make_list(s))
got.emplace_back(v);
expect(got == expected);
}
void
testIter()
{
checkIter("x", {"x"});
checkIter(" x", {"x"});
checkIter("x\t", {"x"});
checkIter("\tx ", {"x"});
checkIter(",x", {"x"});
checkIter("x,", {"x"});
checkIter(",x,", {"x"});
checkIter(" , x\t,\t", {"x"});
checkIter("x,y", {"x", "y"});
checkIter("x, ,y ", {"x", "y"});
checkIter("\"x\"", {"x"});
}
void
testList()
{
expect(token_in_list("x", "x"));
expect(token_in_list("x,y", "x"));
expect(token_in_list("x,y", "y"));
expect(token_in_list("x, y ", "y"));
expect(token_in_list("x", "X"));
expect(token_in_list("Y", "y"));
expect(token_in_list("close, keepalive", "close"));
expect(token_in_list("close, keepalive", "keepalive"));
}
void
run()
{
testSplit();
testIter();
testList();
}
};
BEAST_DEFINE_TESTSUITE(rfc2616,http,beast);
} // test
} // rfc2616
} // beast

View File

@@ -0,0 +1,109 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_STREAMBUF_BODY_H_INCLUDED
#define BEAST_HTTP_STREAMBUF_BODY_H_INCLUDED
#include <beast/http/error.h>
#include <beast/http/message.h>
#include <beast/asio/append_buffers.h>
#include <beast/asio/streambuf.h>
#include <memory>
#include <string>
namespace beast {
namespace http {
/** A Body represented by a Streambuf
*/
template<class Streambuf>
struct basic_streambuf_body
{
using value_type = Streambuf;
class reader
{
value_type& sb_;
public:
template<bool isRequest, class Allocator>
explicit
reader(message<isRequest,
basic_streambuf_body, Allocator>& m) noexcept
: sb_(m.body)
{
}
void
write(void const* data,
std::size_t size, error_code&) noexcept
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
sb_.commit(buffer_copy(
sb_.prepare(size), buffer(data, size)));
}
};
class writer
{
Streambuf const& body_;
public:
template<bool isRequest, class Allocator>
explicit
writer(message<isRequest, basic_streambuf_body,
Allocator> const& m)
: body_(m.body)
{
}
void
init(error_code& ec)
{
}
std::size_t
content_length() const
{
return body_.size();
}
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
write(body_.data());
return true;
}
auto
data() const noexcept
{
return body_.data();
}
};
};
using streambuf_body = basic_streambuf_body<streambuf>;
} // http
} // beast
#endif

View File

@@ -0,0 +1,99 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_STRING_BODY_H_INCLUDED
#define BEAST_HTTP_STRING_BODY_H_INCLUDED
#include <beast/http/error.h>
#include <beast/http/message.h>
#include <beast/http/resume_context.h>
#include <beast/asio/append_buffers.h>
#include <beast/asio/streambuf.h>
#include <memory>
#include <string>
namespace beast {
namespace http {
/** A Body represented by a std::string.
*/
struct string_body
{
using value_type = std::string;
class reader
{
value_type& s_;
public:
template<bool isRequest, class Allocator>
explicit
reader(message<isRequest,
string_body, Allocator>& m) noexcept
: s_(m.body)
{
}
void
write(void const* data,
std::size_t size, error_code&) noexcept
{
auto const n = s_.size();
s_.resize(n + size);
std::memcpy(&s_[n], data, size);
}
};
class writer
{
value_type const& body_;
public:
template<bool isRequest, class Allocator>
explicit
writer(message<isRequest, string_body, Allocator> const& msg)
: body_(msg.body)
{
}
void
init(error_code& ec)
{
}
std::size_t
content_length() const
{
return body_.size();
}
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
write(boost::asio::buffer(body_));
return true;
}
};
};
} // http
} // beast
#endif

View File

@@ -1,86 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#if BEAST_INCLUDE_BEASTCONFIG
#include "../../BeastConfig.h"
#endif
#include <beast/http/rfc2616.h>
#include <beast/unit_test/suite.h>
#include <string>
#include <vector>
namespace beast {
namespace rfc2616 {
class rfc2616_test : public beast::unit_test::suite
{
public:
void
check (std::string const& s,
std::vector <std::string> const& expected)
{
auto const parsed = split_commas(s.begin(), s.end());
expect (parsed == expected);
}
void test_split_commas()
{
testcase("split_commas");
check ("", {});
check (" ", {});
check (" ", {});
check ("\t", {});
check (" \t ", {});
check (",", {});
check (",,", {});
check (" ,", {});
check (" , ,", {});
check ("x", {"x"});
check (" x", {"x"});
check (" \t x", {"x"});
check ("x ", {"x"});
check ("x \t", {"x"});
check (" \t x \t ", {"x"});
check ("\"\"", {});
check (" \"\"", {});
check ("\"\" ", {});
check ("\"x\"", {"x"});
check ("\" \"", {" "});
check ("\" x\"", {" x"});
check ("\"x \"", {"x "});
check ("\" x \"", {" x "});
check ("\"\tx \"", {"\tx "});
check ("x,y", { "x", "y" });
check ("x ,\ty ", { "x", "y" });
check ("x, y, z", {"x","y","z"});
check ("x, \"y\", z", {"x","y","z"});
}
void
run()
{
test_split_commas();
}
};
BEAST_DEFINE_TESTSUITE(rfc2616,http,beast);
}
}

View File

@@ -0,0 +1,84 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_TYPE_CHECK_H_INCLUDED
#define BEAST_HTTP_TYPE_CHECK_H_INCLUDED
#include <beast/http/error.h>
#include <beast/http/message.h>
#include <beast/http/resume_context.h>
#include <beast/asio/type_check.h>
#include <boost/asio/buffer.hpp>
#include <boost/logic/tribool.hpp>
#include <boost/system/error_code.hpp>
#include <functional>
#include <beast/cxx17/type_traits.h> // <type_traits>
namespace beast {
namespace http {
#if GENERATING_DOCS
namespace detail {
#else
namespace concept {
#endif
struct Reader
{
template<bool isRequest, class Body, class Headers>
Reader(message<isRequest, Body, Headers>&) noexcept;
void write(void const*, std::size_t, error_code&) noexcept;
};
} // concept
/// Evaluates to std::true_type if `T` models Body
template<class T>
struct is_Body : std::true_type
{
};
/// Evalulates to std::true_type if Body has a reader
template<class T>
struct is_ReadableBody : std::true_type
{
};
/// Evalulates to std::true_type if Body has a writer
template<class T>
struct is_WritableBody : std::true_type
{
};
/// Evaluates to std::true_type if `T` models HTTPMessage
template<class T>
struct is_HTTPMessage : std::false_type
{
};
/// Evaluates to std::true_type if `HTTPMessage` is a request
template<class HTTPMessage>
struct is_HTTPRequest : std::true_type
{
};
} // http
} // beast
#endif

View File

@@ -0,0 +1,103 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_WRITE_H_INCLUDED
#define BEAST_HTTP_WRITE_H_INCLUDED
#include <beast/http/error.h>
#include <beast/http/message.h>
#include <boost/system/error_code.hpp>
#include <type_traits>
namespace beast {
namespace http {
/** Write a HTTP message to a stream.
@param stream The stream to send the message on.
@param msg The message to send.
@throws boost::system::error code on failure.
*/
template<class SyncWriteStream,
bool isRequest, class Body, class Headers>
void
write(SyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg)
{
error_code ec;
write(stream, msg, ec);
if(ec)
throw boost::system::system_error{ec};
}
/** Write a HTTP message to a stream.
@param stream The stream to send the message on.
@param msg The message to send.
@param ec Set to the error, if any occurred.
*/
template<class SyncWriteStream,
bool isRequest, class Body, class Headers>
void
write(SyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg,
error_code& ec);
/** Start writing a HTTP message to a stream asynchronously.
@param stream The stream to send the message on.
@param msg The message to send.
@param token The handler to be called when the request completes.
Copies will be made of the handler as required. The equivalent
function signature of the handler must be:
@code void handler(
error_code const& error // result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using boost::asio::io_service::post().
@note The message must remain valid at least until the
completion handler is called, no copies are made.
*/
template<class AsyncWriteStream,
bool isRequest, class Body, class Headers,
class CompletionToken>
#if GENERATING_DOCS
void_or_deduced
#else
auto
#endif
async_write(AsyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg,
CompletionToken&& token);
} // http
} // beast
#include <beast/http/impl/write.ipp>
#endif

View File

@@ -0,0 +1,27 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <beast/http/src/test/beast_http_chunked_encoder_test.cpp>
#include <beast/http/src/test/beast_http_message_test.cpp>
#include <beast/http/src/test/beast_http_parser_test.cpp>
#include <beast/http/src/test/beast_http_rfc2616_test.cpp>
// VFALCO Must come last otherwise Windows 10 SDK
// gets a compile error in winnt.h
#include <beast/http/src/beast_http_nodejs_parser.cpp>

View File

@@ -0,0 +1,19 @@
#
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
import os ;
exe http_crawl :
../beast/http/src/beast_http_nodejs_parser.cpp
http_crawl.cpp
urls_large_data.cpp
;
exe http_server :
../beast/http/src/beast_http_nodejs_parser.cpp
http_server.cpp
;

View File

@@ -0,0 +1,94 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
#define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
#include <beast/http/message.h>
#include <beast/http/resume_context.h>
#include <boost/asio/buffer.hpp>
#include <boost/filesystem.hpp>
#include <cstdio>
#include <cstdint>
namespace beast {
namespace http {
struct file_body
{
using value_type = std::string;
class writer
{
std::size_t size_;
std::size_t offset_ = 0;
std::string const& path_;
FILE* file_ = nullptr;
char buf_[4096];
std::size_t buf_len_;
public:
static bool constexpr is_single_pass = false;
template<bool isRequest, class Headers>
writer(message<isRequest, file_body, Headers> const& m) noexcept
: path_(m.body)
{
}
~writer()
{
if(file_)
fclose(file_);
}
void
init(error_code& ec) noexcept
{
file_ = fopen(path_.c_str(), "rb");
if(! file_)
ec = boost::system::errc::make_error_code(
static_cast<boost::system::errc::errc_t>(errno));
else
size_ = boost::filesystem::file_size(path_);
}
auto
content_length() const
{
return size_;
}
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
buf_len_ = std::min(size_ - offset_, sizeof(buf_));
fread(buf_, 1, sizeof(buf_), file_);
offset_ += buf_len_;
write(boost::asio::buffer(buf_, buf_len_));
return offset_ >= size_;
}
};
};
} // http
} // beast
#endif

View File

@@ -0,0 +1,205 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#include "file_body.h"
#include "http_stream.h"
#include <beast/asio/placeholders.h>
#include <boost/asio.hpp>
#include <cstdio>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
namespace beast {
namespace http {
class http_async_server
{
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
using req_type = request<string_body>;
using resp_type = response<file_body>;
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
std::string root_;
std::vector<std::thread> thread_;
public:
http_async_server(endpoint_type const& ep,
int threads, std::string const& root)
: sock_(ios_)
, acceptor_(ios_)
, root_(root)
{
acceptor_.open(ep.protocol());
acceptor_.bind(ep);
acceptor_.listen(
boost::asio::socket_base::max_connections);
acceptor_.async_accept(sock_,
std::bind(&http_async_server::on_accept, this,
beast::asio::placeholders::error));
thread_.reserve(threads);
for(int i = 0; i < threads; ++i)
thread_.emplace_back(
[&] { ios_.run(); });
}
~http_async_server()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
for(auto& t : thread_)
t.join();
}
private:
class peer : public std::enable_shared_from_this<peer>
{
int id_;
stream<socket_type> stream_;
boost::asio::io_service::strand strand_;
std::string root_;
req_type req_;
public:
peer(peer&&) = default;
peer(peer const&) = default;
peer& operator=(peer&&) = delete;
peer& operator=(peer const&) = delete;
explicit
peer(socket_type&& sock, std::string const& root)
: id_([]
{
static int n = 0;
return ++n;
}())
, stream_(std::move(sock))
, strand_(stream_.get_io_service())
, root_(root)
{
}
void run()
{
do_read();
}
void do_read()
{
stream_.async_read(req_, strand_.wrap(
std::bind(&peer::on_read, shared_from_this(),
asio::placeholders::error)));
}
void on_read(error_code ec)
{
if(ec)
return fail(ec, "read");
do_read();
auto path = req_.url;
if(path == "/")
path = "/index.html";
path = root_ + path;
if(! boost::filesystem::exists(path))
{
response<string_body> resp(
{404, "Not Found", req_.version});
resp.headers.replace("Server", "http_async_server");
resp.body = "The file '" + path + "' was not found";
stream_.async_write(std::move(resp),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
return;
}
response<file_body> resp(
{200, "OK", req_.version});
resp.headers.replace("Server", "http_async_server");
resp.headers.replace("Content-Type", "text/html");
resp.body = path;
stream_.async_write(std::move(resp),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
}
void on_write(error_code ec)
{
if(ec)
fail(ec, "write");
}
private:
void
fail(error_code ec, std::string what)
{
if(ec != boost::asio::error::operation_aborted)
{
std::cerr <<
"#" << std::to_string(id_) << " " <<
what << ": " << ec.message() << std::endl;
}
}
};
void
fail(error_code ec, std::string what)
{
std::cerr <<
what << ": " << ec.message() << std::endl;
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
fail(ec, what);
throw ec;
}
}
void
on_accept(error_code ec)
{
if(! acceptor_.is_open())
return;
maybe_throw(ec, "accept");
socket_type sock(std::move(sock_));
acceptor_.async_accept(sock_,
std::bind(&http_async_server::on_accept, this,
asio::placeholders::error));
std::make_shared<peer>(std::move(sock), root_)->run();
}
};
} // http
} // beast
#endif

View File

@@ -0,0 +1,67 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include "http_stream.h"
#include "urls_large_data.h"
#include <boost/asio.hpp>
#include <iostream>
using namespace beast::http;
using namespace boost::asio;
template<class String>
void
err(error_code const& ec, String const& what)
{
std::cerr << what << ": " << ec.message() << std::endl;
}
int main(int, char const*[])
{
io_service ios;
for(auto const& host : urls_large_data())
{
try
{
ip::tcp::resolver r(ios);
auto it = r.resolve(
ip::tcp::resolver::query{host, "http"});
stream<ip::tcp::socket> hs(ios);
connect(hs.lowest_layer(), it);
auto ep = hs.lowest_layer().remote_endpoint();
request<empty_body> req({method_t::http_get, "/", 11});
req.headers.insert("Host", host +
std::string(":") + std::to_string(ep.port()));
req.headers.insert("User-Agent", "beast/http");
hs.write(req);
response<string_body> resp;
hs.read(resp);
std::cout << resp;
}
catch(boost::system::system_error const& ec)
{
std::cerr << host << ": " << ec.what();
}
catch(...)
{
std::cerr << host << ": unknown exception" << std::endl;
}
}
}

View File

@@ -0,0 +1,81 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include "http_async_server.h"
#include "http_sync_server.h"
#include "sig_wait.h"
#include <boost/program_options.hpp>
#include <iostream>
int main(int ac, char const* av[])
{
using namespace beast::http;
namespace po = boost::program_options;
po::options_description desc("Options");
desc.add_options()
("root,r", po::value<std::string>()->implicit_value("."),
"Set the root directory for serving files")
("port,p", po::value<std::uint16_t>()->implicit_value(8080),
"Set the port number for the server")
("ip", po::value<std::string>()->implicit_value("0.0.0.0"),
"Set the IP address to bind to, \"0.0.0.0\" for all")
("threads,n", po::value<std::size_t>()->implicit_value(4),
"Set the number of threads to use")
("sync,s", "Launch a synchronous server")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
std::string root = ".";
if(vm.count("root"))
root = vm["root"].as<std::string>();
std::uint16_t port = 8080;
if(vm.count("port"))
port = vm["port"].as<std::uint16_t>();
std::string ip = "0.0.0.0";
if(vm.count("ip"))
ip = vm["ip"].as<std::string>();
std::size_t threads = 4;
if(vm.count("threads"))
threads = vm["threads"].as<std::size_t>();
bool sync = vm.count("sync") > 0;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
endpoint_type ep{address_type::from_string(ip), port};
if(sync)
{
http_sync_server server(ep, root);
sig_wait();
}
else
{
http_async_server server(ep, threads, root);
sig_wait();
}
}

View File

@@ -0,0 +1,31 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_EXAMPLE_SERVER_RESPONSE_H_INCLUDED
#define BEAST_HTTP_EXAMPLE_SERVER_RESPONSE_H_INCLUDED
namespace beast {
namespace http {
namespace example {
} // example
} // http
} // beast
#endif

View File

@@ -0,0 +1,488 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_STREAM_H_INCLUDED
#define BEAST_HTTP_STREAM_H_INCLUDED
#include <beast/http.h>
#include <beast/asio/basic_streambuf.h>
#include <boost/asio/io_service.hpp>
#include <boost/intrusive/list.hpp>
#include <memory>
namespace beast {
namespace http {
namespace detail {
class stream_base
{
protected:
struct op
: boost::intrusive::list_base_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>
{
virtual ~op() = default;
virtual void operator()() = 0;
virtual void cancel() = 0;
};
using op_list = typename boost::intrusive::make_list<
op, boost::intrusive::constant_time_size<false>>::type;
op_list wr_q_;
bool wr_active_ = false;
};
} // detail
/** Provides message-oriented functionality using HTTP.
The stream class template provides asynchronous and blocking
message-oriented functionality necessary for clients and servers
to utilize the HTTP protocol.
@par Thread Safety
@e Distinct @e objects: Safe.@n
@e Shared @e objects: Unsafe. The application must ensure that
all asynchronous operations are performed within the same
implicit or explicit strand.
@par Example
To use the class template with an `ip::tcp::socket`, you would write:
@code
http::stream<ip::tcp::socket> hs(io_service);
@endcode
Alternatively, you can write:
@code
ip::tcp::socket sock(io_service);
http::stream<ip::tcp::socket&> hs(sock);
@endcode
@note A stream object must not be destroyed while there are
pending asynchronous operations associated with it.
@par Concepts
AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
*/
template<class NextLayer,
class Allocator = std::allocator<char>>
class stream : public detail::stream_base
{
NextLayer next_layer_;
basic_streambuf<Allocator> rd_buf_;
public:
/// The type of the next layer.
using next_layer_type =
std::remove_reference_t<NextLayer>;
/// The type of the lowest layer.
using lowest_layer_type =
typename next_layer_type::lowest_layer_type;
/// The type of endpoint of the lowest layer.
using endpoint_type =
typename lowest_layer_type::endpoint_type;
/// The protocol of the next layer.
using protocol_type =
typename lowest_layer_type::protocol_type;
/// The type of resolver of the next layer.
using resolver_type =
typename protocol_type::resolver;
/** Destructor.
@note A stream object must not be destroyed while there
are pending asynchronous operations associated with it.
*/
~stream();
/** Move constructor.
Undefined behavior if operations are active or pending.
*/
stream(stream&&) = default;
/** Move assignment.
Undefined behavior if operations are active or pending.
*/
stream& operator=(stream&&) = default;
/** Construct a HTTP stream.
This constructor creates a HTTP stream and initialises
the next layer.
@throws Any exceptions thrown by the Stream constructor.
@param args The arguments to be passed to initialise the
next layer. The arguments are forwarded to the next layer's
constructor.
*/
template<class... Args>
explicit
stream(Args&&... args);
/** Get the io_service associated with the stream.
This function may be used to obtain the io_service object
that the stream uses to dispatch handlers for asynchronous
operations.
@return A reference to the io_service object that the stream
will use to dispatch handlers. Ownership is not transferred
to the caller.
*/
boost::asio::io_service&
get_io_service()
{
return next_layer_.lowest_layer().get_io_service();
}
/** Get a reference to the next layer.
This function returns a reference to the next layer
in a stack of stream layers.
@return A reference to the next layer in the stack of
stream layers. Ownership is not transferred to the caller.
*/
next_layer_type&
next_layer()
{
return next_layer_;
}
/** Get a reference to the next layer.
This function returns a reference to the next layer in a
stack of stream layers.
@return A reference to the next layer in the stack of
stream layers. Ownership is not transferred to the caller.
*/
next_layer_type const&
next_layer() const
{
return next_layer_;
}
/** Get a reference to the lowest layer.
This function returns a reference to the lowest layer
in a stack of stream layers.
@return A reference to the lowest layer in the stack of
stream layers. Ownership is not transferred to the caller.
*/
lowest_layer_type&
lowest_layer()
{
return next_layer_.lowest_layer();
}
/** Get a reference to the lowest layer.
This function returns a reference to the lowest layer
in a stack of stream layers.
@return A reference to the lowest layer in the stack of
stream layers. Ownership is not transferred to the caller.
*/
lowest_layer_type const&
lowest_layer() const
{
return next_layer_.lowest_layer();
}
/** Cancel pending operations.
This will cancel all of the asynchronous operations pending,
including pipelined writes that have not been started. Handlers for
canceled writes will be called with
`boost::asio::error::operation_aborted`.
@throws boost::system::system_error Thrown on failure.
*/
void
cancel()
{
error_code ec;
cancel(ec);
if(ec)
throw boost::system::system_error{ec};
}
/** Cancel pending operations.
This will cancel all of the asynchronous operations pending,
including pipelined writes that have not been started. Handlers for
canceled writes will be called with
`boost::asio::error::operation_aborted`.
@param ec Set to indicate what error occurred, if any.
*/
void
cancel(error_code& ec);
/** Read a HTTP message from the stream.
This function is used to read a single HTTP message from the stream.
The call will block until one of the followign conditions is true:
@li A message has been read.
@li An error occurred.
The operation is implemented in terms of zero or more calls to the
next layer's `read_some` function.
@param msg An object used to store the message. The previous
contents of the object will be overwritten.
@throws boost::system::system_error Thrown on failure.
*/
template<bool isRequest, class Body, class Headers>
void
read(message<isRequest, Body, Headers>& msg)
{
error_code ec;
read(msg, ec);
if(ec)
throw boost::system::system_error{ec};
}
/** Read a HTTP message from the stream.
This function is used to read a single HTTP message from the stream.
The call will block until one of the followign conditions is true:
@li A message has been read.
@li An error occurred.
The operation is implemented in terms of zero or more calls to the
next layer's `read_some` function.
@param msg An object used to store the message. The previous
contents of the object will be overwritten.
@param ec Set to indicate what error occurred, if any.
*/
template<bool isRequest, class Body, class Headers>
void
read(message<isRequest, Body, Headers>& msg,
error_code& ec);
/** Start reading a HTTP message from the stream asynchronously.
This function is used to asynchronously read a single HTTP message
from the stream. The function call always returns immediately. The
asynchronous operation will continue until one of the following
conditions is true:
@li The message has been written.
@li An error occurred.
This operation is implemented in terms of zero or more calls to the
next layer's async_read_some function, and is known as a composed
operation. The program must ensure that the stream performs no other
read operations or any other composed operations that perform reads
until this operation completes.
@param msg An object used to store the message. The previous
contents of the object will be overwritten. Ownership of the message
is not transferred; the caller must guarantee that the object remains
valid until the handler is called.
@param handler The handler to be called when the request completes.
Copies will be made of the handler as required. The equivalent
function signature of the handler must be:
@code void handler(
error_code const& error // result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using boost::asio::io_service::post().
*/
template<bool isRequest, class Body, class Headers,
class ReadHandler>
#if GENERATING_DOCS
void_or_deduced
#else
auto
#endif
async_read(message<isRequest, Body, Headers>& msg,
ReadHandler&& handler);
/** Write a HTTP message to the stream.
This function is used to write a single HTTP message to the
stream. The call will block until one of the following conditions
is true:
@li The entire message is sent.
@li An error occurred.
If the semantics of the message require that the connection is
closed to indicate the end of the content body,
`boost::asio::error::eof` is thrown after the message is sent.
successfuly. The caller is responsible for actually closing the
connection. For regular TCP/IP streams this means shutting down the
send side, while SSL streams may call the SSL shutdown function.
@param msg The message to send.
@throws boost::system::system_error Thrown on failure.
*/
template<bool isRequest, class Body, class Headers>
void
write(message<isRequest, Body, Headers> const& msg)
{
error_code ec;
write(msg, ec);
if(ec)
throw boost::system::system_error{ec};
}
/** Write a HTTP message to the stream.
This function is used to write a single HTTP message to the
stream. The call will block until one of the following conditions
is true:
@li The entire message is sent.
@li An error occurred.
If the semantics of the message require that the connection is
closed to indicate the end of the content body,
`boost::asio::error::eof` is returned after the message is sent.
successfuly. The caller is responsible for actually closing the
connection. For regular TCP/IP streams this means shutting down the
send side, while SSL streams may call the SSL shutdown function.
@param msg The message to send.
@param ec Set to the error, if any occurred.
*/
template<bool isRequest, class Body, class Headers>
void
write(message<isRequest, Body, Headers> const& msg,
error_code& ec);
/** Start pipelining a HTTP message to the stream asynchronously.
This function is used to queue a message to be sent on the stream.
Unlike the free function, this version will place the message on an
outgoing message queue if there is already a write pending.
If the semantics of the message require that the connection is
closed to indicate the end of the content body, the handler
is called with the error `boost::asio::error::eof` after the message
has been sent successfully. The caller is responsible for actually
closing the connection. For regular TCP/IP streams this means
shutting down the send side, while SSL streams may call the SSL
`async_shutdown` function.
@param msg The message to send. A copy of the message will be made.
@param handler The handler to be called when the request completes.
Copies will be made of the handler as required. The equivalent
function signature of the handler must be:
@code void handler(
error_code const& error // result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using boost::asio::io_service::post().
*/
template<bool isRequest, class Body, class Headers,
class WriteHandler>
#if GENERATING_DOCS
void_or_deduced
#else
auto
#endif
async_write(message<isRequest, Body, Headers> const& msg,
WriteHandler&& handler);
/** Start pipelining a HTTP message to the stream asynchronously.
This function is used to queue a message to be sent on the stream.
Unlike the free function, this version will place the message on an
outgoing message queue if there is already a write pending.
If the semantics of the message require that the connection is
closed to indicate the end of the content body, the handler
is called with the error boost::asio::error::eof. The caller is
responsible for actually closing the connection. For regular
TCP/IP streams this means shutting down the send side, while SSL
streams may call the SSL async_shutdown function.
@param msg The message to send. Ownership of the message, which
must be movable, is transferred to the implementation. The message
will not be destroyed until the asynchronous operation completes.
@param handler The handler to be called when the request completes.
Copies will be made of the handler as required. The equivalent
function signature of the handler must be:
@code void handler(
error_code const& error // result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using boost::asio::io_service::post().
*/
template<bool isRequest, class Body, class Headers,
class WriteHandler>
#if GENERATING_DOCS
void_or_deduced
#else
auto
#endif
async_write(message<isRequest, Body, Headers>&& msg,
WriteHandler&& handler);
private:
template<bool, class, class, class> class read_op;
template<bool, class, class, class> class write_op;
void
cancel_all();
};
} // http
} // beast
#include "http_stream.ipp"
#endif

View File

@@ -0,0 +1,419 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_STREAM_IPP_INCLUDED
#define BEAST_HTTP_STREAM_IPP_INCLUDED
#include <beast/asio/async_completion.h>
#include <beast/asio/bind_handler.h>
#include <beast/asio/handler_alloc.h>
#include <beast/http/read.h>
#include <beast/http/type_check.h>
#include <beast/http/write.h>
#include <cassert>
namespace beast {
namespace http {
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers,
class Handler>
class stream<NextLayer, Allocator>::read_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
stream<NextLayer>& s;
message<isRequest, Body, Headers>& m;
Handler h;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& s_,
message<isRequest, Body, Headers>& m_)
: s(s_)
, m(m_)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
read_op(read_op&&) = default;
read_op(read_op const&) = default;
template<class DeducedHandler, class... Args>
read_op(DeducedHandler&& h,
stream<NextLayer>& s, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), s,
std::forward<Args>(args)...))
{
(*this)(error_code{}, false);
}
void operator()(error_code const& ec, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, read_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, read_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(read_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, read_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers, class Handler>
void
stream<NextLayer, Allocator>::
read_op<isRequest, Body, Headers, Handler>::
operator()(error_code const& ec, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
d.state = 99;
beast::http::async_read(d.s.next_layer_,
d.s.rd_buf_, d.m, std::move(*this));
return;
}
}
d.h(ec);
}
//------------------------------------------------------------------------------
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers,
class Handler>
class stream<NextLayer, Allocator>::write_op : public op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
stream<NextLayer>& s;
message<isRequest, Body, Headers> m;
Handler h;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& s_,
message<isRequest, Body, Headers> const& m_,
bool cont_)
: s(s_)
, m(m_)
, h(std::forward<DeducedHandler>(h_))
, cont(cont_)
{
}
template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& s_,
message<isRequest, Body, Headers>&& m_,
bool cont_)
: s(s_)
, m(std::move(m_))
, h(std::forward<DeducedHandler>(h_))
, cont(cont_)
{
}
};
std::shared_ptr<data> d_;
public:
write_op(write_op&&) = default;
write_op(write_op const&) = default;
template<class DeducedHandler, class... Args>
write_op(DeducedHandler&& h,
stream<NextLayer>& s, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), s,
std::forward<Args>(args)...))
{
}
void
operator()() override
{
(*this)(error_code{}, false);
}
void cancel() override;
void operator()(error_code const& ec, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(write_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, write_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers, class Handler>
void
stream<NextLayer, Allocator>::
write_op<isRequest, Body, Headers, Handler>::
cancel()
{
auto& d = *d_;
d.s.get_io_service().post(
bind_handler(std::move(*this),
boost::asio::error::operation_aborted));
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers, class Handler>
void
stream<NextLayer, Allocator>::
write_op<isRequest, Body, Headers, Handler>::
operator()(error_code const& ec, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
d.state = 99;
beast::http::async_write(d.s.next_layer_,
d.m, std::move(*this));
return;
}
}
d.h(ec);
if(! d.s.wr_q_.empty())
{
auto& op = d.s.wr_q_.front();
op();
// VFALCO Use allocator
delete &op;
d.s.wr_q_.pop_front();
}
else
{
d.s.wr_active_ = false;
}
}
//------------------------------------------------------------------------------
template<class NextLayer, class Allocator>
stream<NextLayer, Allocator>::
~stream()
{
// Can't destroy with pending operations!
assert(wr_q_.empty());
}
template<class NextLayer, class Allocator>
template<class... Args>
stream<NextLayer, Allocator>::
stream(Args&&... args)
: next_layer_(std::forward<Args>(args)...)
{
}
template<class NextLayer, class Allocator>
void
stream<NextLayer, Allocator>::
cancel(error_code& ec)
{
cancel_all();
lowest_layer().cancel(ec);
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers>
void
stream<NextLayer, Allocator>::
read(message<isRequest, Body, Headers>& msg,
error_code& ec)
{
beast::http::read(next_layer_, rd_buf_, msg, ec);
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers,
class ReadHandler>
auto
stream<NextLayer, Allocator>::
async_read(message<isRequest, Body, Headers>& msg,
ReadHandler&& handler)
{
async_completion<
ReadHandler, void(error_code)
> completion(handler);
read_op<isRequest, Body, Headers,
decltype(completion.handler)>{
completion.handler, *this, msg};
return completion.result.get();
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers>
void
stream<NextLayer, Allocator>::
write(message<isRequest, Body, Headers> const& msg,
error_code& ec)
{
beast::http::write(next_layer_, msg, ec);
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers,
class WriteHandler>
auto
stream<NextLayer, Allocator>::
async_write(message<isRequest, Body, Headers> const& msg,
WriteHandler&& handler)
{
async_completion<
WriteHandler, void(error_code)> completion(handler);
auto const cont = wr_active_ ||
boost_asio_handler_cont_helpers::is_continuation(handler);
if(! wr_active_)
{
wr_active_ = true;
write_op<isRequest, Body, Headers,
decltype(completion.handler)>{
completion.handler, *this, msg, cont }();
}
else
{
// VFALCO Use allocator
wr_q_.push_back(*new write_op<isRequest, Body, Headers,
decltype(completion.handler)>(
completion.handler, *this, msg, cont));
}
return completion.result.get();
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers,
class WriteHandler>
auto
stream<NextLayer, Allocator>::
async_write(message<isRequest, Body, Headers>&& msg,
WriteHandler&& handler)
{
async_completion<
WriteHandler, void(error_code)> completion(handler);
auto const cont = wr_active_ ||
boost_asio_handler_cont_helpers::is_continuation(handler);
if(! wr_active_)
{
wr_active_ = true;
write_op<isRequest, Body, Headers,
decltype(completion.handler)>{completion.handler,
*this, std::move(msg), cont}();
}
else
{
// VFALCO Use allocator
wr_q_.push_back(*new write_op<isRequest, Body, Headers,
decltype(completion.handler)>(completion.handler,
*this, std::move(msg), cont));
}
return completion.result.get();
}
template<class NextLayer, class Allocator>
void
stream<NextLayer, Allocator>::
cancel_all()
{
for(auto it = wr_q_.begin(); it != wr_q_.end();)
{
auto& op = *it++;
op.cancel();
// VFALCO Use allocator
delete &op;
}
wr_q_.clear();
}
} // http
} // beast
#endif

View File

@@ -0,0 +1,170 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
#include "file_body.h"
#include "http_stream.h"
#include <beast/unit_test/suite.h>
#include <boost/asio.hpp>
#include <cstdint>
#include <cstdio>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <utility>
#include <iostream>
namespace beast {
namespace http {
class http_sync_server
{
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
using req_type = request<string_body>;
using resp_type = response<file_body>;
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
std::string root_;
std::thread thread_;
public:
http_sync_server(endpoint_type const& ep,
std::string const& root)
: sock_(ios_)
, acceptor_(ios_)
, root_(root)
{
acceptor_.open(ep.protocol());
acceptor_.bind(ep);
acceptor_.listen(
boost::asio::socket_base::max_connections);
acceptor_.async_accept(sock_,
std::bind(&http_sync_server::on_accept, this,
beast::asio::placeholders::error));
thread_ = std::thread{[&]{ ios_.run(); }};
}
~http_sync_server()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
thread_.join();
}
void
fail(error_code ec, std::string what)
{
std::cerr <<
what << ": " << ec.message() << std::endl;
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
fail(ec, what);
throw ec;
}
}
void
on_accept(error_code ec)
{
if(! acceptor_.is_open())
return;
maybe_throw(ec, "accept");
static int id_ = 0;
std::thread{
[
id = ++id_,
this,
sock = std::move(sock_),
work = boost::asio::io_service::work{ios_}
]() mutable
{
do_peer(id, std::move(sock));
}}.detach();
acceptor_.async_accept(sock_,
std::bind(&http_sync_server::on_accept, this,
asio::placeholders::error));
}
void
fail(int id, error_code const& ec)
{
if(ec != boost::asio::error::operation_aborted &&
ec != boost::asio::error::eof)
std::cerr <<
"#" << std::to_string(id) << " " << std::endl;
}
void
do_peer(int id, socket_type&& sock)
{
http::stream<socket_type> hs(std::move(sock));
error_code ec;
for(;;)
{
req_type req;
hs.read(req, ec);
if(ec)
break;
auto path = req.url;
if(path == "/")
path = "/index.html";
path = root_ + path;
if(! boost::filesystem::exists(path))
{
response<string_body> resp(
{404, "Not Found", req.version});
resp.headers.replace("Server", "http_sync_server");
resp.body = "The file '" + path + "' was not found";
hs.write(resp, ec);
if(ec)
break;
}
response<file_body> resp(
{200, "OK", req.version});
resp.headers.replace("Server", "http_sync_server");
resp.headers.replace("Content-Type", "text/html");
resp.body = path;
hs.write(resp, ec);
if(ec)
break;
}
fail(id, ec);
}
};
} // http
} // beast
#endif

View File

@@ -0,0 +1,49 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_EXAMPLE_SIG_WAIT_H_INCLUDED
#define BEAST_EXAMPLE_SIG_WAIT_H_INCLUDED
#include <boost/asio.hpp>
#include <condition_variable>
#include <mutex>
// Block until SIGINT or SIGTERM
inline
void
sig_wait()
{
boost::asio::io_service ios;
boost::asio::signal_set signals(
ios, SIGINT, SIGTERM);
std::mutex m;
bool stop = false;
std::condition_variable cv;
signals.async_wait(
[&](boost::system::error_code const&, int)
{
std::lock_guard<std::mutex> lock(m);
stop = true;
cv.notify_one();
});
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return stop; });
}
#endif

View File

@@ -17,15 +17,12 @@
*/
//==============================================================================
#include <beast/http/tests/urls_large_data.h>
namespace beast {
namespace http {
#include "urls_large_data.h"
// Data from Alexa top 1 million sites
// http://s3.amazonaws.com/alexa-static/top-1m.csv.zip
//
std::vector <char const*> const&
std::vector<char const*> const&
urls_large_data()
{
static std::vector <char const*> const urls ({
@@ -10032,7 +10029,3 @@ urls_large_data()
return urls;
}
}
}

View File

@@ -17,18 +17,12 @@
*/
//==============================================================================
#ifndef BEAST_HTTP_URLS_LARGE_DATA_H_INCLUDED
#define BEAST_HTTP_URLS_LARGE_DATA_H_INCLUDED
#ifndef URLS_LARGE_DATA_H_INCLUDED
#define URLS_LARGE_DATA_H_INCLUDED
#include <vector>
namespace beast {
namespace http {
std::vector <char const*> const&
std::vector<char const*> const&
urls_large_data();
}
}
#endif

View File

@@ -14,6 +14,7 @@ unit-test all :
asio.cpp
async_completion.cpp
basic_streambuf.cpp
../basic_headers.cpp
bind_handler.cpp
buffers_adapter.cpp
buffers_debug.cpp

View File

@@ -0,0 +1,63 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/http/basic_headers.h>
#include <beast/unit_test/suite.h>
namespace beast {
namespace http {
namespace test {
class basic_headers_test : public unit_test::suite
{
public:
template<class Allocator>
using bha = basic_headers<Allocator>;
using bh = basic_headers<std::allocator<char>>;
template<class Allocator>
static
void
fill(std::size_t n, basic_headers<Allocator>& h)
{
for(std::size_t i = 1; i<= n; ++i)
h.insert(std::to_string(i), i);
}
void testHeaders()
{
bh h1;
expect(h1.empty());
fill(1, h1);
expect(h1.size() == 1);
bh h2;
h2 = h1;
expect(h2.size() == 1);
h2.insert("2", "2");
expect(std::distance(h2.begin(), h2.end()) == 2);
h1 = std::move(h2);
expect(h1.size() == 2);
expect(h2.size() == 0);
bh h3(std::move(h1));
expect(h3.size() == 2);
expect(h1.size() == 0);
}
void run() override
{
testHeaders();
}
};
BEAST_DEFINE_TESTSUITE(basic_headers,http,beast);
} // test
} // asio
} // beast

View File

@@ -17,7 +17,8 @@
*/
//==============================================================================
#include <beast/http/URL.h>
#include <ripple/beast/net/URL.h>
#include <beast/http/src/nodejs_parser.h>
namespace beast {
@@ -104,9 +105,9 @@ parse_URL(std::string const& url)
std::size_t const buflen (url.size ());
char const* const buf (url.c_str ());
joyent::http_parser_url parser;
http_parser_url parser;
if (joyent::http_parser_parse_url (buf, buflen, false, &parser) != 0)
if (http_parser_parse_url (buf, buflen, false, &parser) != 0)
return std::make_pair (false, URL{});
std::string scheme;
@@ -118,54 +119,54 @@ parse_URL(std::string const& url)
std::string fragment;
std::string userinfo;
if ((parser.field_set & (1<<joyent::UF_SCHEMA)) != 0)
if ((parser.field_set & (1<<UF_SCHEMA)) != 0)
{
scheme = std::string (
buf + parser.field_data [joyent::UF_SCHEMA].off,
parser.field_data [joyent::UF_SCHEMA].len);
buf + parser.field_data [UF_SCHEMA].off,
parser.field_data [UF_SCHEMA].len);
}
if ((parser.field_set & (1<<joyent::UF_HOST)) != 0)
if ((parser.field_set & (1<<UF_HOST)) != 0)
{
host = std::string (
buf + parser.field_data [joyent::UF_HOST].off,
parser.field_data [joyent::UF_HOST].len);
buf + parser.field_data [UF_HOST].off,
parser.field_data [UF_HOST].len);
}
if ((parser.field_set & (1<<joyent::UF_PORT)) != 0)
if ((parser.field_set & (1<<UF_PORT)) != 0)
{
port = parser.port;
port_string = std::string (
buf + parser.field_data [joyent::UF_PORT].off,
parser.field_data [joyent::UF_PORT].len);
buf + parser.field_data [UF_PORT].off,
parser.field_data [UF_PORT].len);
}
if ((parser.field_set & (1<<joyent::UF_PATH)) != 0)
if ((parser.field_set & (1<<UF_PATH)) != 0)
{
path = std::string (
buf + parser.field_data [joyent::UF_PATH].off,
parser.field_data [joyent::UF_PATH].len);
buf + parser.field_data [UF_PATH].off,
parser.field_data [UF_PATH].len);
}
if ((parser.field_set & (1<<joyent::UF_QUERY)) != 0)
if ((parser.field_set & (1<<UF_QUERY)) != 0)
{
query = std::string (
buf + parser.field_data [joyent::UF_QUERY].off,
parser.field_data [joyent::UF_QUERY].len);
buf + parser.field_data [UF_QUERY].off,
parser.field_data [UF_QUERY].len);
}
if ((parser.field_set & (1<<joyent::UF_FRAGMENT)) != 0)
if ((parser.field_set & (1<<UF_FRAGMENT)) != 0)
{
fragment = std::string (
buf + parser.field_data [joyent::UF_FRAGMENT].off,
parser.field_data [joyent::UF_FRAGMENT].len);
buf + parser.field_data [UF_FRAGMENT].off,
parser.field_data [UF_FRAGMENT].len);
}
if ((parser.field_set & (1<<joyent::UF_USERINFO)) != 0)
if ((parser.field_set & (1<<UF_USERINFO)) != 0)
{
userinfo = std::string (
buf + parser.field_data [joyent::UF_USERINFO].off,
parser.field_data [joyent::UF_USERINFO].len);
buf + parser.field_data [UF_USERINFO].off,
parser.field_data [UF_USERINFO].len);
}
return std::make_pair (true,

View File

@@ -17,8 +17,7 @@
*/
//==============================================================================
#include <beast/http/URL.h>
#include <ripple/beast/net/URL.h>
#include <beast/unit_test/suite.h>
namespace beast {

View File

@@ -21,4 +21,7 @@
#include <ripple/beast/net/impl/IPAddressV6.cpp>
#include <ripple/beast/net/impl/IPEndpoint.cpp>
#include <ripple/beast/net/impl/IPAddressConversion.cpp>
#include <ripple/beast/net/impl/URL.cpp>
#include <ripple/beast/net/tests/beast_http_URL_test.cpp>
#include <ripple/beast/net/tests/IPEndpoint.test.cpp>

View File

@@ -26,7 +26,7 @@
#include <ripple/protocol/PublicKey.h> // NIKB Breaks levelization (TEMP)
#include <ripple/protocol/SecretKey.h> // NIKB Breaks levelization (TEMP)
#include <ripple/json/json_value.h>
#include <beast/http/URL.h>
#include <ripple/beast/net/URL.h>
#include <ripple/beast/net/IPEndpoint.h>
#include <beast/ci_char_traits.h>
#include <ripple/beast/utility/Journal.h>

View File

@@ -26,7 +26,7 @@
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/SystemParameters.h>
#include <ripple/net/HTTPClient.h>
#include <beast/http/URL.h>
#include <ripple/beast/net/URL.h>
#include <ripple/beast/core/LexicalCast.h>
#include <beast/streams/debug_ostream.h>
#include <beast/ci_char_traits.h>

View File

@@ -81,7 +81,7 @@ public:
virtual
Handoff
onHandoff (std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) = 0;
/** Establish a peer connection to the specified endpoint.

View File

@@ -218,7 +218,7 @@ ConnectAttempt::onHandshake (error_code ec)
if (! sharedValue)
return close(); // makeSharedValue logs
beast::http::message req = makeRequest(
beast::deprecated_http::message req = makeRequest(
! overlay_.peerFinder().config().peerPrivate,
remote_endpoint_.address());
auto const hello = buildHello (
@@ -226,10 +226,9 @@ ConnectAttempt::onHandshake (error_code ec)
overlay_.setup().public_ip,
beast::IPAddressConversion::from_asio(remote_endpoint_),
app_);
appendHello (req, hello);
appendHello (req.headers, hello);
using beast::http::write;
write (write_buf_, req);
beast::deprecated_http::write (write_buf_, req);
setTimer();
stream_.async_write_some (write_buf_.data(),
@@ -293,10 +292,9 @@ ConnectAttempt::onRead (error_code ec, std::size_t bytes_transferred)
if (! ec)
{
write_buf_.commit (bytes_transferred);
std::size_t bytes_consumed;
std::tie (ec, bytes_consumed) = parser_.write(
write_buf_.data());
write_buf_.commit(bytes_transferred);
auto bytes_consumed = parser_.write(
write_buf_.data(), ec);
if (! ec)
{
write_buf_.consume (bytes_consumed);
@@ -332,26 +330,26 @@ ConnectAttempt::onShutdown (error_code ec)
//--------------------------------------------------------------------------
beast::http::message
beast::deprecated_http::message
ConnectAttempt::makeRequest (bool crawl,
boost::asio::ip::address const& remote_address)
{
beast::http::message m;
beast::deprecated_http::message m;
m.method (beast::http::method_t::http_get);
m.url ("/");
m.version (1, 1);
m.headers.append ("User-Agent", BuildInfo::getFullVersionString());
m.headers.append ("Upgrade", "RTXP/1.2");
m.headers.insert ("User-Agent", BuildInfo::getFullVersionString());
m.headers.insert ("Upgrade", "RTXP/1.2");
//std::string("RTXP/") + to_string (BuildInfo::getCurrentProtocol()));
m.headers.append ("Connection", "Upgrade");
m.headers.append ("Connect-As", "Peer");
m.headers.append ("Crawl", crawl ? "public" : "private");
m.headers.insert ("Connection", "Upgrade");
m.headers.insert ("Connect-As", "Peer");
m.headers.insert ("Crawl", crawl ? "public" : "private");
return m;
}
template <class Streambuf>
void
ConnectAttempt::processResponse (beast::http::message const& m,
ConnectAttempt::processResponse (beast::deprecated_http::message const& m,
Streambuf const& body)
{
if (response_.status() == 503)
@@ -392,7 +390,7 @@ ConnectAttempt::processResponse (beast::http::message const& m,
return close();
}
auto hello = parseHello (response_, journal_);
auto hello = parseHello (false, response_.headers, journal_);
if(! hello)
return fail("processResponse: Bad TMHello");

View File

@@ -66,9 +66,9 @@ private:
beast::asio::ssl_bundle::stream_type& stream_;
beast::streambuf read_buf_;
beast::streambuf write_buf_;
beast::http::message response_;
beast::deprecated_http::message response_;
beast::streambuf body_;
beast::http::parser parser_;
beast::deprecated_http::parser parser_;
PeerFinder::Slot::ptr slot_;
public:
@@ -100,12 +100,12 @@ private:
void onShutdown (error_code ec);
static
beast::http::message
beast::deprecated_http::message
makeRequest (bool crawl,
boost::asio::ip::address const& remote_address);
template <class Streambuf>
void processResponse (beast::http::message const& m,
void processResponse (beast::deprecated_http::message const& m,
Streambuf const& body);
template <class = void>

View File

@@ -171,7 +171,7 @@ OverlayImpl::~OverlayImpl ()
Handoff
OverlayImpl::onHandoff (std::unique_ptr <beast::asio::ssl_bundle>&& ssl_bundle,
beast::http::message&& request,
http_request_type&& request,
endpoint_type remote_endpoint)
{
auto const id = next_id_++;
@@ -226,14 +226,14 @@ OverlayImpl::onHandoff (std::unique_ptr <beast::asio::ssl_bundle>&& ssl_bundle,
handoff.moved = false;
handoff.response = makeRedirectResponse(slot, request,
remote_endpoint.address());
handoff.keep_alive = request.keep_alive();
handoff.keep_alive = is_keep_alive(request);
return handoff;
}
}
handoff.moved = true;
auto hello = parseHello (request, journal);
auto hello = parseHello (true, request.headers, journal);
if(! hello)
return handoff;
@@ -260,7 +260,7 @@ OverlayImpl::onHandoff (std::unique_ptr <beast::asio::ssl_bundle>&& ssl_bundle,
handoff.moved = false;
handoff.response = makeRedirectResponse(slot, request,
remote_endpoint.address());
handoff.keep_alive = request.keep_alive();
handoff.keep_alive = is_keep_alive(request);
return handoff;
}
@@ -289,7 +289,19 @@ OverlayImpl::onHandoff (std::unique_ptr <beast::asio::ssl_bundle>&& ssl_bundle,
//------------------------------------------------------------------------------
bool
OverlayImpl::isPeerUpgrade(beast::http::message const& request)
OverlayImpl::isPeerUpgrade(http_request_type const& request)
{
if (! is_upgrade(request))
return false;
auto const versions = parse_ProtocolVersions(
request.headers["Upgrade"]);
if (versions.size() == 0)
return false;
return true;
}
bool
OverlayImpl::isPeerUpgrade(beast::deprecated_http::message const& request)
{
if (! request.upgrade())
return false;
@@ -297,7 +309,7 @@ OverlayImpl::isPeerUpgrade(beast::http::message const& request)
request.headers["Upgrade"]);
if (versions.size() == 0)
return false;
if (! request.request() && request.status() != 101)
if(! request.request() && request.status() != 101)
return false;
return true;
}
@@ -312,7 +324,7 @@ OverlayImpl::makePrefix (std::uint32_t id)
std::shared_ptr<Writer>
OverlayImpl::makeRedirectResponse (PeerFinder::Slot::ptr const& slot,
beast::http::message const& request, address_type remote_address)
http_request_type const& request, address_type remote_address)
{
Json::Value json(Json::objectValue);
{
@@ -322,13 +334,13 @@ OverlayImpl::makeRedirectResponse (PeerFinder::Slot::ptr const& slot,
ips.append(_.address.to_string());
}
beast::http::message m;
beast::deprecated_http::message m;
m.request(false);
m.status(503);
m.reason("Service Unavailable");
m.headers.append("Remote-Address", remote_address.to_string());
m.version(request.version());
if (request.version() == std::make_pair(1, 0))
m.headers.insert("Remote-Address", remote_address.to_string());
m.version(std::make_pair(request.version / 10, request.version % 10));
if (request.version == 10)
{
//?
}
@@ -829,16 +841,17 @@ OverlayImpl::json ()
}
bool
OverlayImpl::processRequest (beast::http::message const& req,
OverlayImpl::processRequest (http_request_type const& req,
Handoff& handoff)
{
if (req.url() != "/crawl")
if (req.url != "/crawl")
return false;
beast::http::message resp;
beast::deprecated_http::message resp;
resp.request(false);
resp.status(200);
resp.reason("OK");
resp.version(std::make_pair(req.version / 10, req.version % 10));
Json::Value v;
v["overlay"] = crawl();
handoff.response = make_JsonWriter(resp, v);

View File

@@ -166,7 +166,7 @@ public:
Handoff
onHandoff (std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
endpoint_type remote_endpoint) override;
PeerSequence
@@ -254,7 +254,11 @@ public:
static
bool
isPeerUpgrade (beast::http::message const& request);
isPeerUpgrade (http_request_type const& request);
static
bool
isPeerUpgrade (beast::deprecated_http::message const& request);
static
std::string
@@ -266,6 +270,18 @@ public:
bool isInbound,
int bytes);
private:
std::shared_ptr<Writer>
makeRedirectResponse (PeerFinder::Slot::ptr const& slot,
http_request_type const& request, address_type remote_address);
bool
processRequest (http_request_type const& req,
Handoff& handoff);
void
connect (beast::IP::Endpoint const& remote_endpoint) override;
/* The number of active peers on the network
Active peers are only those peers that have completed the handshake
and are running the Ripple protocol.
@@ -282,18 +298,6 @@ public:
Json::Value
json() override;
private:
std::shared_ptr<Writer>
makeRedirectResponse (PeerFinder::Slot::ptr const& slot,
beast::http::message const& request, address_type remote_address);
void
connect (beast::IP::Endpoint const& remote_endpoint) override;
bool
processRequest (beast::http::message const& req,
Handoff& handoff);
//--------------------------------------------------------------------------
//

View File

@@ -59,7 +59,7 @@ using namespace std::chrono_literals;
namespace ripple {
PeerImp::PeerImp (Application& app, id_t id, endpoint_type remote_endpoint,
PeerFinder::Slot::ptr const& slot, beast::http::message&& request,
PeerFinder::Slot::ptr const& slot, http_request_type&& request,
protocol::TMHello const& hello, PublicKey const& publicKey,
Resource::Consumer consumer,
std::unique_ptr<beast::asio::ssl_bundle>&& ssl_bundle,
@@ -89,7 +89,8 @@ PeerImp::PeerImp (Application& app, id_t id, endpoint_type remote_endpoint,
, usage_(consumer)
, fee_ (Resource::feeLightPeer)
, slot_ (slot)
, http_message_(std::move(request))
, request_(std::move(request))
, headers_(request_.headers)
{
}
@@ -237,8 +238,8 @@ PeerImp::charge (Resource::Charge const& fee)
bool
PeerImp::crawl() const
{
auto const iter = http_message_.headers.find("Crawl");
if (iter == http_message_.headers.end())
auto const iter = headers_.find("Crawl");
if (iter == headers_.end())
return false;
return beast::ci_equal(iter->second, "public");
}
@@ -586,7 +587,7 @@ PeerImp::onShutdown(error_code ec)
void PeerImp::doAccept()
{
assert(read_buffer_.size() == 0);
assert(http_message_.upgrade());
// assert(request_.upgrade);
JLOG(journal_.debug()) << "doAccept: " << remote_address_;
@@ -601,8 +602,8 @@ void PeerImp::doAccept()
auto resp = makeResponse(
! overlay_.peerFinder().config().peerPrivate,
http_message_, remote_address_, *sharedValue);
beast::http::write (write_buffer_, resp);
request_, remote_address_, *sharedValue);
beast::deprecated_http::write (write_buffer_, resp);
auto const protocol = BuildInfo::make_protocol(hello_.protoversion());
JLOG(journal_.info()) << "Protocol: " << to_string(protocol);
@@ -641,25 +642,25 @@ void PeerImp::doAccept()
onWriteResponse(error_code(), 0);
}
beast::http::message
beast::deprecated_http::message
PeerImp::makeResponse (bool crawl,
beast::http::message const& req,
http_request_type const& req,
beast::IP::Endpoint remote,
uint256 const& sharedValue)
{
beast::http::message resp;
beast::deprecated_http::message resp;
resp.request(false);
resp.status(101);
resp.reason("Switching Protocols");
resp.version(req.version());
resp.headers.append("Connection", "Upgrade");
resp.headers.append("Upgrade", "RTXP/1.2");
resp.headers.append("Connect-AS", "Peer");
resp.headers.append("Server", BuildInfo::getFullVersionString());
resp.headers.append ("Crawl", crawl ? "public" : "private");
resp.version(std::make_pair(req.version / 10, req.version % 10));
resp.headers.insert("Connection", "Upgrade");
resp.headers.insert("Upgrade", "RTXP/1.2");
resp.headers.insert("Connect-AS", "Peer");
resp.headers.insert("Server", BuildInfo::getFullVersionString());
resp.headers.insert("Crawl", crawl ? "public" : "private");
protocol::TMHello hello = buildHello(sharedValue,
overlay_.setup().public_ip, remote, app_);
appendHello(resp, hello);
appendHello(resp.headers, hello);
return resp;
}

View File

@@ -153,8 +153,9 @@ private:
Resource::Charge fee_;
PeerFinder::Slot::ptr slot_;
beast::streambuf read_buffer_;
beast::http::message http_message_;
beast::http::body http_body_;
http_request_type request_;
beast::deprecated_http::message response_;
beast::http::headers<std::allocator<char>> const& headers_;
beast::streambuf write_buffer_;
std::queue<Message::pointer> send_queue_;
bool gracefulClose_ = false;
@@ -171,7 +172,7 @@ public:
/** Create an active incoming peer from an established ssl connection. */
PeerImp (Application& app, id_t id, endpoint_type remote_endpoint,
PeerFinder::Slot::ptr const& slot, beast::http::message&& request,
PeerFinder::Slot::ptr const& slot, http_request_type&& request,
protocol::TMHello const& hello, PublicKey const& publicKey,
Resource::Consumer consumer,
std::unique_ptr<beast::asio::ssl_bundle>&& ssl_bundle,
@@ -182,7 +183,7 @@ public:
template <class Buffers>
PeerImp (Application& app, std::unique_ptr<beast::asio::ssl_bundle>&& ssl_bundle,
Buffers const& buffers, PeerFinder::Slot::ptr&& slot,
beast::http::message&& response, Resource::Consumer usage,
beast::deprecated_http::message&& response, Resource::Consumer usage,
protocol::TMHello const& hello,
PublicKey const& publicKey, id_t id,
OverlayImpl& overlay);
@@ -359,8 +360,8 @@ private:
void
doAccept();
beast::http::message
makeResponse (bool crawl, beast::http::message const& req,
beast::deprecated_http::message
makeResponse (bool crawl, http_request_type const& req,
beast::IP::Endpoint remoteAddress,
uint256 const& sharedValue);
@@ -476,7 +477,7 @@ private:
template <class Buffers>
PeerImp::PeerImp (Application& app, std::unique_ptr<beast::asio::ssl_bundle>&& ssl_bundle,
Buffers const& buffers, PeerFinder::Slot::ptr&& slot,
beast::http::message&& response, Resource::Consumer usage,
beast::deprecated_http::message&& response, Resource::Consumer usage,
protocol::TMHello const& hello,
PublicKey const& publicKey, id_t id,
OverlayImpl& overlay)
@@ -504,7 +505,8 @@ PeerImp::PeerImp (Application& app, std::unique_ptr<beast::asio::ssl_bundle>&& s
, usage_ (usage)
, fee_ (Resource::feeLightPeer)
, slot_ (std::move(slot))
, http_message_(std::move(response))
, response_(std::move(response))
, headers_(response_.headers)
{
read_buffer_.commit (boost::asio::buffer_copy(read_buffer_.prepare(
boost::asio::buffer_size(buffers)), buffers));

View File

@@ -154,43 +154,41 @@ buildHello (
}
void
appendHello (beast::http::message& m,
appendHello (beast::http::headers<std::allocator<char>>& h,
protocol::TMHello const& hello)
{
auto& h = m.headers;
//h.append ("Protocol-Versions",...
h.append ("Public-Key", hello.nodepublic());
h.insert ("Public-Key", hello.nodepublic());
h.append ("Session-Signature", beast::base64_encode (
h.insert ("Session-Signature", beast::base64_encode (
hello.nodeproof()));
if (hello.has_nettime())
h.append ("Network-Time", std::to_string (hello.nettime()));
h.insert ("Network-Time", std::to_string (hello.nettime()));
if (hello.has_ledgerindex())
h.append ("Ledger", std::to_string (hello.ledgerindex()));
h.insert ("Ledger", std::to_string (hello.ledgerindex()));
if (hello.has_ledgerclosed())
h.append ("Closed-Ledger", beast::base64_encode (
h.insert ("Closed-Ledger", beast::base64_encode (
hello.ledgerclosed()));
if (hello.has_ledgerprevious())
h.append ("Previous-Ledger", beast::base64_encode (
h.insert ("Previous-Ledger", beast::base64_encode (
hello.ledgerprevious()));
if (hello.has_local_ip())
h.append ("Local-IP", beast::IP::to_string (
h.insert ("Local-IP", beast::IP::to_string (
beast::IP::AddressV4(hello.local_ip())));
if (hello.has_remote_ip())
h.append ("Remote-IP", beast::IP::to_string (
h.insert ("Remote-IP", beast::IP::to_string (
beast::IP::AddressV4(hello.remote_ip())));
}
std::vector<ProtocolVersion>
parse_ProtocolVersions (std::string const& s)
parse_ProtocolVersions(boost::string_ref const& value)
{
static boost::regex re (
"^" // start of line
@@ -201,7 +199,7 @@ parse_ProtocolVersions (std::string const& s)
"$" // The end of the string
, boost::regex_constants::optimize);
auto const list = beast::rfc2616::split_commas (s);
auto const list = beast::rfc2616::split_commas(value);
std::vector<ProtocolVersion> result;
for (auto const& s : list)
{
@@ -224,10 +222,8 @@ parse_ProtocolVersions (std::string const& s)
}
boost::optional<protocol::TMHello>
parseHello (beast::http::message const& m, beast::Journal journal)
parseHello (bool request, beast::http::headers<std::allocator<char>> const& h, beast::Journal journal)
{
auto const& h = m.headers;
// protocol version in TMHello is obsolete,
// it is supplanted by the values in the headers.
protocol::TMHello hello;
@@ -270,7 +266,7 @@ parseHello (beast::http::message const& m, beast::Journal journal)
}
{
auto const iter = h.find (m.request() ?
auto const iter = h.find (request ?
"User-Agent" : "Server");
if (iter != h.end())
hello.set_fullversion (iter->second);

View File

@@ -27,10 +27,9 @@
#include <beast/http/message.h>
#include <ripple/beast/utility/Journal.h>
#include <utility>
#include <boost/asio/ssl.hpp>
#include <boost/optional.hpp>
#include <boost/utility/string_ref.hpp>
#include "ripple.pb.h"
namespace ripple {
@@ -59,14 +58,14 @@ buildHello (uint256 const& sharedValue,
/** Insert HTTP headers based on the TMHello protocol message. */
void
appendHello (beast::http::message& m, protocol::TMHello const& hello);
appendHello (beast::http::headers<std::allocator<char>>& h, protocol::TMHello const& hello);
/** Parse HTTP headers into TMHello protocol message.
@return A protocol message on success; an empty optional
if the parsing failed.
*/
boost::optional<protocol::TMHello>
parseHello (beast::http::message const& m, beast::Journal journal);
parseHello (bool request, beast::http::headers<std::allocator<char>> const& h, beast::Journal journal);
/** Validate and store the public key in the TMHello.
This includes signature verification on the shared value.
@@ -85,7 +84,7 @@ verifyHello (protocol::TMHello const& h, uint256 const& sharedValue,
excluded from the result set.
*/
std::vector<ProtocolVersion>
parse_ProtocolVersions (std::string const& s);
parse_ProtocolVersions(boost::string_ref const& s);
}

View File

@@ -22,7 +22,6 @@
#include <ripple/server/Handoff.h>
#include <beast/asio/ssl_bundle.h>
#include <beast/http/message.h>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/system/error_code.hpp>
@@ -61,18 +60,18 @@ struct Handler
Handoff
onHandoff (Session& session,
std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) = 0;
virtual
Handoff
onHandoff (Session& session, boost::asio::ip::tcp::socket&& socket,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) = 0;
/** @} */
/** Called when we have a complete HTTP request. */
// VFALCO TODO Pass the beast::http::message as a parameter
// VFALCO TODO Pass the beast::deprecated_http::message as a parameter
virtual void onRequest (Session& session) = 0;
/** Called when the session ends.

View File

@@ -21,10 +21,15 @@
#define RIPPLE_SERVER_HANDOFF_H_INCLUDED
#include <ripple/server/Writer.h>
#include <beast/http/message.h>
#include <beast/http/streambuf_body.h>
#include <memory>
namespace ripple {
using http_request_type =
beast::http::request<beast::http::streambuf_body>;
/** Used to indicate the result of a server connection handoff. */
struct Handoff
{

View File

@@ -144,16 +144,16 @@ write(Streambuf& buf, Json::Value const& json)
*/
template <class = void>
std::shared_ptr<Writer>
make_JsonWriter (beast::http::message& m, Json::Value const& json)
make_JsonWriter (beast::deprecated_http::message& m, Json::Value const& json)
{
beast::streambuf prebody;
beast::streambuf body;
write(body, json);
// VFALCO TODO Better way to set a field
m.headers.erase ("Content-Length");
m.headers.append("Content-Length", std::to_string(body.size()));
m.headers.insert("Content-Length", std::to_string(body.size()));
m.headers.erase ("Content-Type");
m.headers.append("Content-Type", "application/json");
m.headers.insert("Content-Type", "application/json");
write(prebody, m);
return std::make_shared<streambufs_writer>(
std::move(prebody), std::move(body));

View File

@@ -88,7 +88,7 @@ public:
/** Fills in boilerplate HTTP header field values. */
static
void
appendStandardFields (beast::http::message& message);
appendStandardFields (beast::deprecated_http::message& message);
};
//------------------------------------------------------------------------------

View File

@@ -66,14 +66,9 @@ public:
/** Returns the current HTTP request. */
virtual
beast::http::message&
http_request_type&
request() = 0;
/** Returns the Content-Body of the current HTTP request. */
virtual
beast::http::body const&
body() = 0;
/** Send a copy of data asynchronously. */
/** @{ */
void

View File

@@ -28,8 +28,10 @@
#include <ripple/beast/net/IPAddressConversion.h>
#include <beast/asio/placeholders.h>
#include <beast/asio/ssl_error.h> // for is_short_read?
#include <beast/http/read.h>
#include <beast/http/message.h>
#include <beast/http/parser.h>
#include <beast/http/streambuf_body.h>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/streambuf.hpp>
@@ -95,8 +97,7 @@ protected:
std::size_t nid_;
boost::asio::streambuf read_buf_;
beast::http::message message_;
beast::http::body body_;
http_request_type message_;
std::vector<buffer> wq_;
std::vector<buffer> wq2_;
std::mutex mutex_;
@@ -186,18 +187,12 @@ protected:
return beast::IPAddressConversion::from_asio(remote_address_);
}
beast::http::message&
http_request_type&
request() override
{
return message_;
}
beast::http::body const&
body() override
{
return body_;
}
void
write (void const* buffer, std::size_t bytes) override;
@@ -319,64 +314,12 @@ void
BaseHTTPPeer<Impl>::do_read (yield_context yield)
{
complete_ = false;
error_code ec;
bool eof = false;
body_.clear();
beast::http::parser parser (message_, body_, true);
for(;;)
{
if (read_buf_.size() == 0)
{
start_timer();
auto const bytes_transferred = boost::asio::async_read (
impl().stream_, read_buf_.prepare (bufferSize),
boost::asio::transfer_at_least(1), yield[ec]);
cancel_timer();
eof = ec == boost::asio::error::eof;
if (eof)
{
ec = error_code{};
}
else if (! ec)
{
bytes_in_ += bytes_transferred;
read_buf_.commit (bytes_transferred);
}
}
if (! ec)
{
if (! eof)
{
// VFALCO TODO Currently parsing errors are treated the
// same as the connection dropping. Instead, we
// should request that the handler compose a proper HTTP error
// response. This requires refactoring HTTPReply() into
// something sensible.
std::size_t used;
std::tie (ec, used) = parser.write (read_buf_.data());
if (! ec)
read_buf_.consume (used);
}
else
{
ec = parser.write_eof();
}
}
if (! ec)
{
if (parser.complete())
return do_request();
if (eof)
ec = boost::asio::error::eof; // incomplete request
}
if (ec)
return fail (ec, "read");
}
beast::http::async_read(impl().stream_,
read_buf_, message_, yield[ec]);
// VFALCO What if the connection was closed?
cancel_timer();
do_request();
}
// Send everything in the write queue.
@@ -513,7 +456,7 @@ BaseHTTPPeer<Impl>::complete()
return strand_.post(std::bind (&BaseHTTPPeer<Impl>::complete,
impl().shared_from_this()));
message_ = beast::http::message{};
message_ = {};
complete_ = true;
{

View File

@@ -108,7 +108,7 @@ PlainHTTPPeer::do_request()
}
// Perform half-close when Connection: close and not SSL
if (! message_.keep_alive())
if (! is_keep_alive(message_))
stream_.shutdown (socket_type::shutdown_receive, ec);
if (ec)
return fail (ec, "request");

View File

@@ -19,7 +19,7 @@
#include <ripple/server/Port.h>
#include <beast/http/rfc2616.h>
#include <beast/module/core/text/LexicalCast.h>
#include <ripple/beast/core/LexicalCast.h>
namespace ripple {
@@ -169,7 +169,7 @@ parse_Port (ParsedPort& port, Section const& section, std::ostream& log)
if (*port.port == 0)
Throw<std::exception> ();
}
catch (std::exception const& ex)
catch (std::exception const&)
{
log <<
"Invalid value '" << result.first << "' for key " <<
@@ -199,7 +199,7 @@ parse_Port (ParsedPort& port, Section const& section, std::ostream& log)
port.limit = static_cast<int> (
beast::lexicalCastThrow<std::uint16_t>(lim));
}
catch (std::exception const& ex)
catch (std::exception const&)
{
log <<
"Invalid value '" << lim << "' for key " <<

View File

@@ -116,7 +116,7 @@ ServerHandlerImp::onAccept (Session& session,
auto
ServerHandlerImp::onHandoff (Session& session,
std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) ->
Handoff
{
@@ -138,7 +138,7 @@ ServerHandlerImp::onHandoff (Session& session,
auto
ServerHandlerImp::onHandoff (Session& session,
boost::asio::ip::tcp::socket&& socket,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) ->
Handoff
{
@@ -163,6 +163,23 @@ Json::Output makeOutput (Session& session)
};
}
// HACK!
template<class Allocator>
static
std::map<std::string, std::string>
build_map(beast::http::headers<Allocator> const& h)
{
std::map <std::string, std::string> c;
for (auto const& e : h)
{
auto key (e.first);
// TODO Replace with safe C++14 version
std::transform (key.begin(), key.end(), key.begin(), ::tolower);
c [key] = e.second;
}
return c;
}
void
ServerHandlerImp::onRequest (Session& session)
{
@@ -207,12 +224,32 @@ ServerHandlerImp::onStopped (Server&)
//------------------------------------------------------------------------------
template<class ConstBufferSequence>
static
std::string
buffers_to_string(ConstBufferSequence const& bs)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(bs));
for(auto const& b : bs)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
for(auto i = s.size(); i-- > 0;)
if(s[i] == '\r')
s.replace(i, 1, "\\r");
else if(s[i] == '\n')
s.replace(i, 1, "\\n\n");
return s;
}
// Run as a couroutine.
void
ServerHandlerImp::processSession (std::shared_ptr<Session> const& session,
std::shared_ptr<JobCoro> jobCoro)
{
processRequest (session->port(), to_string (session->body()),
processRequest (session->port(), buffers_to_string(session->request().body.data()),
session->remoteAddress().at_port (0), makeOutput (*session), jobCoro,
[&]
{
@@ -233,7 +270,7 @@ ServerHandlerImp::processSession (std::shared_ptr<Session> const& session,
return std::string{};
}());
if (session->request().keep_alive())
if(is_keep_alive(session->request()))
session->complete();
else
session->close (true);
@@ -425,9 +462,9 @@ ServerHandlerImp::processRequest (Port const& port,
// Returns `true` if the HTTP request is a Websockets Upgrade
// http://en.wikipedia.org/wiki/HTTP/1.1_Upgrade_header#Use_with_WebSockets
bool
ServerHandlerImp::isWebsocketUpgrade (beast::http::message const& request)
ServerHandlerImp::isWebsocketUpgrade (http_request_type const& request)
{
if (request.upgrade())
if (is_upgrade(request))
return request.headers["Upgrade"] == "websocket";
return false;
}
@@ -457,7 +494,7 @@ ServerHandlerImp::authorized (Port const& port,
//------------------------------------------------------------------------------
void
ServerHandler::appendStandardFields (beast::http::message& message)
ServerHandler::appendStandardFields (beast::deprecated_http::message& message)
{
}

View File

@@ -95,12 +95,12 @@ private:
Handoff
onHandoff (Session& session,
std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override;
Handoff
onHandoff (Session& session, boost::asio::ip::tcp::socket&& socket,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override;
void
onRequest (Session& session) override;
@@ -126,7 +126,7 @@ private:
private:
bool
isWebsocketUpgrade (beast::http::message const& request);
isWebsocketUpgrade (http_request_type const& request);
bool
authorized (Port const& port,

View File

@@ -107,7 +107,7 @@ public:
Handoff
onHandoff (Session& session,
std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override
{
return Handoff{};
@@ -115,7 +115,7 @@ public:
Handoff
onHandoff (Session& session, boost::asio::ip::tcp::socket&& socket,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override
{
return Handoff{};
@@ -125,7 +125,7 @@ public:
onRequest (Session& session) override
{
session.write (std::string ("Hello, world!\n"));
if (session.request().keep_alive())
if (is_keep_alive(session.request()))
session.complete();
else
session.close (true);
@@ -309,7 +309,7 @@ public:
Handoff
onHandoff (Session& session,
std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override
{
return Handoff{};
@@ -317,7 +317,7 @@ public:
Handoff
onHandoff (Session& session, boost::asio::ip::tcp::socket&& socket,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override
{
return Handoff{};

View File

@@ -122,8 +122,8 @@ public:
read_until(stream_, bin_, "\r\n\r\n");
beast::streambuf body;
beast::http::message m;
beast::http::parser p(
beast::deprecated_http::message m;
beast::deprecated_http::parser p(
[&](void const* data, std::size_t size)
{
body.commit(buffer_copy(
@@ -132,11 +132,12 @@ public:
for(;;)
{
auto const result = p.write(bin_.data());
if (result.first)
Throw<boost::system::system_error>(result.first);
bin_.consume(result.second);
boost::system::error_code ec;
auto used = p.write(bin_.data(), ec);
if(ec)
Throw<boost::system::system_error>(ec);
bin_.consume(used);
// VFALCO What do we do if bin_ still has data?
if(p.complete())
break;
bin_.commit(stream_.read_some(

View File

@@ -30,8 +30,6 @@
// Include this to get all the basic includes included, to prevent errors
#include <ripple/beast/core/core.unity.cpp>
#include <beast/http/HTTP.unity.cpp>
#include <beast/unit_test/define_print.cpp>
#endif

View File

@@ -543,13 +543,13 @@ public:
streambuf sb;
read_until(s_, sb, "\r\n\r\n");
using namespace beast;
http::body b;
http::message m;
http::parser p(m, b, false);
auto const result = p.write(sb.data());
if (result.first || ! p.complete())
throw std::runtime_error(result.first.message());
sb.consume(result.second);
deprecated_http::body b;
deprecated_http::message m;
deprecated_http::parser p(m, b, false);
auto const used = p.write(sb.data(), ec);
if (ec || ! p.complete())
throw std::runtime_error(ec.message());
sb.consume(used);
}
// write a text message