mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-27 22:45:52 +00:00
Beast.WebSocket:
Beast.WebSocket provides developers with a robust WebSocket implementation built on Boost.Asio with a consistent asynchronous model using a modern C++ approach.
This commit is contained in:
18
Jamroot
18
Jamroot
@@ -34,7 +34,16 @@ else if [ os.name ] = HAIKU
|
|||||||
lib network ;
|
lib network ;
|
||||||
}
|
}
|
||||||
|
|
||||||
build-project test/asio ;
|
if [ os.name ] = NT
|
||||||
|
{
|
||||||
|
lib ssl : : <name>ssleay32 ;
|
||||||
|
lib crypto : : <name>libeay32 ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lib ssl ;
|
||||||
|
lib crypto ;
|
||||||
|
}
|
||||||
|
|
||||||
project beast
|
project beast
|
||||||
: requirements
|
: requirements
|
||||||
@@ -49,6 +58,8 @@ project beast
|
|||||||
<threading>multi
|
<threading>multi
|
||||||
<link>static
|
<link>static
|
||||||
<runtime-link>static
|
<runtime-link>static
|
||||||
|
<toolset>gcc:<cxxflags>-std=c++14
|
||||||
|
<toolset>clang:<cxxflags>-std=c++14
|
||||||
<os>LINUX:<define>_XOPEN_SOURCE=600
|
<os>LINUX:<define>_XOPEN_SOURCE=600
|
||||||
<os>LINUX:<define>_GNU_SOURCE=1
|
<os>LINUX:<define>_GNU_SOURCE=1
|
||||||
<os>SOLARIS:<define>_XOPEN_SOURCE=500
|
<os>SOLARIS:<define>_XOPEN_SOURCE=500
|
||||||
@@ -68,7 +79,10 @@ project beast
|
|||||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
|
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
|
||||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
|
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
|
||||||
: usage-requirements
|
: usage-requirements
|
||||||
<include>.
|
<include>.
|
||||||
:
|
:
|
||||||
build-dir bin
|
build-dir bin
|
||||||
;
|
;
|
||||||
|
|
||||||
|
build-project test ;
|
||||||
|
build-project examples ;
|
||||||
|
|||||||
8
TODO.txt
Normal file
8
TODO.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
* kick out non-beast code
|
||||||
|
|
||||||
|
* redo directory structure
|
||||||
|
|
||||||
|
* Change build options to C++11 only
|
||||||
|
|
||||||
|
* Replace Jamroot with Jamfile
|
||||||
|
|
||||||
@@ -27,7 +27,6 @@ namespace beast {
|
|||||||
namespace debug {
|
namespace debug {
|
||||||
|
|
||||||
template<class Buffers>
|
template<class Buffers>
|
||||||
static
|
|
||||||
std::string
|
std::string
|
||||||
buffers_to_string(Buffers const& bs)
|
buffers_to_string(Buffers const& bs)
|
||||||
{
|
{
|
||||||
|
|||||||
21
beast/unity/beast_wsproto_unity.cpp
Normal file
21
beast/unity/beast_wsproto_unity.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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/wsproto/src/test/beast_wsproto_ws_test.cpp>
|
||||||
|
#include <beast/wsproto/src/test/beast_wsproto_ws_echo_test.cpp>
|
||||||
30
beast/wsproto.h
Normal file
30
beast/wsproto.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/error.h>
|
||||||
|
#include <beast/wsproto/option.h>
|
||||||
|
#include <beast/wsproto/rfc6455.h>
|
||||||
|
#include <beast/wsproto/socket.h>
|
||||||
|
#include <beast/wsproto/static_string.h>
|
||||||
|
#include <beast/wsproto/teardown.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
232
beast/wsproto/README.md
Normal file
232
beast/wsproto/README.md
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
# Beast.WSProto
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Beast.WSProto provides developers with a robust WebSocket implementation
|
||||||
|
built on Boost.Asio with a consistent asynchronous model using a modern
|
||||||
|
C++ approach.
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Today's web applications increasingly rely on alternatives to standard HTTP
|
||||||
|
to achieve performance and/or responsiveness. While WebSocket implementations
|
||||||
|
are widely available in common web development languages such as Javascript,
|
||||||
|
good implementations in C++ are scarce. A survey of existing C++ WebSocket
|
||||||
|
solutions reveals interfaces which have performance limitations, place
|
||||||
|
unecessary restrictions on callers, exhibit excess complexity, and fail to
|
||||||
|
take advantage of C++ features or the underlying network transport.
|
||||||
|
|
||||||
|
Beast.WSProto is built on Boost.Asio, a robust cross platform networking
|
||||||
|
framework that is part of Boost and also offered as a standalone library.
|
||||||
|
A proposal to add networking functionality to the C++ standard library,
|
||||||
|
based on Boost.Asio, is under consideration by the standards committee.
|
||||||
|
Since the final approved networking interface for the C++ standard library
|
||||||
|
will likely closely resemble the current interface of Boost.Asio, it is
|
||||||
|
logical for Beast.WSProto to use Boost.Asio as its network transport.
|
||||||
|
|
||||||
|
Beast.WSProto addresses the following goals:
|
||||||
|
|
||||||
|
* **Ease of Use.** WSProto offers only one socket object, whose interface
|
||||||
|
resembles that of Boost.Asio socket as closely as possible. Users familiar
|
||||||
|
with Boost.Asio will be immediately comfortable using a `wsproto::socket`.
|
||||||
|
|
||||||
|
* **Flexibility.** Library interfaces should provide callers with maximum
|
||||||
|
flexibility in implementation; Important decisions such as how to manage
|
||||||
|
buffers or be notified of completed asynchronous operations should be made
|
||||||
|
by callers not the library.
|
||||||
|
|
||||||
|
* **Performance.** The implementation should achieve the highest level
|
||||||
|
of performance possible, with no penalty for using abstractions.
|
||||||
|
|
||||||
|
* **Scalability.** The library should facilitate the development of
|
||||||
|
network applications that scale to thousands of concurrent connections.
|
||||||
|
|
||||||
|
* **Efficiency.** The library should support techniques such as
|
||||||
|
scatter-gather I/O, and allow programs to minimise data copying.
|
||||||
|
|
||||||
|
* **Basis for further abstraction.** The library should permit the
|
||||||
|
development of other libraries that provide higher levels of abstraction.
|
||||||
|
|
||||||
|
Beast.WSProto takes advantage of Boost.Asio's universal Asynchronous
|
||||||
|
model, handler allocation, and handler invocation hooks. Calls to wsproto
|
||||||
|
asynchronous initiation functions allow callers the choice of using a
|
||||||
|
completion handler, stackful or stackless coroutines, futures, or user
|
||||||
|
defined customizations (for example, Boost.Fiber). The implementation
|
||||||
|
uses handler invocation hooks (`asio_handler_invoke`), providing
|
||||||
|
execution guarantees on composed operations in a manner identical to
|
||||||
|
Boost.Asio. The implementation also uses handler allocation hooks
|
||||||
|
(`asio_handler_allocate`) when allocating memory internally for composed
|
||||||
|
operations.
|
||||||
|
|
||||||
|
There is no need for inheritance or virtual members in `wsproto::socket`.
|
||||||
|
All operations are templated and transparent to the compiler, allowing for
|
||||||
|
maximum inlining and optimization.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
All examples and identifiers mentioned in this document are written as
|
||||||
|
if the following declarations are in effect:
|
||||||
|
```C++
|
||||||
|
#include <beast/wsproto.h>
|
||||||
|
using namespace beast;
|
||||||
|
using namespace boost::asio;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Creating a Socket
|
||||||
|
|
||||||
|
To participate in a WebSocket connection, callers create an instance
|
||||||
|
of `wsproto::socket` templated on the `Stream` argument, which must meet
|
||||||
|
the requirements of `AsyncReadStream`, `AsyncWriteStream`, `SyncReadStream`,
|
||||||
|
and `SyncWriteStream`. Examples of types that meet these requirements are
|
||||||
|
`ip::tcp::socket` and `ssl::stream<...>`:
|
||||||
|
```c++
|
||||||
|
io_service ios;
|
||||||
|
wsproto::socket<ip::tcp::socket> ws1(ios); // owns the socket
|
||||||
|
|
||||||
|
ssl::context ctx(ssl::context::sslv23);
|
||||||
|
wsproto::socket<ssl::stream<
|
||||||
|
ip::tcp::socket>> wss(ios, ctx); // owns the socket
|
||||||
|
|
||||||
|
ip::tcp::socket sock(ios);
|
||||||
|
wsproto::socket<ip::tcp::socket&> ws2(sock); // does not own the socket
|
||||||
|
```
|
||||||
|
|
||||||
|
### Connection Establishment
|
||||||
|
|
||||||
|
Callers are responsible for performing tasks such as connection establishment
|
||||||
|
before attempting websocket activities.
|
||||||
|
```c++
|
||||||
|
io_service ios;
|
||||||
|
wsproto::socket<ip::tcp::socket> ws(ios);
|
||||||
|
ws.next_layer().connect(ip::tcp::endpoint(
|
||||||
|
ip::tcp::address::from_string("127.0.0.1"), 80));
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebSocket Handshake
|
||||||
|
|
||||||
|
After the connection is established, the socket may be used to initiate
|
||||||
|
or accept a WebSocket Update request.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// send a WebSocket Upgrade request.
|
||||||
|
ws.handshake();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sending and Receiving Messages
|
||||||
|
|
||||||
|
After the WebSocket handshake is accomplished, callers may send and receive
|
||||||
|
messages using the message oriented interface:
|
||||||
|
```c++
|
||||||
|
void echo(wsproto::socket<ip::tcp::socket>& ws)
|
||||||
|
{
|
||||||
|
streambuf sb;
|
||||||
|
wsproto::opcode op;
|
||||||
|
wsproto::read(ws, op, sb);
|
||||||
|
wsproto::write(ws, op, sb.data());
|
||||||
|
sb.consume(sb.size());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, callers may process incoming message data
|
||||||
|
incrementally:
|
||||||
|
```c++
|
||||||
|
void echo(wsproto::socket<ip::tcp::socket>& ws)
|
||||||
|
{
|
||||||
|
streambuf sb;
|
||||||
|
wsproto::msg_info mi{};
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
ws.read_some(mi, sb);
|
||||||
|
if(mi.fin)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
wsproto::write(ws, op, sb.data());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Asynchronous Completions, Coroutines, and Futures
|
||||||
|
|
||||||
|
Asynchronous versions are available for all functions:
|
||||||
|
```c++
|
||||||
|
wsproto::async_read(ws, sb, std::bind(
|
||||||
|
&on_read, beast::asio::placeholders::error));
|
||||||
|
```
|
||||||
|
|
||||||
|
Calls to WSProto asynchronous initiation functions support
|
||||||
|
asio-style completion handlers, and other completion tokens
|
||||||
|
such as support for coroutines or futures:
|
||||||
|
```c++
|
||||||
|
void echo(wsproto::socket<ip::tcp::socket>& ws,
|
||||||
|
boost::asio::yield_context yield)
|
||||||
|
{
|
||||||
|
wsproto::async_read(ws, sb, yield);
|
||||||
|
std::future<wsproto::error_code> fut =
|
||||||
|
wsproto::async_write(ws, sb.data(), boost::use_future);
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
### Buffers
|
||||||
|
|
||||||
|
Because calls to read WebSocket data may return a variable amount of bytes,
|
||||||
|
the interface to calls that read data require an object that meets the
|
||||||
|
requirements of `Streambuf`. This concept is modeled on
|
||||||
|
`boost::asio::basic_streambuf`, which meets the requirements of `Streambuf`
|
||||||
|
defined below.
|
||||||
|
|
||||||
|
The `Streambuf` concept is intended to permit the following implementation
|
||||||
|
strategies:
|
||||||
|
|
||||||
|
* A single contiguous character array, which is reallocated as necessary to
|
||||||
|
accommodate changes in the size of the byte sequence. This is the
|
||||||
|
implementation approach currently used in `boost::asio::basic_streambuf`.
|
||||||
|
* A sequence of one or more byte arrays, where each array is of the same
|
||||||
|
size. Additional byte array objects are appended to the sequence to
|
||||||
|
accommodate changes in the size of the byte sequence.
|
||||||
|
* A sequence of one or more byte arrays of varying sizes. Additional byte
|
||||||
|
array objects are appended to the sequence to accommodate changes in the
|
||||||
|
size of the byte sequence. This is the implementation approach currently
|
||||||
|
used in `beast::basic_streambuf`.
|
||||||
|
|
||||||
|
#### `Streambuf` requirements:
|
||||||
|
|
||||||
|
In the table below, `X` denotes a class, `a` denotes a value
|
||||||
|
of type `X`, `n` denotes a value convertible to `std::size_t`,
|
||||||
|
and `U` and `T` denote unspecified types.
|
||||||
|
|
||||||
|
expression | return | type assertion/note/pre/post-condition
|
||||||
|
------------------------- | ------------- | --------------------------------------
|
||||||
|
`X::const_buffers_type` | `T` | `T` meets the requirements for `ConstBufferSequence`.
|
||||||
|
`X::mutable_buffers_type` | `U` | `U` meets the requirements for `MutableBufferSequence`.
|
||||||
|
`a.commit(n)` | | Moves bytes from the output sequence to the input sequence.
|
||||||
|
`a.consume(n)` | | Removes bytes from the input sequence.
|
||||||
|
`a.data()` | `T` | Returns a list of buffers that represents the input sequence.
|
||||||
|
`a.prepare(n)` | `U` | Returns a list of buffers that represents the output sequence, with the given size.
|
||||||
|
`a.size()` | `std::size_t` | Returns the size of the input sequence.
|
||||||
|
`a.max_size()` | `std::size_t` | Returns the maximum size of the `Streambuf`.
|
||||||
|
|
||||||
|
### Thread Safety
|
||||||
|
|
||||||
|
Like a regular asio socket, a `wsproto::socket` is not thread safe. Callers are
|
||||||
|
responsible for synchronizing operations on the socket using an implicit or
|
||||||
|
explicit strand, as per the Asio documentation. A `wsproto::socket` supports
|
||||||
|
one active read and one active write at the same time (caller initiated close,
|
||||||
|
ping, and pong operations count as a write).
|
||||||
|
|
||||||
|
### Buffering
|
||||||
|
|
||||||
|
The implementation does not perform queueing or buffering of messages. If desired,
|
||||||
|
these features should be implemented by callers. The impact of this design is
|
||||||
|
that the caller is in full control of the allocation strategy used to store
|
||||||
|
data and the back-pressure applied on the read and write side of the underlying
|
||||||
|
TCP/IP connection.
|
||||||
|
|
||||||
|
### The `io_service`
|
||||||
|
|
||||||
|
The creation and operation of the `boost::asio::io_service` associated with the
|
||||||
|
Stream object underlying the `wsproto::socket` is completely left up to the
|
||||||
|
user of the library, permitting any implementation strategy including one that
|
||||||
|
does not require threads for environments where threads are unavailable.
|
||||||
|
Beast.WSProto itself does not use or require threads.
|
||||||
88
beast/wsproto/detail/debug.h
Normal file
88
beast/wsproto/detail/debug.h
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_DEBUG_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_DEBUG_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
std::string
|
||||||
|
to_hex(boost::asio::const_buffer b)
|
||||||
|
{
|
||||||
|
using namespace boost::asio;
|
||||||
|
std::stringstream ss;
|
||||||
|
auto p = buffer_cast<std::uint8_t const*>(b);
|
||||||
|
auto n = buffer_size(b);
|
||||||
|
while(n--)
|
||||||
|
{
|
||||||
|
ss <<
|
||||||
|
std::setfill('0') <<
|
||||||
|
std::setw(2) <<
|
||||||
|
std::hex << int(*p++) << " ";
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Buffers>
|
||||||
|
std::string
|
||||||
|
to_hex(Buffers const& bs)
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
for(auto const& b : bs)
|
||||||
|
s.append(to_hex(boost::asio::const_buffer(b)));
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Buffers>
|
||||||
|
std::string
|
||||||
|
buffers_to_string(Buffers const& bs)
|
||||||
|
{
|
||||||
|
using namespace boost::asio;
|
||||||
|
std::string s;
|
||||||
|
s.reserve(buffer_size(bs));
|
||||||
|
for(auto const& b : bs)
|
||||||
|
s.append(buffer_cast<char const*>(b),
|
||||||
|
buffer_size(b));
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
std::string
|
||||||
|
format(std::string s)
|
||||||
|
{
|
||||||
|
auto const w = 84;
|
||||||
|
for(int n = w*(s.size()/w); n>0; n-=w)
|
||||||
|
s.insert(n, 1, '\n');
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
112
beast/wsproto/detail/decorator.h
Normal file
112
beast/wsproto/detail/decorator.h
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_DECORATOR_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_DECORATOR_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/empty_body.h>
|
||||||
|
#include <beast/http/message.h>
|
||||||
|
#include <beast/http/string_body.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
using request_type = http::request<http::empty_body>;
|
||||||
|
|
||||||
|
using response_type = http::response<http::string_body>;
|
||||||
|
|
||||||
|
struct abstract_decorator
|
||||||
|
{
|
||||||
|
virtual
|
||||||
|
~abstract_decorator() = default;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
operator()(request_type& req) = 0;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
operator()(response_type& resp) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class decorator : public abstract_decorator
|
||||||
|
{
|
||||||
|
T t_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
decorator() = default;
|
||||||
|
|
||||||
|
decorator(T&& t)
|
||||||
|
: t_(std::move(t))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
decorator(T const& t)
|
||||||
|
: t_(t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(request_type& req) override
|
||||||
|
{
|
||||||
|
t_(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(response_type& resp) override
|
||||||
|
{
|
||||||
|
t_(resp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct default_decorator
|
||||||
|
{
|
||||||
|
static
|
||||||
|
char const*
|
||||||
|
version()
|
||||||
|
{
|
||||||
|
return "Beast.WSProto/1.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<true, Body, Headers>& req)
|
||||||
|
{
|
||||||
|
req.headers.replace("User-Agent", version());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<false, Body, Headers>& resp)
|
||||||
|
{
|
||||||
|
resp.headers.replace("Server", version());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using decorator_type =
|
||||||
|
std::unique_ptr<abstract_decorator>;
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
104
beast/wsproto/detail/error.h
Normal file
104
beast/wsproto/detail/error.h
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_DETAIL_ERROR_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_DETAIL_ERROR_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/error.h>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace system {
|
||||||
|
template<>
|
||||||
|
struct is_error_code_enum<beast::wsproto::error>
|
||||||
|
{
|
||||||
|
static bool const value = true;
|
||||||
|
};
|
||||||
|
} // system
|
||||||
|
} // boost
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class error_category : public boost::system::error_category
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const char*
|
||||||
|
name() const noexcept override
|
||||||
|
{
|
||||||
|
return "wsproto";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
message(int ev) const override
|
||||||
|
{
|
||||||
|
switch(static_cast<error>(ev))
|
||||||
|
{
|
||||||
|
case error::closed: return "WebSocket connection closed normally";
|
||||||
|
case error::failed: return "WebSocket connection failed due to a protocol violation";
|
||||||
|
case error::handshake_failed: return "WebSocket Upgrade handshake failed";
|
||||||
|
case error::keep_alive: return "WebSocket Upgrade handshake failed but connection is still open";
|
||||||
|
|
||||||
|
case error::response_malformed: return "malformed HTTP response";
|
||||||
|
case error::response_failed: return "upgrade request failed";
|
||||||
|
case error::response_denied: return "upgrade request denied";
|
||||||
|
case error::request_malformed: return "malformed HTTP request";
|
||||||
|
case error::request_invalid: return "upgrade request invalid";
|
||||||
|
case error::request_denied: return "upgrade request denied";
|
||||||
|
default:
|
||||||
|
return "wsproto.error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::system::error_condition
|
||||||
|
default_error_condition(int ev) const noexcept override
|
||||||
|
{
|
||||||
|
return boost::system::error_condition(ev, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
equivalent(int ev,
|
||||||
|
boost::system::error_condition const& condition
|
||||||
|
) const noexcept override
|
||||||
|
{
|
||||||
|
return condition.value() == ev &&
|
||||||
|
&condition.category() == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
equivalent(error_code const& error, int ev) const noexcept override
|
||||||
|
{
|
||||||
|
return error.value() == ev &&
|
||||||
|
&error.category() == this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
boost::system::error_category const&
|
||||||
|
get_error_category()
|
||||||
|
{
|
||||||
|
static detail::error_category const cat{};
|
||||||
|
return cat;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
375
beast/wsproto/detail/frame.h
Normal file
375
beast/wsproto/detail/frame.h
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_FRAME_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_FRAME_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/rfc6455.h>
|
||||||
|
#include <beast/wsproto/static_string.h>
|
||||||
|
#include <beast/wsproto/detail/utf8_checker.h>
|
||||||
|
#include <beast/asio/consuming_buffers.h>
|
||||||
|
#include <beast/asio/static_streambuf.h>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/endian/buffers.hpp>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// Contents of a WebSocket frame header
|
||||||
|
struct frame_header
|
||||||
|
{
|
||||||
|
opcode op;
|
||||||
|
bool fin;
|
||||||
|
bool mask;
|
||||||
|
bool rsv1;
|
||||||
|
bool rsv2;
|
||||||
|
bool rsv3;
|
||||||
|
std::uint64_t len;
|
||||||
|
std::uint32_t key;
|
||||||
|
};
|
||||||
|
|
||||||
|
// holds the largest possible frame header
|
||||||
|
using fh_streambuf =
|
||||||
|
static_streambuf_n<14>;
|
||||||
|
|
||||||
|
// holds the largest possible control frame
|
||||||
|
using frame_streambuf =
|
||||||
|
static_streambuf_n< 2 + 8 + 4 + 125 >;
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool constexpr
|
||||||
|
is_reserved(opcode op)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
(op >= opcode::rsv3 && op <= opcode::rsv7) ||
|
||||||
|
(op >= opcode::crsvb && op <= opcode::crsvf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool constexpr
|
||||||
|
is_valid(opcode op)
|
||||||
|
{
|
||||||
|
return op <= opcode::crsvf;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool constexpr
|
||||||
|
is_control(opcode op)
|
||||||
|
{
|
||||||
|
return op >= opcode::close;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns `true` if a close code is valid
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_valid(close_code code)
|
||||||
|
{
|
||||||
|
auto const v = static_cast<
|
||||||
|
std::uint16_t>(code);
|
||||||
|
switch(v)
|
||||||
|
{
|
||||||
|
case 1000:
|
||||||
|
case 1001:
|
||||||
|
case 1002:
|
||||||
|
case 1003:
|
||||||
|
case 1007:
|
||||||
|
case 1008:
|
||||||
|
case 1009:
|
||||||
|
case 1010:
|
||||||
|
case 1011:
|
||||||
|
case 1012:
|
||||||
|
case 1013:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// explicitly reserved
|
||||||
|
case 1004:
|
||||||
|
case 1005:
|
||||||
|
case 1006:
|
||||||
|
case 1014:
|
||||||
|
case 1015:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// reserved
|
||||||
|
if(v >= 1016 && v <= 2999)
|
||||||
|
return false;
|
||||||
|
// not used
|
||||||
|
if(v >= 0 && v <= 999)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Write frame header to streambuf
|
||||||
|
//
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
write(Streambuf& sb, frame_header const& fh)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using namespace boost::endian;
|
||||||
|
std::size_t n;
|
||||||
|
std::uint8_t b[14];
|
||||||
|
b[0] = (fh.fin ? 0x80 : 0x00) | static_cast<std::uint8_t>(fh.op);
|
||||||
|
b[1] = fh.mask ? 0x80 : 0x00;
|
||||||
|
if (fh.len <= 125)
|
||||||
|
{
|
||||||
|
b[1] |= fh.len;
|
||||||
|
n = 2;
|
||||||
|
}
|
||||||
|
else if (fh.len <= 65535)
|
||||||
|
{
|
||||||
|
b[1] |= 126;
|
||||||
|
::new(&b[2]) big_uint16_buf_t{
|
||||||
|
(std::uint16_t)fh.len};
|
||||||
|
n = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
b[1] |= 127;
|
||||||
|
::new(&b[2]) big_uint64_buf_t{fh.len};
|
||||||
|
n = 10;
|
||||||
|
}
|
||||||
|
if(fh.mask)
|
||||||
|
{
|
||||||
|
little_uint32_buf_t key(fh.key);
|
||||||
|
std::copy(key.data(),
|
||||||
|
key.data() + 4, &b[n]);
|
||||||
|
n += 4;
|
||||||
|
}
|
||||||
|
sb.commit(buffer_copy(
|
||||||
|
sb.prepare(n), buffer(b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read fixed frame header
|
||||||
|
// Requires at least 2 bytes
|
||||||
|
//
|
||||||
|
template<class Streambuf>
|
||||||
|
std::size_t
|
||||||
|
read_fh1(frame_header& fh, Streambuf& sb,
|
||||||
|
role_type role, close_code& code)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
std::uint8_t b[2];
|
||||||
|
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||||
|
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||||
|
std::size_t need;
|
||||||
|
fh.len = b[1] & 0x7f;
|
||||||
|
switch(fh.len)
|
||||||
|
{
|
||||||
|
case 126: need = 2; break;
|
||||||
|
case 127: need = 8; break;
|
||||||
|
default:
|
||||||
|
need = 0;
|
||||||
|
}
|
||||||
|
if((fh.mask = (b[1] & 0x80) != 0))
|
||||||
|
need += 4;
|
||||||
|
fh.op = static_cast<opcode>(b[0] & 0x0f);
|
||||||
|
fh.fin = (b[0] & 0x80) != 0;
|
||||||
|
fh.rsv1 = (b[0] & 0x40) != 0;
|
||||||
|
fh.rsv2 = (b[0] & 0x20) != 0;
|
||||||
|
fh.rsv3 = (b[0] & 0x10) != 0;
|
||||||
|
// invalid length for control message
|
||||||
|
if(is_control(fh.op) && fh.len > 125)
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// reserved bits not cleared
|
||||||
|
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// reserved opcode
|
||||||
|
if(is_reserved(fh.op))
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// invalid opcode
|
||||||
|
// (only in locally generated headers)
|
||||||
|
if(! is_valid(fh.op))
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// fragmented control message
|
||||||
|
if(is_control(fh.op) && ! fh.fin)
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// unmasked frame from client
|
||||||
|
if(role == role_type::server && ! fh.mask)
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// masked frame from server
|
||||||
|
if(role == role_type::client && fh.mask)
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
code = close_code::none;
|
||||||
|
return need;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode variable frame header from stream
|
||||||
|
//
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
read_fh2(frame_header& fh, Streambuf& sb,
|
||||||
|
role_type role, close_code& code)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
using namespace boost::endian;
|
||||||
|
switch(fh.len)
|
||||||
|
{
|
||||||
|
case 126:
|
||||||
|
{
|
||||||
|
std::uint8_t b[2];
|
||||||
|
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||||
|
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||||
|
fh.len = reinterpret_cast<
|
||||||
|
big_uint16_buf_t const*>(&b[0])->value();
|
||||||
|
// length not canonical
|
||||||
|
if(fh.len < 126)
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 127:
|
||||||
|
{
|
||||||
|
std::uint8_t b[8];
|
||||||
|
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||||
|
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||||
|
fh.len = reinterpret_cast<
|
||||||
|
big_uint64_buf_t const*>(&b[0])->value();
|
||||||
|
// length not canonical
|
||||||
|
if(fh.len < 65536)
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fh.mask)
|
||||||
|
{
|
||||||
|
std::uint8_t b[4];
|
||||||
|
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||||
|
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||||
|
fh.key = reinterpret_cast<
|
||||||
|
little_uint32_buf_t const*>(&b[0])->value();
|
||||||
|
}
|
||||||
|
code = close_code::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read data from buffers
|
||||||
|
// This is for ping and pong payloads
|
||||||
|
//
|
||||||
|
template<class Buffers>
|
||||||
|
void
|
||||||
|
read(ping_payload_type& data,
|
||||||
|
Buffers const& bs, close_code& code)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
using boost::asio::mutable_buffers_1;
|
||||||
|
assert(buffer_size(bs) <= data.max_size());
|
||||||
|
data.resize(buffer_size(bs));
|
||||||
|
buffer_copy(mutable_buffers_1{
|
||||||
|
data.data(), data.size()}, bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read close_reason, return true on success
|
||||||
|
// This is for the close payload
|
||||||
|
//
|
||||||
|
template<class Buffers>
|
||||||
|
void
|
||||||
|
read(close_reason& cr,
|
||||||
|
Buffers const& bs, close_code& code)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
using namespace boost::endian;
|
||||||
|
auto n = buffer_size(bs);
|
||||||
|
assert(n <= 125);
|
||||||
|
if(n == 0)
|
||||||
|
{
|
||||||
|
cr = close_reason{};
|
||||||
|
code = close_code::none;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(n == 1)
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
consuming_buffers<Buffers> cb(bs);
|
||||||
|
{
|
||||||
|
std::uint8_t b[2];
|
||||||
|
buffer_copy(buffer(b), cb);
|
||||||
|
cr.code = static_cast<close_code>(
|
||||||
|
reinterpret_cast<
|
||||||
|
big_uint16_buf_t const*>(&b[0])->value());
|
||||||
|
cb.consume(2);
|
||||||
|
n -= 2;
|
||||||
|
if(! is_valid(cr.code))
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(n > 0)
|
||||||
|
{
|
||||||
|
cr.reason.resize(n);
|
||||||
|
buffer_copy(buffer(&cr.reason[0], n), cb);
|
||||||
|
if(! detail::check_utf8(
|
||||||
|
cr.reason.data(), cr.reason.size()))
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cr.reason = "";
|
||||||
|
}
|
||||||
|
code = close_code::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
66
beast/wsproto/detail/hybi13.h
Normal file
66
beast/wsproto/detail/hybi13.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_HYBI13_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_HYBI13_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/crypto/base64.h>
|
||||||
|
#include <beast/crypto/sha.h>
|
||||||
|
#include <boost/utility/string_ref.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class Gen>
|
||||||
|
std::string
|
||||||
|
make_sec_ws_key(Gen& g)
|
||||||
|
{
|
||||||
|
union U
|
||||||
|
{
|
||||||
|
std::array<std::uint32_t, 4> a4;
|
||||||
|
std::array<std::uint8_t, 16> a16;
|
||||||
|
};
|
||||||
|
U u;
|
||||||
|
for(int i = 0; i < 4; ++i)
|
||||||
|
u.a4[i] = g();
|
||||||
|
return base64_encode(u.a16.data(), u.a16.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
std::string
|
||||||
|
make_sec_ws_accept(boost::string_ref const& key)
|
||||||
|
{
|
||||||
|
std::string s(key.data(), key.size());
|
||||||
|
s += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||||
|
beast::sha_hasher h;
|
||||||
|
h(s.data(), s.size());
|
||||||
|
auto const digest = static_cast<
|
||||||
|
beast::sha_hasher::result_type>(h);
|
||||||
|
return base64_encode(digest.data(), digest.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
168
beast/wsproto/detail/invokable.h
Normal file
168
beast/wsproto/detail/invokable.h
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_INVOKABLE_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_INVOKABLE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
#include <new>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// "Parks" a composed operation, to invoke later
|
||||||
|
//
|
||||||
|
class invokable
|
||||||
|
{
|
||||||
|
struct base
|
||||||
|
{
|
||||||
|
base() = default;
|
||||||
|
base(base &&) = default;
|
||||||
|
virtual ~base() = default;
|
||||||
|
virtual void move(void* p) = 0;
|
||||||
|
virtual void operator()() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class F>
|
||||||
|
struct holder : base
|
||||||
|
{
|
||||||
|
F f;
|
||||||
|
|
||||||
|
holder(holder&&) = default;
|
||||||
|
|
||||||
|
template<class U>
|
||||||
|
explicit
|
||||||
|
holder(U&& u)
|
||||||
|
: f(std::forward<U>(u))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
move(void* p) override
|
||||||
|
{
|
||||||
|
::new(p) holder(std::move(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()() override
|
||||||
|
{
|
||||||
|
F f_(std::move(f));
|
||||||
|
this->~holder();
|
||||||
|
// invocation of f_() can
|
||||||
|
// assign a new invokable.
|
||||||
|
f_();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct exemplar
|
||||||
|
{
|
||||||
|
std::shared_ptr<int> _;
|
||||||
|
void operator()(){}
|
||||||
|
};
|
||||||
|
|
||||||
|
using buf_type = std::uint8_t[
|
||||||
|
sizeof(holder<exemplar>)];
|
||||||
|
|
||||||
|
bool b_ = false;
|
||||||
|
alignas(holder<exemplar>) buf_type buf_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifndef NDEBUG
|
||||||
|
~invokable()
|
||||||
|
{
|
||||||
|
// Engaged invokables must be invoked before
|
||||||
|
// destruction otherwise the io_service
|
||||||
|
// invariants are broken w.r.t completions.
|
||||||
|
assert(! b_);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
invokable() = default;
|
||||||
|
invokable(invokable const&) = delete;
|
||||||
|
invokable& operator=(invokable const&) = delete;
|
||||||
|
|
||||||
|
invokable(invokable&& other)
|
||||||
|
: b_(other.b_)
|
||||||
|
{
|
||||||
|
if(other.b_)
|
||||||
|
{
|
||||||
|
other.get().move(buf_);
|
||||||
|
other.b_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invokable&
|
||||||
|
operator=(invokable&& other)
|
||||||
|
{
|
||||||
|
// Engaged invokables must be invoked before
|
||||||
|
// assignment otherwise the io_service
|
||||||
|
// invariants are broken w.r.t completions.
|
||||||
|
assert(! b_);
|
||||||
|
|
||||||
|
if(other.b_)
|
||||||
|
{
|
||||||
|
b_ = true;
|
||||||
|
other.get().move(buf_);
|
||||||
|
other.b_ = false;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class F>
|
||||||
|
void
|
||||||
|
emplace(F&& f);
|
||||||
|
|
||||||
|
void
|
||||||
|
maybe_invoke()
|
||||||
|
{
|
||||||
|
if(b_)
|
||||||
|
{
|
||||||
|
b_ = false;
|
||||||
|
get()();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
base&
|
||||||
|
get()
|
||||||
|
{
|
||||||
|
return *reinterpret_cast<base*>(buf_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class F>
|
||||||
|
void
|
||||||
|
invokable::emplace(F&& f)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(buf_type) >= sizeof(holder<F>),
|
||||||
|
"buffer too small");
|
||||||
|
assert(! b_);
|
||||||
|
::new(buf_) holder<F>(std::forward<F>(f));
|
||||||
|
b_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
389
beast/wsproto/detail/mask.h
Normal file
389
beast/wsproto/detail/mask.h
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_DETAIL_MASKGEN_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_DETAIL_MASKGEN_H_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <array>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <random>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// Pseudo-random source of mask keys
|
||||||
|
//
|
||||||
|
template<class = void>
|
||||||
|
class maskgen_t
|
||||||
|
{
|
||||||
|
std::mt19937 g_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using result_type = typename std::mt19937::result_type;
|
||||||
|
|
||||||
|
maskgen_t(maskgen_t const&) = delete;
|
||||||
|
maskgen_t& operator=(maskgen_t const&) = delete;
|
||||||
|
|
||||||
|
maskgen_t();
|
||||||
|
|
||||||
|
result_type
|
||||||
|
operator()() noexcept;
|
||||||
|
|
||||||
|
void
|
||||||
|
rekey();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
maskgen_t<_>::maskgen_t()
|
||||||
|
{
|
||||||
|
rekey();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
auto
|
||||||
|
maskgen_t<_>::operator()() noexcept ->
|
||||||
|
result_type
|
||||||
|
{
|
||||||
|
for(;;)
|
||||||
|
if(auto key = g_())
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
void
|
||||||
|
maskgen_t<_>::rekey()
|
||||||
|
{
|
||||||
|
std::random_device rng;
|
||||||
|
std::array<std::uint32_t, 32> e;
|
||||||
|
for(auto& i : e)
|
||||||
|
i = rng();
|
||||||
|
std::seed_seq ss(e.begin(), e.end());
|
||||||
|
g_.seed(ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
using maskgen = maskgen_t<>;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//using prepared_key_type = std::size_t;
|
||||||
|
using prepared_key_type = std::uint32_t;
|
||||||
|
//using prepared_key_type = std::uint64_t;
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
prepare_key(std::uint32_t& prepared, std::uint32_t key)
|
||||||
|
{
|
||||||
|
prepared = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
prepare_key(std::uint64_t& prepared, std::uint32_t key)
|
||||||
|
{
|
||||||
|
prepared =
|
||||||
|
(static_cast<std::uint64_t>(key) << 32) | key;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline
|
||||||
|
std::enable_if_t<std::is_integral<T>::value, T>
|
||||||
|
rol(T t, unsigned n = 1)
|
||||||
|
{
|
||||||
|
auto constexpr bits =
|
||||||
|
static_cast<unsigned>(
|
||||||
|
sizeof(T) * CHAR_BIT);
|
||||||
|
n &= bits-1;
|
||||||
|
return static_cast<T>((t << n) |
|
||||||
|
(static_cast<std::make_unsigned_t<T>>(t) >> (bits - n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
std::enable_if_t<std::is_integral<T>::value, T>
|
||||||
|
ror(T t, unsigned n = 1)
|
||||||
|
{
|
||||||
|
auto constexpr bits =
|
||||||
|
static_cast<unsigned>(
|
||||||
|
sizeof(T) * CHAR_BIT);
|
||||||
|
n &= bits-1;
|
||||||
|
return static_cast<T>((t << (bits - n)) |
|
||||||
|
(static_cast<std::make_unsigned_t<T>>(t) >> n));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 32-bit Uuoptimized
|
||||||
|
//
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
mask_inplace_safe(
|
||||||
|
boost::asio::mutable_buffer const& b,
|
||||||
|
std::uint32_t& key)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
auto n = buffer_size(b);
|
||||||
|
auto p = buffer_cast<std::uint8_t*>(b);
|
||||||
|
for(auto i = n / sizeof(key); i; --i)
|
||||||
|
{
|
||||||
|
*p ^= key ; ++p;
|
||||||
|
*p ^= (key >> 8); ++p;
|
||||||
|
*p ^= (key >>16); ++p;
|
||||||
|
*p ^= (key >>24); ++p;
|
||||||
|
}
|
||||||
|
n %= sizeof(key);
|
||||||
|
switch(n)
|
||||||
|
{
|
||||||
|
case 3: p[2] ^= (key >>16);
|
||||||
|
case 2: p[1] ^= (key >> 8);
|
||||||
|
case 1: p[0] ^= key;
|
||||||
|
key = ror(key, n*8);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 64-bit unoptimized
|
||||||
|
//
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
mask_inplace_safe(
|
||||||
|
boost::asio::mutable_buffer const& b,
|
||||||
|
std::uint64_t& key)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
auto n = buffer_size(b);
|
||||||
|
auto p = buffer_cast<std::uint8_t*>(b);
|
||||||
|
for(auto i = n / sizeof(key); i; --i)
|
||||||
|
{
|
||||||
|
*p ^= key ; ++p;
|
||||||
|
*p ^= (key >> 8); ++p;
|
||||||
|
*p ^= (key >>16); ++p;
|
||||||
|
*p ^= (key >>24); ++p;
|
||||||
|
*p ^= (key >>32); ++p;
|
||||||
|
*p ^= (key >>40); ++p;
|
||||||
|
*p ^= (key >>48); ++p;
|
||||||
|
*p ^= (key >>56); ++p;
|
||||||
|
}
|
||||||
|
n %= sizeof(key);
|
||||||
|
switch(n)
|
||||||
|
{
|
||||||
|
case 7: p[6] ^= (key >>16);
|
||||||
|
case 6: p[5] ^= (key >> 8);
|
||||||
|
case 5: p[4] ^= key;
|
||||||
|
case 4: p[3] ^= (key >>24);
|
||||||
|
case 3: p[2] ^= (key >>16);
|
||||||
|
case 2: p[1] ^= (key >> 8);
|
||||||
|
case 1: p[0] ^= key;
|
||||||
|
key = ror(key, n*8);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 32-bit optimized
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
mask_inplace_32(
|
||||||
|
boost::asio::mutable_buffer const& b,
|
||||||
|
std::uint32_t& key)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
auto n = buffer_size(b);
|
||||||
|
auto p = buffer_cast<std::uint8_t*>(b);
|
||||||
|
auto m = reinterpret_cast<
|
||||||
|
uintptr_t>(p) % sizeof(key);
|
||||||
|
switch(m)
|
||||||
|
{
|
||||||
|
case 1: *p ^= key ; ++p; --n;
|
||||||
|
case 2: *p ^= (key >> 8); ++p; --n;
|
||||||
|
case 3: *p ^= (key >>16); ++p; --n;
|
||||||
|
key = ror(key, m * 8);
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for(auto i = n / sizeof(key); i; --i)
|
||||||
|
{
|
||||||
|
*reinterpret_cast<
|
||||||
|
std::uint32_t*>(p) ^= key;
|
||||||
|
p += sizeof(key);
|
||||||
|
}
|
||||||
|
n %= sizeof(key);
|
||||||
|
switch(n)
|
||||||
|
{
|
||||||
|
case 3: p[2] ^= (key >>16);
|
||||||
|
case 2: p[1] ^= (key >> 8);
|
||||||
|
case 1: p[0] ^= key;
|
||||||
|
key = ror(key, n*8);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 64-bit optimized
|
||||||
|
//
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
mask_inplace_64(
|
||||||
|
boost::asio::mutable_buffer const& b,
|
||||||
|
std::uint64_t& key)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
auto n = buffer_size(b);
|
||||||
|
auto p = buffer_cast<std::uint8_t*>(b);
|
||||||
|
auto m = reinterpret_cast<
|
||||||
|
uintptr_t>(p) % sizeof(key);
|
||||||
|
switch(m)
|
||||||
|
{
|
||||||
|
case 1: *p ^= key ; ++p; --n;
|
||||||
|
case 2: *p ^= (key >> 8); ++p; --n;
|
||||||
|
case 3: *p ^= (key >>16); ++p; --n;
|
||||||
|
case 4: *p ^= (key >>24); ++p; --n;
|
||||||
|
case 5: *p ^= (key >>32); ++p; --n;
|
||||||
|
case 6: *p ^= (key >>40); ++p; --n;
|
||||||
|
case 7: *p ^= (key >>48); ++p; --n;
|
||||||
|
key = ror(key, m * 8);
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for(auto i = n / sizeof(key); i; --i)
|
||||||
|
{
|
||||||
|
*reinterpret_cast<
|
||||||
|
std::uint64_t*>(p) ^= key;
|
||||||
|
p += sizeof(key);
|
||||||
|
}
|
||||||
|
n %= sizeof(key);
|
||||||
|
switch(n)
|
||||||
|
{
|
||||||
|
case 3: p[2] ^= (key >>16);
|
||||||
|
case 2: p[1] ^= (key >> 8);
|
||||||
|
case 1: p[0] ^= key;
|
||||||
|
key = ror(key, n*8);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 32-bit x86 optimized
|
||||||
|
//
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
mask_inplace_x86(
|
||||||
|
boost::asio::mutable_buffer const& b,
|
||||||
|
std::uint32_t& key)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
auto n = buffer_size(b);
|
||||||
|
auto p = buffer_cast<std::uint8_t*>(b);
|
||||||
|
for(auto i = n / sizeof(key); i; --i)
|
||||||
|
{
|
||||||
|
*reinterpret_cast<
|
||||||
|
std::uint32_t*>(p) ^= key;
|
||||||
|
p += sizeof(key);
|
||||||
|
}
|
||||||
|
n %= sizeof(key);
|
||||||
|
switch(n)
|
||||||
|
{
|
||||||
|
case 3: p[2] ^= (key >>16);
|
||||||
|
case 2: p[1] ^= (key >> 8);
|
||||||
|
case 1: p[0] ^= key;
|
||||||
|
key = ror(key, n*8);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 64-bit amd64 optimized
|
||||||
|
//
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
mask_inplace_amd(
|
||||||
|
boost::asio::mutable_buffer const& b,
|
||||||
|
std::uint64_t& key)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
auto n = buffer_size(b);
|
||||||
|
auto p = buffer_cast<std::uint8_t*>(b);
|
||||||
|
for(auto i = n / sizeof(key); i; --i)
|
||||||
|
{
|
||||||
|
*reinterpret_cast<
|
||||||
|
std::uint64_t*>(p) ^= key;
|
||||||
|
p += sizeof(key);
|
||||||
|
}
|
||||||
|
n %= sizeof(key);
|
||||||
|
switch(n)
|
||||||
|
{
|
||||||
|
case 7: p[6] ^= (key >>16);
|
||||||
|
case 6: p[5] ^= (key >> 8);
|
||||||
|
case 5: p[4] ^= key;
|
||||||
|
case 4: p[3] ^= (key >>24);
|
||||||
|
case 3: p[2] ^= (key >>16);
|
||||||
|
case 2: p[1] ^= (key >> 8);
|
||||||
|
case 1: p[0] ^= key;
|
||||||
|
key = ror(key, n*8);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
mask_inplace(
|
||||||
|
boost::asio::mutable_buffer const& b,
|
||||||
|
std::uint32_t& key)
|
||||||
|
{
|
||||||
|
mask_inplace_safe(b, key);
|
||||||
|
//mask_inplace_32(b, key);
|
||||||
|
//mask_inplace_x86(b, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
mask_inplace(
|
||||||
|
boost::asio::mutable_buffer const& b,
|
||||||
|
std::uint64_t& key)
|
||||||
|
{
|
||||||
|
mask_inplace_safe(b, key);
|
||||||
|
//mask_inplace_64(b, key);
|
||||||
|
//mask_inplace_amd(b, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply mask in place
|
||||||
|
//
|
||||||
|
template<class MutableBuffers, class KeyType>
|
||||||
|
void
|
||||||
|
mask_inplace(
|
||||||
|
MutableBuffers const& bs, KeyType& key)
|
||||||
|
{
|
||||||
|
for(auto const& b : bs)
|
||||||
|
mask_inplace(b, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
141
beast/wsproto/detail/socket_base.h
Normal file
141
beast/wsproto/detail/socket_base.h
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_SOCKET_BASE_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_SOCKET_BASE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/error.h>
|
||||||
|
#include <beast/wsproto/rfc6455.h>
|
||||||
|
#include <beast/wsproto/detail/decorator.h>
|
||||||
|
#include <beast/wsproto/detail/frame.h>
|
||||||
|
#include <beast/wsproto/detail/invokable.h>
|
||||||
|
#include <beast/wsproto/detail/mask.h>
|
||||||
|
#include <beast/wsproto/detail/utf8_checker.h>
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
#include <beast/http/empty_body.h>
|
||||||
|
#include <beast/http/message.h>
|
||||||
|
#include <beast/http/string_body.h>
|
||||||
|
#include <boost/asio/error.hpp>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class String>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
maybe_throw(error_code const& ec, String const&)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class UInt>
|
||||||
|
static
|
||||||
|
std::size_t
|
||||||
|
clamp(UInt x)
|
||||||
|
{
|
||||||
|
if(x >= std::numeric_limits<std::size_t>::max())
|
||||||
|
return std::numeric_limits<std::size_t>::max();
|
||||||
|
return static_cast<std::size_t>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class UInt>
|
||||||
|
static
|
||||||
|
std::size_t
|
||||||
|
clamp(UInt x, std::size_t limit)
|
||||||
|
{
|
||||||
|
if(x >= limit)
|
||||||
|
return limit;
|
||||||
|
return static_cast<std::size_t>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct socket_base
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
struct op {};
|
||||||
|
|
||||||
|
detail::maskgen maskgen_; // source of mask keys
|
||||||
|
decorator_type d_; // adorns http messages
|
||||||
|
bool keep_alive_ = false; // close on failed upgrade
|
||||||
|
role_type role_; // server or client
|
||||||
|
bool error_ = false; // non-zero ec was delivered
|
||||||
|
|
||||||
|
std::size_t rd_msg_max_ =
|
||||||
|
16 * 1024 * 1024; // max message size
|
||||||
|
detail::frame_header rd_fh_; // current frame header
|
||||||
|
detail::prepared_key_type rd_key_; // prepared masking key
|
||||||
|
detail::utf8_checker rd_utf8_check_;// for current text msg
|
||||||
|
std::uint64_t rd_size_; // size of the current message so far
|
||||||
|
std::uint64_t rd_need_ = 0; // bytes left in msg frame payload
|
||||||
|
opcode rd_opcode_; // opcode of current msg
|
||||||
|
bool rd_cont_ = false; // expecting a continuation frame
|
||||||
|
bool rd_close_ = false; // got close frame
|
||||||
|
op* rd_block_ = nullptr; // op currently reading
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
wr_frag_size_ = 16 * 1024; // size of auto-fragments
|
||||||
|
std::size_t wr_buf_size_ = 4096; // write buffer size
|
||||||
|
opcode wr_opcode_ = opcode::text; // outgoing message type
|
||||||
|
bool wr_close_ = false; // sent close frame
|
||||||
|
bool wr_cont_ = false; // next write is continuation frame
|
||||||
|
op* wr_block_ = nullptr; // op currenly writing
|
||||||
|
|
||||||
|
invokable rd_op_; // invoked after write completes
|
||||||
|
invokable wr_op_; // invoked after read completes
|
||||||
|
close_reason cr_; // set from received close frame
|
||||||
|
|
||||||
|
socket_base()
|
||||||
|
: d_(std::make_unique<
|
||||||
|
decorator<default_decorator>>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
socket_base(socket_base&&) = default;
|
||||||
|
socket_base(socket_base const&) = delete;
|
||||||
|
socket_base& operator=(socket_base&&) = default;
|
||||||
|
socket_base& operator=(socket_base const&) = delete;
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
prepare_fh(close_code& code);
|
||||||
|
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
write_close(Streambuf& sb,
|
||||||
|
close_reason const& rc);
|
||||||
|
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
write_ping(Streambuf& sb, opcode op,
|
||||||
|
ping_payload_type const& data);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
184
beast/wsproto/detail/utf8_checker.h
Normal file
184
beast/wsproto/detail/utf8_checker.h
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_UTF8_CHECKER_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_UTF8_CHECKER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string> // DEPRECATED
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// Code adapted from
|
||||||
|
// http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||||
|
/*
|
||||||
|
Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject
|
||||||
|
to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||||
|
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||||
|
*/
|
||||||
|
template<class = void>
|
||||||
|
class utf8_checker_t
|
||||||
|
{
|
||||||
|
// Table for the UTF8 decode state machine
|
||||||
|
using lut_type = std::uint8_t[400];
|
||||||
|
static
|
||||||
|
lut_type const&
|
||||||
|
lut()
|
||||||
|
{
|
||||||
|
// 400 elements
|
||||||
|
static std::uint8_t constexpr tab[] = {
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
|
||||||
|
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
|
||||||
|
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
|
||||||
|
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
|
||||||
|
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
|
||||||
|
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
|
||||||
|
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
|
||||||
|
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
|
||||||
|
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1 // s7..s8
|
||||||
|
};
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t state_ = 0;
|
||||||
|
std::uint32_t codepoint_ = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
utf8_checker_t() = default;
|
||||||
|
utf8_checker_t(utf8_checker_t&&) = default;
|
||||||
|
utf8_checker_t(utf8_checker_t const&) = default;
|
||||||
|
utf8_checker_t& operator=(utf8_checker_t&&) = default;
|
||||||
|
utf8_checker_t& operator=(utf8_checker_t const&) = default;
|
||||||
|
|
||||||
|
void
|
||||||
|
reset();
|
||||||
|
|
||||||
|
// Returns `true` on success
|
||||||
|
bool
|
||||||
|
write(void const* buffer, std::size_t size);
|
||||||
|
|
||||||
|
// Returns `true` on success
|
||||||
|
template<class BufferSequence>
|
||||||
|
bool
|
||||||
|
write(BufferSequence const& bs);
|
||||||
|
|
||||||
|
// Returns `true` on success
|
||||||
|
bool
|
||||||
|
finish();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
void
|
||||||
|
utf8_checker_t<_>::reset()
|
||||||
|
{
|
||||||
|
state_ = 0;
|
||||||
|
codepoint_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
bool
|
||||||
|
utf8_checker_t<_>::write(void const* buffer, std::size_t size)
|
||||||
|
{
|
||||||
|
auto p = static_cast<std::uint8_t const*>(buffer);
|
||||||
|
auto plut = &lut()[0];
|
||||||
|
while(size)
|
||||||
|
{
|
||||||
|
auto const byte = *p;
|
||||||
|
auto const type = plut[byte];
|
||||||
|
if(state_)
|
||||||
|
codepoint_ = (byte & 0x3fu) | (codepoint_ << 6);
|
||||||
|
else
|
||||||
|
codepoint_ = (0xff >> type) & byte;
|
||||||
|
state_ = plut[256 + state_ * 16 + type];
|
||||||
|
if(state_ == 1)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++p;
|
||||||
|
--size;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
template<class BufferSequence>
|
||||||
|
bool
|
||||||
|
utf8_checker_t<_>::write(BufferSequence const& bs)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
for (auto const& b : bs)
|
||||||
|
if(! write(buffer_cast<void const*>(b),
|
||||||
|
buffer_size(b)))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
bool
|
||||||
|
utf8_checker_t<_>::finish()
|
||||||
|
{
|
||||||
|
auto const success = state_ == 0;
|
||||||
|
reset();
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
using utf8_checker = utf8_checker_t<>;
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
bool
|
||||||
|
check_utf8(char const* p, std::size_t n)
|
||||||
|
{
|
||||||
|
utf8_checker c;
|
||||||
|
if(! c.write(p, n))
|
||||||
|
return false;
|
||||||
|
return c.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
72
beast/wsproto/error.h
Normal file
72
beast/wsproto/error.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_ERROR_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_ERROR_H_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
using error_code = boost::system::error_code;
|
||||||
|
|
||||||
|
/// Error values
|
||||||
|
enum class error
|
||||||
|
{
|
||||||
|
/// Both sides performed a WebSocket close
|
||||||
|
closed = 1,
|
||||||
|
|
||||||
|
/// WebSocket connection failed, protocol violation
|
||||||
|
failed,
|
||||||
|
|
||||||
|
/// Upgrade request failed, connection is closed
|
||||||
|
handshake_failed,
|
||||||
|
|
||||||
|
/// Upgrade request failed, but connection is still open
|
||||||
|
keep_alive,
|
||||||
|
|
||||||
|
/// HTTP response is malformed
|
||||||
|
response_malformed,
|
||||||
|
|
||||||
|
/// HTTP response failed the upgrade
|
||||||
|
response_failed,
|
||||||
|
|
||||||
|
/// Upgrade request denied for invalid fields.
|
||||||
|
response_denied,
|
||||||
|
|
||||||
|
/// Upgrade request is malformed
|
||||||
|
request_malformed,
|
||||||
|
|
||||||
|
/// Upgrade request fields incorrect
|
||||||
|
request_invalid,
|
||||||
|
|
||||||
|
/// Upgrade request denied
|
||||||
|
request_denied
|
||||||
|
};
|
||||||
|
|
||||||
|
error_code
|
||||||
|
make_error_code(error e);
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/wsproto/impl/error.ipp>
|
||||||
|
|
||||||
|
#endif
|
||||||
158
beast/wsproto/impl/accept_op.ipp
Normal file
158
beast/wsproto/impl/accept_op.ipp
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_ACCEPT_OP_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_ACCEPT_OP_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/impl/response_op.ipp>
|
||||||
|
#include <beast/asio/handler_alloc.h>
|
||||||
|
#include <beast/asio/prepare_buffers.h>
|
||||||
|
#include <beast/http/parser.h>
|
||||||
|
#include <beast/http/read.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
// read and respond to an upgrade request
|
||||||
|
//
|
||||||
|
template<class Stream>
|
||||||
|
template<class Handler>
|
||||||
|
class socket<Stream>::accept_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
socket<Stream>& ws;
|
||||||
|
http::request<http::empty_body> req;
|
||||||
|
Handler h;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class Buffers>
|
||||||
|
data(DeducedHandler&& h_, socket<Stream>& ws_,
|
||||||
|
Buffers const& buffers)
|
||||||
|
: ws(ws_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
ws.stream_.buffer().commit(buffer_copy(
|
||||||
|
ws.stream_.buffer().prepare(
|
||||||
|
buffer_size(buffers)), buffers));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
accept_op(accept_op&&) = default;
|
||||||
|
accept_op(accept_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
accept_op(DeducedHandler&& h,
|
||||||
|
socket<Stream>& ws, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code const& ec)
|
||||||
|
{
|
||||||
|
(*this)(ec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code const& ec,
|
||||||
|
std::size_t bytes_transferred, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_allocate(
|
||||||
|
std::size_t size, accept_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, accept_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(accept_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f, accept_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Handler>
|
||||||
|
void
|
||||||
|
socket<Stream>::accept_op<Handler>::
|
||||||
|
operator()(error_code const& ec,
|
||||||
|
std::size_t bytes_transferred, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
// read message
|
||||||
|
d.state = 1;
|
||||||
|
http::async_read(d.ws.next_layer_,
|
||||||
|
d.ws.stream_.buffer(), d.req,
|
||||||
|
std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// got message
|
||||||
|
case 1:
|
||||||
|
// respond to request
|
||||||
|
response_op<Handler>{
|
||||||
|
std::move(d.h), d.ws, d.req, true};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
198
beast/wsproto/impl/close_op.ipp
Normal file
198
beast/wsproto/impl/close_op.ipp
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_CLOSE_OP_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_CLOSE_OP_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/handler_alloc.h>
|
||||||
|
#include <beast/asio/static_streambuf.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
// send the close message and wait for the response
|
||||||
|
//
|
||||||
|
template<class Stream>
|
||||||
|
template<class Handler>
|
||||||
|
class socket<Stream>::close_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
using fb_type =
|
||||||
|
detail::frame_streambuf;
|
||||||
|
using fmb_type =
|
||||||
|
typename fb_type::mutable_buffers_type;
|
||||||
|
|
||||||
|
struct data : op
|
||||||
|
{
|
||||||
|
socket<Stream>& ws;
|
||||||
|
close_reason cr;
|
||||||
|
Handler h;
|
||||||
|
fb_type fb;
|
||||||
|
fmb_type fmb;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, socket<Stream>& ws_,
|
||||||
|
close_reason const& cr_)
|
||||||
|
: ws(ws_)
|
||||||
|
, cr(cr_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
ws.template write_close<
|
||||||
|
static_streambuf>(fb, cr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
close_op(close_op&&) = default;
|
||||||
|
close_op(close_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
close_op(DeducedHandler&& h,
|
||||||
|
socket<Stream>& ws, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()()
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = false;
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code const& ec)
|
||||||
|
{
|
||||||
|
(*this)(ec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(error_code ec,
|
||||||
|
std::size_t bytes_transferred, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_allocate(
|
||||||
|
std::size_t size, close_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, close_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(close_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f, close_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Handler>
|
||||||
|
void
|
||||||
|
socket<Stream>::close_op<Handler>::operator()(
|
||||||
|
error_code ec, std::size_t bytes_transferred, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if(d.ws.wr_block_)
|
||||||
|
{
|
||||||
|
// suspend
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.rd_op_.template emplace<
|
||||||
|
close_op>(std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(d.ws.error_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.ws.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this),
|
||||||
|
boost::asio::error::operation_aborted, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.state = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// resume
|
||||||
|
case 1:
|
||||||
|
if(d.ws.error_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
ec = boost::asio::error::operation_aborted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.state = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// send close
|
||||||
|
d.state = 99;
|
||||||
|
assert(! d.ws.wr_close_);
|
||||||
|
d.ws.wr_close_ = true;
|
||||||
|
assert(! d.ws.wr_block_);
|
||||||
|
d.ws.wr_block_ = &d;
|
||||||
|
boost::asio::async_write(d.ws.stream_,
|
||||||
|
d.fb.data(), std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ec)
|
||||||
|
d.ws.error_ = true;
|
||||||
|
if(d.ws.wr_block_ == &d)
|
||||||
|
d.ws.wr_block_ = nullptr;
|
||||||
|
d.h(ec);
|
||||||
|
d.ws.rd_op_.maybe_invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
39
beast/wsproto/impl/error.ipp
Normal file
39
beast/wsproto/impl/error.ipp
Normal 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_WSPROTO_ERROR_IPP_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_ERROR_IPP_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/detail/error.h>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
inline
|
||||||
|
error_code
|
||||||
|
make_error_code(error e)
|
||||||
|
{
|
||||||
|
return error_code(
|
||||||
|
static_cast<int>(e), detail::get_error_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
170
beast/wsproto/impl/handshake_op.ipp
Normal file
170
beast/wsproto/impl/handshake_op.ipp
Normal 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_WSPROTO_HANDSHAKE_OP_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_HANDSHAKE_OP_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/handler_alloc.h>
|
||||||
|
#include <beast/http/empty_body.h>
|
||||||
|
#include <beast/http/message.h>
|
||||||
|
#include <beast/http/read.h>
|
||||||
|
#include <beast/http/write.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
// send the upgrade request and process the response
|
||||||
|
//
|
||||||
|
template<class Stream>
|
||||||
|
template<class Handler>
|
||||||
|
class socket<Stream>::handshake_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
socket<Stream>& ws;
|
||||||
|
Handler h;
|
||||||
|
std::string key;
|
||||||
|
http::request<http::empty_body> req;
|
||||||
|
http::response<http::string_body> resp;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, socket<Stream>& ws_,
|
||||||
|
boost::string_ref const& host,
|
||||||
|
boost::string_ref const& resource)
|
||||||
|
: ws(ws_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, req(ws.build_request(host, resource, key))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
handshake_op(handshake_op&&) = default;
|
||||||
|
handshake_op(handshake_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
handshake_op(DeducedHandler&& h,
|
||||||
|
socket<Stream>& ws, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code const& ec)
|
||||||
|
{
|
||||||
|
(*this)(ec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code ec,
|
||||||
|
std::size_t bytes_transferred, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_allocate(
|
||||||
|
std::size_t size, handshake_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, handshake_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(handshake_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f, handshake_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Handler>
|
||||||
|
void
|
||||||
|
socket<Stream>::handshake_op<
|
||||||
|
Handler>::operator()(error_code ec,
|
||||||
|
std::size_t bytes_transferred, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
// send http upgrade
|
||||||
|
d.state = 1;
|
||||||
|
// VFALCO Do we need the ability to move
|
||||||
|
// a message on the async_write?
|
||||||
|
http::async_write(d.ws.stream_,
|
||||||
|
d.req, std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sent upgrade
|
||||||
|
case 1:
|
||||||
|
// read http response
|
||||||
|
d.state = 2;
|
||||||
|
http::async_read(d.ws.next_layer_,
|
||||||
|
d.ws.stream_.buffer(), d.resp,
|
||||||
|
std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// got response
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
d.ws.do_response(d.resp, d.key, ec);
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
519
beast/wsproto/impl/read_frame_op.ipp
Normal file
519
beast/wsproto/impl/read_frame_op.ipp
Normal file
@@ -0,0 +1,519 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_READ_FRAME_OP_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_READ_FRAME_OP_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/teardown.h>
|
||||||
|
#include <beast/asio/handler_alloc.h>
|
||||||
|
#include <beast/asio/prepare_buffers.h>
|
||||||
|
#include <beast/asio/static_streambuf.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
// Reads a single message frame,
|
||||||
|
// processes any received control frames.
|
||||||
|
//
|
||||||
|
template<class Stream>
|
||||||
|
template<class Streambuf, class Handler>
|
||||||
|
class socket<Stream>::read_frame_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
using fb_type =
|
||||||
|
detail::frame_streambuf;
|
||||||
|
|
||||||
|
using fmb_type =
|
||||||
|
typename fb_type::mutable_buffers_type;
|
||||||
|
|
||||||
|
using smb_type =
|
||||||
|
typename Streambuf::mutable_buffers_type;
|
||||||
|
|
||||||
|
struct data : op
|
||||||
|
{
|
||||||
|
socket<Stream>& ws;
|
||||||
|
frame_info& fi;
|
||||||
|
Streambuf& sb;
|
||||||
|
smb_type smb;
|
||||||
|
Handler h;
|
||||||
|
fb_type fb;
|
||||||
|
fmb_type fmb;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, socket<Stream>& ws_,
|
||||||
|
frame_info& fi_, Streambuf& sb_)
|
||||||
|
: ws(ws_)
|
||||||
|
, fi(fi_)
|
||||||
|
, sb(sb_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
read_frame_op(read_frame_op&&) = default;
|
||||||
|
read_frame_op(read_frame_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
read_frame_op(DeducedHandler&& h,
|
||||||
|
socket<Stream>& ws, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()()
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = false;
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code const& ec)
|
||||||
|
{
|
||||||
|
(*this)(ec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code ec,
|
||||||
|
std::size_t bytes_transferred, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_allocate(
|
||||||
|
std::size_t size, read_frame_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_frame_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(read_frame_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f, read_frame_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
void
|
||||||
|
socket<Stream>::read_frame_op<Buffers, Handler>::
|
||||||
|
operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
close_code code;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if(d.ws.error_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.ws.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this),
|
||||||
|
boost::asio::error::operation_aborted, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(d.ws.rd_need_ > 0)
|
||||||
|
{
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.state = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// read payload
|
||||||
|
d.state = 3;
|
||||||
|
d.smb = d.sb.prepare(
|
||||||
|
detail::clamp(d.ws.rd_need_));
|
||||||
|
d.ws.stream_.async_read_some(
|
||||||
|
d.smb, std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// read fixed header
|
||||||
|
d.state = 5;
|
||||||
|
boost::asio::async_read(d.ws.stream_,
|
||||||
|
d.fb.prepare(2), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// got payload
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
d.ws.rd_need_ -= bytes_transferred;
|
||||||
|
auto const pb = prepare_buffers(
|
||||||
|
bytes_transferred, d.smb);
|
||||||
|
if(d.ws.rd_fh_.mask)
|
||||||
|
detail::mask_inplace(pb, d.ws.rd_key_);
|
||||||
|
if(d.ws.rd_opcode_ == opcode::text)
|
||||||
|
{
|
||||||
|
if(! d.ws.rd_utf8_check_.write(pb) ||
|
||||||
|
(d.ws.rd_need_ == 0 && d.ws.rd_fh_.fin &&
|
||||||
|
! d.ws.rd_utf8_check_.finish()))
|
||||||
|
{
|
||||||
|
// invalid utf8
|
||||||
|
d.state = 16;
|
||||||
|
code = close_code::bad_payload;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.sb.commit(bytes_transferred);
|
||||||
|
d.state = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call handler
|
||||||
|
case 4:
|
||||||
|
d.state = 99;
|
||||||
|
d.fi.op = d.ws.rd_opcode_;
|
||||||
|
d.fi.fin = d.ws.rd_fh_.fin &&
|
||||||
|
d.ws.rd_need_ == 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// got fixed header
|
||||||
|
case 5:
|
||||||
|
{
|
||||||
|
d.fb.commit(bytes_transferred);
|
||||||
|
code = close_code::none;
|
||||||
|
auto const n = detail::read_fh1(
|
||||||
|
d.ws.rd_fh_, d.fb, d.ws.role_, code);
|
||||||
|
if(code != close_code::none)
|
||||||
|
{
|
||||||
|
// protocol error
|
||||||
|
d.state = 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.state = 6;
|
||||||
|
if (n == 0)
|
||||||
|
{
|
||||||
|
bytes_transferred = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// read variable header
|
||||||
|
boost::asio::async_read(d.ws.stream_,
|
||||||
|
d.fb.prepare(n), std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// got variable header
|
||||||
|
case 6:
|
||||||
|
d.fb.commit(bytes_transferred);
|
||||||
|
code = close_code::none;
|
||||||
|
detail::read_fh2(d.ws.rd_fh_,
|
||||||
|
d.fb, d.ws.role_, code);
|
||||||
|
if(code == close_code::none)
|
||||||
|
d.ws.prepare_fh(code);
|
||||||
|
if(code != close_code::none)
|
||||||
|
{
|
||||||
|
// protocol error
|
||||||
|
d.state = 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(detail::is_control(d.ws.rd_fh_.op))
|
||||||
|
{
|
||||||
|
if(d.ws.rd_fh_.len > 0)
|
||||||
|
{
|
||||||
|
// read control payload
|
||||||
|
d.state = 7;
|
||||||
|
d.fmb = d.fb.prepare(static_cast<
|
||||||
|
std::size_t>(d.ws.rd_fh_.len));
|
||||||
|
boost::asio::async_read(d.ws.stream_,
|
||||||
|
d.fmb, std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.state = 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(d.ws.rd_need_ > 0)
|
||||||
|
{
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(! d.ws.rd_fh_.fin)
|
||||||
|
{
|
||||||
|
d.state = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// empty frame with fin
|
||||||
|
d.state = 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// got control payload
|
||||||
|
case 7:
|
||||||
|
if(d.ws.rd_fh_.mask)
|
||||||
|
detail::mask_inplace(
|
||||||
|
d.fmb, d.ws.rd_key_);
|
||||||
|
d.fb.commit(bytes_transferred);
|
||||||
|
d.state = 8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// do control
|
||||||
|
case 8:
|
||||||
|
if(d.ws.rd_fh_.op == opcode::ping)
|
||||||
|
{
|
||||||
|
code = close_code::none;
|
||||||
|
ping_payload_type data;
|
||||||
|
detail::read(data, d.fb.data(), code);
|
||||||
|
if(code != close_code::none)
|
||||||
|
{
|
||||||
|
// protocol error
|
||||||
|
d.state = 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.fb.reset();
|
||||||
|
if(d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
d.state = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.ws.template write_ping<static_streambuf>(
|
||||||
|
d.fb, opcode::pong, data);
|
||||||
|
if(d.ws.wr_block_)
|
||||||
|
{
|
||||||
|
assert(d.ws.wr_block_ != &d);
|
||||||
|
// suspend
|
||||||
|
d.state = 13;
|
||||||
|
d.ws.rd_op_.template emplace<
|
||||||
|
read_frame_op>(std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.state = 14;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if(d.ws.rd_fh_.op == opcode::pong)
|
||||||
|
{
|
||||||
|
code = close_code::none;
|
||||||
|
ping_payload_type data;
|
||||||
|
detail::read(data, d.fb.data(), code);
|
||||||
|
if(code != close_code::none)
|
||||||
|
{
|
||||||
|
// protocol error
|
||||||
|
d.state = 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.fb.reset();
|
||||||
|
// VFALCO TODO maybe_invoke an async pong handler
|
||||||
|
// For now just ignore the pong.
|
||||||
|
d.state = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert(d.ws.rd_fh_.op == opcode::close);
|
||||||
|
{
|
||||||
|
detail::read(d.ws.cr_, d.fb.data(), code);
|
||||||
|
if(code != close_code::none)
|
||||||
|
{
|
||||||
|
d.state = 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(! d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
auto cr = d.ws.cr_;
|
||||||
|
if(cr.code == close_code::none)
|
||||||
|
cr.code = close_code::normal;
|
||||||
|
cr.reason = "";
|
||||||
|
d.fb.reset();
|
||||||
|
d.ws.template write_close<
|
||||||
|
static_streambuf>(d.fb, cr);
|
||||||
|
if(d.ws.wr_block_)
|
||||||
|
{
|
||||||
|
// suspend
|
||||||
|
d.state = 9;
|
||||||
|
d.ws.rd_op_.template emplace<
|
||||||
|
read_frame_op>(std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.state = 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// call handler;
|
||||||
|
d.state = 99;
|
||||||
|
ec = error::closed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resume
|
||||||
|
case 9:
|
||||||
|
if(d.ws.error_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
ec = boost::asio::error::operation_aborted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
ec = error::closed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.state = 10;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// send close
|
||||||
|
case 10:
|
||||||
|
d.state = 11;
|
||||||
|
assert(! d.ws.wr_block_);
|
||||||
|
d.ws.wr_block_ = &d;
|
||||||
|
boost::asio::async_write(d.ws.stream_,
|
||||||
|
d.fb.data(), std::move(*this));
|
||||||
|
return;;
|
||||||
|
|
||||||
|
// teardown
|
||||||
|
case 11:
|
||||||
|
d.state = 12;
|
||||||
|
wsproto_helpers::call_async_teardown(
|
||||||
|
d.ws.next_layer_, std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 12:
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
ec = error::closed;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// resume
|
||||||
|
case 13:
|
||||||
|
if(d.ws.error_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
ec = boost::asio::error::operation_aborted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
d.fb.reset();
|
||||||
|
d.state = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.state = 14;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 14:
|
||||||
|
// write ping/pong
|
||||||
|
d.state = 15;
|
||||||
|
assert(! d.ws.wr_block_);
|
||||||
|
d.ws.wr_block_ = &d;
|
||||||
|
boost::asio::async_write(d.ws.stream_,
|
||||||
|
d.fb.data(), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// sent ping/pong
|
||||||
|
case 15:
|
||||||
|
d.fb.reset();
|
||||||
|
d.state = 2;
|
||||||
|
d.ws.wr_block_ = nullptr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// fail the connection
|
||||||
|
case 16:
|
||||||
|
if(! d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
d.fb.reset();
|
||||||
|
d.ws.template write_close<
|
||||||
|
static_streambuf>(d.fb, code);
|
||||||
|
if(d.ws.wr_block_)
|
||||||
|
{
|
||||||
|
// suspend
|
||||||
|
d.state = 17;
|
||||||
|
d.ws.rd_op_.template emplace<
|
||||||
|
read_frame_op>(std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.state = 18;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resume
|
||||||
|
case 17:
|
||||||
|
if(d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
d.state = 19;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.state = 18;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 18:
|
||||||
|
// send close
|
||||||
|
d.state = 19;
|
||||||
|
d.ws.wr_close_ = true;
|
||||||
|
assert(! d.ws.wr_block_);
|
||||||
|
d.ws.wr_block_ = &d;
|
||||||
|
boost::asio::async_write(d.ws.stream_,
|
||||||
|
d.fb.data(), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// teardown
|
||||||
|
case 19:
|
||||||
|
d.state = 20;
|
||||||
|
wsproto_helpers::call_async_teardown(
|
||||||
|
d.ws.next_layer_, std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 20:
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
ec = error::failed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ec)
|
||||||
|
d.ws.error_ = true;
|
||||||
|
if(d.ws.wr_block_ == &d)
|
||||||
|
d.ws.wr_block_ = nullptr;
|
||||||
|
d.h(ec);
|
||||||
|
d.ws.wr_op_.maybe_invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
144
beast/wsproto/impl/read_op.ipp
Normal file
144
beast/wsproto/impl/read_op.ipp
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_READ_OP_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_READ_OP_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/handler_alloc.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
// read an entire message
|
||||||
|
//
|
||||||
|
template<class Stream>
|
||||||
|
template<class Streambuf, class Handler>
|
||||||
|
class socket<Stream>::read_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
socket<Stream>& ws;
|
||||||
|
opcode& op;
|
||||||
|
Streambuf& sb;
|
||||||
|
Handler h;
|
||||||
|
frame_info fi;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_,
|
||||||
|
socket<Stream>& ws_, opcode& op_,
|
||||||
|
Streambuf& sb_)
|
||||||
|
: ws(ws_)
|
||||||
|
, op(op_)
|
||||||
|
, sb(sb_)
|
||||||
|
, 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,
|
||||||
|
socket<Stream>& ws, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
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 Stream>
|
||||||
|
template<class Streambuf, class Handler>
|
||||||
|
void
|
||||||
|
socket<Stream>::read_op<Streambuf, 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:
|
||||||
|
// read payload
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.async_read_frame(
|
||||||
|
d.fi, d.sb, std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// got payload
|
||||||
|
case 1:
|
||||||
|
d.op = d.fi.op;
|
||||||
|
d.state = d.fi.fin ? 99 : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
147
beast/wsproto/impl/response_op.ipp
Normal file
147
beast/wsproto/impl/response_op.ipp
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_RESPONSE_OP_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_RESPONSE_OP_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/handler_alloc.h>
|
||||||
|
#include <beast/http/string_body.h>
|
||||||
|
#include <beast/http/write.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
// Respond to an upgrade HTTP request
|
||||||
|
template<class Stream>
|
||||||
|
template<class Handler>
|
||||||
|
class socket<Stream>::response_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
socket<Stream>& ws;
|
||||||
|
http::response<http::string_body> resp;
|
||||||
|
Handler h;
|
||||||
|
error_code final_ec;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler,
|
||||||
|
class Body, class Headers>
|
||||||
|
data(DeducedHandler&& h_, socket<Stream>& ws_,
|
||||||
|
http::message<true, Body, Headers> const& req,
|
||||||
|
bool cont_)
|
||||||
|
: ws(ws_)
|
||||||
|
, resp(ws_.build_response(req))
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(cont_)
|
||||||
|
{
|
||||||
|
if(resp.status != 101)
|
||||||
|
final_ec = error::handshake_failed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
response_op(response_op&&) = default;
|
||||||
|
response_op(response_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
response_op(DeducedHandler&& h,
|
||||||
|
socket<Stream>& ws, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(
|
||||||
|
error_code ec, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_allocate(
|
||||||
|
std::size_t size, response_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, response_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(response_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f, response_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Handler>
|
||||||
|
void
|
||||||
|
socket<Stream>::response_op<Handler>::
|
||||||
|
operator()(error_code ec, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
// send response
|
||||||
|
d.state = 1;
|
||||||
|
http::async_write(d.ws.next_layer_,
|
||||||
|
d.resp, std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// sent response
|
||||||
|
case 1:
|
||||||
|
d.state = 99;
|
||||||
|
ec = d.final_ec;
|
||||||
|
if(! ec)
|
||||||
|
d.ws.role_ = role_type::server;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
815
beast/wsproto/impl/socket.ipp
Normal file
815
beast/wsproto/impl/socket.ipp
Normal file
@@ -0,0 +1,815 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_IMPL_SOCKET_IPP_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_IMPL_SOCKET_IPP_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/teardown.h>
|
||||||
|
#include <beast/wsproto/detail/hybi13.h>
|
||||||
|
#include <beast/wsproto/impl/accept_op.ipp>
|
||||||
|
#include <beast/wsproto/impl/close_op.ipp>
|
||||||
|
#include <beast/wsproto/impl/handshake_op.ipp>
|
||||||
|
#include <beast/wsproto/impl/read_op.ipp>
|
||||||
|
#include <beast/wsproto/impl/read_frame_op.ipp>
|
||||||
|
#include <beast/wsproto/impl/response_op.ipp>
|
||||||
|
#include <beast/wsproto/impl/write_op.ipp>
|
||||||
|
#include <beast/wsproto/impl/write_frame_op.ipp>
|
||||||
|
#include <beast/asio/append_buffers.h>
|
||||||
|
#include <beast/asio/async_completion.h>
|
||||||
|
#include <beast/asio/consuming_buffers.h>
|
||||||
|
#include <beast/asio/prepare_buffers.h>
|
||||||
|
#include <beast/asio/static_streambuf.h>
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
#include <beast/asio/type_check.h>
|
||||||
|
#include <beast/http/read.h>
|
||||||
|
#include <beast/http/write.h>
|
||||||
|
#include <beast/http/reason.h>
|
||||||
|
#include <beast/http/rfc2616.h>
|
||||||
|
#include <boost/endian/buffers.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
void
|
||||||
|
socket_base::prepare_fh(close_code& code)
|
||||||
|
{
|
||||||
|
// continuation without an active message
|
||||||
|
if(! rd_cont_ && rd_fh_.op == opcode::cont)
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// new data frame when continuation expected
|
||||||
|
if(rd_cont_ && ! is_control(rd_fh_.op) &&
|
||||||
|
rd_fh_.op != opcode::cont)
|
||||||
|
{
|
||||||
|
code = close_code::protocol_error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(rd_fh_.mask)
|
||||||
|
prepare_key(rd_key_, rd_fh_.key);
|
||||||
|
if(! is_control(rd_fh_.op))
|
||||||
|
{
|
||||||
|
if(rd_fh_.op != opcode::cont)
|
||||||
|
{
|
||||||
|
rd_size_ = rd_fh_.len;
|
||||||
|
rd_opcode_ = rd_fh_.op;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(rd_size_ > std::numeric_limits<
|
||||||
|
std::uint64_t>::max() - rd_fh_.len)
|
||||||
|
{
|
||||||
|
code = close_code::too_big;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rd_size_ += rd_fh_.len;
|
||||||
|
}
|
||||||
|
if(rd_size_ > rd_msg_max_)
|
||||||
|
{
|
||||||
|
code = close_code::too_big;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rd_need_ = rd_fh_.len;
|
||||||
|
rd_cont_ = ! rd_fh_.fin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
socket_base::write_close(
|
||||||
|
Streambuf& sb, close_reason const& cr)
|
||||||
|
{
|
||||||
|
using namespace boost::endian;
|
||||||
|
frame_header fh;
|
||||||
|
fh.op = opcode::close;
|
||||||
|
fh.fin = true;
|
||||||
|
fh.rsv1 = false;
|
||||||
|
fh.rsv2 = false;
|
||||||
|
fh.rsv3 = false;
|
||||||
|
fh.len = cr.code == close_code::none ?
|
||||||
|
0 : 2 + cr.reason.size();
|
||||||
|
if((fh.mask = (role_ == role_type::client)))
|
||||||
|
fh.key = maskgen_();
|
||||||
|
detail::write(sb, fh);
|
||||||
|
if(cr.code != close_code::none)
|
||||||
|
{
|
||||||
|
detail::prepared_key_type key;
|
||||||
|
if(fh.mask)
|
||||||
|
detail::prepare_key(key, fh.key);
|
||||||
|
{
|
||||||
|
std::uint8_t b[2];
|
||||||
|
::new(&b[0]) big_uint16_buf_t{
|
||||||
|
(std::uint16_t)cr.code};
|
||||||
|
auto d = sb.prepare(2);
|
||||||
|
boost::asio::buffer_copy(d,
|
||||||
|
boost::asio::buffer(b));
|
||||||
|
if(fh.mask)
|
||||||
|
detail::mask_inplace(d, key);
|
||||||
|
sb.commit(2);
|
||||||
|
}
|
||||||
|
if(! cr.reason.empty())
|
||||||
|
{
|
||||||
|
auto d = sb.prepare(cr.reason.size());
|
||||||
|
boost::asio::buffer_copy(d,
|
||||||
|
boost::asio::const_buffer(
|
||||||
|
cr.reason.data(), cr.reason.size()));
|
||||||
|
if(fh.mask)
|
||||||
|
detail::mask_inplace(d, key);
|
||||||
|
sb.commit(cr.reason.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
socket_base::write_ping(Streambuf& sb,
|
||||||
|
opcode op, ping_payload_type const& data)
|
||||||
|
{
|
||||||
|
frame_header fh;
|
||||||
|
fh.op = op;
|
||||||
|
fh.fin = true;
|
||||||
|
fh.rsv1 = false;
|
||||||
|
fh.rsv2 = false;
|
||||||
|
fh.rsv3 = false;
|
||||||
|
fh.len = data.size();
|
||||||
|
if((fh.mask = (role_ == role_type::client)))
|
||||||
|
fh.key = maskgen_();
|
||||||
|
detail::write(sb, fh);
|
||||||
|
if(data.empty())
|
||||||
|
return;
|
||||||
|
detail::prepared_key_type key;
|
||||||
|
if(fh.mask)
|
||||||
|
detail::prepare_key(key, fh.key);
|
||||||
|
auto d = sb.prepare(data.size());
|
||||||
|
boost::asio::buffer_copy(d,
|
||||||
|
boost::asio::const_buffers_1(
|
||||||
|
data.data(), data.size()));
|
||||||
|
if(fh.mask)
|
||||||
|
detail::mask_inplace(d, key);
|
||||||
|
sb.commit(data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class... Args>
|
||||||
|
socket<Stream>::socket(Args&&... args)
|
||||||
|
: next_layer_(std::forward<Args>(args)...)
|
||||||
|
, stream_(next_layer_)
|
||||||
|
{
|
||||||
|
static_assert(is_Stream<next_layer_type>::value,
|
||||||
|
"Stream requirements not met");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
void
|
||||||
|
socket<Stream>::accept(error_code& ec)
|
||||||
|
{
|
||||||
|
accept(boost::asio::null_buffers{}, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class AcceptHandler>
|
||||||
|
auto
|
||||||
|
socket<Stream>::async_accept(AcceptHandler&& handler)
|
||||||
|
{
|
||||||
|
return async_accept(boost::asio::null_buffers{},
|
||||||
|
std::forward<AcceptHandler>(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
socket<Stream>::accept(
|
||||||
|
ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
static_assert(is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
accept(buffers, ec);
|
||||||
|
detail::maybe_throw(ec, "accept");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
socket<Stream>::accept(
|
||||||
|
ConstBufferSequence const& buffers, error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
stream_.buffer().commit(buffer_copy(
|
||||||
|
stream_.buffer().prepare(
|
||||||
|
buffer_size(buffers)), buffers));
|
||||||
|
http::request<http::empty_body> m;
|
||||||
|
http::read(next_layer_, stream_.buffer(), m, ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
accept(m, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class ConstBufferSequence, class AcceptHandler>
|
||||||
|
auto
|
||||||
|
socket<Stream>::async_accept(
|
||||||
|
ConstBufferSequence const& bs, AcceptHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
AcceptHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
accept_op<decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, bs};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
socket<Stream>::accept(
|
||||||
|
http::message<true, Body, Headers> const& request)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
accept(request, ec);
|
||||||
|
detail::maybe_throw(ec, "accept");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
socket<Stream>::accept(
|
||||||
|
http::message<true, Body, Headers> const& req,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
auto resp = build_response(req);
|
||||||
|
http::write(stream_, resp, ec);
|
||||||
|
if(resp.status != 101)
|
||||||
|
{
|
||||||
|
ec = error::handshake_failed;
|
||||||
|
// VFALCO TODO Respect keep alive setting, perform
|
||||||
|
// teardown if Connection: close.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
role_ = role_type::server;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Body, class Headers, class AcceptHandler>
|
||||||
|
auto
|
||||||
|
socket<Stream>::async_accept(
|
||||||
|
http::message<true, Body, Headers> const& req,
|
||||||
|
AcceptHandler&& handler)
|
||||||
|
{
|
||||||
|
beast::async_completion<
|
||||||
|
AcceptHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
response_op<decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, req,
|
||||||
|
boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(completion.handler)};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
void
|
||||||
|
socket<Stream>::handshake(boost::string_ref const& host,
|
||||||
|
boost::string_ref const& resource, error_code& ec)
|
||||||
|
{
|
||||||
|
std::string key;
|
||||||
|
http::write(stream_,
|
||||||
|
build_request(host, resource, key), ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
http::response<http::string_body> resp;
|
||||||
|
http::read(next_layer_, stream_.buffer(), resp, ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
do_response(resp, key, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class HandshakeHandler>
|
||||||
|
auto
|
||||||
|
socket<Stream>::async_handshake(boost::string_ref const& host,
|
||||||
|
boost::string_ref const& resource, HandshakeHandler&& handler)
|
||||||
|
{
|
||||||
|
beast::async_completion<
|
||||||
|
HandshakeHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
handshake_op<decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, host, resource};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
void
|
||||||
|
socket<Stream>::close(
|
||||||
|
close_reason const& cr, error_code& ec)
|
||||||
|
{
|
||||||
|
assert(! wr_close_);
|
||||||
|
wr_close_ = true;
|
||||||
|
detail::frame_streambuf fb;
|
||||||
|
write_close<static_streambuf>(fb, cr);
|
||||||
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
|
error_ = ec != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class CloseHandler>
|
||||||
|
auto
|
||||||
|
socket<Stream>::async_close(
|
||||||
|
close_reason const& cr, CloseHandler&& handler)
|
||||||
|
{
|
||||||
|
beast::async_completion<
|
||||||
|
CloseHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
close_op<decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, cr};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
socket<Stream>::
|
||||||
|
read(opcode& op, Streambuf& streambuf, error_code& ec)
|
||||||
|
{
|
||||||
|
frame_info fi;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
read_frame(fi, streambuf, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
op = fi.op;
|
||||||
|
if(fi.fin)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Streambuf, class ReadHandler>
|
||||||
|
auto
|
||||||
|
socket<Stream>::
|
||||||
|
async_read(opcode& op,
|
||||||
|
Streambuf& streambuf, ReadHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(beast::is_Streambuf<Streambuf>::value,
|
||||||
|
"Streambuf requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
ReadHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
read_op<Streambuf, decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, op, streambuf};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
socket<Stream>::read_frame(frame_info& fi,
|
||||||
|
Streambuf& streambuf, error_code& ec)
|
||||||
|
{
|
||||||
|
close_code code{};
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if(rd_need_ == 0)
|
||||||
|
{
|
||||||
|
// read header
|
||||||
|
detail::frame_streambuf fb;
|
||||||
|
do_read_fh(fb, code, ec);
|
||||||
|
if((error_ = ec != 0))
|
||||||
|
return;
|
||||||
|
if(code != close_code::none)
|
||||||
|
break;
|
||||||
|
if(detail::is_control(rd_fh_.op))
|
||||||
|
{
|
||||||
|
// read control payload
|
||||||
|
if(rd_fh_.len > 0)
|
||||||
|
{
|
||||||
|
auto const mb = fb.prepare(
|
||||||
|
static_cast<std::size_t>(rd_fh_.len));
|
||||||
|
fb.commit(boost::asio::read(stream_, mb, ec));
|
||||||
|
if((error_ = ec != 0))
|
||||||
|
return;
|
||||||
|
if(rd_fh_.mask)
|
||||||
|
detail::mask_inplace(mb, rd_key_);
|
||||||
|
fb.commit(static_cast<std::size_t>(rd_fh_.len));
|
||||||
|
}
|
||||||
|
if(rd_fh_.op == opcode::ping)
|
||||||
|
{
|
||||||
|
ping_payload_type data;
|
||||||
|
detail::read(data, fb.data(), code);
|
||||||
|
if(code != close_code::none)
|
||||||
|
break;
|
||||||
|
fb.reset();
|
||||||
|
write_ping<static_streambuf>(
|
||||||
|
fb, opcode::pong, data);
|
||||||
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
|
if((error_ = ec != 0))
|
||||||
|
return;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(rd_fh_.op == opcode::pong)
|
||||||
|
{
|
||||||
|
ping_payload_type data;
|
||||||
|
detail::read(data, fb.data(), code);
|
||||||
|
if((error_ = ec != 0))
|
||||||
|
break;
|
||||||
|
// VFALCO How to notify callers using
|
||||||
|
// the synchronous interface?
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(rd_fh_.op == opcode::close);
|
||||||
|
{
|
||||||
|
detail::read(cr_, fb.data(), code);
|
||||||
|
if(code != close_code::none)
|
||||||
|
break;
|
||||||
|
if(! wr_close_)
|
||||||
|
{
|
||||||
|
auto cr = cr_;
|
||||||
|
if(cr.code == close_code::none)
|
||||||
|
cr.code = close_code::normal;
|
||||||
|
cr.reason = "";
|
||||||
|
fb.reset();
|
||||||
|
wr_close_ = true;
|
||||||
|
write_close<static_streambuf>(fb, cr);
|
||||||
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
|
if((error_ = ec != 0))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rd_need_ == 0 && ! rd_fh_.fin)
|
||||||
|
{
|
||||||
|
// empty frame
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// read payload
|
||||||
|
auto smb = streambuf.prepare(
|
||||||
|
detail::clamp(rd_need_));
|
||||||
|
auto const bytes_transferred =
|
||||||
|
stream_.read_some(smb, ec);
|
||||||
|
if((error_ = ec != 0))
|
||||||
|
return;
|
||||||
|
rd_need_ -= bytes_transferred;
|
||||||
|
auto const pb = prepare_buffers(
|
||||||
|
bytes_transferred, smb);
|
||||||
|
if(rd_fh_.mask)
|
||||||
|
detail::mask_inplace(pb, rd_key_);
|
||||||
|
if(rd_opcode_ == opcode::text)
|
||||||
|
{
|
||||||
|
if(! rd_utf8_check_.write(pb) ||
|
||||||
|
(rd_need_ == 0 && rd_fh_.fin &&
|
||||||
|
! rd_utf8_check_.finish()))
|
||||||
|
{
|
||||||
|
code = close_code::bad_payload;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
streambuf.commit(bytes_transferred);
|
||||||
|
fi.op = rd_opcode_;
|
||||||
|
fi.fin = rd_fh_.fin && rd_need_ == 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(code != close_code::none)
|
||||||
|
{
|
||||||
|
// Fail the connection (per rfc6455)
|
||||||
|
if(! wr_close_)
|
||||||
|
{
|
||||||
|
wr_close_ = true;
|
||||||
|
detail::frame_streambuf fb;
|
||||||
|
write_close<static_streambuf>(fb, code);
|
||||||
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
|
if((error_ = ec != 0))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wsproto_helpers::call_teardown(next_layer_, ec);
|
||||||
|
if((error_ = ec != 0))
|
||||||
|
return;
|
||||||
|
ec = error::failed;
|
||||||
|
error_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(! ec)
|
||||||
|
wsproto_helpers::call_teardown(next_layer_, ec);
|
||||||
|
if(! ec)
|
||||||
|
ec = error::closed;
|
||||||
|
error_ = ec != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Streambuf, class ReadHandler>
|
||||||
|
auto
|
||||||
|
socket<Stream>::async_read_frame(frame_info& fi,
|
||||||
|
Streambuf& streambuf, ReadHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(beast::is_Streambuf<Streambuf>::value,
|
||||||
|
"Streambuf requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
ReadHandler, void(error_code)> completion(handler);
|
||||||
|
read_frame_op<Streambuf, decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, fi, streambuf};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
socket<Stream>::write(
|
||||||
|
ConstBufferSequence const& bs, error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
consuming_buffers<ConstBufferSequence> cb(bs);
|
||||||
|
auto remain = buffer_size(cb);
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
auto const n =
|
||||||
|
detail::clamp(remain, wr_frag_size_);
|
||||||
|
remain -= n;
|
||||||
|
auto const fin = remain <= 0;
|
||||||
|
write_frame(fin, prepare_buffers(n, cb), ec);
|
||||||
|
cb.consume(n);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
if(fin)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class ConstBufferSequence, class WriteHandler>
|
||||||
|
auto
|
||||||
|
socket<Stream>::async_write(
|
||||||
|
ConstBufferSequence const& bs, WriteHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
WriteHandler, void(error_code)> completion(handler);
|
||||||
|
write_op<ConstBufferSequence, decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, bs};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
socket<Stream>::write_frame(bool fin,
|
||||||
|
ConstBufferSequence const& bs, error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
using boost::asio::mutable_buffers_1;
|
||||||
|
detail::frame_header fh;
|
||||||
|
fh.op = wr_cont_ ? opcode::cont : wr_opcode_;
|
||||||
|
wr_cont_ = ! fin;
|
||||||
|
fh.fin = fin;
|
||||||
|
fh.rsv1 = false;
|
||||||
|
fh.rsv2 = false;
|
||||||
|
fh.rsv3 = false;
|
||||||
|
fh.len = buffer_size(bs);
|
||||||
|
if((fh.mask = (role_ == role_type::client)))
|
||||||
|
fh.key = maskgen_();
|
||||||
|
detail::fh_streambuf fh_buf;
|
||||||
|
detail::write<static_streambuf>(fh_buf, fh);
|
||||||
|
if(! fh.mask)
|
||||||
|
{
|
||||||
|
// send header and payload
|
||||||
|
boost::asio::write(stream_,
|
||||||
|
append_buffers(fh_buf.data(), bs), ec);
|
||||||
|
error_ = ec != 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
detail::prepared_key_type key;
|
||||||
|
detail::prepare_key(key, fh.key);
|
||||||
|
auto const tmp_size = detail::clamp(
|
||||||
|
fh.len, wr_buf_size_);
|
||||||
|
std::unique_ptr<std::uint8_t[]> up(
|
||||||
|
new std::uint8_t[tmp_size]);
|
||||||
|
auto const tmp = up.get();
|
||||||
|
std::uint64_t remain = fh.len;
|
||||||
|
consuming_buffers<ConstBufferSequence> cb(bs);
|
||||||
|
{
|
||||||
|
auto const n =
|
||||||
|
detail::clamp(remain, tmp_size);
|
||||||
|
mutable_buffers_1 mb{tmp, n};
|
||||||
|
buffer_copy(mb, cb);
|
||||||
|
cb.consume(n);
|
||||||
|
remain -= n;
|
||||||
|
detail::mask_inplace(mb, key);
|
||||||
|
// send header and payload
|
||||||
|
boost::asio::write(stream_,
|
||||||
|
append_buffers(fh_buf.data(), mb), ec);
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
error_ = ec != 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(remain > 0)
|
||||||
|
{
|
||||||
|
auto const n =
|
||||||
|
detail::clamp(remain, tmp_size);
|
||||||
|
mutable_buffers_1 mb{tmp, n};
|
||||||
|
buffer_copy(mb, cb);
|
||||||
|
cb.consume(n);
|
||||||
|
remain -= n;
|
||||||
|
detail::mask_inplace(mb, key);
|
||||||
|
// send payload
|
||||||
|
boost::asio::write(stream_, mb, ec);
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
error_ = ec != 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class ConstBufferSequence, class WriteHandler>
|
||||||
|
auto
|
||||||
|
socket<Stream>::async_write_frame(bool fin,
|
||||||
|
ConstBufferSequence const& bs, WriteHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
WriteHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
write_frame_op<ConstBufferSequence, decltype(
|
||||||
|
completion.handler)>{completion.handler,
|
||||||
|
*this, fin, bs};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
http::request<http::empty_body>
|
||||||
|
socket<Stream>::build_request(boost::string_ref const& host,
|
||||||
|
boost::string_ref const& resource, std::string& key)
|
||||||
|
{
|
||||||
|
http::request<http::empty_body> req;
|
||||||
|
req.url = "/";
|
||||||
|
req.version = 11;
|
||||||
|
req.method = http::method_t::http_get;
|
||||||
|
req.headers.insert("Host", host);
|
||||||
|
req.headers.insert("Connection", "upgrade");
|
||||||
|
req.headers.insert("Upgrade", "websocket");
|
||||||
|
key = detail::make_sec_ws_key(maskgen_);
|
||||||
|
req.headers.insert("Sec-WebSocket-Key", key);
|
||||||
|
req.headers.insert("Sec-WebSocket-Version", "13");
|
||||||
|
(*d_)(req);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Body, class Headers>
|
||||||
|
http::response<http::string_body>
|
||||||
|
socket<Stream>::build_response(
|
||||||
|
http::message<true, Body, Headers> const& req)
|
||||||
|
{
|
||||||
|
auto err =
|
||||||
|
[&](auto const& text)
|
||||||
|
{
|
||||||
|
http::response<http::string_body> resp(
|
||||||
|
{400, http::reason_string(400), req.version});
|
||||||
|
resp.body = text;
|
||||||
|
// VFALCO TODO respect keep-alive here
|
||||||
|
return resp;
|
||||||
|
};
|
||||||
|
if(req.version < 11)
|
||||||
|
return err("HTTP version 1.1 required");
|
||||||
|
if(req.method != http::method_t::http_get)
|
||||||
|
return err("Wrong method");
|
||||||
|
if(! is_upgrade(req))
|
||||||
|
return err("Expected Upgrade request");
|
||||||
|
if(! req.headers.exists("Host"))
|
||||||
|
return err("Missing Host");
|
||||||
|
if(! req.headers.exists("Sec-WebSocket-Key"))
|
||||||
|
return err("Missing Sec-WebSocket-Key");
|
||||||
|
{
|
||||||
|
auto const version =
|
||||||
|
req.headers["Sec-WebSocket-Version"];
|
||||||
|
if(version.empty())
|
||||||
|
return err("Missing Sec-WebSocket-Version");
|
||||||
|
if(version != "13")
|
||||||
|
return err("Unsupported Sec-WebSocket-Version");
|
||||||
|
}
|
||||||
|
if(! rfc2616::token_in_list(
|
||||||
|
req.headers["Upgrade"], "websocket"))
|
||||||
|
return err("Missing websocket Upgrade token");
|
||||||
|
http::response<http::string_body> resp(
|
||||||
|
{101, http::reason_string(101), req.version});
|
||||||
|
resp.headers.insert("Upgrade", "websocket");
|
||||||
|
resp.headers.insert("Connection", "upgrade");
|
||||||
|
{
|
||||||
|
auto const key =
|
||||||
|
req.headers["Sec-WebSocket-Key"];
|
||||||
|
resp.headers.insert("Sec-WebSocket-Key", key);
|
||||||
|
resp.headers.insert("Sec-WebSocket-Accept",
|
||||||
|
detail::make_sec_ws_accept(key));
|
||||||
|
}
|
||||||
|
resp.headers.replace("Server", "Beast.WSProto");
|
||||||
|
(*d_)(resp);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
socket<Stream>::do_response(
|
||||||
|
http::message<false, Body, Headers> const& resp,
|
||||||
|
boost::string_ref const& key, error_code& ec)
|
||||||
|
{
|
||||||
|
// VFALCO Review these error codes
|
||||||
|
auto fail = [&]{ ec = error::response_failed; };
|
||||||
|
if(resp.status != 101)
|
||||||
|
return fail();
|
||||||
|
if(! is_upgrade(resp))
|
||||||
|
return fail();
|
||||||
|
if(! rfc2616::ci_equal(
|
||||||
|
resp.headers["Upgrade"], "websocket"))
|
||||||
|
return fail();
|
||||||
|
if(! resp.headers.exists("Sec-WebSocket-Accept"))
|
||||||
|
return fail();
|
||||||
|
if(resp.headers["Sec-WebSocket-Accept"] !=
|
||||||
|
detail::make_sec_ws_accept(key))
|
||||||
|
return fail();
|
||||||
|
role_ = role_type::client;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
void
|
||||||
|
socket<Stream>::do_read_fh(
|
||||||
|
detail::frame_streambuf& fb,
|
||||||
|
close_code& code, error_code& ec)
|
||||||
|
{
|
||||||
|
fb.commit(boost::asio::read(
|
||||||
|
stream_, fb.prepare(2), ec));
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
auto const n = detail::read_fh1(
|
||||||
|
rd_fh_, fb, role_, code);
|
||||||
|
if(code != close_code::none)
|
||||||
|
return;
|
||||||
|
if(n > 0)
|
||||||
|
{
|
||||||
|
fb.commit(boost::asio::read(
|
||||||
|
stream_, fb.prepare(n), ec));
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
detail::read_fh2(
|
||||||
|
rd_fh_, fb, role_, code);
|
||||||
|
if(code != close_code::none)
|
||||||
|
return;
|
||||||
|
prepare_fh(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
170
beast/wsproto/impl/ssl.ipp
Normal file
170
beast/wsproto/impl/ssl.ipp
Normal 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_WSPROTO_SSL_IPP_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_SSL_IPP_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/async_completion.h>
|
||||||
|
#include <beast/asio/type_check.h>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
See
|
||||||
|
http://stackoverflow.com/questions/32046034/what-is-the-proper-way-to-securely-disconnect-an-asio-ssl-socket/32054476#32054476
|
||||||
|
|
||||||
|
Behavior of ssl::stream regarding close_
|
||||||
|
|
||||||
|
If the remote host calls async_shutdown then the
|
||||||
|
local host's async_read will complete with eof.
|
||||||
|
|
||||||
|
If both hosts call async_shutdown then the calls
|
||||||
|
to async_shutdown will complete with eof.
|
||||||
|
|
||||||
|
*/
|
||||||
|
template<class AsyncStream, class Handler>
|
||||||
|
class teardown_ssl_op
|
||||||
|
{
|
||||||
|
using stream_type =
|
||||||
|
boost::asio::ssl::stream<AsyncStream>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
stream_type& stream;
|
||||||
|
Handler h;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_,
|
||||||
|
stream_type& stream_)
|
||||||
|
: stream(stream_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<class DeducedHandler>
|
||||||
|
explicit
|
||||||
|
teardown_ssl_op(
|
||||||
|
DeducedHandler&& h,
|
||||||
|
stream_type& stream)
|
||||||
|
: d_(std::make_shared<data>(
|
||||||
|
std::forward<DeducedHandler>(h),
|
||||||
|
stream))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(error_code ec, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_allocate(std::size_t size,
|
||||||
|
teardown_ssl_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_deallocate(void* p,
|
||||||
|
std::size_t size, teardown_ssl_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(
|
||||||
|
teardown_ssl_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f,
|
||||||
|
teardown_ssl_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class AsyncStream, class Handler>
|
||||||
|
void
|
||||||
|
teardown_ssl_op<AsyncStream, Handler>::
|
||||||
|
operator()(error_code ec, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(!ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
d.state = 99;
|
||||||
|
d.stream.async_shutdown(*this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class AsyncStream>
|
||||||
|
void
|
||||||
|
teardown(
|
||||||
|
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
stream.shutdown(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class AsyncStream, class TeardownHandler>
|
||||||
|
void
|
||||||
|
async_teardown(
|
||||||
|
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||||
|
TeardownHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(beast::is_Handler<
|
||||||
|
TeardownHandler, void(error_code)>::value,
|
||||||
|
"TeardownHandler requirements not met");
|
||||||
|
detail::teardown_ssl_op<AsyncStream, std::decay_t<
|
||||||
|
TeardownHandler>>{std::forward<TeardownHandler>(
|
||||||
|
handler), stream};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
183
beast/wsproto/impl/teardown.ipp
Normal file
183
beast/wsproto/impl/teardown.ipp
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_TEARDOWN_IPP_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_TEARDOWN_IPP_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/async_completion.h>
|
||||||
|
#include <beast/asio/type_check.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class Handler>
|
||||||
|
class teardown_tcp_op
|
||||||
|
{
|
||||||
|
using socket_type =
|
||||||
|
boost::asio::ip::tcp::socket;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
socket_type& socket;
|
||||||
|
Handler h;
|
||||||
|
char buf[8192];
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_,
|
||||||
|
socket_type& socket_)
|
||||||
|
: socket(socket_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<class DeducedHandler>
|
||||||
|
teardown_tcp_op(
|
||||||
|
DeducedHandler&& h,
|
||||||
|
socket_type& socket)
|
||||||
|
: d_(std::make_shared<data>(
|
||||||
|
std::forward<DeducedHandler>(h),
|
||||||
|
socket))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(
|
||||||
|
error_code ec, std::size_t, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_allocate(std::size_t size,
|
||||||
|
teardown_tcp_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_deallocate(void* p,
|
||||||
|
std::size_t size, teardown_tcp_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(teardown_tcp_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f,
|
||||||
|
teardown_tcp_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Handler>
|
||||||
|
void
|
||||||
|
teardown_tcp_op<Handler>::
|
||||||
|
operator()(error_code ec, std::size_t, bool again)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
d.state = 1;
|
||||||
|
d.socket.shutdown(
|
||||||
|
boost::asio::ip::tcp::socket::shutdown_send, ec);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
d.socket.async_read_some(
|
||||||
|
buffer(d.buf), std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ec == boost::asio::error::eof)
|
||||||
|
{
|
||||||
|
d.socket.close(ec);
|
||||||
|
ec = error_code{};
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
teardown(
|
||||||
|
boost::asio::ip::tcp::socket& socket,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
socket.shutdown(
|
||||||
|
boost::asio::ip::tcp::socket::shutdown_send, ec);
|
||||||
|
while(! ec)
|
||||||
|
{
|
||||||
|
char buf[8192];
|
||||||
|
auto const n = socket.read_some(
|
||||||
|
buffer(buf), ec);
|
||||||
|
if(! n)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(ec == boost::asio::error::eof)
|
||||||
|
ec = error_code{};
|
||||||
|
socket.close(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TeardownHandler>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
async_teardown(
|
||||||
|
boost::asio::ip::tcp::socket& socket,
|
||||||
|
TeardownHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(beast::is_Handler<
|
||||||
|
TeardownHandler, void(error_code)>::value,
|
||||||
|
"TeardownHandler requirements not met");
|
||||||
|
detail::teardown_tcp_op<std::decay_t<
|
||||||
|
TeardownHandler>>{std::forward<
|
||||||
|
TeardownHandler>(handler), socket};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
277
beast/wsproto/impl/write_frame_op.ipp
Normal file
277
beast/wsproto/impl/write_frame_op.ipp
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_WRITE_FRAME_OP_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_WRITE_FRAME_OP_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/append_buffers.h>
|
||||||
|
#include <beast/asio/bind_handler.h>
|
||||||
|
#include <beast/asio/consuming_buffers.h>
|
||||||
|
#include <beast/asio/handler_alloc.h>
|
||||||
|
#include <beast/asio/static_streambuf.h>
|
||||||
|
#include <beast/wsproto/detail/frame.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
// write a frame
|
||||||
|
//
|
||||||
|
template<class Stream>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
class socket<Stream>::write_frame_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data : op
|
||||||
|
{
|
||||||
|
socket<Stream>& ws;
|
||||||
|
consuming_buffers<Buffers> cb;
|
||||||
|
Handler h;
|
||||||
|
detail::frame_header fh;
|
||||||
|
detail::fh_streambuf fh_buf;
|
||||||
|
detail::prepared_key_type key;
|
||||||
|
void* tmp;
|
||||||
|
std::size_t tmp_size;
|
||||||
|
std::uint64_t remain;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, socket<Stream>& ws_,
|
||||||
|
bool fin, Buffers const& bs)
|
||||||
|
: ws(ws_)
|
||||||
|
, cb(bs)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
fh.op = ws.wr_cont_ ?
|
||||||
|
opcode::cont : ws.wr_opcode_;
|
||||||
|
ws.wr_cont_ = ! fin;
|
||||||
|
fh.fin = fin;
|
||||||
|
fh.rsv1 = 0;
|
||||||
|
fh.rsv2 = 0;
|
||||||
|
fh.rsv3 = 0;
|
||||||
|
fh.len = boost::asio::buffer_size(cb);
|
||||||
|
if((fh.mask = (ws.role_ == role_type::client)))
|
||||||
|
{
|
||||||
|
fh.key = ws.maskgen_();
|
||||||
|
detail::prepare_key(key, fh.key);
|
||||||
|
tmp_size = detail::clamp(
|
||||||
|
fh.len, ws.wr_buf_size_);
|
||||||
|
tmp = boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(tmp_size, h);
|
||||||
|
remain = fh.len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tmp = nullptr;
|
||||||
|
}
|
||||||
|
detail::write<static_streambuf>(fh_buf, fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
~data()
|
||||||
|
{
|
||||||
|
if(tmp)
|
||||||
|
boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(tmp, tmp_size, h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
write_frame_op(write_frame_op&&) = default;
|
||||||
|
write_frame_op(write_frame_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
write_frame_op(DeducedHandler&& h,
|
||||||
|
socket<Stream>& ws, Args&&... args)
|
||||||
|
: d_(std::make_shared<data>(
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()()
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = false;
|
||||||
|
(*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, write_frame_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_frame_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(write_frame_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f, write_frame_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Stream>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
void
|
||||||
|
socket<Stream>::
|
||||||
|
write_frame_op<Buffers, Handler>::
|
||||||
|
operator()(
|
||||||
|
error_code ec, std::size_t bytes_transferred, bool again)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::mutable_buffers_1;
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if(d.ws.wr_block_)
|
||||||
|
{
|
||||||
|
// suspend
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.wr_op_.template emplace<
|
||||||
|
write_frame_op>(std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(d.ws.error_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.ws.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this),
|
||||||
|
boost::asio::error::operation_aborted, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert(! d.ws.wr_close_);
|
||||||
|
d.state = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// resume
|
||||||
|
case 1:
|
||||||
|
if(d.ws.error_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
ec = boost::asio::error::operation_aborted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.state = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
if(! d.fh.mask)
|
||||||
|
{
|
||||||
|
// send header and payload
|
||||||
|
d.state = 99;
|
||||||
|
assert(! d.ws.wr_block_);
|
||||||
|
d.ws.wr_block_ = &d;
|
||||||
|
boost::asio::async_write(d.ws.stream_,
|
||||||
|
append_buffers(d.fh_buf.data(), d.cb),
|
||||||
|
std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto const n =
|
||||||
|
detail::clamp(d.remain, d.tmp_size);
|
||||||
|
mutable_buffers_1 mb{d.tmp, n};
|
||||||
|
buffer_copy(mb, d.cb);
|
||||||
|
d.cb.consume(n);
|
||||||
|
d.remain -= n;
|
||||||
|
detail::mask_inplace(mb, d.key);
|
||||||
|
// send header and payload
|
||||||
|
d.state = d.remain > 0 ? 3 : 99;
|
||||||
|
assert(! d.ws.wr_block_);
|
||||||
|
d.ws.wr_block_ = &d;
|
||||||
|
boost::asio::async_write(d.ws.stream_,
|
||||||
|
append_buffers(d.fh_buf.data(),
|
||||||
|
mb), std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sent masked payload
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
auto const n =
|
||||||
|
detail::clamp(d.remain, d.tmp_size);
|
||||||
|
mutable_buffers_1 mb{d.tmp,
|
||||||
|
static_cast<std::size_t>(n)};
|
||||||
|
buffer_copy(mb, d.cb);
|
||||||
|
d.cb.consume(n);
|
||||||
|
d.remain -= n;
|
||||||
|
detail::mask_inplace(mb, d.key);
|
||||||
|
// send payload
|
||||||
|
if(d.remain == 0)
|
||||||
|
d.state = 99;
|
||||||
|
assert(! d.ws.wr_block_);
|
||||||
|
d.ws.wr_block_ = &d;
|
||||||
|
boost::asio::async_write(
|
||||||
|
d.ws.stream_, mb, std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ec)
|
||||||
|
d.ws.error_ = true;
|
||||||
|
if(d.ws.wr_block_ == &d)
|
||||||
|
d.ws.wr_block_ = nullptr;
|
||||||
|
if(d.tmp)
|
||||||
|
{
|
||||||
|
boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(d.tmp, d.tmp_size, d.h);
|
||||||
|
d.tmp = nullptr;
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
d.ws.rd_op_.maybe_invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
150
beast/wsproto/impl/write_op.ipp
Normal file
150
beast/wsproto/impl/write_op.ipp
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_WRITE_OP_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_WRITE_OP_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/consuming_buffers.h>
|
||||||
|
#include <beast/asio/prepare_buffers.h>
|
||||||
|
#include <beast/asio/handler_alloc.h>
|
||||||
|
#include <beast/asio/temp_buffer.h>
|
||||||
|
#include <beast/wsproto/detail/frame.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
// write a message
|
||||||
|
//
|
||||||
|
template<class Stream>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
class socket<Stream>::write_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data : op
|
||||||
|
{
|
||||||
|
socket<Stream>& ws;
|
||||||
|
consuming_buffers<Buffers> cb;
|
||||||
|
Handler h;
|
||||||
|
std::size_t remain;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_,
|
||||||
|
socket<Stream>& ws_, Buffers const& bs)
|
||||||
|
: ws(ws_)
|
||||||
|
, cb(bs)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, remain(boost::asio::buffer_size(cb))
|
||||||
|
, 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>
|
||||||
|
explicit
|
||||||
|
write_op(DeducedHandler&& h,
|
||||||
|
socket<Stream>& ws, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code 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 Stream>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
void
|
||||||
|
socket<Stream>::
|
||||||
|
write_op<Buffers, Handler>::
|
||||||
|
operator()(error_code ec, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
auto const n = std::min(
|
||||||
|
d.remain, d.ws.wr_frag_size_);
|
||||||
|
d.remain -= n;
|
||||||
|
auto const fin = d.remain <= 0;
|
||||||
|
if(fin)
|
||||||
|
d.state = 99;
|
||||||
|
d.ws.async_write_frame(fin,
|
||||||
|
prepare_buffers(n, d.cb), std::move(*this));
|
||||||
|
d.cb.consume(n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
305
beast/wsproto/option.h
Normal file
305
beast/wsproto/option.h
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_OPTION_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_OPTION_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/detail/socket_base.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
/** Automatic fragmentation size option.
|
||||||
|
|
||||||
|
Sets the maximum size of fragments generated when sending
|
||||||
|
messages on a WebSocket socket.
|
||||||
|
|
||||||
|
When the automatic fragmentation size is non-zero, messages
|
||||||
|
exceeding the size will be split into multiple frames no
|
||||||
|
larger than the size. This setting does not affect frames
|
||||||
|
send explicitly using `write_frame` or `async_write_frame`.
|
||||||
|
|
||||||
|
The default setting is to fragment messages into 16KB frames.
|
||||||
|
|
||||||
|
@note Objects of this type are passed to socket::set_option.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
Setting the automatic fragmentation size option:
|
||||||
|
@code
|
||||||
|
...
|
||||||
|
wsproto::socket<ip::tcp::socket> stream(ios);
|
||||||
|
stream.set_option(auto_fragment_size{8192});
|
||||||
|
@endcode
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using auto_fragment_size = implementation_defined;
|
||||||
|
#else
|
||||||
|
struct auto_fragment_size
|
||||||
|
{
|
||||||
|
std::size_t value;
|
||||||
|
|
||||||
|
auto_fragment_size(std::size_t n)
|
||||||
|
: value(n)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** HTTP decorator option.
|
||||||
|
|
||||||
|
The decorator transforms the HTTP requests and responses used
|
||||||
|
when requesting or responding to the WebSocket Upgrade. This may
|
||||||
|
be used to set or change header fields. For example to set the
|
||||||
|
Server or User-Agent fields. The default setting applies no
|
||||||
|
transformation to the HTTP message.
|
||||||
|
|
||||||
|
For synchronous operations, the implementation will call the
|
||||||
|
decorator before the function call to perform the operation
|
||||||
|
returns.
|
||||||
|
|
||||||
|
For asynchronous operations, the implementation guarantees that
|
||||||
|
calls to the decorator will be made from the same implicit or
|
||||||
|
explicit strand used to call the asynchronous initiation
|
||||||
|
function.
|
||||||
|
|
||||||
|
The default setting is no decorator.
|
||||||
|
|
||||||
|
@note Objects of this type are passed to socket::set_option.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
Setting the decorator.
|
||||||
|
@code
|
||||||
|
struct identity
|
||||||
|
{
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<isRequest, Body, Headers>& m)
|
||||||
|
{
|
||||||
|
if(isRequest)
|
||||||
|
m.headers.replace("User-Agent", "MyClient");
|
||||||
|
else
|
||||||
|
m.headers.replace("Server", "MyServer");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
...
|
||||||
|
websocket::stream<ip::tcp::socket> ws(ios);
|
||||||
|
ws.set_option(decorate(identity{}));
|
||||||
|
@endcode
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using decorate = implementation_defined;
|
||||||
|
#else
|
||||||
|
template<class Decorator>
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
decorate(Decorator&& d)
|
||||||
|
{
|
||||||
|
return std::make_unique<detail::decorator<
|
||||||
|
std::decay_t<Decorator>>>(
|
||||||
|
std::forward<Decorator>(d));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Keep-alive option.
|
||||||
|
|
||||||
|
Determines if the connection is closed after a failed upgrade
|
||||||
|
request.
|
||||||
|
|
||||||
|
This setting only affects the behavior of HTTP requests that
|
||||||
|
implicitly or explicitly ask for a keepalive. For HTTP requests
|
||||||
|
that indicate the connection should be closed, the connection is
|
||||||
|
closed as per rfc2616.
|
||||||
|
|
||||||
|
The default setting is to close connections after a failed
|
||||||
|
upgrade request.
|
||||||
|
|
||||||
|
@note Objects of this type are passed to socket::set_option.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
Setting the keep alive option.
|
||||||
|
@code
|
||||||
|
...
|
||||||
|
websocket::stream<ip::tcp::socket> ws(ios);
|
||||||
|
ws.set_option(keep_alive{8192});
|
||||||
|
@endcode
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using keep_alive = implementation_defined;
|
||||||
|
#else
|
||||||
|
struct keep_alive
|
||||||
|
{
|
||||||
|
bool value;
|
||||||
|
|
||||||
|
keep_alive(bool v)
|
||||||
|
: value(v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Message type option.
|
||||||
|
|
||||||
|
This controls the opcode set for outgoing messages. Valid
|
||||||
|
choices are opcode::binary or opcode::text. The setting is
|
||||||
|
only applied at the start when a caller begins a new message.
|
||||||
|
Changing the opcode after a message is started will only
|
||||||
|
take effect after the current message being sent is complete.
|
||||||
|
|
||||||
|
The default setting is opcode::text.
|
||||||
|
|
||||||
|
@note Objects of this type are passed to socket::set_option.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
Setting the message type to binary.
|
||||||
|
@code
|
||||||
|
...
|
||||||
|
websocket::stream<ip::tcp::socket> ws(ios);
|
||||||
|
ws.set_option(message_type{opcode::binary});
|
||||||
|
@endcode
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using message_type = implementation_defined;
|
||||||
|
#else
|
||||||
|
struct message_type
|
||||||
|
{
|
||||||
|
opcode value;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
message_type(opcode op)
|
||||||
|
{
|
||||||
|
if(op != opcode::binary && op != opcode::text)
|
||||||
|
throw std::domain_error("bad opcode");
|
||||||
|
value = op;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Read buffer size option.
|
||||||
|
|
||||||
|
Sets the number of bytes allocated to the socket's read buffer.
|
||||||
|
If this is zero, then reads are not buffered. Setting this
|
||||||
|
higher can improve performance when expecting to receive
|
||||||
|
many small frames.
|
||||||
|
|
||||||
|
The default is no buffering.
|
||||||
|
|
||||||
|
@note Objects of this type are passed to socket::set_option.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
Setting the read buffer size.
|
||||||
|
@code
|
||||||
|
...
|
||||||
|
websocket::stream<ip::tcp::socket> ws(ios);
|
||||||
|
ws.set_option(read_buffer_size{16 * 1024});
|
||||||
|
@endcode
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using read_buffer_size = implementation_defined;
|
||||||
|
#else
|
||||||
|
struct read_buffer_size
|
||||||
|
{
|
||||||
|
std::size_t value;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
read_buffer_size(std::size_t n)
|
||||||
|
: value(n)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Maximum incoming message size option.
|
||||||
|
|
||||||
|
Sets the largest permissible incoming message size. Message
|
||||||
|
frame headers indicating a size that would bring the total
|
||||||
|
message size over this limit will cause a protocol failure.
|
||||||
|
|
||||||
|
The default setting is 16 megabytes.
|
||||||
|
|
||||||
|
@note Objects of this type are passed to socket::set_option.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
Setting the maximum read message size.
|
||||||
|
@code
|
||||||
|
...
|
||||||
|
websocket::stream<ip::tcp::socket> ws(ios);
|
||||||
|
ws.set_option(read_message_max{65536});
|
||||||
|
@endcode
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using read_message_max = implementation_defined;
|
||||||
|
#else
|
||||||
|
struct read_message_max
|
||||||
|
{
|
||||||
|
std::size_t value;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
read_message_max(std::size_t n)
|
||||||
|
: value(n)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Write buffer size option.
|
||||||
|
|
||||||
|
Sets the number of bytes allocated to the socket's write buffer.
|
||||||
|
This buffer is used to hold masked frame payload data. Lowering
|
||||||
|
the size of the buffer can decrease the memory requirements for
|
||||||
|
each connection, at the cost of an increased number of calls to
|
||||||
|
perform socket writes.
|
||||||
|
|
||||||
|
This setting does not affect connections operating in the server
|
||||||
|
role, since servers do not apply a masking key to frame payloads.
|
||||||
|
|
||||||
|
The default setting is 4096. The minimum value is 1024.
|
||||||
|
|
||||||
|
@note Objects of this type are passed to socket::set_option.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
Setting the write buffer size.
|
||||||
|
@code
|
||||||
|
...
|
||||||
|
websocket::stream<ip::tcp::socket> ws(ios);
|
||||||
|
ws.set_option(write_buffer_size{8192});
|
||||||
|
@endcode
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using write_buffer_size = implementation_defined;
|
||||||
|
#else
|
||||||
|
struct write_buffer_size
|
||||||
|
{
|
||||||
|
std::size_t value;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
write_buffer_size(std::size_t n)
|
||||||
|
: value(n)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
163
beast/wsproto/rfc6455.h
Normal file
163
beast/wsproto/rfc6455.h
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_RFC6455_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_RFC6455_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/static_string.h>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
/** WebSocket frame header opcodes. */
|
||||||
|
enum class opcode : std::uint8_t
|
||||||
|
{
|
||||||
|
cont = 0,
|
||||||
|
text = 1,
|
||||||
|
binary = 2,
|
||||||
|
rsv3 = 3,
|
||||||
|
rsv4 = 4,
|
||||||
|
rsv5 = 5,
|
||||||
|
rsv6 = 6,
|
||||||
|
rsv7 = 7,
|
||||||
|
close = 8,
|
||||||
|
ping = 9,
|
||||||
|
pong = 10,
|
||||||
|
crsvb = 11,
|
||||||
|
crsvc = 12,
|
||||||
|
crsvd = 13,
|
||||||
|
crsve = 14,
|
||||||
|
crsvf = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Close status codes.
|
||||||
|
|
||||||
|
These codes accompany close frames.
|
||||||
|
|
||||||
|
@see RFC 6455 7.4.1 Defined Status Codes
|
||||||
|
https://tools.ietf.org/html/rfc6455#section-7.4.1
|
||||||
|
*/
|
||||||
|
enum class close_code : std::uint16_t
|
||||||
|
{
|
||||||
|
// used internally to mean "no error"
|
||||||
|
none = 0,
|
||||||
|
|
||||||
|
normal = 1000,
|
||||||
|
going_away = 1001,
|
||||||
|
protocol_error = 1002,
|
||||||
|
unknown_data = 1003,
|
||||||
|
bad_payload = 1007,
|
||||||
|
policy_error = 1008,
|
||||||
|
too_big = 1009,
|
||||||
|
needs_extension = 1010,
|
||||||
|
internal_error = 1011,
|
||||||
|
|
||||||
|
service_restart = 1012,
|
||||||
|
try_again_later = 1013,
|
||||||
|
|
||||||
|
reserved1 = 1004,
|
||||||
|
no_status = 1005, // illegal on wire
|
||||||
|
abnormal = 1006, // illegal on wire
|
||||||
|
reserved2 = 1015,
|
||||||
|
|
||||||
|
last = 5000 // satisfy warnings
|
||||||
|
};
|
||||||
|
|
||||||
|
#if ! GENERATING_DOCS
|
||||||
|
|
||||||
|
using reason_string_type =
|
||||||
|
static_string<123, char>;
|
||||||
|
|
||||||
|
/// Payload type for pings and pongs
|
||||||
|
using ping_payload_type =
|
||||||
|
static_string<125, char>;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Description of the close reason.
|
||||||
|
|
||||||
|
This object stores the close code (if any) and the optional
|
||||||
|
utf-8 encoded implementation defined reason string.
|
||||||
|
*/
|
||||||
|
struct close_reason
|
||||||
|
{
|
||||||
|
/// The close code.
|
||||||
|
close_code code = close_code::none;
|
||||||
|
|
||||||
|
/// The optional utf8-encoded reason string.
|
||||||
|
reason_string_type reason;
|
||||||
|
|
||||||
|
/** Default constructor.
|
||||||
|
|
||||||
|
The code will be none. Default constructed objects
|
||||||
|
will explicitly convert to bool as `false`.
|
||||||
|
*/
|
||||||
|
close_reason() = default;
|
||||||
|
|
||||||
|
/// Construct from a code.
|
||||||
|
close_reason(close_code code_)
|
||||||
|
: code(code_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from a reason. code is close_code::normal.
|
||||||
|
template<class CharT>
|
||||||
|
close_reason(CharT const* reason_)
|
||||||
|
: code(close_code::normal)
|
||||||
|
, reason(reason_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from a code and reason.
|
||||||
|
template<class CharT>
|
||||||
|
close_reason(close_code code_,
|
||||||
|
CharT const* reason_)
|
||||||
|
: code(code_)
|
||||||
|
, reason(reason_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if a code was specified
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return code != close_code::none;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if ! GENERATING_DOCS
|
||||||
|
|
||||||
|
/// Identifies the role of a WebSockets stream.
|
||||||
|
enum class role_type
|
||||||
|
{
|
||||||
|
/// Stream is operating as a client.
|
||||||
|
client,
|
||||||
|
|
||||||
|
/// Stream is operating as a server.
|
||||||
|
server
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
1190
beast/wsproto/socket.h
Normal file
1190
beast/wsproto/socket.h
Normal file
File diff suppressed because it is too large
Load Diff
285
beast/wsproto/src/test/async_echo_peer.h
Normal file
285
beast/wsproto/src/test/async_echo_peer.h
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_ASYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_ASYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
#include <beast/unit_test/thread.h>
|
||||||
|
#include <beast/asio/placeholders.h>
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
#include <beast/wsproto.h>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
// Asynchronous WebSocket echo client/server
|
||||||
|
//
|
||||||
|
class async_echo_peer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::size_t constexpr autobahnCycles = 520;
|
||||||
|
|
||||||
|
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_;
|
||||||
|
std::vector<unit_test::thread> thread_;
|
||||||
|
std::size_t n_ = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
async_echo_peer(bool server,
|
||||||
|
endpoint_type const& ep, unit_test::suite& suite)
|
||||||
|
: suite_(suite)
|
||||||
|
, sock_(ios_)
|
||||||
|
, acceptor_(ios_)
|
||||||
|
{
|
||||||
|
if(server)
|
||||||
|
{
|
||||||
|
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(&async_echo_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Peer{std::move(sock_), ep, suite_};
|
||||||
|
}
|
||||||
|
auto const n = 1;
|
||||||
|
thread_.reserve(n);
|
||||||
|
for(int i = 0; i < n; ++i)
|
||||||
|
thread_.emplace_back(suite_,
|
||||||
|
[&] { ios_.run(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
~async_echo_peer()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
ios_.dispatch(
|
||||||
|
[&]{ acceptor_.close(ec); });
|
||||||
|
for(auto& t : thread_)
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Peer
|
||||||
|
{
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
int state = 0;
|
||||||
|
unit_test::suite& suite;
|
||||||
|
boost::optional<endpoint_type> ep;
|
||||||
|
wsproto::socket<socket_type> ws;
|
||||||
|
wsproto::opcode op;
|
||||||
|
beast::streambuf sb;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
data(socket_type&& sock_,
|
||||||
|
unit_test::suite& suite_)
|
||||||
|
: suite(suite_)
|
||||||
|
, ws(std::move(sock_))
|
||||||
|
, id([]
|
||||||
|
{
|
||||||
|
static int n = 0;
|
||||||
|
return ++n;
|
||||||
|
}())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
data(socket_type&& sock_, endpoint_type const& ep_,
|
||||||
|
unit_test::suite& suite_)
|
||||||
|
: suite(suite_)
|
||||||
|
, ep(ep_)
|
||||||
|
, ws(std::move(sock_))
|
||||||
|
, id([]
|
||||||
|
{
|
||||||
|
static int n = 0;
|
||||||
|
return ++n;
|
||||||
|
}())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Peer(Peer&&) = default;
|
||||||
|
Peer(Peer const&) = default;
|
||||||
|
Peer& operator=(Peer&&) = delete;
|
||||||
|
Peer& operator=(Peer const&) = delete;
|
||||||
|
|
||||||
|
struct identity
|
||||||
|
{
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<true, Body, Headers>& req)
|
||||||
|
{
|
||||||
|
req.headers.replace("User-Agent", "async_echo_client");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<false, Body, Headers>& resp)
|
||||||
|
{
|
||||||
|
resp.headers.replace("Server", "async_echo_server");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
explicit
|
||||||
|
Peer(socket_type&& sock, Args&&... args)
|
||||||
|
: d_(std::make_shared<data>(
|
||||||
|
std::forward<socket_type>(sock),
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.ws.set_option(decorate(identity{}));
|
||||||
|
//d.ws.set_option(auto_fragment_size(0));
|
||||||
|
d.ws.set_option(read_message_max(64 * 1024 * 1024));
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
if(! d.ep)
|
||||||
|
{
|
||||||
|
d.ws.async_accept(std::move(*this));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d.state = 4;
|
||||||
|
d.ws.next_layer().async_connect(
|
||||||
|
*d.ep, std::move(*this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code ec)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
switch(d_->state)
|
||||||
|
{
|
||||||
|
// did accept
|
||||||
|
case 0:
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_accept");
|
||||||
|
|
||||||
|
// start
|
||||||
|
case 1:
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_handshake");
|
||||||
|
d.sb.consume(d.sb.size());
|
||||||
|
// read message
|
||||||
|
d.state = 2;
|
||||||
|
d.ws.async_read(d.op, d.sb, std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// got message
|
||||||
|
case 2:
|
||||||
|
if(ec == wsproto::error::closed)
|
||||||
|
return;
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_read");
|
||||||
|
// write message
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.set_option(wsproto::message_type(d.op));
|
||||||
|
d.ws.async_write(d.sb.data(), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// connected
|
||||||
|
case 4:
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_connect");
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.async_handshake(
|
||||||
|
d.ep->address().to_string() + ":" +
|
||||||
|
std::to_string(d.ep->port()),
|
||||||
|
"/", std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec != wsproto::error::closed)
|
||||||
|
{
|
||||||
|
d_->suite.log <<
|
||||||
|
"#" << std::to_string(d_->id) << " " <<
|
||||||
|
what << ": " << ec.message();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
suite_.log <<
|
||||||
|
what << ": " << ec.message();
|
||||||
|
}
|
||||||
|
|
||||||
|
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_));
|
||||||
|
if(n_ < autobahnCycles)
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&async_echo_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
Peer{std::move(sock), suite_};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
94
beast/wsproto/src/test/beast_wsproto_ws_echo_test.cpp
Normal file
94
beast/wsproto/src/test/beast_wsproto_ws_echo_test.cpp
Normal 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.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <beast/wsproto/src/test/async_echo_peer.h>
|
||||||
|
#include <beast/wsproto/src/test/sync_echo_peer.h>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class ws_echo_test : public unit_test::suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
async_echo_peer s1(true, endpoint_type{
|
||||||
|
address_type::from_string("127.0.0.1"),
|
||||||
|
6000 }, *this);
|
||||||
|
|
||||||
|
sync_echo_peer s2(true, endpoint_type{
|
||||||
|
address_type::from_string("127.0.0.1"),
|
||||||
|
6001 }, *this);
|
||||||
|
|
||||||
|
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& ec,
|
||||||
|
int signal_number)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
stop = true;
|
||||||
|
cv.notify_one();
|
||||||
|
});
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.wait(lock, [&]{ return stop; });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ws_client_test : public unit_test::suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
pass();
|
||||||
|
{
|
||||||
|
async_echo_peer s1(false, endpoint_type{
|
||||||
|
address_type::from_string("127.0.0.1"),
|
||||||
|
9001 }, *this);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
sync_echo_peer s2(false, endpoint_type{
|
||||||
|
address_type::from_string("127.0.0.1"),
|
||||||
|
9001 }, *this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE_MANUAL(ws_echo, asio, beast);
|
||||||
|
BEAST_DEFINE_TESTSUITE_MANUAL(ws_client, asio, beast);
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
362
beast/wsproto/src/test/beast_wsproto_ws_test.cpp
Normal file
362
beast/wsproto/src/test/beast_wsproto_ws_test.cpp
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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/wsproto/src/test/async_echo_peer.h>
|
||||||
|
#include <beast/wsproto/src/test/sync_echo_peer.h>
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
#include <beast/unit_test/thread.h>
|
||||||
|
#include <beast/http.h>
|
||||||
|
#include <boost/asio/spawn.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
class ws_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;
|
||||||
|
using yield_context = boost::asio::yield_context;
|
||||||
|
|
||||||
|
endpoint_type ep_;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// opcodes for creating the test plans
|
||||||
|
|
||||||
|
// concurrent read and write
|
||||||
|
struct case_1{};
|
||||||
|
|
||||||
|
// write a bad frame and shut down
|
||||||
|
struct case_2{};
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class coro_peer
|
||||||
|
{
|
||||||
|
error_code ec_;
|
||||||
|
boost::asio::io_service ios_;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
socket_type sock_;
|
||||||
|
socket<socket_type&> ws_;
|
||||||
|
opcode op_;
|
||||||
|
beast::streambuf rb_;
|
||||||
|
beast::streambuf wb_;
|
||||||
|
yield_context* yield_;
|
||||||
|
int state_ = 0;
|
||||||
|
//unit_test::suite& test_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
coro_peer(coro_peer&&) = default;
|
||||||
|
coro_peer(coro_peer const&) = delete;
|
||||||
|
coro_peer& operator=(coro_peer&&) = delete;
|
||||||
|
coro_peer& operator=(coro_peer const&) = delete;
|
||||||
|
|
||||||
|
template<class... Ops>
|
||||||
|
coro_peer(bool server, endpoint_type ep,
|
||||||
|
unit_test::suite& test, Ops const&... ops)
|
||||||
|
: acceptor_(ios_)
|
||||||
|
, sock_(ios_)
|
||||||
|
, ws_(sock_)
|
||||||
|
//, test_(test)
|
||||||
|
{
|
||||||
|
if(server)
|
||||||
|
{
|
||||||
|
acceptor_.open(ep.protocol());
|
||||||
|
acceptor_.bind(ep);
|
||||||
|
acceptor_.listen(
|
||||||
|
boost::asio::socket_base::max_connections);
|
||||||
|
boost::asio::spawn(ios_,
|
||||||
|
[=](auto yield)
|
||||||
|
{
|
||||||
|
yield_ = &yield;
|
||||||
|
state_ = 10;
|
||||||
|
acceptor_.async_accept(sock_, (*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return this->fail("accept");
|
||||||
|
state_ = 20;
|
||||||
|
ws_.async_accept((*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return this->fail("ws.accept");
|
||||||
|
this->invoke(ops...);
|
||||||
|
state_ = -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boost::asio::spawn(ios_,
|
||||||
|
[=](auto yield)
|
||||||
|
{
|
||||||
|
yield_ = &yield;
|
||||||
|
state_ = 30;
|
||||||
|
sock_.async_connect(ep, (*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return this->fail("connect");
|
||||||
|
state_ = 40;
|
||||||
|
ws_.async_handshake(ep.address().to_string() +
|
||||||
|
std::to_string(ep.port()), "/", (*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return this->fail("handshake");
|
||||||
|
this->invoke(ops...);
|
||||||
|
state_ = -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~coro_peer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
state() const
|
||||||
|
{
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run_one()
|
||||||
|
{
|
||||||
|
ios_.run_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
step_to(int to = 0)
|
||||||
|
{
|
||||||
|
while(state_ != to)
|
||||||
|
ios_.run_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class String>
|
||||||
|
void fail(String const& s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke_1(case_1)
|
||||||
|
{
|
||||||
|
ws_.async_read(op_, rb_,
|
||||||
|
[&](auto ec)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
return this->fail(ec);
|
||||||
|
rb_.consume(rb_.size());
|
||||||
|
});
|
||||||
|
state_ = 100;
|
||||||
|
ws_.async_write(
|
||||||
|
boost::asio::null_buffers{}, (*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return fail("write");
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke_1(case_2)
|
||||||
|
{
|
||||||
|
detail::frame_header fh;
|
||||||
|
fh.op = opcode::rsv5; // bad opcode
|
||||||
|
fh.fin = true;
|
||||||
|
fh.mask = true;
|
||||||
|
fh.rsv1 = false;
|
||||||
|
fh.rsv2 = false;
|
||||||
|
fh.rsv3 = false;
|
||||||
|
fh.len = 0;
|
||||||
|
fh.key = 0;
|
||||||
|
detail::write(wb_, fh);
|
||||||
|
state_ = 200;
|
||||||
|
boost::asio::async_write(
|
||||||
|
ws_.next_layer(), wb_.data(),
|
||||||
|
(*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return fail("write");
|
||||||
|
ws_.next_layer().shutdown(
|
||||||
|
socket_type::shutdown_both, ec_);
|
||||||
|
if(ec_)
|
||||||
|
return fail("shutdown");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
invoke()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Op, class... Ops>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
invoke(Op op, Ops const&... ops)
|
||||||
|
{
|
||||||
|
invoke_1(op);
|
||||||
|
invoke(ops...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
testInvokable()
|
||||||
|
{
|
||||||
|
endpoint_type const ep{
|
||||||
|
address_type::from_string(
|
||||||
|
"127.0.0.1"), 6000};
|
||||||
|
coro_peer server(true, ep, *this, case_1{});
|
||||||
|
coro_peer client(false, ep, *this, case_2{});
|
||||||
|
server.step_to(10); // async_accept
|
||||||
|
client.step_to(30); // async_connect
|
||||||
|
server.step_to(20); // async_accept(ws)
|
||||||
|
client.step_to(40); // async_handshake
|
||||||
|
server.step_to(100); // case_1
|
||||||
|
client.step_to(200); // case_2
|
||||||
|
client.step_to(-1);
|
||||||
|
server.step_to(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
maybe_fail(error_code const& ec, std::string const& what)
|
||||||
|
{
|
||||||
|
return expect(! ec, what + ": " + ec.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
maybe_throw(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
maybe_fail(ec, what);
|
||||||
|
throw ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Buffers>
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
buffers_to_string(Buffers const& bs)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
std::string s;
|
||||||
|
s.reserve(buffer_size(bs));
|
||||||
|
for(auto const& b : bs)
|
||||||
|
s.append(buffer_cast<char const*>(b),
|
||||||
|
buffer_size(b));
|
||||||
|
for(auto i = s.size(); i-- > 0;)
|
||||||
|
if(s[i] == '\r')
|
||||||
|
s.replace(i, 1, "\\r");
|
||||||
|
else if(s[i] == '\n')
|
||||||
|
s.replace(i, 1, "\\n\n");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
makeRequest(endpoint_type ep, std::string const& s)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
boost::asio::io_service ios;
|
||||||
|
boost::asio::ip::tcp::socket sock(ios);
|
||||||
|
sock.connect(ep);
|
||||||
|
write(sock, append_buffers(
|
||||||
|
buffer(s), buffer("\r\n")));
|
||||||
|
|
||||||
|
using namespace http;
|
||||||
|
response<string_body> resp;
|
||||||
|
streambuf sb;
|
||||||
|
read(sock, sb, resp);
|
||||||
|
return resp.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
expectStatus(endpoint_type ep,
|
||||||
|
int status, std::string const& s)
|
||||||
|
{
|
||||||
|
expect(makeRequest(ep, s) == status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testHandshake(endpoint_type ep)
|
||||||
|
{
|
||||||
|
expectStatus(ep, 400, "GET / HTTP/1.0\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
syncEchoClient(endpoint_type ep)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
error_code ec;
|
||||||
|
boost::asio::io_service ios;
|
||||||
|
wsproto::socket<socket_type> ws(ios);
|
||||||
|
ws.next_layer().connect(ep, ec);
|
||||||
|
if(! maybe_fail(ec, "connect"))
|
||||||
|
return;
|
||||||
|
ws.handshake(ep.address().to_string(), "/", ec);
|
||||||
|
if(! maybe_fail(ec, "upgrade"))
|
||||||
|
return;
|
||||||
|
std::string s(65535, '*');
|
||||||
|
ws.write_frame(true, buffer(s), ec);
|
||||||
|
if(! maybe_fail(ec, "write"))
|
||||||
|
return;
|
||||||
|
boost::asio::streambuf sb;
|
||||||
|
wsproto::opcode op;
|
||||||
|
ws.read(op, sb, ec);
|
||||||
|
if(! maybe_fail(ec, "read"))
|
||||||
|
return;
|
||||||
|
if(! ec)
|
||||||
|
expect(op == wsproto::opcode::text);
|
||||||
|
expect(buffers_to_string(sb.data()) == s);
|
||||||
|
sb.consume(sb.size());
|
||||||
|
ws.close({}, ec);
|
||||||
|
if(! maybe_fail(ec, "close"))
|
||||||
|
return;
|
||||||
|
while(! ec)
|
||||||
|
{
|
||||||
|
ws.read(op, sb, ec);
|
||||||
|
if(! ec)
|
||||||
|
sb.consume(sb.size());
|
||||||
|
}
|
||||||
|
if(ec != error::closed)
|
||||||
|
maybe_fail(ec, "teardown");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
//testInvokable();
|
||||||
|
|
||||||
|
{
|
||||||
|
endpoint_type ep{
|
||||||
|
address_type::from_string("127.0.0.1"), 6000};
|
||||||
|
testcase("Echo Server");
|
||||||
|
test::sync_echo_peer s(true, ep, *this);
|
||||||
|
//testHandshake(ep);
|
||||||
|
syncEchoClient(ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
endpoint_type ep{
|
||||||
|
address_type::from_string("127.0.0.1"), 6001};
|
||||||
|
testcase("Async Echo Server");
|
||||||
|
test::async_echo_peer s(true, ep, *this);
|
||||||
|
//testHandshake(ep);
|
||||||
|
syncEchoClient(ep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(ws,asio,beast);
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
182
beast/wsproto/src/test/sync_echo_peer.h
Normal file
182
beast/wsproto/src/test/sync_echo_peer.h
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
#include <beast/wsproto.h>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
// Synchronous WebSocket echo client/server
|
||||||
|
//
|
||||||
|
class sync_echo_peer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::size_t constexpr autobahnCycles = 520;
|
||||||
|
|
||||||
|
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_;
|
||||||
|
std::size_t n_ = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
sync_echo_peer(bool 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_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
thread_ = unit_test::thread(suite_,
|
||||||
|
[&]
|
||||||
|
{
|
||||||
|
ios_.run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
~sync_echo_peer()
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
fail(ec, what);
|
||||||
|
throw ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_accept(error_code ec)
|
||||||
|
{
|
||||||
|
if(ec == boost::asio::error::operation_aborted)
|
||||||
|
return;
|
||||||
|
maybe_throw(ec, "accept");
|
||||||
|
++n_;
|
||||||
|
std::thread{
|
||||||
|
[
|
||||||
|
this,
|
||||||
|
sock = std::move(sock_),
|
||||||
|
work = boost::asio::io_service::work{ios_}
|
||||||
|
]() mutable
|
||||||
|
{
|
||||||
|
do_peer(std::move(sock));
|
||||||
|
}}.detach();
|
||||||
|
if(n_ < autobahnCycles)
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&sync_echo_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct identity
|
||||||
|
{
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<true, Body, Headers>& req)
|
||||||
|
{
|
||||||
|
req.headers.replace("User-Agent", "sync_echo_client");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<false, Body, Headers>& resp)
|
||||||
|
{
|
||||||
|
resp.headers.replace("Server", "sync_echo_server");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
do_peer(socket_type&& sock)
|
||||||
|
{
|
||||||
|
wsproto::socket<socket_type> ws(std::move(sock));
|
||||||
|
ws.set_option(decorate(identity{}));
|
||||||
|
ws.set_option(read_message_max(64 * 1024 * 1024));
|
||||||
|
//ws.set_option(auto_fragment_size(0));
|
||||||
|
error_code ec;
|
||||||
|
ws.accept(ec);
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
fail(ec, "accept");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
wsproto::opcode op;
|
||||||
|
beast::streambuf sb;
|
||||||
|
ws.read(op, sb, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
ws.set_option(wsproto::message_type(op));
|
||||||
|
ws.write(sb.data(), ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(ec && ec != wsproto::error::closed)
|
||||||
|
{
|
||||||
|
fail(ec, "read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
86
beast/wsproto/ssl.h
Normal file
86
beast/wsproto/ssl.h
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_SSL_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_SSL_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/teardown.h>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/asio/ssl/stream.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
/** Tear down a `boost::asio::ssl::stream`.
|
||||||
|
|
||||||
|
This tears down a connection. The implementation will call
|
||||||
|
the overload of this function based on the `Stream` parameter
|
||||||
|
used to consruct the socket. When `Stream` is a user defined
|
||||||
|
type, and not a `boost::asio::ip::tcp::socket` or any
|
||||||
|
`boost::asio::ssl::stream`, callers are responsible for
|
||||||
|
providing a suitable overload of this function.
|
||||||
|
|
||||||
|
@param socket The stream to tear down.
|
||||||
|
|
||||||
|
@param ec Set to the error if any occurred.
|
||||||
|
*/
|
||||||
|
template<class AsyncStream>
|
||||||
|
void
|
||||||
|
teardown(
|
||||||
|
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Start tearing down a `boost::asio::ssl::stream`.
|
||||||
|
|
||||||
|
This begins tearing down a connection asynchronously.
|
||||||
|
The implementation will call the overload of this function
|
||||||
|
based on the `Stream` parameter used to consruct the socket.
|
||||||
|
When `Stream` is a user defined type, and not a
|
||||||
|
`boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
|
||||||
|
callers are responsible for providing a suitable overload
|
||||||
|
of this function.
|
||||||
|
|
||||||
|
@param stream The stream to tear down.
|
||||||
|
|
||||||
|
@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<class AsyncStream, class TeardownHandler>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
async_teardown(
|
||||||
|
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||||
|
TeardownHandler&& handler);
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/wsproto/impl/ssl.ipp>
|
||||||
|
|
||||||
|
#endif
|
||||||
337
beast/wsproto/static_string.h
Normal file
337
beast/wsproto/static_string.h
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_STATIC_STRING_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_STATIC_STRING_H_INCLUDED
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iterator>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
/** A string with a fixed-size storage area.
|
||||||
|
|
||||||
|
`static_string` objects behave like `std::string` except that
|
||||||
|
the storage is not dynamically allocated but rather fixed in
|
||||||
|
size.
|
||||||
|
|
||||||
|
These strings offer performance advantages when a protocol
|
||||||
|
imposes a natural small upper limit on the size of a value.
|
||||||
|
*/
|
||||||
|
template<std::size_t N, class CharT,
|
||||||
|
class Traits = std::char_traits<CharT>>
|
||||||
|
class static_string
|
||||||
|
{
|
||||||
|
std::size_t n_;
|
||||||
|
std::array<CharT, N+1> s_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using traits_type = Traits;
|
||||||
|
using value_type = typename Traits::char_type;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = value_type*;
|
||||||
|
using reference = value_type&;
|
||||||
|
using const_pointer = value_type const*;
|
||||||
|
using const_reference = value_type const&;
|
||||||
|
using iterator = value_type*;
|
||||||
|
using const_iterator = value_type const*;
|
||||||
|
using reverse_iterator =
|
||||||
|
std::reverse_iterator<iterator>;
|
||||||
|
using const_reverse_iterator =
|
||||||
|
std::reverse_iterator<const_iterator>;
|
||||||
|
|
||||||
|
static_string()
|
||||||
|
{
|
||||||
|
resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_string(static_string const& s)
|
||||||
|
: n_(s.n_)
|
||||||
|
{
|
||||||
|
std::copy(&s.s_[0], &s.s_[0]+s.n_+1, &s_[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_string&
|
||||||
|
operator=(static_string const& s)
|
||||||
|
{
|
||||||
|
n_ = s.n_;
|
||||||
|
std::copy(&s.s_[0], &s.s_[0]+s.n_+1, &s_[0]);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static_string(CharT const* s)
|
||||||
|
{
|
||||||
|
assign(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_string& operator=(CharT const* s)
|
||||||
|
{
|
||||||
|
assign(s);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
reference
|
||||||
|
at(size_type pos)
|
||||||
|
{
|
||||||
|
if(pos >= n_)
|
||||||
|
throw std::out_of_range("static_string::at");
|
||||||
|
return s_[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reference
|
||||||
|
at(size_type pos) const
|
||||||
|
{
|
||||||
|
if(pos >= n_)
|
||||||
|
throw std::out_of_range("static_string::at");
|
||||||
|
return s_[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
reference
|
||||||
|
operator[](size_type pos);
|
||||||
|
|
||||||
|
const_reference
|
||||||
|
operator[](size_type pos) const;
|
||||||
|
|
||||||
|
CharT&
|
||||||
|
front()
|
||||||
|
{
|
||||||
|
return s_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
CharT const&
|
||||||
|
front() const
|
||||||
|
{
|
||||||
|
return s_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
CharT&
|
||||||
|
back()
|
||||||
|
{
|
||||||
|
return s_[n_-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
CharT const&
|
||||||
|
back() const
|
||||||
|
{
|
||||||
|
return s_[n_-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
CharT*
|
||||||
|
data()
|
||||||
|
{
|
||||||
|
return &s_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
CharT const*
|
||||||
|
data() const
|
||||||
|
{
|
||||||
|
return &s_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
CharT const*
|
||||||
|
c_str() const
|
||||||
|
{
|
||||||
|
s_[n_] = 0;
|
||||||
|
return &s_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator
|
||||||
|
begin()
|
||||||
|
{
|
||||||
|
return &s_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator
|
||||||
|
begin() const
|
||||||
|
{
|
||||||
|
return &s_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator
|
||||||
|
cbegin() const
|
||||||
|
{
|
||||||
|
return &s_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator
|
||||||
|
end()
|
||||||
|
{
|
||||||
|
return &s_[n_];
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator
|
||||||
|
end() const
|
||||||
|
{
|
||||||
|
return &s_[n_];
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator
|
||||||
|
cend() const
|
||||||
|
{
|
||||||
|
return &s_[n_];
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_iterator
|
||||||
|
rbegin()
|
||||||
|
{
|
||||||
|
return reverse_iterator{end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reverse_iterator
|
||||||
|
rbegin() const
|
||||||
|
{
|
||||||
|
return reverse_iterator{end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reverse_iterator
|
||||||
|
crbegin() const
|
||||||
|
{
|
||||||
|
return reverse_iterator{cend()};
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_iterator
|
||||||
|
rend()
|
||||||
|
{
|
||||||
|
return reverse_iterator{begin()};
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reverse_iterator
|
||||||
|
rend() const
|
||||||
|
{
|
||||||
|
return reverse_iterator{begin()};
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reverse_iterator
|
||||||
|
crend() const
|
||||||
|
{
|
||||||
|
return reverse_iterator{cbegin()};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
empty() const
|
||||||
|
{
|
||||||
|
return n_ == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type
|
||||||
|
size() const
|
||||||
|
{
|
||||||
|
return n_;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type constexpr
|
||||||
|
max_size() const
|
||||||
|
{
|
||||||
|
return N;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type
|
||||||
|
capacity() const
|
||||||
|
{
|
||||||
|
return N;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
shrink_to_fit()
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clear()
|
||||||
|
{
|
||||||
|
resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does not perform value-initialization
|
||||||
|
void
|
||||||
|
resize(std::size_t n);
|
||||||
|
|
||||||
|
std::basic_string<CharT, Traits>
|
||||||
|
to_string() const
|
||||||
|
{
|
||||||
|
return std::basic_string<
|
||||||
|
CharT, Traits>{&s_[0], n_};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
assign(CharT const* s);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<std::size_t N, class CharT, class Traits>
|
||||||
|
auto
|
||||||
|
static_string<N, CharT, Traits>::
|
||||||
|
operator[](size_type pos) ->
|
||||||
|
reference
|
||||||
|
{
|
||||||
|
static CharT null{0};
|
||||||
|
if(pos == n_)
|
||||||
|
return null;
|
||||||
|
return s_[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t N, class CharT, class Traits>
|
||||||
|
auto
|
||||||
|
static_string<N, CharT, Traits>::
|
||||||
|
operator[](size_type pos) const ->
|
||||||
|
const_reference
|
||||||
|
{
|
||||||
|
static CharT constexpr null{0};
|
||||||
|
if(pos == n_)
|
||||||
|
return null;
|
||||||
|
return s_[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t N, class CharT, class Traits>
|
||||||
|
void
|
||||||
|
static_string<N, CharT, Traits>::
|
||||||
|
resize(std::size_t n)
|
||||||
|
{
|
||||||
|
if(n > N)
|
||||||
|
throw std::length_error(
|
||||||
|
"static_string overflow");
|
||||||
|
n_ = n;
|
||||||
|
s_[n_] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t N, class CharT, class Traits>
|
||||||
|
void
|
||||||
|
static_string<N, CharT, Traits>::
|
||||||
|
assign(CharT const* s)
|
||||||
|
{
|
||||||
|
size_type n = 0;
|
||||||
|
for(auto p = s; *p; ++p)
|
||||||
|
++n;
|
||||||
|
if(n > N)
|
||||||
|
throw std::out_of_range(
|
||||||
|
"too large");
|
||||||
|
std::copy(s, s+n, s_.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
161
beast/wsproto/teardown.h
Normal file
161
beast/wsproto/teardown.h
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_TEARDOWN_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_TEARDOWN_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/wsproto/error.h>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
/** Tear down a connection.
|
||||||
|
|
||||||
|
This tears down a connection. The implementation will call
|
||||||
|
the overload of this function based on the `Stream` parameter
|
||||||
|
used to consruct the socket. When `Stream` is a user defined
|
||||||
|
type, and not a `boost::asio::ip::tcp::socket` or any
|
||||||
|
`boost::asio::ssl::stream`, callers are responsible for
|
||||||
|
providing a suitable overload of this function.
|
||||||
|
|
||||||
|
@param socket The socket to tear down.
|
||||||
|
|
||||||
|
@param ec Set to the error if any occurred.
|
||||||
|
*/
|
||||||
|
template<class Socket>
|
||||||
|
void
|
||||||
|
teardown(Socket& socket, error_code& ec) = delete;
|
||||||
|
|
||||||
|
/** Start tearing down a connection.
|
||||||
|
|
||||||
|
This begins tearing down a connection asynchronously.
|
||||||
|
The implementation will call the overload of this function
|
||||||
|
based on the `Stream` parameter used to consruct the socket.
|
||||||
|
When `Stream` is a user defined type, and not a
|
||||||
|
`boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
|
||||||
|
callers are responsible for providing a suitable overload
|
||||||
|
of this function.
|
||||||
|
|
||||||
|
@param socket The socket to tear down.
|
||||||
|
|
||||||
|
@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<class AsyncSocket, class TeardownHandler>
|
||||||
|
void
|
||||||
|
async_teardown(AsyncSocket& socket, TeardownHandler&& handler) = delete;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Tear down a `boost::asio::ip::tcp::socket`.
|
||||||
|
|
||||||
|
This tears down a connection. The implementation will call
|
||||||
|
the overload of this function based on the `Stream` parameter
|
||||||
|
used to consruct the socket. When `Stream` is a user defined
|
||||||
|
type, and not a `boost::asio::ip::tcp::socket` or any
|
||||||
|
`boost::asio::ssl::stream`, callers are responsible for
|
||||||
|
providing a suitable overload of this function.
|
||||||
|
|
||||||
|
@param socket The socket to tear down.
|
||||||
|
|
||||||
|
@param ec Set to the error if any occurred.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
teardown(
|
||||||
|
boost::asio::ip::tcp::socket& socket,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Start tearing down a `boost::asio::ip::tcp::socket`.
|
||||||
|
|
||||||
|
This begins tearing down a connection asynchronously.
|
||||||
|
The implementation will call the overload of this function
|
||||||
|
based on the `Stream` parameter used to consruct the socket.
|
||||||
|
When `Stream` is a user defined type, and not a
|
||||||
|
`boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
|
||||||
|
callers are responsible for providing a suitable overload
|
||||||
|
of this function.
|
||||||
|
|
||||||
|
@param socket The socket to tear down.
|
||||||
|
|
||||||
|
@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<class TeardownHandler>
|
||||||
|
void
|
||||||
|
async_teardown(
|
||||||
|
boost::asio::ip::tcp::socket& socket,
|
||||||
|
TeardownHandler&& handler);
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace wsproto_helpers {
|
||||||
|
|
||||||
|
// Calls to teardown and async_teardown must be made from
|
||||||
|
// a namespace that does not contain any overloads of these
|
||||||
|
// functions. The wsproto_helpers namespace is defined here
|
||||||
|
// for that purpose.
|
||||||
|
|
||||||
|
template<class Socket>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
call_teardown(Socket& socket, wsproto::error_code& ec)
|
||||||
|
{
|
||||||
|
using wsproto::teardown;
|
||||||
|
teardown(socket, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Socket, class TeardownHandler>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
call_async_teardown(Socket& socket, TeardownHandler&& handler)
|
||||||
|
{
|
||||||
|
using wsproto::async_teardown;
|
||||||
|
async_teardown(socket,
|
||||||
|
std::forward<TeardownHandler>(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // wsproto_helpers
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/wsproto/impl/teardown.ipp>
|
||||||
|
|
||||||
|
#endif
|
||||||
4
doc/.gitignore
vendored
Normal file
4
doc/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
html
|
||||||
|
temp
|
||||||
|
reference.qbk
|
||||||
|
out.txt
|
||||||
83
doc/Jamfile
Normal file
83
doc/Jamfile
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#
|
||||||
|
# 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 ;
|
||||||
|
|
||||||
|
local broot = [ os.environ BOOST_ROOT ] ;
|
||||||
|
|
||||||
|
project beast/doc ;
|
||||||
|
|
||||||
|
using boostbook ;
|
||||||
|
using quickbook ;
|
||||||
|
using doxygen ;
|
||||||
|
|
||||||
|
xml beast_boostbook : beast.qbk ;
|
||||||
|
|
||||||
|
path-constant out : . ;
|
||||||
|
|
||||||
|
install stylesheets
|
||||||
|
:
|
||||||
|
$(broot)/doc/src/boostbook.css
|
||||||
|
:
|
||||||
|
<location>$(out)/html
|
||||||
|
;
|
||||||
|
|
||||||
|
explicit stylesheets ;
|
||||||
|
|
||||||
|
install images
|
||||||
|
:
|
||||||
|
[ glob $(broot)/doc/src/images/*.png ]
|
||||||
|
images/beast.png
|
||||||
|
images/body.png
|
||||||
|
images/message.png
|
||||||
|
:
|
||||||
|
<location>$(out)/html/images
|
||||||
|
;
|
||||||
|
|
||||||
|
explicit images ;
|
||||||
|
|
||||||
|
install callouts
|
||||||
|
:
|
||||||
|
[ glob $(broot)/doc/src/images/callouts/*.png ]
|
||||||
|
:
|
||||||
|
<location>$(out)/html/images/callouts
|
||||||
|
;
|
||||||
|
|
||||||
|
explicit callout ;
|
||||||
|
|
||||||
|
boostbook doc
|
||||||
|
:
|
||||||
|
beast_boostbook
|
||||||
|
:
|
||||||
|
<xsl:param>chapter.autolabel=0
|
||||||
|
<xsl:param>boost.image.src=images/beast.png
|
||||||
|
<xsl:param>boost.image.alt="Beast Logo"
|
||||||
|
<xsl:param>boost.image.w=1007
|
||||||
|
<xsl:param>boost.image.h=107
|
||||||
|
<xsl:param>boost.root=$(broot)
|
||||||
|
<xsl:param>chapter.autolabel=0
|
||||||
|
<xsl:param>chunk.first.sections=1 # Chunk the first top-level section?
|
||||||
|
<xsl:param>chunk.section.depth=8 # Depth to which sections should be chunked
|
||||||
|
<xsl:param>generate.section.toc.level=1 # Control depth of TOC generation in sections
|
||||||
|
<xsl:param>toc.max.depth=2 # How many levels should be created for each TOC?
|
||||||
|
<xsl:param>toc.section.depth=2 # How deep should recursive sections appear in the TOC?
|
||||||
|
:
|
||||||
|
<location>temp
|
||||||
|
<dependency>stylesheets
|
||||||
|
<dependency>images
|
||||||
|
;
|
||||||
|
|
||||||
|
#explicit doc ;
|
||||||
|
# <xsl:param>nav.layout=none
|
||||||
|
# <format>html:<xsl:param>location=../bin/doc/html
|
||||||
|
# <xsl:param>generate.toc="chapter nop section nop"
|
||||||
|
# <xsl:param>root.filename=index
|
||||||
|
# <xsl:param>output-root="../bin/html"
|
||||||
|
|
||||||
|
|
||||||
|
#[include reference.qbk]
|
||||||
|
#[xinclude index.xml]
|
||||||
363
doc/beast.dox
Normal file
363
doc/beast.dox
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Project related configuration options
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
DOXYFILE_ENCODING = UTF-8
|
||||||
|
PROJECT_NAME = "Beast"
|
||||||
|
PROJECT_NUMBER =
|
||||||
|
PROJECT_BRIEF = C++ Library
|
||||||
|
PROJECT_LOGO = images/beast.png
|
||||||
|
OUTPUT_DIRECTORY =
|
||||||
|
CREATE_SUBDIRS = NO
|
||||||
|
ALLOW_UNICODE_NAMES = NO
|
||||||
|
OUTPUT_LANGUAGE = English
|
||||||
|
BRIEF_MEMBER_DESC = YES
|
||||||
|
REPEAT_BRIEF = YES
|
||||||
|
ABBREVIATE_BRIEF =
|
||||||
|
ALWAYS_DETAILED_SEC = NO
|
||||||
|
INLINE_INHERITED_MEMB = YES
|
||||||
|
FULL_PATH_NAMES = NO
|
||||||
|
STRIP_FROM_PATH =
|
||||||
|
STRIP_FROM_INC_PATH =
|
||||||
|
SHORT_NAMES = NO
|
||||||
|
JAVADOC_AUTOBRIEF = YES
|
||||||
|
QT_AUTOBRIEF = NO
|
||||||
|
MULTILINE_CPP_IS_BRIEF = NO
|
||||||
|
INHERIT_DOCS = YES
|
||||||
|
SEPARATE_MEMBER_PAGES = NO
|
||||||
|
TAB_SIZE = 4
|
||||||
|
ALIASES =
|
||||||
|
TCL_SUBST =
|
||||||
|
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||||
|
OPTIMIZE_OUTPUT_JAVA = NO
|
||||||
|
OPTIMIZE_FOR_FORTRAN = NO
|
||||||
|
OPTIMIZE_OUTPUT_VHDL = NO
|
||||||
|
EXTENSION_MAPPING =
|
||||||
|
MARKDOWN_SUPPORT = YES
|
||||||
|
AUTOLINK_SUPPORT = YES
|
||||||
|
BUILTIN_STL_SUPPORT = NO
|
||||||
|
CPP_CLI_SUPPORT = NO
|
||||||
|
SIP_SUPPORT = NO
|
||||||
|
IDL_PROPERTY_SUPPORT = YES
|
||||||
|
DISTRIBUTE_GROUP_DOC = NO
|
||||||
|
GROUP_NESTED_COMPOUNDS = NO
|
||||||
|
SUBGROUPING = YES
|
||||||
|
INLINE_GROUPED_CLASSES = NO
|
||||||
|
INLINE_SIMPLE_STRUCTS = NO
|
||||||
|
TYPEDEF_HIDES_STRUCT = NO
|
||||||
|
LOOKUP_CACHE_SIZE = 0
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Build related configuration options
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
EXTRACT_ALL = YES
|
||||||
|
EXTRACT_PRIVATE = YES
|
||||||
|
EXTRACT_PACKAGE = YES
|
||||||
|
EXTRACT_STATIC = YES
|
||||||
|
EXTRACT_LOCAL_CLASSES = NO
|
||||||
|
EXTRACT_LOCAL_METHODS = NO
|
||||||
|
EXTRACT_ANON_NSPACES = NO
|
||||||
|
HIDE_UNDOC_MEMBERS = NO
|
||||||
|
HIDE_UNDOC_CLASSES = YES
|
||||||
|
HIDE_FRIEND_COMPOUNDS = NO
|
||||||
|
HIDE_IN_BODY_DOCS = NO
|
||||||
|
INTERNAL_DOCS = NO
|
||||||
|
CASE_SENSE_NAMES = YES
|
||||||
|
HIDE_SCOPE_NAMES = NO
|
||||||
|
HIDE_COMPOUND_REFERENCE= NO
|
||||||
|
SHOW_INCLUDE_FILES = NO
|
||||||
|
SHOW_GROUPED_MEMB_INC = NO
|
||||||
|
FORCE_LOCAL_INCLUDES = NO
|
||||||
|
INLINE_INFO = NO
|
||||||
|
SORT_MEMBER_DOCS = NO
|
||||||
|
SORT_BRIEF_DOCS = NO
|
||||||
|
SORT_MEMBERS_CTORS_1ST = YES
|
||||||
|
SORT_GROUP_NAMES = NO
|
||||||
|
SORT_BY_SCOPE_NAME = NO
|
||||||
|
STRICT_PROTO_MATCHING = NO
|
||||||
|
GENERATE_TODOLIST = NO
|
||||||
|
GENERATE_TESTLIST = NO
|
||||||
|
GENERATE_BUGLIST = NO
|
||||||
|
GENERATE_DEPRECATEDLIST= NO
|
||||||
|
ENABLED_SECTIONS =
|
||||||
|
MAX_INITIALIZER_LINES = 30
|
||||||
|
SHOW_USED_FILES = NO
|
||||||
|
SHOW_FILES = NO
|
||||||
|
SHOW_NAMESPACES = NO
|
||||||
|
FILE_VERSION_FILTER =
|
||||||
|
LAYOUT_FILE =
|
||||||
|
CITE_BIB_FILES =
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to warning and progress messages
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
QUIET = NO
|
||||||
|
WARNINGS = YES
|
||||||
|
WARN_IF_UNDOCUMENTED = YES
|
||||||
|
WARN_IF_DOC_ERROR = YES
|
||||||
|
WARN_NO_PARAMDOC = NO
|
||||||
|
WARN_AS_ERROR = NO
|
||||||
|
WARN_FORMAT = "$file:$line: $text"
|
||||||
|
WARN_LOGFILE =
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the input files
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
INPUT = \
|
||||||
|
../beast/asio/append_buffers.h \
|
||||||
|
../beast/asio/async_completion.h \
|
||||||
|
../beast/asio/basic_streambuf.h \
|
||||||
|
../beast/asio/bind_handler.h \
|
||||||
|
../beast/asio/buffers_adapter.h \
|
||||||
|
../beast/asio/consuming_buffers.h \
|
||||||
|
../beast/asio/handler_alloc.h \
|
||||||
|
../beast/asio/prepare_buffers.h \
|
||||||
|
../beast/asio/static_streambuf.h \
|
||||||
|
../beast/asio/streambuf.h \
|
||||||
|
../beast/asio/streambuf_readstream.h \
|
||||||
|
../beast/asio/type_check.h \
|
||||||
|
../beast/http/basic_headers.h \
|
||||||
|
../beast/http/basic_parser.h \
|
||||||
|
../beast/http/chunk_encode.h \
|
||||||
|
../beast/http/empty_body.h \
|
||||||
|
../beast/http/error.h \
|
||||||
|
../beast/http/message.h \
|
||||||
|
../beast/http/parser.h \
|
||||||
|
../beast/http/read.h \
|
||||||
|
../beast/http/resume_context.h \
|
||||||
|
../beast/http/stream.h \
|
||||||
|
../beast/http/streambuf_body.h \
|
||||||
|
../beast/http/string_body.h \
|
||||||
|
../beast/http/type_check.h \
|
||||||
|
../beast/http/write.h \
|
||||||
|
../beast/wsproto/error.h \
|
||||||
|
../beast/wsproto/option.h \
|
||||||
|
../beast/wsproto/rfc6455.h \
|
||||||
|
../beast/wsproto/socket.h \
|
||||||
|
../beast/wsproto/static_string.h \
|
||||||
|
../beast/wsproto/teardown.h \
|
||||||
|
|
||||||
|
INPUT_ENCODING = UTF-8
|
||||||
|
FILE_PATTERNS =
|
||||||
|
RECURSIVE = NO
|
||||||
|
EXCLUDE = ../beast/http/URL.h
|
||||||
|
EXCLUDE_SYMLINKS = NO
|
||||||
|
EXCLUDE_PATTERNS =
|
||||||
|
EXCLUDE_SYMBOLS =
|
||||||
|
EXAMPLE_PATH =
|
||||||
|
EXAMPLE_PATTERNS =
|
||||||
|
EXAMPLE_RECURSIVE = NO
|
||||||
|
IMAGE_PATH =
|
||||||
|
INPUT_FILTER =
|
||||||
|
FILTER_PATTERNS =
|
||||||
|
FILTER_SOURCE_FILES = NO
|
||||||
|
FILTER_SOURCE_PATTERNS =
|
||||||
|
USE_MDFILE_AS_MAINPAGE =
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to source browsing
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
SOURCE_BROWSER = NO
|
||||||
|
INLINE_SOURCES = NO
|
||||||
|
STRIP_CODE_COMMENTS = YES
|
||||||
|
REFERENCED_BY_RELATION = NO
|
||||||
|
REFERENCES_RELATION = NO
|
||||||
|
REFERENCES_LINK_SOURCE = YES
|
||||||
|
SOURCE_TOOLTIPS = YES
|
||||||
|
USE_HTAGS = NO
|
||||||
|
VERBATIM_HEADERS = YES
|
||||||
|
CLANG_ASSISTED_PARSING = NO
|
||||||
|
CLANG_OPTIONS =
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the alphabetical class index
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
ALPHABETICAL_INDEX = YES
|
||||||
|
COLS_IN_ALPHA_INDEX = 5
|
||||||
|
IGNORE_PREFIX =
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the HTML output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
GENERATE_HTML = NO
|
||||||
|
HTML_OUTPUT = dhtm
|
||||||
|
HTML_FILE_EXTENSION = .html
|
||||||
|
HTML_HEADER =
|
||||||
|
HTML_FOOTER =
|
||||||
|
HTML_STYLESHEET =
|
||||||
|
HTML_EXTRA_STYLESHEET =
|
||||||
|
HTML_EXTRA_FILES =
|
||||||
|
HTML_COLORSTYLE_HUE = 220
|
||||||
|
HTML_COLORSTYLE_SAT = 100
|
||||||
|
HTML_COLORSTYLE_GAMMA = 80
|
||||||
|
HTML_TIMESTAMP = NO
|
||||||
|
HTML_DYNAMIC_SECTIONS = NO
|
||||||
|
HTML_INDEX_NUM_ENTRIES = 100
|
||||||
|
GENERATE_DOCSET = NO
|
||||||
|
DOCSET_FEEDNAME = "Doxygen generated docs"
|
||||||
|
DOCSET_BUNDLE_ID = org.doxygen.Project
|
||||||
|
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
|
||||||
|
DOCSET_PUBLISHER_NAME = Publisher
|
||||||
|
GENERATE_HTMLHELP = NO
|
||||||
|
CHM_FILE =
|
||||||
|
HHC_LOCATION =
|
||||||
|
GENERATE_CHI = NO
|
||||||
|
CHM_INDEX_ENCODING =
|
||||||
|
BINARY_TOC = NO
|
||||||
|
TOC_EXPAND = NO
|
||||||
|
GENERATE_QHP = NO
|
||||||
|
QCH_FILE =
|
||||||
|
QHP_NAMESPACE = org.doxygen.Project
|
||||||
|
QHP_VIRTUAL_FOLDER = doc
|
||||||
|
QHP_CUST_FILTER_NAME =
|
||||||
|
QHP_CUST_FILTER_ATTRS =
|
||||||
|
QHP_SECT_FILTER_ATTRS =
|
||||||
|
QHG_LOCATION =
|
||||||
|
GENERATE_ECLIPSEHELP = NO
|
||||||
|
ECLIPSE_DOC_ID = org.doxygen.Project
|
||||||
|
DISABLE_INDEX = NO
|
||||||
|
GENERATE_TREEVIEW = NO
|
||||||
|
ENUM_VALUES_PER_LINE = 4
|
||||||
|
TREEVIEW_WIDTH = 250
|
||||||
|
EXT_LINKS_IN_WINDOW = NO
|
||||||
|
FORMULA_FONTSIZE = 10
|
||||||
|
FORMULA_TRANSPARENT = YES
|
||||||
|
USE_MATHJAX = NO
|
||||||
|
MATHJAX_FORMAT = HTML-CSS
|
||||||
|
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||||
|
MATHJAX_EXTENSIONS =
|
||||||
|
MATHJAX_CODEFILE =
|
||||||
|
SEARCHENGINE = YES
|
||||||
|
SERVER_BASED_SEARCH = NO
|
||||||
|
EXTERNAL_SEARCH = NO
|
||||||
|
SEARCHENGINE_URL =
|
||||||
|
SEARCHDATA_FILE = searchdata.xml
|
||||||
|
EXTERNAL_SEARCH_ID =
|
||||||
|
EXTRA_SEARCH_MAPPINGS =
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the LaTeX output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
GENERATE_LATEX = NO
|
||||||
|
LATEX_OUTPUT = latex
|
||||||
|
LATEX_CMD_NAME = latex
|
||||||
|
MAKEINDEX_CMD_NAME = makeindex
|
||||||
|
COMPACT_LATEX = NO
|
||||||
|
PAPER_TYPE = a4
|
||||||
|
EXTRA_PACKAGES =
|
||||||
|
LATEX_HEADER =
|
||||||
|
LATEX_FOOTER =
|
||||||
|
LATEX_EXTRA_STYLESHEET =
|
||||||
|
LATEX_EXTRA_FILES =
|
||||||
|
PDF_HYPERLINKS = YES
|
||||||
|
USE_PDFLATEX = YES
|
||||||
|
LATEX_BATCHMODE = NO
|
||||||
|
LATEX_HIDE_INDICES = NO
|
||||||
|
LATEX_SOURCE_CODE = NO
|
||||||
|
LATEX_BIB_STYLE = plain
|
||||||
|
LATEX_TIMESTAMP = NO
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the RTF output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
GENERATE_RTF = NO
|
||||||
|
RTF_OUTPUT = rtf
|
||||||
|
COMPACT_RTF = NO
|
||||||
|
RTF_HYPERLINKS = NO
|
||||||
|
RTF_STYLESHEET_FILE =
|
||||||
|
RTF_EXTENSIONS_FILE =
|
||||||
|
RTF_SOURCE_CODE = NO
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the man page output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
GENERATE_MAN = NO
|
||||||
|
MAN_OUTPUT = man
|
||||||
|
MAN_EXTENSION = .3
|
||||||
|
MAN_SUBDIR =
|
||||||
|
MAN_LINKS = NO
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the XML output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
GENERATE_XML = YES
|
||||||
|
XML_OUTPUT = ../bin/doc/xml
|
||||||
|
XML_PROGRAMLISTING = YES
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the DOCBOOK output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
GENERATE_DOCBOOK = NO
|
||||||
|
DOCBOOK_OUTPUT = docbook
|
||||||
|
DOCBOOK_PROGRAMLISTING = NO
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options for the AutoGen Definitions output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
GENERATE_AUTOGEN_DEF = NO
|
||||||
|
GENERATE_PERLMOD = NO
|
||||||
|
PERLMOD_LATEX = NO
|
||||||
|
PERLMOD_PRETTY = YES
|
||||||
|
PERLMOD_MAKEVAR_PREFIX =
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the preprocessor
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
ENABLE_PREPROCESSING = YES
|
||||||
|
MACRO_EXPANSION = YES
|
||||||
|
EXPAND_ONLY_PREDEF = YES
|
||||||
|
SEARCH_INCLUDES = YES
|
||||||
|
INCLUDE_PATH = ../
|
||||||
|
INCLUDE_FILE_PATTERNS =
|
||||||
|
PREDEFINED = DOXYGEN \
|
||||||
|
GENERATING_DOCS
|
||||||
|
EXPAND_AS_DEFINED =
|
||||||
|
SKIP_FUNCTION_MACROS = YES
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to external references
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
TAGFILES =
|
||||||
|
GENERATE_TAGFILE =
|
||||||
|
ALLEXTERNALS = NO
|
||||||
|
EXTERNAL_GROUPS = YES
|
||||||
|
EXTERNAL_PAGES = YES
|
||||||
|
PERL_PATH = /usr/bin/perl
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to the dot tool
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
CLASS_DIAGRAMS = NO
|
||||||
|
MSCGEN_PATH =
|
||||||
|
DIA_PATH =
|
||||||
|
HIDE_UNDOC_RELATIONS = YES
|
||||||
|
HAVE_DOT = NO
|
||||||
|
DOT_NUM_THREADS = 0
|
||||||
|
DOT_FONTNAME = Helvetica
|
||||||
|
DOT_FONTSIZE = 10
|
||||||
|
DOT_FONTPATH =
|
||||||
|
CLASS_GRAPH = YES
|
||||||
|
COLLABORATION_GRAPH = YES
|
||||||
|
GROUP_GRAPHS = YES
|
||||||
|
UML_LOOK = NO
|
||||||
|
UML_LIMIT_NUM_FIELDS = 10
|
||||||
|
TEMPLATE_RELATIONS = NO
|
||||||
|
INCLUDE_GRAPH = YES
|
||||||
|
INCLUDED_BY_GRAPH = YES
|
||||||
|
CALL_GRAPH = NO
|
||||||
|
CALLER_GRAPH = NO
|
||||||
|
GRAPHICAL_HIERARCHY = YES
|
||||||
|
DIRECTORY_GRAPH = YES
|
||||||
|
DOT_IMAGE_FORMAT = png
|
||||||
|
INTERACTIVE_SVG = NO
|
||||||
|
DOT_PATH =
|
||||||
|
DOTFILE_DIRS =
|
||||||
|
MSCFILE_DIRS =
|
||||||
|
DIAFILE_DIRS =
|
||||||
|
PLANTUML_JAR_PATH =
|
||||||
|
PLANTUML_INCLUDE_PATH =
|
||||||
|
DOT_GRAPH_MAX_NODES = 50
|
||||||
|
MAX_DOT_GRAPH_DEPTH = 0
|
||||||
|
DOT_TRANSPARENT = NO
|
||||||
|
DOT_MULTI_TARGETS = NO
|
||||||
|
GENERATE_LEGEND = YES
|
||||||
|
DOT_CLEANUP = YES
|
||||||
177
doc/beast.qbk
Normal file
177
doc/beast.qbk
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
[/
|
||||||
|
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)
|
||||||
|
]
|
||||||
|
|
||||||
|
[library Beast
|
||||||
|
[quickbook 1.6]
|
||||||
|
[copyright 2013 - 2016 Vinnie Falco]
|
||||||
|
[purpose C++ Library]
|
||||||
|
[license
|
||||||
|
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])
|
||||||
|
]
|
||||||
|
[authors [Falco, Vinnie]]
|
||||||
|
[category template]
|
||||||
|
[category generic]
|
||||||
|
]
|
||||||
|
|
||||||
|
[template mdash[] '''— ''']
|
||||||
|
[template indexterm1[term1] '''<indexterm><primary>'''[term1]'''</primary></indexterm>''']
|
||||||
|
[template indexterm2[term1 term2] '''<indexterm><primary>'''[term1]'''</primary><secondary>'''[term2]'''</secondary></indexterm>''']
|
||||||
|
[template ticket[number]'''<ulink url="https://svn.boost.org/trac/boost/ticket/'''[number]'''">'''#[number]'''</ulink>''']
|
||||||
|
[def __POSIX__ /POSIX/]
|
||||||
|
[def __Windows__ /Windows/]
|
||||||
|
[def __accept__ [@http://www.opengroup.org/onlinepubs/000095399/functions/accept.html `accept()`]]
|
||||||
|
[def __connect__ [@http://www.opengroup.org/onlinepubs/000095399/functions/connect.html `connect()`]]
|
||||||
|
[def __getpeername__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getpeername.html `getpeername()`]]
|
||||||
|
[def __getsockname__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getsockname.html `getsockname()`]]
|
||||||
|
[def __getsockopt__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getsockopt.html `getsockopt()`]]
|
||||||
|
[def __ioctl__ [@http://www.opengroup.org/onlinepubs/000095399/functions/ioctl.html `ioctl()`]]
|
||||||
|
[def __recvfrom__ [@http://www.opengroup.org/onlinepubs/000095399/functions/recvfrom.html `recvfrom()`]]
|
||||||
|
[def __sendto__ [@http://www.opengroup.org/onlinepubs/000095399/functions/sendto.html `sendto()`]]
|
||||||
|
[def __setsockopt__ [@http://www.opengroup.org/onlinepubs/000095399/functions/setsockopt.html `setsockopt()`]]
|
||||||
|
[def __socket__ [@http://www.opengroup.org/onlinepubs/000095399/functions/socket.html `socket()`]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:intro Introduction]
|
||||||
|
|
||||||
|
Beast is a cross-platform C++ library built on Boost, containing two modules
|
||||||
|
implementing widely used network protocols. Beast.HTTP offers a universal
|
||||||
|
model for describing, sending, and receiving HTTP messages while Beast.WSProto
|
||||||
|
provides a complete implementation of the WebSocket protocol. Their design
|
||||||
|
achieves these goals:
|
||||||
|
|
||||||
|
* [*Symmetry.] Interfaces are role-agnostic; the same interfaces can be
|
||||||
|
used to build clients, servers, or both.
|
||||||
|
|
||||||
|
* [*Ease of Use.] HTTP messages are modeled using simple, readily
|
||||||
|
accessible objects. Functions and classes used to send and receive HTTP
|
||||||
|
or WebSocket messages are designed to resemble Boost.Asio as closely as
|
||||||
|
possible. Users familiar with Boost.Asio will be immediately comfortable
|
||||||
|
using this library.
|
||||||
|
|
||||||
|
* [*Flexibility.] Interfaces do not mandate specific implementation
|
||||||
|
strategies; important decisions such as buffer or thread management are
|
||||||
|
left to users of the library.
|
||||||
|
|
||||||
|
* [*Performance.] The implementation performs competitively, making it a
|
||||||
|
realistic choice for building a high performance network server.
|
||||||
|
|
||||||
|
* [*Scalability.] Development of network applications that scale to thousands
|
||||||
|
of concurrent connections is possible with the implementation.
|
||||||
|
|
||||||
|
* [*Basis for further abstraction.] The interfaces facilitate the
|
||||||
|
development of other libraries that provide higher levels of abstraction.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:requirements Requirements]
|
||||||
|
|
||||||
|
Beast requires:
|
||||||
|
|
||||||
|
* [*C++11.] A minimum of C++11 is needed.
|
||||||
|
* [*Boost.] Beast is built on Boost, especially Boost.Asio.
|
||||||
|
* [*OpenSSL.] If using TLS/Secure sockets (optional).
|
||||||
|
|
||||||
|
[note Tested compilers: msvc-14+, gcc 5+, clang 3.6+]
|
||||||
|
|
||||||
|
Most of the library is header-only; however, the HTTP parser used is written
|
||||||
|
in C. To link an application that uses Beast, it is necessary to add a single
|
||||||
|
.cpp file from beast into your project's build script.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:example Examples]
|
||||||
|
|
||||||
|
These usage examples are intended to quickly impress upon readers the
|
||||||
|
flavor of the library.
|
||||||
|
|
||||||
|
[note
|
||||||
|
All examples and identifiers mentioned in this document are written as
|
||||||
|
if the following statements are in effect:
|
||||||
|
```
|
||||||
|
#include <beast/http.h>
|
||||||
|
#include <beast/wsproto.h>
|
||||||
|
using namespace beast;
|
||||||
|
using namespace beast::http;
|
||||||
|
using namespace boost::asio;
|
||||||
|
using namespace boost::asio::ip;
|
||||||
|
```
|
||||||
|
]
|
||||||
|
|
||||||
|
Use HTTP to request the root page from a website and receive the response:
|
||||||
|
```
|
||||||
|
std::string const host = "boost.org";
|
||||||
|
io_service ios;
|
||||||
|
tcp::resolver r(ios);
|
||||||
|
tcp::socket sock(ios);
|
||||||
|
connect(sock, r.resolve(tcp::resolver::query{host, "http"}));
|
||||||
|
|
||||||
|
request<empty_body> req(method_t::http_get, "/", 11);
|
||||||
|
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
|
||||||
|
req.headers.replace("User-Agent", "Beast");
|
||||||
|
write(sock, prepare(req, connection(close)));
|
||||||
|
|
||||||
|
parsed_response<streambuf_body> resp;
|
||||||
|
read(sock, resp);
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Establish a WebSocket connection, send a message and receive the reply:
|
||||||
|
```
|
||||||
|
std::string const host = "boost.org";
|
||||||
|
io_service ios;
|
||||||
|
tcp::resolver r(ios);
|
||||||
|
tcp::socket sock(ios);
|
||||||
|
connect(sock, r.resolve(tcp::resolver::query{host, "ws"}));
|
||||||
|
|
||||||
|
wsproto::socket<tcp::socket&> ws(sock);
|
||||||
|
ws.handshake();
|
||||||
|
ws.write(ws, buffer("Hello, world!"));
|
||||||
|
|
||||||
|
streambuf sb;
|
||||||
|
wsproto::opcode op;
|
||||||
|
ws.read(ws, op, sb);
|
||||||
|
|
||||||
|
ws.close(); // WebSocket protocol close
|
||||||
|
```
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:credits Credits]
|
||||||
|
|
||||||
|
Beast would not be possible without the considerable time and patience
|
||||||
|
contributed by David Schwartz, Edward Hennis, Howard Hinnant, Miguel Portilla,
|
||||||
|
Nikolaos Bougalis, Scott Determan, Scott Schurr, and Ripple Labs for
|
||||||
|
supporting its development. Thanks also to Christopher Kohloff, whose Asio
|
||||||
|
C++ library is the inspiration behind which much of the design and
|
||||||
|
documentation is based.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[include http.qbk]
|
||||||
|
[include wsproto.qbk]
|
||||||
|
[include types.qbk]
|
||||||
|
[include design.qbk]
|
||||||
|
[section:quickref Quick Reference]
|
||||||
|
[xinclude quickref.xml]
|
||||||
|
[endsect]
|
||||||
|
[include reference.qbk]
|
||||||
|
[section:idx Index]
|
||||||
|
[xinclude index.xml]
|
||||||
|
[endsect]
|
||||||
439
doc/boostbook.dtd
Normal file
439
doc/boostbook.dtd
Normal file
@@ -0,0 +1,439 @@
|
|||||||
|
<!--
|
||||||
|
BoostBook DTD - development version
|
||||||
|
|
||||||
|
For further information, see: http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost_Documentation_Format
|
||||||
|
|
||||||
|
Copyright (c) 2002 by Peter Simons <simons@cryp.to>
|
||||||
|
Copyright (c) 2003-2004 by Douglas Gregor <doug.gregor -at- gmail.com>
|
||||||
|
Copyright (c) 2007 by Frank Mori Hess <fmhess@users.sourceforge.net>
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
The latest stable DTD module is identified by the PUBLIC and SYSTEM identifiers:
|
||||||
|
|
||||||
|
PUBLIC "-//Boost//DTD BoostBook XML V1.1//EN"
|
||||||
|
SYSTEM "http://www.boost.org/tools/boostbook/dtd/1.1/boostbook.dtd"
|
||||||
|
|
||||||
|
$Revision$
|
||||||
|
$Date$
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--========== Define XInclude features. ==========-->
|
||||||
|
<!-- This is not really integrated into the DTD yet. Needs more
|
||||||
|
research. -->
|
||||||
|
<!--
|
||||||
|
<!ELEMENT xi:include (xi:fallback)?>
|
||||||
|
<!ATTLIST xi:include
|
||||||
|
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude"
|
||||||
|
href CDATA #REQUIRED
|
||||||
|
parse (xml|text) "xml"
|
||||||
|
encoding CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT xi:fallback ANY>
|
||||||
|
<!ATTLIST xi:fallback
|
||||||
|
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude">
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!ENTITY % local.common.attrib "last-revision CDATA #IMPLIED">
|
||||||
|
|
||||||
|
<!--========== Define the BoostBook extensions ==========-->
|
||||||
|
<!ENTITY % boost.common.attrib "%local.common.attrib;
|
||||||
|
id CDATA #IMPLIED">
|
||||||
|
|
||||||
|
<!ENTITY % boost.namespace.mix
|
||||||
|
"class|class-specialization|struct|struct-specialization|
|
||||||
|
union|union-specialization|typedef|enum|
|
||||||
|
free-function-group|function|overloaded-function|
|
||||||
|
namespace">
|
||||||
|
|
||||||
|
<!ENTITY % boost.template.mix
|
||||||
|
"template-type-parameter|template-nontype-parameter|template-varargs">
|
||||||
|
|
||||||
|
<!ENTITY % boost.class.members
|
||||||
|
"static-constant|typedef|enum|
|
||||||
|
copy-assignment|constructor|destructor|method-group|
|
||||||
|
method|overloaded-method|data-member|class|class-specialization|struct|
|
||||||
|
struct-specialization|union|union-specialization">
|
||||||
|
|
||||||
|
<!ENTITY % boost.class.mix
|
||||||
|
"%boost.class.members;|free-function-group|function|overloaded-function">
|
||||||
|
|
||||||
|
<!ENTITY % boost.class.content
|
||||||
|
"template?, inherit*, purpose?, description?,
|
||||||
|
(%boost.class.mix;|access)*">
|
||||||
|
|
||||||
|
<!ENTITY % boost.class-specialization.content
|
||||||
|
"template?, specialization?, inherit?, purpose?, description?,
|
||||||
|
(%boost.class.mix;|access)*">
|
||||||
|
|
||||||
|
<!ENTITY % boost.function.semantics
|
||||||
|
"purpose?, description?, requires?, effects?, postconditions?,
|
||||||
|
returns?, throws?, complexity?, notes?, rationale?">
|
||||||
|
|
||||||
|
<!ENTITY % library.content
|
||||||
|
"libraryinfo, (title, ((section|library-reference|testsuite))+)?">
|
||||||
|
|
||||||
|
<!ELEMENT library (%library.content;)>
|
||||||
|
<!ATTLIST library
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
dirname CDATA #REQUIRED
|
||||||
|
html-only CDATA #IMPLIED
|
||||||
|
url CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT boostbook (title, (chapter|library)*)>
|
||||||
|
<!ATTLIST boostbook %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT libraryinfo (author+, copyright*, legalnotice*, librarypurpose, librarycategory*)>
|
||||||
|
<!ATTLIST libraryinfo %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT librarypurpose (#PCDATA|code|ulink|functionname|methodname|classname|macroname|headername|enumname|globalname)*>
|
||||||
|
<!ATTLIST librarypurpose %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT librarycategory (#PCDATA)>
|
||||||
|
<!ATTLIST librarycategory
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT libraryname (#PCDATA)>
|
||||||
|
<!ATTLIST libraryname %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT library-reference ANY>
|
||||||
|
<!ATTLIST library-reference
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT librarylist EMPTY>
|
||||||
|
<!ATTLIST librarylist %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT librarycategorylist (librarycategorydef)*>
|
||||||
|
<!ATTLIST librarycategorylist %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT librarycategorydef (#PCDATA)>
|
||||||
|
<!ATTLIST librarycategorydef
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT header ANY>
|
||||||
|
<!ATTLIST header
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT namespace (%boost.namespace.mix;)*>
|
||||||
|
<!ATTLIST namespace
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT class (%boost.class.content;)>
|
||||||
|
<!ATTLIST class
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT struct (%boost.class.content;)>
|
||||||
|
<!ATTLIST struct
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT union (%boost.class.content;)>
|
||||||
|
<!ATTLIST union
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT class-specialization (%boost.class-specialization.content;)>
|
||||||
|
<!ATTLIST class-specialization
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT struct-specialization (%boost.class-specialization.content;)>
|
||||||
|
<!ATTLIST struct-specialization
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT union-specialization (%boost.class-specialization.content;)>
|
||||||
|
<!ATTLIST union-specialization
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT access (%boost.class.members;)+>
|
||||||
|
<!ATTLIST access
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!--========= C++ Templates =========-->
|
||||||
|
<!ELEMENT template (%boost.template.mix;)*>
|
||||||
|
<!ATTLIST template %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT template-type-parameter (default?, purpose?)>
|
||||||
|
<!ATTLIST template-type-parameter
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
pack CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT template-nontype-parameter (type, default?, purpose?)>
|
||||||
|
<!ATTLIST template-nontype-parameter
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
pack CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT template-varargs EMPTY>
|
||||||
|
<!ATTLIST template-varargs %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT specialization (template-arg)*>
|
||||||
|
<!ATTLIST specialization %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT template-arg ANY>
|
||||||
|
<!ATTLIST template-arg
|
||||||
|
pack CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT default ANY>
|
||||||
|
<!ATTLIST default %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT inherit (type, purpose?)>
|
||||||
|
<!ATTLIST inherit
|
||||||
|
access CDATA #IMPLIED
|
||||||
|
pack CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT purpose ANY>
|
||||||
|
<!ATTLIST purpose %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT description ANY>
|
||||||
|
<!ATTLIST description %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT type ANY>
|
||||||
|
<!ATTLIST type %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT typedef (type, purpose?, description?)>
|
||||||
|
<!ATTLIST typedef
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT enum (enumvalue*, purpose?, description?)>
|
||||||
|
<!ATTLIST enum
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT enumvalue (default?, purpose?, description?)>
|
||||||
|
<!ATTLIST enumvalue
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT static-constant (type, default, purpose?, description?)>
|
||||||
|
<!ATTLIST static-constant
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT data-member (type, purpose?, description?)>
|
||||||
|
<!ATTLIST data-member
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT paramtype ANY>
|
||||||
|
<!ATTLIST paramtype %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT effects ANY>
|
||||||
|
<!ATTLIST effects %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT postconditions ANY>
|
||||||
|
<!ATTLIST postconditions %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT method-group (method|overloaded-method)*>
|
||||||
|
<!ATTLIST method-group
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT constructor (template?, parameter*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST constructor
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT destructor (%boost.function.semantics;)>
|
||||||
|
<!ATTLIST destructor
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT method (template?, type, parameter*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST method
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
cv CDATA #IMPLIED
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT function (template?, type, parameter*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST function
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT overloaded-method (signature*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST overloaded-method
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT overloaded-function (signature*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST overloaded-function
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT signature (template?, type, parameter*)>
|
||||||
|
<!ATTLIST signature
|
||||||
|
cv CDATA #IMPLIED
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT requires ANY>
|
||||||
|
<!ATTLIST requires %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT returns ANY>
|
||||||
|
<!ATTLIST returns %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT throws ANY>
|
||||||
|
<!ATTLIST throws %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT complexity ANY>
|
||||||
|
<!ATTLIST complexity %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT notes ANY>
|
||||||
|
<!ATTLIST notes %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT rationale ANY>
|
||||||
|
<!ATTLIST rationale %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT functionname (#PCDATA)>
|
||||||
|
<!ATTLIST functionname
|
||||||
|
alt CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT enumname (#PCDATA)>
|
||||||
|
<!ATTLIST enumname
|
||||||
|
alt CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT macroname (#PCDATA)>
|
||||||
|
<!ATTLIST macroname
|
||||||
|
alt CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT headername (#PCDATA)>
|
||||||
|
<!ATTLIST headername
|
||||||
|
alt CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT globalname (#PCDATA)>
|
||||||
|
<!ATTLIST globalname
|
||||||
|
alt CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT copy-assignment
|
||||||
|
(template?, type?, parameter*, %boost.function.semantics;)>
|
||||||
|
<!ATTLIST copy-assignment
|
||||||
|
cv CDATA #IMPLIED
|
||||||
|
specifiers CDATA #IMPLIED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT free-function-group (function|overloaded-function)*>
|
||||||
|
<!ATTLIST free-function-group
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT precondition ANY>
|
||||||
|
<!ATTLIST precondition %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT code ANY>
|
||||||
|
<!ATTLIST code %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT using-namespace EMPTY>
|
||||||
|
<!ATTLIST using-namespace
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT using-class EMPTY>
|
||||||
|
<!ATTLIST using-class
|
||||||
|
name CDATA #REQUIRED
|
||||||
|
%boost.common.attrib;>
|
||||||
|
|
||||||
|
<!--========== Boost Testsuite Extensions ==========-->
|
||||||
|
<!ENTITY % boost.testsuite.tests
|
||||||
|
"compile-test|link-test|run-test|
|
||||||
|
compile-fail-test|link-fail-test|run-fail-test">
|
||||||
|
<!ENTITY % boost.testsuite.test.content
|
||||||
|
"source*, lib*, requirement*, purpose, if-fails?">
|
||||||
|
|
||||||
|
<!ELEMENT testsuite ((%boost.testsuite.tests;)+)>
|
||||||
|
<!ATTLIST testsuite %boost.common.attrib;>
|
||||||
|
|
||||||
|
<!ELEMENT compile-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST compile-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT link-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST link-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT run-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST run-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT compile-fail-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST compile-fail-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT link-fail-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST link-fail-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT run-fail-test (%boost.testsuite.test.content;)>
|
||||||
|
<!ATTLIST run-fail-test
|
||||||
|
filename CDATA #REQUIRED
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT source (#PCDATA|snippet)*>
|
||||||
|
|
||||||
|
<!ELEMENT snippet EMPTY>
|
||||||
|
<!ATTLIST snippet
|
||||||
|
name CDATA #REQUIRED>
|
||||||
|
|
||||||
|
<!ELEMENT lib (#PCDATA)>
|
||||||
|
|
||||||
|
<!ELEMENT requirement (#PCDATA)>
|
||||||
|
<!ATTLIST requirement
|
||||||
|
name CDATA #REQUIRED>
|
||||||
|
|
||||||
|
<!ELEMENT if-fails ANY>
|
||||||
|
|
||||||
|
<!ELEMENT parameter (paramtype, default?, description?)>
|
||||||
|
<!ATTLIST parameter
|
||||||
|
name CDATA #IMPLIED
|
||||||
|
pack CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT programlisting ANY>
|
||||||
|
<!ATTLIST programlisting
|
||||||
|
name CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!--========== Customize the DocBook DTD ==========-->
|
||||||
|
<!ENTITY % local.tech.char.class "|functionname|libraryname|enumname|headername|macroname|code">
|
||||||
|
<!ENTITY % local.para.class
|
||||||
|
"|using-namespace|using-class|librarylist|librarycategorylist">
|
||||||
|
<!ENTITY % local.descobj.class "|libraryinfo">
|
||||||
|
<!ENTITY % local.classname.attrib "alt CDATA #IMPLIED">
|
||||||
|
<!ENTITY % local.methodname.attrib "alt CDATA #IMPLIED">
|
||||||
|
<!ENTITY % local.refentry.class "|library-reference|testsuite">
|
||||||
|
<!ENTITY % local.title.char.mix "">
|
||||||
|
<!ENTITY % programlisting.module "IGNORE">
|
||||||
|
<!ENTITY % parameter.module "IGNORE">
|
||||||
|
<!ENTITY % function.module "IGNORE">
|
||||||
|
<!ENTITY % type.module "IGNORE">
|
||||||
|
|
||||||
|
<!--========== Import DocBook DTD ==========-->
|
||||||
|
<!ENTITY % DocBook PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||||
|
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||||
|
|
||||||
|
%DocBook;
|
||||||
213
doc/design.qbk
Normal file
213
doc/design.qbk
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
[/
|
||||||
|
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
|
||||||
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
]
|
||||||
|
|
||||||
|
[section:design Design choices]
|
||||||
|
|
||||||
|
The implementations are driven by business needs of cryptocurrency server
|
||||||
|
applications ([link https://ripple.com Ripple] written in C++. These
|
||||||
|
needs were not met by existing solutions so new code was written. The new
|
||||||
|
code tries to avoid design flaws encountered in the already-existing software
|
||||||
|
libraries:
|
||||||
|
|
||||||
|
* Don't sacrifice performance.
|
||||||
|
|
||||||
|
* Don't do too much, otherwise interfaces become rigid.
|
||||||
|
|
||||||
|
* Symmetric interfaces (client and server the same, or close to it).
|
||||||
|
|
||||||
|
* Emulate Boost.Asio interfaces as much as possible, since Asio is
|
||||||
|
proven and it is familiar to users.
|
||||||
|
|
||||||
|
* Let library users make the important decisions such as how to
|
||||||
|
allocate memory or how to leverage flow control.
|
||||||
|
|
||||||
|
Beast formalizes the [link beast.types.Streambuf [*`Streambuf`]] concept,
|
||||||
|
and relies heavily on the Boost.Asio [*`ConstBufferSequence`] and
|
||||||
|
[*`MutableBufferSequence`] concepts for passing buffers to functions.
|
||||||
|
The authors have found the `Streambuf` and buffer sequence interfaces
|
||||||
|
to be optimal for interacting with Asio, and for other tasks such as
|
||||||
|
incremental parsing of data in buffers (for example, parsing websocket
|
||||||
|
frames stored in a [link beast.ref.static_streambuf `static_streambuf`]).
|
||||||
|
|
||||||
|
During the development of Beast the authors have studied other software
|
||||||
|
packages and in particular the comments left during the Boost Review process
|
||||||
|
of other packages offering similar functionality. In this section we attempt
|
||||||
|
to address those issues.
|
||||||
|
|
||||||
|
[variablelist
|
||||||
|
[[
|
||||||
|
"I would also like to see instances of this library being used
|
||||||
|
in production. That would give some evidence that the design
|
||||||
|
works in practice.""
|
||||||
|
][
|
||||||
|
Beast.HTTP and Beast.WebSocket will be used in [*rippled], an
|
||||||
|
asynchronous peer to peer server that implements the
|
||||||
|
[*Ripple Consensus Protocol]. These servers are deployed in multiple
|
||||||
|
production environments, with banks in many countries running client
|
||||||
|
applications that connect to [*rippled].
|
||||||
|
]]
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
[section:http HTTP]
|
||||||
|
|
||||||
|
For HTTP we to model the message to maximize flexibility of implementation
|
||||||
|
strategies while allowing familiar verbs such as [*`read`] and [*`write`].
|
||||||
|
The HTTP interface is further driven by the needs of the WebSocket module,
|
||||||
|
as a WebSocket session requires a HTTP Upgrade handshake exchange at the
|
||||||
|
start. Other design goals:
|
||||||
|
|
||||||
|
* Don't try to invent a complete web server or client
|
||||||
|
|
||||||
|
* Have simple free functions to send and receive messages.
|
||||||
|
|
||||||
|
* Allow the message object to be customized,
|
||||||
|
|
||||||
|
[variablelist
|
||||||
|
|
||||||
|
[[
|
||||||
|
"Some more advanced examples, e.g. including TLS with client/server
|
||||||
|
certificates would help.""
|
||||||
|
][
|
||||||
|
The HTTP interface doesn't try to reinvent the wheel, it just uses
|
||||||
|
the `boost::asio::ip::tcp::socket` or `boost::asio::ssl::stream` that
|
||||||
|
you set up beforehand. Callers use the interfaces already existing
|
||||||
|
on those objects to make outgoing connections, accept incoming connections,
|
||||||
|
or establish TLS sessions with certificates. We find the available Asio
|
||||||
|
examples for performing these tasks sufficient.
|
||||||
|
]]
|
||||||
|
|
||||||
|
[[
|
||||||
|
"A built-in router?"
|
||||||
|
][
|
||||||
|
We presume this means a facility to match expressions against the URI
|
||||||
|
in HTTP requests, and dispatch them to calling code. The authors feel
|
||||||
|
that this is a responsibility of higher level code. Beast.HTTP does
|
||||||
|
not try to offer a web server.
|
||||||
|
]]
|
||||||
|
|
||||||
|
[[
|
||||||
|
"Cookies? Forms/File Uploads?""
|
||||||
|
][
|
||||||
|
Cookies, or managing these types of HTTP headers in general, is the
|
||||||
|
responsibility of higher levels. Beast.HTTP just tries to get complete
|
||||||
|
messages to and from the calling code. It deals in the HTTP headers just
|
||||||
|
enough to process the message body and leaves the rest to callers. However,
|
||||||
|
for forms and file uploads the symmetric interface of the message class
|
||||||
|
allows HTTP requests to include arbitrary body types including those needed
|
||||||
|
to upload a file or fill out a form.
|
||||||
|
]]
|
||||||
|
|
||||||
|
[[
|
||||||
|
"...supporting TLS (is this a feature? If not this would be a show-stopper),
|
||||||
|
etc.
|
||||||
|
][
|
||||||
|
Beast.HTTP does not provide direct facilities for implementing TLS
|
||||||
|
connections; however, the interfaces already existing on the
|
||||||
|
`boost::asio::ssl::stream` are available and can be used to establish
|
||||||
|
secure connections. Then, functions like `http::read` or `http::async_write`
|
||||||
|
can work with those encrypted connections with no problem.
|
||||||
|
]]
|
||||||
|
|
||||||
|
[[
|
||||||
|
"There should also be more examples of how to integrate the http service
|
||||||
|
with getting files from the file system, generating responses CGI-style"
|
||||||
|
][
|
||||||
|
The design goal for the library is to not try to invent a web server.
|
||||||
|
We feel that there is a strong need for a basic implementation that
|
||||||
|
models the HTTP message and provides functions to send and receive them
|
||||||
|
over Asio. Such an implementation should serve as a building block upon
|
||||||
|
which higher abstractions such as the aforementioned HTTP service or
|
||||||
|
cgi-gateway can be built.
|
||||||
|
]]
|
||||||
|
|
||||||
|
[[
|
||||||
|
"You should send a 100-continue to ask for the rest of the body if required."
|
||||||
|
][
|
||||||
|
These behaviors are best left to the calling software. A future library
|
||||||
|
can build on Beast.HTTP to provide these behaviors.
|
||||||
|
]]
|
||||||
|
|
||||||
|
[[
|
||||||
|
"What about HTTP/2?""
|
||||||
|
][
|
||||||
|
Many reviewers feel that HTTP/2 support is an essential feature of
|
||||||
|
a HTTP library. The authors agree that HTTP/2 is important but also
|
||||||
|
feel that the most sensible implementation is one that does not re-use
|
||||||
|
the same network reading and writing interface for 2 as that for 1.0
|
||||||
|
and 1.1.
|
||||||
|
|
||||||
|
The Beast.HTTP message model is suitable for HTTP/2 and can be re-used.
|
||||||
|
The IEFT HTTP Working Group adopted message compatiblity with HTTP/1.x
|
||||||
|
as an explicit goal. A parser can simply emit full headers after
|
||||||
|
decoding the compressed HTTP/2 headers. The stream ID is not logicaly
|
||||||
|
part of the message but rather message metadata and should be
|
||||||
|
communicated out-of-band (see below). HTTP/2 sessions begin with a
|
||||||
|
traditional HTTP/1.1 Upgrade similar in fashion to the WebSocket
|
||||||
|
upgrade. A HTTP/2 implementation can use existing Beast.HTTP primitives
|
||||||
|
to perform this handshake.
|
||||||
|
|
||||||
|
Free functions for HTTP/2 sessions are not possible because of the
|
||||||
|
requirement to maintain per-session state. For example, to decode the
|
||||||
|
compressed headers. Or to remember and respect the remote peer's window
|
||||||
|
settings. The authors propose that a HTTP/2 implementation be written
|
||||||
|
as a separate class template, similar to the `websocket::stream` but with
|
||||||
|
additional interfaces to support version 2 features. We feel that
|
||||||
|
Beast.HTTP offers enough useful functionality to justify inclusion,
|
||||||
|
so that developers can take advantage of it right away instead of
|
||||||
|
waiting.
|
||||||
|
]]
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
[section:websocket WebSocket]
|
||||||
|
|
||||||
|
[variablelist
|
||||||
|
[[
|
||||||
|
What about message compression?
|
||||||
|
][
|
||||||
|
The feature is not currently present in the library, but the choice
|
||||||
|
of type requirements for buffers passed to the read functions have been
|
||||||
|
made with compression in mind. There is the plan to add this feature;
|
||||||
|
however, we feel that even without compression users can begin taking
|
||||||
|
advantage of the WebSocket protocol immediately with this library.
|
||||||
|
]]
|
||||||
|
|
||||||
|
[[
|
||||||
|
Where is the TLS/SSL interface?
|
||||||
|
][
|
||||||
|
The `websocket::stream` just wraps the socket or stream that you
|
||||||
|
provide (for example, a `boost::asio::ip::tcp::socket` or a
|
||||||
|
`boost::asio::ssl::stream`). You establish your TLS connection
|
||||||
|
using the interface on `ssl::stream` like shown in all of the Asio
|
||||||
|
examples, they construct your `websocket::stream` around it.
|
||||||
|
It works perfectly fine - Beast.WebSocket doesn't try to reinvent the
|
||||||
|
wheel or put a fresh coat of interface paint on the `ssl::stream`.
|
||||||
|
|
||||||
|
The WebSocket implementation [*does] provides support for shutting down
|
||||||
|
the TLS connection through the use of the ADL compile-time virtual functions
|
||||||
|
[link beast.ref.wsproto__teardown `teardown`] and
|
||||||
|
[link beast.ref.wsproto__async_teardown `async_teardown`]. These will
|
||||||
|
properly close the connection as per rfc6455 and overloads are available
|
||||||
|
for TLS streams. Callers may provide their own overloads of these functions
|
||||||
|
for user-defined next layer types.
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[endsect]
|
||||||
215
doc/http.qbk
Normal file
215
doc/http.qbk
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
[/
|
||||||
|
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
|
||||||
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
]
|
||||||
|
|
||||||
|
[section:http HTTP]
|
||||||
|
|
||||||
|
Beast.HTTP offers programmers simple and performant models of HTTP messages and
|
||||||
|
their associated operations including synchronous and asynchronous reading and
|
||||||
|
writing using Boost.Asio.
|
||||||
|
|
||||||
|
The HTTP protocol is described fully in
|
||||||
|
[@https://tools.ietf.org/html/rfc2616 rfc2616]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:motivation Motivation]
|
||||||
|
|
||||||
|
The HTTP protocol is pervasive in network applications. As C++ is a logical
|
||||||
|
choice for high performance network servers, there is great utility in solid
|
||||||
|
building blocks for manipulating, sending, and receiving HTTP messages
|
||||||
|
compliant with the Hypertext Transfer Protocol and the supplements that
|
||||||
|
follow. Unfortunately reliable implementations or industry standards do not
|
||||||
|
exist in C++.
|
||||||
|
|
||||||
|
Beast.HTTP is built on Boost.Asio and uses HTTP parser from NodeJS, which is
|
||||||
|
extensively field tested and exceptionally robust. A proposal to add networking
|
||||||
|
functionality to the C++ standard library, based on Boost.Asio, is under
|
||||||
|
consideration by the standards committee. Since the final approved networking
|
||||||
|
interface for the C++ standard library will likely closely resemble the current
|
||||||
|
interface of Boost.Asio, it is logical for Beast.HTTP to use Boost.Asio as its
|
||||||
|
network transport.
|
||||||
|
|
||||||
|
[note The documentation which follows assumes familiarity with
|
||||||
|
both Boost.Asio and the HTTP protocol specification described in
|
||||||
|
[@https://tools.ietf.org/html/rfc2616 rfc2616] ]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:example Example]
|
||||||
|
|
||||||
|
All examples and identifiers mentioned in this document are written as
|
||||||
|
if the following declarations are in effect:
|
||||||
|
```
|
||||||
|
#include <beast/http.h>
|
||||||
|
using namespace beast::http;
|
||||||
|
using namespace boost::asio;
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a HTTP request:
|
||||||
|
```
|
||||||
|
request<string_body> req({method_t::http_get, "/", 11});
|
||||||
|
req.headers.insert("Host", "127.0.0.1:80");
|
||||||
|
req.headers.insert("User-Agent", "Beast.HTTP");
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
To send a message it must first be prepared through a call to `prepare`. This
|
||||||
|
customization point transforms the `message` into a `prepared_message`,
|
||||||
|
filling in some standard HTTP behavior and allowing the Body associated with
|
||||||
|
the message to perform preparatory steps. For example, a string body may set
|
||||||
|
the Content-Length and Content-Type appropriately.
|
||||||
|
```
|
||||||
|
void send_request(ip::tcp::socket& sock,
|
||||||
|
request<string_body>&& req)
|
||||||
|
{
|
||||||
|
// Send the request on the socket
|
||||||
|
write(sock, prepare(req, connection(keep_alive));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Messages can be read from the network and parsed into a `parsed_message` object,
|
||||||
|
which extends the `message` by adding parse-specific metadata such as the
|
||||||
|
keep-alive which is context sensitive (depending on the HTTP version for
|
||||||
|
example). When preparing a response for sending, `prepare` must be called with
|
||||||
|
an additional parameter, the corresponding parsed request. The implementation
|
||||||
|
inspects the contents of the request to set dependent fields of the response.
|
||||||
|
This example reads a message, builds a response, and sends it.
|
||||||
|
```
|
||||||
|
void handle_connection(ip::tcp::socket& sock)
|
||||||
|
{
|
||||||
|
parsed_request<string_body> req;
|
||||||
|
read(sock, req);
|
||||||
|
response<string_body> resp;
|
||||||
|
...
|
||||||
|
write(sock, prepare(resp, req));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:message Message model]
|
||||||
|
|
||||||
|
A HTTP message (referred to hereafter as "message") contains a request or
|
||||||
|
response line, a series of zero or more name/value pairs (collectively
|
||||||
|
termed "headers"), and a series of octets called the message body which may
|
||||||
|
be zero in length. Applications using HTTP often perform these operations
|
||||||
|
on messages:
|
||||||
|
|
||||||
|
* [*Parse] a new message from a series of octets.
|
||||||
|
|
||||||
|
* [*Assemble] a new message from scratch or from an existing message.
|
||||||
|
|
||||||
|
* [*Serialize] a message into a series of octets.
|
||||||
|
|
||||||
|
* [*Read] a message from a stream. This can be thought of as a compound
|
||||||
|
operation; a network read, followed by a [*parse].
|
||||||
|
|
||||||
|
* [*Write] a message to a stream. This can be thought of as a compound
|
||||||
|
operation: a [*serialize] followed by a network write.
|
||||||
|
|
||||||
|
The spectrum of hardware and software platforms which perform these typical
|
||||||
|
HTTP operations is vast, ranging from powerful servers in large datacenters
|
||||||
|
to tiny resource-limited embedded devices. No single concrete implementation
|
||||||
|
of a class intended to model messages can efficiently serve all needs.
|
||||||
|
For example, an object that minimizes resources during parsing may not be
|
||||||
|
able to edit and change headers dynamically. A message that represents the
|
||||||
|
message body as a disk file may support sending but not parsing. Many efficient
|
||||||
|
and correct models of messages exist, supporting some or all of the
|
||||||
|
operations listed above.
|
||||||
|
|
||||||
|
To support a variety of implementation strategies, we introduce customization
|
||||||
|
points for the header and body via class template arguments. This illustration
|
||||||
|
shows the message class template (boilerplate present in the actual
|
||||||
|
declaration has been removed for clarity):
|
||||||
|
|
||||||
|
[$images/message.png [width 580px] [height 225px]]
|
||||||
|
|
||||||
|
The default constructor, move special members, and copy special members are
|
||||||
|
all defaulted. A message is movable, copyable, or default constructible based
|
||||||
|
on the capabilities of its template arguments.
|
||||||
|
|
||||||
|
Messages modeled in this fashion are ['complete], containing all of the
|
||||||
|
information required to perform the supported set of operations. They are
|
||||||
|
['first-class types], returnable from functions and composable. HTTP
|
||||||
|
requests and responses are distinct types, allowing functions to be
|
||||||
|
overloaded on the type of message.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:body Body parameter]
|
||||||
|
|
||||||
|
The `Body` template argument in the `message` class must meet the
|
||||||
|
[link beast.types.Body [*`Body`] requirements]. It provides customization
|
||||||
|
of the data member in the message, the algorithm for parsing, and the
|
||||||
|
algorithm for serialization:
|
||||||
|
|
||||||
|
[$images/body.png [width 510px] [height 210px]]
|
||||||
|
|
||||||
|
The following types, provided by the library, meet the `Body` requirements:
|
||||||
|
|
||||||
|
* [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
|
||||||
|
Used in GET requests where there is no message body.
|
||||||
|
|
||||||
|
* [link beast.ref.http__string_body [*`string_body`:]] A body with a
|
||||||
|
`value_type` of `std::string`. Useful for quickly putting together a request
|
||||||
|
or response with simple text in the message body (such as an error message).
|
||||||
|
Subject to the performance limit of strings if large amounts of data are
|
||||||
|
inserted.
|
||||||
|
|
||||||
|
* [link beast.ref.http__basic_streambuf_body [*`basic_streambuf_body`:]] A body with a
|
||||||
|
`value_type` of `streambuf`. A streambuf is an efficient storage object which
|
||||||
|
uses multiple octet arrays of varying lengths to represent data.
|
||||||
|
|
||||||
|
Instances of the optional nested types `writer` and `reader` perform
|
||||||
|
serialization and deserialization of the message body. If either or
|
||||||
|
both of these types are present, the message becomes serializable, parsable,
|
||||||
|
or both. They model [link beast.types.Reader [*`Reader`]] and
|
||||||
|
[link beast.types.Writer [*`Writer`]] respectively.
|
||||||
|
|
||||||
|
For specialized applications, users may implement their own types which
|
||||||
|
meet the requirements. The examples included with this library provide a
|
||||||
|
Body implementation used to serve files in a HTTP server.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:headers Headers container]
|
||||||
|
|
||||||
|
The `Headers` type represents the field/value pairs present in every HTTP
|
||||||
|
message. These types implement the
|
||||||
|
[link beast.types.FieldSequence [*`FieldSequence`]]
|
||||||
|
concept. The value type of a field sequence is an object meeting the
|
||||||
|
requirements of [link beast.types.Field [*`Field`]]. The implementation can
|
||||||
|
serialize any instance of `Headers` that meets the field sequence requirements.
|
||||||
|
This example shows a function which returns `true` if the specified field
|
||||||
|
sequence has a connect field:
|
||||||
|
```
|
||||||
|
template<class FieldSequence>
|
||||||
|
bool
|
||||||
|
has_connect(FieldSequence const& fs)
|
||||||
|
{
|
||||||
|
return std::find_if(fs.begin(), fs.end(),
|
||||||
|
[&](auto const& field)
|
||||||
|
{
|
||||||
|
return ci_equal(field.name(), "Connect");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
BIN
doc/images/beast.png
Normal file
BIN
doc/images/beast.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
doc/images/beast.psd
Normal file
BIN
doc/images/beast.psd
Normal file
Binary file not shown.
BIN
doc/images/body.png
Normal file
BIN
doc/images/body.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
BIN
doc/images/body.psd
Normal file
BIN
doc/images/body.psd
Normal file
Binary file not shown.
BIN
doc/images/message.png
Normal file
BIN
doc/images/message.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
BIN
doc/images/message.psd
Normal file
BIN
doc/images/message.psd
Normal file
Binary file not shown.
13
doc/index.xml
Normal file
13
doc/index.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" "boostbook.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
|
||||||
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
-->
|
||||||
|
|
||||||
|
<section id="index">
|
||||||
|
<index/>
|
||||||
|
</section>
|
||||||
13
doc/makeqbk.sh
Normal file
13
doc/makeqbk.sh
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
mkdir -p ../bin/doc/xml
|
||||||
|
doxygen beast.dox
|
||||||
|
cd ../bin/doc/xml
|
||||||
|
xsltproc combine.xslt index.xml > all.xml
|
||||||
|
cd ../../../doc
|
||||||
|
xsltproc reference.xsl ../bin/doc/xml/all.xml > reference.qbk
|
||||||
163
doc/quickref.xml
Normal file
163
doc/quickref.xml
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" "boostbook.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
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)
|
||||||
|
-->
|
||||||
|
|
||||||
|
<informaltable frame="all">
|
||||||
|
<tgroup cols="4">
|
||||||
|
<colspec colname="a"/>
|
||||||
|
<colspec colname="b"/>
|
||||||
|
<colspec colname="c"/>
|
||||||
|
<colspec colname="d"/>
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry valign="center" namest="a" nameend="b">
|
||||||
|
<bridgehead renderas="sect2">HTTP</bridgehead>
|
||||||
|
</entry>
|
||||||
|
<entry valign="center" namest="c" nameend="d">
|
||||||
|
<bridgehead renderas="sect2">WebSocket</bridgehead>
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Classes</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.http__basic_headers">basic_headers</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__basic_parser">basic_parser</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__basic_streambuf_body">basic_streambuf_body</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__empty_body">empty_body</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__error_code">error_code</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__message">message</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__resume_context">resume_context</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__stream">stream</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
|
||||||
|
</simplelist>
|
||||||
|
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__async_write">async_write</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__chunk_encode">chunk_encode</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__chunk_encode_final">chunk_encode_final</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__read">read</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__write">write</link></member>
|
||||||
|
</simplelist>
|
||||||
|
<bridgehead renderas="sect3">Concepts</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.types.Body">Body</link></member>
|
||||||
|
<member><link linkend="beast.types.Field">Field</link></member>
|
||||||
|
<member><link linkend="beast.types.FieldSequence">FieldSequence</link></member>
|
||||||
|
<member><link linkend="beast.types.Reader">Reader</link></member>
|
||||||
|
<member><link linkend="beast.types.Writer">Writer</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Classes</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.wsproto__close_reason">close_reason</link></member>
|
||||||
|
<member><link linkend="beast.ref.wsproto__socket">socket</link></member>
|
||||||
|
<member><link linkend="beast.ref.wsproto__static_string">static_string</link></member>
|
||||||
|
</simplelist>
|
||||||
|
<bridgehead renderas="sect3">Options</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.wsproto__auto_fragment_size">auto_fragment_size</link></member>
|
||||||
|
<member><link linkend="beast.ref.wsproto__decorate">decorate</link></member>
|
||||||
|
<member><link linkend="beast.ref.wsproto__keep_alive">keep_alive</link></member>
|
||||||
|
<member><link linkend="beast.ref.wsproto__read_buffer_size">read_buffer_size</link></member>
|
||||||
|
<member><link linkend="beast.ref.wsproto__read_message_max">read_message_max</link></member>
|
||||||
|
<member><link linkend="beast.ref.wsproto__write_buffer_size">write_buffer_size</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.wsproto__async_teardown">async_teardown</link></member>
|
||||||
|
<member><link linkend="beast.ref.wsproto__teardown">teardown</link></member>
|
||||||
|
</simplelist>
|
||||||
|
<bridgehead renderas="sect3">Constants</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.wsproto__close_code">close_code</link></member>
|
||||||
|
<member><link linkend="beast.ref.wsproto__opcode">opcode</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
<tgroup cols="4">
|
||||||
|
<colspec colname="a"/>
|
||||||
|
<colspec colname="b"/>
|
||||||
|
<colspec colname="c"/>
|
||||||
|
<colspec colname="d"/>
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry valign="center" namest="a" nameend="d">
|
||||||
|
<bridgehead renderas="sect2">Core</bridgehead>
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Classes</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.async_completion">async_completion</link></member>
|
||||||
|
<member><link linkend="beast.ref.basic_streambuf">basic_streambuf</link></member>
|
||||||
|
<member><link linkend="beast.ref.buffers_adapter">buffers_adapter</link></member>
|
||||||
|
<member><link linkend="beast.ref.consuming_buffers">consuming_buffers</link></member>
|
||||||
|
<member><link linkend="beast.ref.handler_alloc">handler_alloc</link></member>
|
||||||
|
<member><link linkend="beast.ref.prepared_buffers">prepared_buffers</link></member>
|
||||||
|
<member><link linkend="beast.ref.static_streambuf">static_streambuf</link></member>
|
||||||
|
<member><link linkend="beast.ref.static_streambuf_n">static_streambuf_n</link></member>
|
||||||
|
<member><link linkend="beast.ref.streambuf">streambuf</link></member>
|
||||||
|
<member><link linkend="beast.ref.streambuf_readstream">streambuf_readstream</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.append_buffers">append_buffers</link></member>
|
||||||
|
<member><link linkend="beast.ref.bind_handler">bind_handler</link></member>
|
||||||
|
<member><link linkend="beast.ref.prepare_buffer">prepare_buffer</link></member>
|
||||||
|
<member><link linkend="beast.ref.prepare_buffers">prepare_buffers</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.is_AsyncReadStream">is_AsyncReadStream</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_AsyncWriteStream">is_AsyncWriteStream</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_BufferSequence">is_BufferSequence</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_ConstBufferSequence">is_ConstBufferSequence</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_Handler">is_Handler</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_MutableBufferSequence">is_MutableBufferSequence</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_Stream">is_Stream</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_Streambuf">is_Streambuf</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_SyncReadStream">is_SyncReadStream</link></member>
|
||||||
|
<member><link linkend="beast.ref.is_SyncWriteStream">is_SyncWriteStream</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Concepts</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.types.BufferSequence">BufferSequence</link></member>
|
||||||
|
<member><link linkend="beast.types.Stream">Stream</link></member>
|
||||||
|
<member><link linkend="beast.types.Streambuf">Streambuf</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</informaltable>
|
||||||
1725
doc/reference.xsl
Normal file
1725
doc/reference.xsl
Normal file
File diff suppressed because it is too large
Load Diff
446
doc/types.qbk
Normal file
446
doc/types.qbk
Normal file
@@ -0,0 +1,446 @@
|
|||||||
|
[/
|
||||||
|
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
|
||||||
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
]
|
||||||
|
|
||||||
|
[section:types Type Requirements]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Body Body]
|
||||||
|
|
||||||
|
In this table:
|
||||||
|
|
||||||
|
* `X` is a type meeting the requirements of [*`Body`].
|
||||||
|
|
||||||
|
[table Body requirements
|
||||||
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`X::value_type`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
The type of the `message::body` member.
|
||||||
|
If this is not movable or not copyable, the containing message
|
||||||
|
will be not movable or not copyable.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`X:value_type{}`]
|
||||||
|
[]
|
||||||
|
[`DefaultConstructible`]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`Body::reader`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
If present, a type meeting the requirements of
|
||||||
|
[link beast.types.Reader [*`Reader`]].
|
||||||
|
Provides an implementation to parse the body.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`Body::writer`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
If present, a type meeting the requirements of
|
||||||
|
[link beast.types.Writer [*`Writer`]].
|
||||||
|
Provides an implementation to serialize the body.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:BufferSequence BufferSequence]
|
||||||
|
|
||||||
|
A `BufferSequence` meets [*one of] the following requirements:
|
||||||
|
|
||||||
|
* `ConstBufferSequence`
|
||||||
|
* `MutableBufferSequence`
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Field Field]
|
||||||
|
|
||||||
|
A [*`Field`] represents a single HTTP header field/value pair.
|
||||||
|
|
||||||
|
In this table:
|
||||||
|
|
||||||
|
* `X` denotes a type meeting the requirements of [*`Field`].
|
||||||
|
* `a` denotes a value of type `X`.
|
||||||
|
|
||||||
|
[table Field requirements
|
||||||
|
|
||||||
|
[[operation][type][semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`a.name()`]
|
||||||
|
[`boost::string_ref`]
|
||||||
|
[
|
||||||
|
This function returns a value implicitly convertible to
|
||||||
|
`boost::string_ref` containing the case-insensitve field
|
||||||
|
name, without leading or trailing white space.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.value()`]
|
||||||
|
[`boost::string_ref`]
|
||||||
|
[
|
||||||
|
This function returns a value implicitly convertible to
|
||||||
|
`boost::string_ref` containing the value for the field. The
|
||||||
|
value is considered canonical if there is no leading or
|
||||||
|
trailing whitespace.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:FieldSequence FieldSequence]
|
||||||
|
|
||||||
|
A [*`FieldSequence`] is an iterable container whose value type meets
|
||||||
|
the requirements of [link beast.types.Field [*`Field`]].
|
||||||
|
|
||||||
|
In this table:
|
||||||
|
|
||||||
|
* `X` denotes a type that meets the requirements of [*`FieldSequence`].
|
||||||
|
|
||||||
|
* `a` is a value of type `X`.
|
||||||
|
|
||||||
|
[table FieldSequence requirements
|
||||||
|
[[operation][type][semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`X::value_type`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
A type that meets the requirements of `Field`.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`X::const_iterator`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
A type that meets the requirements of `ForwardIterator`.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.begin()`]
|
||||||
|
[`X::const_iterator`]
|
||||||
|
[
|
||||||
|
Returns an iterator to the beginning of the field sequence.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.end()`]
|
||||||
|
[`X::const_iterator`]
|
||||||
|
[
|
||||||
|
Returns an iterator to the end of the field sequence.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Reader Reader]
|
||||||
|
|
||||||
|
Parser implementations will construct the corresponding `reader` object
|
||||||
|
during the parse. This customization point allows the Body to determine
|
||||||
|
the strategy for storing incoming message body data.
|
||||||
|
|
||||||
|
In this table:
|
||||||
|
|
||||||
|
* `X` denotes a type meeting the requirements of [*`Reader`].
|
||||||
|
|
||||||
|
* `a` denotes a value of type `X`.
|
||||||
|
|
||||||
|
* `p` is any pointer.
|
||||||
|
|
||||||
|
* `n` is a value convertible to `std::size_t`.
|
||||||
|
|
||||||
|
* `ec` is a value of type `error_code&`.
|
||||||
|
|
||||||
|
* `m` denotes a value of type `message const&` where
|
||||||
|
`std::is_same<decltype(m.body), Body::value_type>:value == true`
|
||||||
|
|
||||||
|
|
||||||
|
[table Reader requirements
|
||||||
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`X a(m);`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
`a` is constructible from `m`. The lifetime of `m` is
|
||||||
|
guaranteed to end no earlier than after `a` is destroyed.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.write(p, n, ec)`]
|
||||||
|
[`void`]
|
||||||
|
[
|
||||||
|
Deserializes the input sequence into the body.
|
||||||
|
If `ec` is set, the deserialization is aborted and the error
|
||||||
|
is returned to the caller.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[note Definitions for required `Reader` member functions should be declared
|
||||||
|
inline so the generated code becomes part of the implementation. ]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Writer Writer]
|
||||||
|
|
||||||
|
A `Writer` serializes the message body. The implementation creates an instance
|
||||||
|
of this type when serializing a message, and calls into it zero or more times
|
||||||
|
to provide buffers containing the data. The interface of `Writer` is intended
|
||||||
|
to allow serialization in these scenarios:
|
||||||
|
|
||||||
|
* A body that does not entirely fit in memory.
|
||||||
|
* A body produced incrementally from coroutine output.
|
||||||
|
* A body represented by zero or more buffers already in memory.
|
||||||
|
* A body as a series of buffers when the content size is not known ahead of time.
|
||||||
|
* Body data generated on demand from other threads.
|
||||||
|
* Body data computed algorithmically.
|
||||||
|
|
||||||
|
In this table:
|
||||||
|
|
||||||
|
* `X` denotes a type meeting the requirements of `Writer`.
|
||||||
|
|
||||||
|
* `a` denotes a value of type `X`.
|
||||||
|
|
||||||
|
* `m` denotes a value of type `message const&` where
|
||||||
|
`std::is_same<decltype(m.body), Body::value_type>:value == true`.
|
||||||
|
|
||||||
|
* `rc` is an object of type [link beast.reference.http__resume_context resume_context].
|
||||||
|
|
||||||
|
* `ec` is a value of type `error_code&`.
|
||||||
|
|
||||||
|
* `wf` is a [*write function]: a function object of unspecified type provided
|
||||||
|
by the implementation which accepts any value meeting the requirements
|
||||||
|
of `ConstBufferSequence` as its single parameter.
|
||||||
|
|
||||||
|
[table Writer requirements
|
||||||
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`X a(m);`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
`a` is constructible from `m`. The lifetime of `m` is
|
||||||
|
guaranteed to end no earlier than after `a` is destroyed.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.init(ec)`]
|
||||||
|
[`void`]
|
||||||
|
[
|
||||||
|
Called immediately after construction.
|
||||||
|
If `ec` is set, the serialization is aborted and the error
|
||||||
|
is propagated to the caller.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.content_length()`]
|
||||||
|
[`std::size_t`]
|
||||||
|
[
|
||||||
|
If this member is present, it is called after initialization
|
||||||
|
and before calls to provide buffers. The serialized message will
|
||||||
|
have the Content-Length field set to the value returned from
|
||||||
|
this function. If this member is absent, the serialized message
|
||||||
|
body will be chunk-encoded for HTTP versions 1.1 and later, else
|
||||||
|
the serialized message body will be sent unmodified, with the
|
||||||
|
error `boost::asio::error::eof` returned to the caller, to notify
|
||||||
|
they should close the connection to indicate the end of the message.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a(rc, ec, wf)`]
|
||||||
|
[`boost::tribool`]
|
||||||
|
[
|
||||||
|
Called repeatedly after `init` succeeds.
|
||||||
|
`wf` is a function object which takes as its single parameter,
|
||||||
|
any value meeting the requirements of `ConstBufferSequence`.
|
||||||
|
Buffers provided by the `writer` to this [*write function] must
|
||||||
|
remain valid until the next member function of `writer` is
|
||||||
|
invoked (which may be the destructor). This function returns `true`
|
||||||
|
to indicate all message body data has been written, or `false`
|
||||||
|
if there is more body data. If the return value is
|
||||||
|
`boost::indeterminate`, the implementation will suspend the operation
|
||||||
|
until the writer invokes `rc`. It is the writers responsibility when
|
||||||
|
returning `boost::indeterminate`, to acquire ownership of the
|
||||||
|
`resume_context` via move construction and eventually call it or else
|
||||||
|
undefined behavior results.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[note Definitions for required `Writer` member functions should be declared
|
||||||
|
inline so the generated code becomes part of the implementation. ]
|
||||||
|
|
||||||
|
Exemplar:
|
||||||
|
```
|
||||||
|
struct writer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Construct the writer.
|
||||||
|
|
||||||
|
The msg object is guaranteed to exist for the lifetime of the writer.
|
||||||
|
|
||||||
|
Exceptions:
|
||||||
|
No-throw guarantee.
|
||||||
|
|
||||||
|
@param msg The message whose body is to be written.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
explicit
|
||||||
|
writer(message<isRequest, Body, Headers> const& msg);
|
||||||
|
|
||||||
|
/** Initialize the writer.
|
||||||
|
|
||||||
|
Called once immediately after construction.
|
||||||
|
The writer can perform initialization which may fail.
|
||||||
|
|
||||||
|
@param ec Contains the error code if any errors occur.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
init(error_code& ec);
|
||||||
|
|
||||||
|
/** Returns the content length.
|
||||||
|
|
||||||
|
If this member is present, the implementation will set the
|
||||||
|
Content-Length field accordingly. If absent, the implementation will
|
||||||
|
use chunk-encoding or terminate the connection to indicate the end
|
||||||
|
of the message.
|
||||||
|
*/
|
||||||
|
std::size_t
|
||||||
|
content_length() const;
|
||||||
|
|
||||||
|
/** Write zero or one buffer representing the message body.
|
||||||
|
|
||||||
|
Postconditions:
|
||||||
|
|
||||||
|
If return value is `true`:
|
||||||
|
* Callee does not take ownership of resume.
|
||||||
|
* Callee made zero or one calls to `write`.
|
||||||
|
* There is no more data remaining to write.
|
||||||
|
|
||||||
|
If return value is `false`:
|
||||||
|
* Callee does not take ownership of resume.
|
||||||
|
* Callee made one call to `write`.
|
||||||
|
|
||||||
|
If return value is boost::indeterminate:
|
||||||
|
* Callee takes ownership of `resume`.
|
||||||
|
* Caller suspends the write operation
|
||||||
|
until `resume` is invoked.
|
||||||
|
|
||||||
|
When the caller takes ownership of resume, the
|
||||||
|
asynchronous operation will not complete until the
|
||||||
|
caller destroys the object.
|
||||||
|
|
||||||
|
@param resume A functor to call to resume the write operation
|
||||||
|
after the writer has returned boost::indeterminate.
|
||||||
|
|
||||||
|
@param ec Set to indicate an error. This will cause an
|
||||||
|
asynchronous write operation to complete with the error.
|
||||||
|
|
||||||
|
@param write A functor the writer will call to provide the next
|
||||||
|
set of buffers. Ownership of the buffers is not transferred,
|
||||||
|
the writer must guarantee that the buffers remain valid until the
|
||||||
|
next member function is invoked, which may be the destructor.
|
||||||
|
|
||||||
|
@return `true` if there is data, `false` when done,
|
||||||
|
boost::indeterminate to suspend.
|
||||||
|
|
||||||
|
@note Undefined behavior if the callee takes ownership
|
||||||
|
of resume but does not return boost::indeterminate.
|
||||||
|
*/
|
||||||
|
template<class WriteFunction>
|
||||||
|
boost::tribool
|
||||||
|
operator()(resume_context&&, error_code&, WriteFunction&& write);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Stream Stream]
|
||||||
|
|
||||||
|
A `Stream` meets the following requirements:
|
||||||
|
|
||||||
|
* `SyncReadStream`
|
||||||
|
* `SyncWriteStream`
|
||||||
|
* `AsyncReadStream`
|
||||||
|
* `AsyncWriteStream`
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:Streambuf Streambuf]
|
||||||
|
|
||||||
|
In the table below, `X` denotes a class, `a` denotes a value
|
||||||
|
of type `X`, `n` denotes a value convertible to `std::size_t`,
|
||||||
|
and `U` and `T` denote unspecified types.
|
||||||
|
|
||||||
|
[table Streambuf requirements
|
||||||
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`X::const_buffers_type`]
|
||||||
|
[`T`]
|
||||||
|
[`T` meets the requirements for `ConstBufferSequence`.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`X::mutable_buffers_type`]
|
||||||
|
[`U`]
|
||||||
|
[`U` meets the requirements for `MutableBufferSequence`.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.commit(n)`]
|
||||||
|
[`void`]
|
||||||
|
[Moves bytes from the output sequence to the input sequence.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.consume(n)`]
|
||||||
|
[`void`]
|
||||||
|
[Removes bytes from the input sequence.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.data()`]
|
||||||
|
[`T`]
|
||||||
|
[Returns a list of buffers that represents the input sequence.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.prepare(n)`]
|
||||||
|
[`U`]
|
||||||
|
[Returns a list of buffers that represents the output sequence, with
|
||||||
|
the given size.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.size()`]
|
||||||
|
[`std::size_t`]
|
||||||
|
[Returns the size of the input sequence.]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.max_size()`]
|
||||||
|
[`std::size_t`]
|
||||||
|
[Returns the maximum size of the `Streambuf`.]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[endsect]
|
||||||
391
doc/wsproto.qbk
Normal file
391
doc/wsproto.qbk
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
[/
|
||||||
|
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
|
||||||
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
]
|
||||||
|
|
||||||
|
[section:wsproto WSProto]
|
||||||
|
|
||||||
|
The WebSocket Protocol enables two-way communication between a client
|
||||||
|
running untrusted code in a controlled environment to a remote host that has
|
||||||
|
opted-in to communications from that code. The protocol consists of an opening
|
||||||
|
handshake followed by basic message framing, layered over TCP. The goal of
|
||||||
|
this technology is to provide a mechanism for browser-based applications that
|
||||||
|
need two-way communication with servers that does not rely on opening multiple
|
||||||
|
HTTP connections.
|
||||||
|
|
||||||
|
Beast.WSProto provides developers with a robust WebSocket implementation
|
||||||
|
built on Boost.Asio with a consistent asynchronous model using a modern
|
||||||
|
C++ approach.
|
||||||
|
|
||||||
|
The WebSocket protocol is described fully in
|
||||||
|
[@https://tools.ietf.org/html/rfc6455 rfc6455]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:motivation Motivation]
|
||||||
|
|
||||||
|
Today's web applications increasingly rely on alternatives to standard HTTP
|
||||||
|
to achieve performance and/or responsiveness. While WebSocket implementations
|
||||||
|
are widely available in common web development languages such as Javascript,
|
||||||
|
good implementations in C++ are scarce. A survey of existing C++ WebSocket
|
||||||
|
solutions reveals interfaces which lack symmetry, impose performance penalties,
|
||||||
|
and needlessly restrict implementation strategies.
|
||||||
|
|
||||||
|
Beast.WSProto is built on Boost.Asio, a robust cross platform networking
|
||||||
|
framework that is part of Boost and also offered as a standalone library.
|
||||||
|
A proposal to add networking functionality to the C++ standard library,
|
||||||
|
based on Boost.Asio, is under consideration by the standards committee.
|
||||||
|
Since the final approved networking interface for the C++ standard library
|
||||||
|
will likely closely resemble the current interface of Boost.Asio, it is
|
||||||
|
logical for Beast.WSProto to use Boost.Asio as its network transport.
|
||||||
|
|
||||||
|
Beast.WSProto takes advantage of Boost.Asio's extensible asynchronous
|
||||||
|
model, handler allocation, and handler invocation hooks. Calls to
|
||||||
|
Beast.WSProto asynchronous initiation functions allow callers the choice
|
||||||
|
of using a completion handler, stackful or stackless coroutines, futures,
|
||||||
|
or user defined customizations (for example, Boost.Fiber). The
|
||||||
|
implementation uses handler invocation hooks (`asio_handler_invoke`),
|
||||||
|
providing execution guarantees on composed operations in a manner
|
||||||
|
identical to Boost.Asio. The implementation also uses handler allocation
|
||||||
|
hooks (`asio_handler_allocate`) when allocating memory internally for
|
||||||
|
composed operations.
|
||||||
|
|
||||||
|
There is no need for inheritance or virtual members in `wsproto::socket`.
|
||||||
|
All operations are templated and transparent to the compiler, allowing for
|
||||||
|
maximum inlining and optimization.
|
||||||
|
|
||||||
|
[note The documentation which follows assumes familiarity with
|
||||||
|
both Boost.Asio and the WebSocket protocol specification described in
|
||||||
|
[@https://tools.ietf.org/html/rfc6455 rfc6455] ]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:creating Creating the socket]
|
||||||
|
|
||||||
|
The interface to Beast's WebSocket implementation is a single template
|
||||||
|
class which wraps an object meeting the requirements of `SyncStream` if
|
||||||
|
synchronous operations are performed, `AsyncStream` if asynchronous
|
||||||
|
operations are performed, or both. Arguments supplied during construction
|
||||||
|
are passed to the `Stream` constructor. Here we declare two websockets
|
||||||
|
which contain the underlying stream:
|
||||||
|
```
|
||||||
|
io_service ios;
|
||||||
|
wsproto::socket<tcp::socket> ws(ios);
|
||||||
|
|
||||||
|
ssl::context ctx(ssl::context::sslv23);
|
||||||
|
wsproto::socket<ssl::stream<tcp::socket>> wss(ios, ctx);
|
||||||
|
```
|
||||||
|
|
||||||
|
For servers that can handshake in multiple protocols, it may be desired
|
||||||
|
to wrap a socket that already exists. The socket can be moved in:
|
||||||
|
```
|
||||||
|
tcp::socket&& sock;
|
||||||
|
...
|
||||||
|
wsproto::socket<tcp::socket> ws(std::move(sock));
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, the wrapper can be constructed with a non-owning reference. In
|
||||||
|
this case, the caller is responsible for managing the lifetime of the
|
||||||
|
underlying socket being wrapped:
|
||||||
|
```
|
||||||
|
tcp::socket sock;
|
||||||
|
...
|
||||||
|
wsproto::socket<tcp::socket&> ws(sock);
|
||||||
|
```
|
||||||
|
|
||||||
|
The stream being wrapped can be accessed through the websocket's "next layer",
|
||||||
|
permitting callers to interact directly with its interface.
|
||||||
|
```
|
||||||
|
ssl::context ctx(ssl::context::sslv23);
|
||||||
|
wsproto::socket<ssl::stream<tcp::socket>> ws(ios, ctx);
|
||||||
|
...
|
||||||
|
ws.next_layer().shutdown(); // ssl::stream shutdown
|
||||||
|
```
|
||||||
|
|
||||||
|
[important Initiating read and write operations on the next layer while
|
||||||
|
websocket operations are being performed can break invariants, and
|
||||||
|
result in undefined behavior. ]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:connecting Making connections]
|
||||||
|
|
||||||
|
Connections are established by using the interfaces which already exist for
|
||||||
|
the wrapped stream. For example, making an outgoing connection:
|
||||||
|
```
|
||||||
|
std::string const host = "mywebapp.com";
|
||||||
|
io_service ios;
|
||||||
|
tcp::resolver r(ios);
|
||||||
|
wsproto::socket<tcp::socket> ws(ios);
|
||||||
|
connect(ws.next_layer(), r.resolve(tcp::resolver::query{host, "ws"}));
|
||||||
|
```
|
||||||
|
|
||||||
|
Accepting an incoming connection:
|
||||||
|
```
|
||||||
|
void do_accept(tcp::acceptor& acceptor)
|
||||||
|
{
|
||||||
|
wsproto::socket<tcp::socket> ws(acceptor.get_io_service());
|
||||||
|
acceptor.accept(ws.next_layer());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[note Examples use synchronous interfaces for clarity of exposition. ]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:handshaking Handshaking]
|
||||||
|
|
||||||
|
A WebSocket session begins when one side sends the HTTP Upgrade request
|
||||||
|
for websocket, and the other side sends an appropriate HTTP response
|
||||||
|
indicating that the request was accepted and that the connection has
|
||||||
|
been upgraded. The HTTP Upgrade request must include the Host HTTP field,
|
||||||
|
and the URI of the resource to request. `wsproto::socket::hanshake` is
|
||||||
|
used to send the request with the required host and resource strings.
|
||||||
|
```
|
||||||
|
wsproto::socket<tcp::socket> ws(ios);
|
||||||
|
...
|
||||||
|
ws.set_option(wsproto::keep_alive(true));
|
||||||
|
ws.handshake("ws.mywebapp.com:80", "/cgi-bin/bitcoin-prices");
|
||||||
|
```
|
||||||
|
|
||||||
|
The `wsproto::socket` automatically handles receiving and processing
|
||||||
|
the HTTP response to the handshake request. The call to handshake is
|
||||||
|
successful if a HTTP response is received with the 101 "Switching Protocols"
|
||||||
|
status code. On failure, an error is returned or an exception is thrown.
|
||||||
|
Depending on the keep alive setting, the socket may remain open for a
|
||||||
|
subsequent handshake attempt
|
||||||
|
|
||||||
|
Performing a handshake for an incoming websocket upgrade request operates
|
||||||
|
similarly. If the handshake fails, an error is returned or exception thrown:
|
||||||
|
```
|
||||||
|
wsproto::socket<tcp::socket> ws(ios);
|
||||||
|
...
|
||||||
|
ws.accept();
|
||||||
|
```
|
||||||
|
|
||||||
|
Servers that can handshake in multiple protocols may have already read data
|
||||||
|
on the connection, or might have already received an entire HTTP request
|
||||||
|
containing the upgrade request. Overloads of `accept` allow callers to
|
||||||
|
pass in this additional buffered handshake data.
|
||||||
|
```
|
||||||
|
void do_accept(tcp::socket& sock)
|
||||||
|
{
|
||||||
|
boost::asio::streambuf sb;
|
||||||
|
read_until(sock, sb, "\r\n\r\n");
|
||||||
|
...
|
||||||
|
wsproto::socket<tcp::socket&> ws(sock);
|
||||||
|
ws.accept(sb.data());
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, the caller can pass an entire HTTP request if it was
|
||||||
|
obtained elsewhere:
|
||||||
|
```
|
||||||
|
void do_accept(tcp::socket& sock)
|
||||||
|
{
|
||||||
|
boost::asio::streambuf sb;
|
||||||
|
http::parsed_request<http::empty_body> request;
|
||||||
|
http::read(sock, request);
|
||||||
|
...
|
||||||
|
wsproto::socket<tcp::socket&> ws(sock);
|
||||||
|
ws.accept(request);
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[note Identifiers in the `http` namespace are part of Beast.HTTP. ]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:messages Messages]
|
||||||
|
|
||||||
|
After the WebSocket handshake is accomplished, callers may send and receive
|
||||||
|
messages using the message oriented interface. This interface requires that
|
||||||
|
all of the buffers representing the message are known ahead of time:
|
||||||
|
```
|
||||||
|
void echo(wsproto::socket<ip::tcp::socket>& ws)
|
||||||
|
{
|
||||||
|
streambuf sb;
|
||||||
|
wsproto::opcode::value op;
|
||||||
|
ws.read(sb);
|
||||||
|
|
||||||
|
ws.set_option(wsproto::message_type(op));
|
||||||
|
wsproto::write(ws, sb.data());
|
||||||
|
sb.consume(sb.size());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[important Calls to `wsproto::socket::set_option` must be made from the same
|
||||||
|
implicit or explicit strand as that used to perform other operations. ]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:frames Frames]
|
||||||
|
|
||||||
|
Some use-cases make it impractical or impossible to buffer the entire
|
||||||
|
message ahead of time:
|
||||||
|
|
||||||
|
* Streaming multimedia to an endpoint.
|
||||||
|
* Sending a message that does not fit in memory at once.
|
||||||
|
* Providing incremental results as they become available.
|
||||||
|
|
||||||
|
For these cases, the frame oriented interface may be used. This
|
||||||
|
example reads and echoes a complete message using this interface:
|
||||||
|
```
|
||||||
|
void echo(wsproto::socket<ip::tcp::socket>& ws)
|
||||||
|
{
|
||||||
|
beast::streambuf sb;
|
||||||
|
wsproto::frame_info fi;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
ws.read_frame(fi, sb);
|
||||||
|
if(fi.fin)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ws.set_option(wsproto::message_type(fi.op));
|
||||||
|
beast::consuming_buffers<streambuf::const_buffers_type> cb(sb.data());
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
std::size_t size = std::min(buffer_size(cb));
|
||||||
|
if(size > 512)
|
||||||
|
{
|
||||||
|
ws.write_frame(false, beast::prepare_buffers(512, cb));
|
||||||
|
cb.consume(512);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ws.write_frame(true, cb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:controlframes Control frames]
|
||||||
|
|
||||||
|
During read operations, the implementation automatically reads and processes
|
||||||
|
WebSocket control frames such as ping, pong, and close. Pings are replied
|
||||||
|
to as soon as possible, pongs are noted. The receipt of a close frame
|
||||||
|
initiates the WebSocket close procedure, eventually resulting in the
|
||||||
|
error code `wsproto::error::closed` being delivered to the caller in
|
||||||
|
a subsequent read operation, assuming no other error takes place.
|
||||||
|
|
||||||
|
To ensure timely delivery of control frames, large messages are broken up
|
||||||
|
into smaller sized frames. The implementation chooses the size and number
|
||||||
|
of the frames making up the message. The automatic fragment size option
|
||||||
|
gives callers control over the size of these frames:
|
||||||
|
```
|
||||||
|
...
|
||||||
|
ws.set_option(wsproto::auto_fragment_size(8192));
|
||||||
|
```
|
||||||
|
|
||||||
|
The WebSocket protocol defines a procedure and control message for initiating
|
||||||
|
a close of the session. Handling of close initiated by the remote end of the
|
||||||
|
connection is performed automatically. To manually initiate a close, use
|
||||||
|
`wsproto::socket::close`:
|
||||||
|
```
|
||||||
|
ws.close();
|
||||||
|
```
|
||||||
|
|
||||||
|
[note To receive the `wsproto::error::closed` error, a read operation
|
||||||
|
is required. ]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:buffers Buffers]
|
||||||
|
|
||||||
|
Because calls to read data may return a variable amount of bytes, the
|
||||||
|
interface to calls that read data require an object that meets the requirements
|
||||||
|
of `Streambuf`. This concept is modeled on `boost::asio::basic_streambuf`.
|
||||||
|
|
||||||
|
The implementation does not perform queueing or buffering of messages. If
|
||||||
|
desired, these features should be provided by callers. The impact of this
|
||||||
|
design is that library users are in full control of the allocation strategy
|
||||||
|
used to store data and the back-pressure applied on the read and write side
|
||||||
|
of the underlying TCP/IP connection.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
[section:async Asynchronous interface]
|
||||||
|
|
||||||
|
Asynchronous versions are available for all functions:
|
||||||
|
```
|
||||||
|
wsproto::opcode op;
|
||||||
|
ws.async_read(op, sb,
|
||||||
|
[](boost::system::error_code const& ec)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Calls to asynchronous initiation functions support the extensible asynchronous
|
||||||
|
model developed by the Boost.Asio author, allowing for traditional completion
|
||||||
|
handlers, stackful or stackless coroutines, and even futures:
|
||||||
|
```
|
||||||
|
void echo(wsproto::socket<ip::tcp::socket>& ws,
|
||||||
|
boost::asio::yield_context yield)
|
||||||
|
{
|
||||||
|
ws.async_read(sb, yield);
|
||||||
|
std::future<wsproto::error_code> fut =
|
||||||
|
ws.async_write, sb.data(), boost::use_future);
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:io_service io_service]
|
||||||
|
|
||||||
|
The creation and operation of the `boost::asio::io_service` associated with
|
||||||
|
the underlying stream is left to the callers, permitting any implementation
|
||||||
|
strategy including one that does not require threads for environments where
|
||||||
|
threads are unavailable. Beast.WSProto itself does not use or require threads.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:safety Thread Safety]
|
||||||
|
|
||||||
|
Like a regular asio socket, a `wsproto::socket` is not thread safe. Callers
|
||||||
|
are responsible for synchronizing operations on the socket using an implicit
|
||||||
|
or explicit strand, as per the Asio documentation. The asynchronous interface
|
||||||
|
supports one active read and one active write simultaneously. Undefined
|
||||||
|
behavior results if two or more reads or two or more writes are attempted
|
||||||
|
concurrently. Caller initiated WebSocket ping, pong, and close operations
|
||||||
|
each count as an active write.
|
||||||
|
|
||||||
|
The implementation uses composed asynchronous operations internally; a high
|
||||||
|
level read can cause both reads and writes to take place on the underlying
|
||||||
|
stream. This behavior is transparent to callers.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
[include quickref.xml]
|
||||||
@@ -17,3 +17,8 @@ exe http_server :
|
|||||||
../beast/http/src/beast_http_nodejs_parser.cpp
|
../beast/http/src/beast_http_nodejs_parser.cpp
|
||||||
http_server.cpp
|
http_server.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
|
exe wsproto_echo :
|
||||||
|
../beast/http/src/beast_http_nodejs_parser.cpp
|
||||||
|
wsproto_echo.cpp
|
||||||
|
;
|
||||||
|
|||||||
267
examples/wsproto_async_echo_peer.h
Normal file
267
examples/wsproto_async_echo_peer.h
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_ASYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_ASYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/placeholders.h>
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
#include <beast/wsproto.h>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
// Asynchronous WebSocket echo client/server
|
||||||
|
//
|
||||||
|
class async_echo_peer
|
||||||
|
{
|
||||||
|
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:
|
||||||
|
boost::asio::io_service ios_;
|
||||||
|
socket_type sock_;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
std::vector<std::thread> thread_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
async_echo_peer(bool server,
|
||||||
|
endpoint_type const& ep, std::size_t threads)
|
||||||
|
: sock_(ios_)
|
||||||
|
, acceptor_(ios_)
|
||||||
|
{
|
||||||
|
if(server)
|
||||||
|
{
|
||||||
|
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(&async_echo_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Peer{std::move(sock_), ep};
|
||||||
|
}
|
||||||
|
thread_.reserve(threads);
|
||||||
|
for(std::size_t i = 0; i < threads; ++i)
|
||||||
|
thread_.emplace_back(
|
||||||
|
[&]{ ios_.run(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
~async_echo_peer()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
ios_.dispatch(
|
||||||
|
[&]{ acceptor_.close(ec); });
|
||||||
|
for(auto& t : thread_)
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Peer
|
||||||
|
{
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
int state = 0;
|
||||||
|
boost::optional<endpoint_type> ep;
|
||||||
|
wsproto::socket<socket_type> ws;
|
||||||
|
wsproto::opcode op;
|
||||||
|
beast::streambuf sb;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
data(socket_type&& sock_)
|
||||||
|
: ws(std::move(sock_))
|
||||||
|
, id([]
|
||||||
|
{
|
||||||
|
static int n = 0;
|
||||||
|
return ++n;
|
||||||
|
}())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
data(socket_type&& sock_,
|
||||||
|
endpoint_type const& ep_)
|
||||||
|
: ep(ep_)
|
||||||
|
, ws(std::move(sock_))
|
||||||
|
, id([]
|
||||||
|
{
|
||||||
|
static int n = 0;
|
||||||
|
return ++n;
|
||||||
|
}())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Peer(Peer&&) = default;
|
||||||
|
Peer(Peer const&) = default;
|
||||||
|
Peer& operator=(Peer&&) = delete;
|
||||||
|
Peer& operator=(Peer const&) = delete;
|
||||||
|
|
||||||
|
struct identity
|
||||||
|
{
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<true, Body, Headers>& req)
|
||||||
|
{
|
||||||
|
req.headers.replace("User-Agent", "async_echo_client");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<false, Body, Headers>& resp)
|
||||||
|
{
|
||||||
|
resp.headers.replace("Server", "async_echo_server");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
explicit
|
||||||
|
Peer(socket_type&& sock, Args&&... args)
|
||||||
|
: d_(std::make_shared<data>(
|
||||||
|
std::forward<socket_type>(sock),
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.ws.set_option(decorate(identity{}));
|
||||||
|
d.ws.set_option(read_message_max(64 * 1024 * 1024));
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
if(! d.ep)
|
||||||
|
{
|
||||||
|
d.ws.async_accept(std::move(*this));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d.state = 4;
|
||||||
|
d.ws.next_layer().async_connect(
|
||||||
|
*d.ep, std::move(*this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code ec)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
switch(d_->state)
|
||||||
|
{
|
||||||
|
// did accept
|
||||||
|
case 0:
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_accept");
|
||||||
|
|
||||||
|
// start
|
||||||
|
case 1:
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_handshake");
|
||||||
|
d.sb.consume(d.sb.size());
|
||||||
|
// read message
|
||||||
|
d.state = 2;
|
||||||
|
d.ws.async_read(d.op, d.sb, std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// got message
|
||||||
|
case 2:
|
||||||
|
if(ec == wsproto::error::closed)
|
||||||
|
return;
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_read");
|
||||||
|
// write message
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.set_option(wsproto::message_type(d.op));
|
||||||
|
d.ws.async_write(d.sb.data(), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// connected
|
||||||
|
case 4:
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "async_connect");
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.async_handshake(
|
||||||
|
d.ep->address().to_string() + ":" +
|
||||||
|
std::to_string(d.ep->port()),
|
||||||
|
"/", std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec != wsproto::error::closed)
|
||||||
|
std::cerr << "#" << d_->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(&async_echo_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
Peer{std::move(sock)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
53
examples/wsproto_echo.cpp
Normal file
53
examples/wsproto_echo.cpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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 "wsproto_async_echo_peer.h"
|
||||||
|
#include "wsproto_sync_echo_peer.h"
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
|
||||||
|
beast::wsproto::async_echo_peer s1(true, endpoint_type{
|
||||||
|
address_type::from_string("127.0.0.1"), 6000 }, 4);
|
||||||
|
|
||||||
|
beast::wsproto::sync_echo_peer s2(true, endpoint_type{
|
||||||
|
address_type::from_string("127.0.0.1"), 6001 });
|
||||||
|
|
||||||
|
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& ec,
|
||||||
|
int signal_number)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
stop = true;
|
||||||
|
cv.notify_one();
|
||||||
|
});
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.wait(lock, [&]{ return stop; });
|
||||||
|
}
|
||||||
179
examples/wsproto_sync_echo_peer.h
Normal file
179
examples/wsproto_sync_echo_peer.h
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
#define BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
#include <beast/wsproto.h>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
// Synchronous WebSocket echo client/server
|
||||||
|
//
|
||||||
|
class sync_echo_peer
|
||||||
|
{
|
||||||
|
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:
|
||||||
|
boost::asio::io_service ios_;
|
||||||
|
socket_type sock_;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
std::thread thread_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
sync_echo_peer(bool server, endpoint_type ep)
|
||||||
|
: 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_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
thread_ = std::thread{[&]{ ios_.run(); }};
|
||||||
|
}
|
||||||
|
|
||||||
|
~sync_echo_peer()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
ios_.dispatch(
|
||||||
|
[&]{ acceptor_.close(ec); });
|
||||||
|
thread_.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
std::cerr <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
fail(int id, error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
std::cerr << "#" << std::to_string(id) << " " <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
maybe_throw(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
fail(ec, what);
|
||||||
|
throw ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_accept(error_code ec)
|
||||||
|
{
|
||||||
|
if(ec == boost::asio::error::operation_aborted)
|
||||||
|
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(&sync_echo_peer::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct identity
|
||||||
|
{
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<true, Body, Headers>& req)
|
||||||
|
{
|
||||||
|
req.headers.replace("User-Agent", "sync_echo_client");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
operator()(http::message<false, Body, Headers>& resp)
|
||||||
|
{
|
||||||
|
resp.headers.replace("Server", "sync_echo_server");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
do_peer(int id, socket_type&& sock)
|
||||||
|
{
|
||||||
|
wsproto::socket<socket_type> ws(std::move(sock));
|
||||||
|
ws.set_option(decorate(identity{}));
|
||||||
|
ws.set_option(read_message_max(64 * 1024 * 1024));
|
||||||
|
error_code ec;
|
||||||
|
ws.accept(ec);
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
fail(id, ec, "accept");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
wsproto::opcode op;
|
||||||
|
beast::streambuf sb;
|
||||||
|
ws.read(op, sb, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
ws.set_option(wsproto::message_type(op));
|
||||||
|
ws.write(sb.data(), ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(ec && ec != wsproto::error::closed)
|
||||||
|
{
|
||||||
|
fail(id, ec, "read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -7,14 +7,14 @@
|
|||||||
|
|
||||||
import os ;
|
import os ;
|
||||||
|
|
||||||
path-constant main : ../../beast/unit_test/src/main.cpp ;
|
path-constant test_main : ../beast/unit_test/src/main.cpp ;
|
||||||
|
|
||||||
unit-test all :
|
unit-test all :
|
||||||
append_buffers.cpp
|
append_buffers.cpp
|
||||||
asio.cpp
|
asio.cpp
|
||||||
async_completion.cpp
|
async_completion.cpp
|
||||||
basic_streambuf.cpp
|
basic_streambuf.cpp
|
||||||
../basic_headers.cpp
|
basic_headers.cpp
|
||||||
bind_handler.cpp
|
bind_handler.cpp
|
||||||
buffers_adapter.cpp
|
buffers_adapter.cpp
|
||||||
buffers_debug.cpp
|
buffers_debug.cpp
|
||||||
@@ -26,5 +26,5 @@ unit-test all :
|
|||||||
streambuf.cpp
|
streambuf.cpp
|
||||||
streambuf_readstream.cpp
|
streambuf_readstream.cpp
|
||||||
type_check.cpp
|
type_check.cpp
|
||||||
$(main)
|
$(test_main)
|
||||||
;
|
;
|
||||||
358
test/beast_wsproto_ws_test.cpp
Normal file
358
test/beast_wsproto_ws_test.cpp
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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/wsproto/src/test/async_echo_peer.h>
|
||||||
|
#include <beast/wsproto/src/test/sync_echo_peer.h>
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
#include <beast/unit_test/thread.h>
|
||||||
|
#include <beast/http.h>
|
||||||
|
#include <boost/asio/spawn.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace wsproto {
|
||||||
|
|
||||||
|
class ws_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;
|
||||||
|
using yield_context = boost::asio::yield_context;
|
||||||
|
|
||||||
|
endpoint_type ep_;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// opcodes for creating the test plans
|
||||||
|
|
||||||
|
// concurrent read and write
|
||||||
|
struct case_1{};
|
||||||
|
|
||||||
|
// write a bad frame and shut down
|
||||||
|
struct case_2{};
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class coro_peer
|
||||||
|
{
|
||||||
|
error_code ec_;
|
||||||
|
boost::asio::io_service ios_;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
socket_type sock_;
|
||||||
|
socket<socket_type&> ws_;
|
||||||
|
opcode::value op_;
|
||||||
|
beast::streambuf rb_;
|
||||||
|
beast::streambuf wb_;
|
||||||
|
yield_context* yield_;
|
||||||
|
int state_ = 0;
|
||||||
|
//unit_test::suite& test_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
coro_peer(coro_peer&&) = default;
|
||||||
|
coro_peer(coro_peer const&) = delete;
|
||||||
|
coro_peer& operator=(coro_peer&&) = delete;
|
||||||
|
coro_peer& operator=(coro_peer const&) = delete;
|
||||||
|
|
||||||
|
template<class... Ops>
|
||||||
|
coro_peer(bool server, endpoint_type ep,
|
||||||
|
unit_test::suite& test, Ops const&... ops)
|
||||||
|
: acceptor_(ios_)
|
||||||
|
, sock_(ios_)
|
||||||
|
, ws_(sock_)
|
||||||
|
//, test_(test)
|
||||||
|
{
|
||||||
|
if(server)
|
||||||
|
{
|
||||||
|
acceptor_.open(ep.protocol());
|
||||||
|
acceptor_.bind(ep);
|
||||||
|
acceptor_.listen(
|
||||||
|
boost::asio::socket_base::max_connections);
|
||||||
|
boost::asio::spawn(ios_,
|
||||||
|
[=](auto yield)
|
||||||
|
{
|
||||||
|
yield_ = &yield;
|
||||||
|
state_ = 10;
|
||||||
|
acceptor_.async_accept(sock_, (*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return this->fail("accept");
|
||||||
|
state_ = 20;
|
||||||
|
ws_.async_accept((*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return this->fail("ws.accept");
|
||||||
|
this->invoke(ops...);
|
||||||
|
state_ = -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boost::asio::spawn(ios_,
|
||||||
|
[=](auto yield)
|
||||||
|
{
|
||||||
|
yield_ = &yield;
|
||||||
|
state_ = 30;
|
||||||
|
sock_.async_connect(ep, (*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return this->fail("connect");
|
||||||
|
state_ = 40;
|
||||||
|
ws_.async_handshake(ep.address().to_string() +
|
||||||
|
std::to_string(ep.port()), "/", (*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return this->fail("handshake");
|
||||||
|
this->invoke(ops...);
|
||||||
|
state_ = -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~coro_peer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
state() const
|
||||||
|
{
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run_one()
|
||||||
|
{
|
||||||
|
ios_.run_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
step_to(int to = 0)
|
||||||
|
{
|
||||||
|
while(state_ != to)
|
||||||
|
ios_.run_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class String>
|
||||||
|
void fail(String const& s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke_1(case_1)
|
||||||
|
{
|
||||||
|
async_read(ws_, op_, rb_,
|
||||||
|
[&](auto ec)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
return this->fail(ec);
|
||||||
|
rb_.consume(rb_.size());
|
||||||
|
});
|
||||||
|
state_ = 100;
|
||||||
|
async_write(ws_, opcode::text,
|
||||||
|
boost::asio::null_buffers{}, (*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return fail("write");
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke_1(case_2)
|
||||||
|
{
|
||||||
|
detail::frame_header fh;
|
||||||
|
fh.op = opcode::rsv5; // bad opcode
|
||||||
|
fh.fin = true;
|
||||||
|
fh.mask = true;
|
||||||
|
fh.rsv1 = false;
|
||||||
|
fh.rsv2 = false;
|
||||||
|
fh.rsv3 = false;
|
||||||
|
fh.len = 0;
|
||||||
|
fh.key = 0;
|
||||||
|
detail::write(wb_, fh);
|
||||||
|
state_ = 200;
|
||||||
|
boost::asio::async_write(
|
||||||
|
ws_.next_layer(), wb_.data(),
|
||||||
|
(*yield_)[ec_]);
|
||||||
|
if(ec_)
|
||||||
|
return fail("write");
|
||||||
|
ws_.next_layer().shutdown(
|
||||||
|
socket_type::shutdown_both, ec_);
|
||||||
|
if(ec_)
|
||||||
|
return fail("shutdown");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
invoke()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Op, class... Ops>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
invoke(Op op, Ops const&... ops)
|
||||||
|
{
|
||||||
|
invoke_1(op);
|
||||||
|
invoke(ops...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
testInvokable()
|
||||||
|
{
|
||||||
|
endpoint_type const ep{
|
||||||
|
address_type::from_string(
|
||||||
|
"127.0.0.1"), 6000};
|
||||||
|
coro_peer server(true, ep, *this, case_1{});
|
||||||
|
coro_peer client(false, ep, *this, case_2{});
|
||||||
|
server.step_to(10); // async_accept
|
||||||
|
client.step_to(30); // async_connect
|
||||||
|
server.step_to(20); // async_accept(ws)
|
||||||
|
client.step_to(40); // async_handshake
|
||||||
|
server.step_to(100); // case_1
|
||||||
|
client.step_to(200); // case_2
|
||||||
|
client.step_to(-1);
|
||||||
|
server.step_to(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
maybe_fail(error_code const& ec, std::string const& what)
|
||||||
|
{
|
||||||
|
expect(! ec, what + ": " + ec.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
maybe_throw(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
maybe_fail(ec, what);
|
||||||
|
throw ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Buffers>
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
buffers_to_string(Buffers const& bs)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
std::string s;
|
||||||
|
s.reserve(buffer_size(bs));
|
||||||
|
for(auto const& b : bs)
|
||||||
|
s.append(buffer_cast<char const*>(b),
|
||||||
|
buffer_size(b));
|
||||||
|
for(auto i = s.size(); i-- > 0;)
|
||||||
|
if(s[i] == '\r')
|
||||||
|
s.replace(i, 1, "\\r");
|
||||||
|
else if(s[i] == '\n')
|
||||||
|
s.replace(i, 1, "\\n\n");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
makeRequest(endpoint_type ep, std::string const& s)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
boost::asio::io_service ios;
|
||||||
|
boost::asio::ip::tcp::socket sock(ios);
|
||||||
|
sock.connect(ep);
|
||||||
|
write(sock, append_buffers(
|
||||||
|
buffer(s), buffer("\r\n")));
|
||||||
|
|
||||||
|
using namespace http;
|
||||||
|
parsed_response<string_body> m;
|
||||||
|
streambuf sb;
|
||||||
|
read(sock, sb, m);
|
||||||
|
return m.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
expectStatus(endpoint_type ep,
|
||||||
|
int status, std::string const& s)
|
||||||
|
{
|
||||||
|
expect(makeRequest(ep, s) == status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testHandshake(endpoint_type ep)
|
||||||
|
{
|
||||||
|
expectStatus(ep, 400, "GET / HTTP/1.0\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
syncEchoClient(endpoint_type ep)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
error_code ec;
|
||||||
|
boost::asio::io_service ios;
|
||||||
|
wsproto::socket<socket_type> ws(ios);
|
||||||
|
ws.next_layer().connect(ep, ec);
|
||||||
|
maybe_fail(ec, "connect");
|
||||||
|
ws.handshake(ep.address().to_string(), "/", ec);
|
||||||
|
maybe_fail(ec, "upgrade");
|
||||||
|
std::string const s = "Hello, world!";
|
||||||
|
ws.write(wsproto::opcode::text, true, buffer(s), ec);
|
||||||
|
maybe_fail(ec, "write");
|
||||||
|
boost::asio::streambuf sb;
|
||||||
|
wsproto::opcode::value op;
|
||||||
|
read(ws, op, sb, ec);
|
||||||
|
maybe_fail(ec, "read");
|
||||||
|
if(! ec)
|
||||||
|
expect(op == wsproto::opcode::text);
|
||||||
|
expect(buffers_to_string(sb.data()) == s);
|
||||||
|
sb.consume(sb.size());
|
||||||
|
ws.close({}, ec);
|
||||||
|
maybe_fail(ec, "close");
|
||||||
|
while(! ec)
|
||||||
|
{
|
||||||
|
read(ws, op, sb, ec);
|
||||||
|
if(! ec)
|
||||||
|
sb.consume(sb.size());
|
||||||
|
}
|
||||||
|
if(ec != error::closed)
|
||||||
|
maybe_fail(ec, "teardown");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
//testInvokable();
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
endpoint_type ep{
|
||||||
|
address_type::from_string("127.0.0.1"), 6000};
|
||||||
|
testcase("Echo Server");
|
||||||
|
test::sync_echo_peer s(true, ep, *this);
|
||||||
|
testHandshake(ep);
|
||||||
|
syncEchoClient(ep);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
endpoint_type ep{
|
||||||
|
address_type::from_string("127.0.0.1"), 6001};
|
||||||
|
testcase("Async Echo Server");
|
||||||
|
test::async_echo_peer s(true, ep, *this);
|
||||||
|
//testHandshake(ep);
|
||||||
|
syncEchoClient(ep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(ws,asio,beast);
|
||||||
|
|
||||||
|
} // wsproto
|
||||||
|
} // beast
|
||||||
43
test/ssl_error.cpp
Normal file
43
test/ssl_error.cpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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/asio/ssl_error.h>
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
class ssl_error_test : public unit_test::suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
boost::system::error_code ec =
|
||||||
|
boost::system::error_code (335544539,
|
||||||
|
boost::asio::error::get_ssl_category ());
|
||||||
|
std::string const s = beast::error_message_with_ssl(ec);
|
||||||
|
expect(s == " (20,0,219) error:140000DB:SSL routines:SSL routines:short read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(ssl_error,asio,beast);
|
||||||
|
|
||||||
|
} // beast
|
||||||
@@ -67,7 +67,7 @@ public:
|
|||||||
std::integral_constant<bool, Move>;
|
std::integral_constant<bool, Move>;
|
||||||
using propagate_on_container_swap =
|
using propagate_on_container_swap =
|
||||||
std::integral_constant<bool, Swap>;
|
std::integral_constant<bool, Swap>;
|
||||||
|
|
||||||
template<class U>
|
template<class U>
|
||||||
struct rebind
|
struct rebind
|
||||||
{
|
{
|
||||||
@@ -122,7 +122,7 @@ public:
|
|||||||
::operator delete(p);
|
::operator delete(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
id() const
|
id() const
|
||||||
{
|
{
|
||||||
return id_;
|
return id_;
|
||||||
@@ -274,11 +274,6 @@ public:
|
|||||||
sb_type sb3(sb, alloc_type{});
|
sb_type sb3(sb, alloc_type{});
|
||||||
//expect(sb3.get_allocator().id() == 3);
|
//expect(sb3.get_allocator().id() == 3);
|
||||||
}
|
}
|
||||||
{
|
|
||||||
using alloc_type =
|
|
||||||
test_allocator<char, false, false, false, false>;
|
|
||||||
using sb_type = basic_streambuf<alloc_type>;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void run() override
|
void run() override
|
||||||
Reference in New Issue
Block a user