mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
1ab7a2f Set version to 1.0.0-b22 2eb4b0c Fix code sample in websocket.qbk 58802f4 Fix typos in design.qbk 19dc4bb Update documentation examples 10dbc5b Disable Boost.Coroutine deprecation warning 01c76c7 Fix websocket stream read documentation d152c96 Update README.md example programs 995d86f Avoid copies in handler_alloc 851cb62 Add handler helpers 114175c Implement asio dealloc-before-invoke guarantee: 681db2e Add missing include 7db3c6e Fix broken Intellisense (MSVC) 09c183d Set version to 1.0.0-b21 1cb01fe Remove extraneous includes 62e65ed Set version to 1.0.0-b20 45eaa8c Increase utf8 checker code coverage 9ff1a27 Add zlib module: a0a3359 Refactor HTTP identifier names (API Change): 79be7f8 Set version to 1.0.0-b19 eda1120 Tidy up internal name 4130ad4 Better buffer_cat: f94f21d Fix consuming_buffers value_type (API Change): 2c524b4 prepared_buffers is private (API Change) df2a108 Fix prepare_buffers value_type: a4af9d6 Use boost::lexical_cast instead of std::to_string 62d670b Fix with_body example: a63bd84 Increase code coverage 84a6775 Boost library min/max guidance: 02feea5 Add read, async_read for message_headers: f224585 Add write, async_write, operator<< for message_headers: ea48bcf Make chunk_encode public: f6dd744 Refactor message and message_headers declarations: 9fd8aed Move sync_ostream to core/detail c98b2d3 Optimize mask operations d4dfc1a Optimize utf8 validation 7b4de4b Set version to 1.0.0-b18 feb5204 Add websocket::stream pong and async_pong d4ffde5 Close connection during async_read on close frame: 644d518 Move clamp to core 427ba38 Fix write_frame masking and auto-fragment handling 54a51b1 Write buffer option does not change capacity 591dbc0 Meet DynamicBuffer requirements for static_streambuf 46d5e72 Reorganize source files and definitions efa4b8f Override incremental link flags: eef6e86 Higher optimization settings for MSVC builds b6f3a36 Check invariants in parse_op: 47b0fa6 Remove unused field in test 8b8e57e unit_test improvements: e907252 Clean up message docs 1e3543f Set version to 1.0.0-b17 de97a69 Trim unused code 796b484 Doc fixes 95c37e2 Fix unused parameter warnings and missing includes: 8b0d285 Refactor read_size_helper 97a9dcb Improve websocket example in README.md 236caef Engaged invokable is destructible: d107ba1 Add headers_parser: 2f90627 Fix handling of body_what::pause in basic_parser_v1 9353d04 Add basic_parser_v1::reset 658e03c Add on_body_what parser callback (API Change): 50bd446 Fix parser traits detection (API Change): df8d306 Tidy up documentation: 47105f8 Tidy up basic_headers for documentation ada1f60 Refine message class hierarchy: cf43f51 Rework HTTP concepts (API Change): 8a261ca HTTP Reader (API Change): 183055a Parser callbacks may not throw (API Change) ebebe52 Add basic_streambuf::alloc_size c9cd171 Fix basic_streambuf::capacity 0eb0e48 Tidying: c5c436d Change implicit_value to default_value 01f939d Set version to 1.0.0-b16 206d0a9 Fix websocket failure tests 6b4fb28 Fix Writer exemplar in docs 4224a3a Relax ForwardIterator requirements in FieldSequence 14d7f8d Refactor base_parser_v1 callback traits: d812344 Add pause option to on_headers interface: c59bd53 Improve first line serialization 78ff20b Constrain parser_v1 constructor 2765a67 Refine Parser concept: c329d33 Fix on_headers called twice from basic_parser_v1 55c4c93 Put back missing Design section in docs 90cec54 Make auto_fragment a boolean option 03642fb Rename to write_buffer_size 0ca8964 Frame processing routines are member functions d99dfb3 Make value optional in param-list 325f579 Set version to 1.0.0-b15 c54762a Fix handling empty HTTP headers in parser_v1.hpp c39cc06 Regression test for empty headers 60e637b Tidy up error types: d54d597 Tidy up DynamicBuffer requirements 707fb5e Fix doc reference section 38af0f7 Fix message_v1 constructor 027c4e8 Add Secure WebSocket example 5baaa49 Add HTTPS example 076456b rfc7230 section 3.3.2 compliance a09a044 Use bin/sh 1ff192d Update README.md for CppCon 2016 presentation 70b8555 Set version to 1.0.0-b14 b4a8342 Update and tidy documentation 8607af5 Update README.md 4abb43e Use BOOST_ASSERT b5bffee Don't rely on undefined behavior 8ee7a21 Better WebSocket decorator: 38f0d95 Update build scripts for MSVC, MinGW 2a5b116 Fix error handling in server examples 4c7065a Add missing rebind to handler_alloc git-subtree-dir: src/beast git-subtree-split: 1ab7a2f04ca9a0b35f2032877cab78d94e96ebad
480 lines
16 KiB
Plaintext
480 lines
16 KiB
Plaintext
[/
|
|
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:websocket WebSocket]
|
|
|
|
[block '''
|
|
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
|
|
<member><link linkend="beast.websocket.creation">Creation</link></member>
|
|
<member><link linkend="beast.websocket.connections">Making connections</link></member>
|
|
<member><link linkend="beast.websocket.handshaking">Handshaking</link></member>
|
|
<member><link linkend="beast.websocket.messages">Messages</link></member>
|
|
<member><link linkend="beast.websocket.frames">Frames</link></member>
|
|
<member><link linkend="beast.websocket.control">Control Frames</link></member>
|
|
<member><link linkend="beast.websocket.buffers">Buffers</link></member>
|
|
<member><link linkend="beast.websocket.async">Asynchronous interface</link></member>
|
|
<member><link linkend="beast.websocket.io_service">The io_service</link></member>
|
|
<member><link linkend="beast.websocket.threads">Thread Safety</link></member>
|
|
</simplelist></entry></row></tbody></tgroup></informaltable>
|
|
''']
|
|
|
|
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.WebSocket 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]
|
|
|
|
[note
|
|
The following documentation assumes familiarity with both
|
|
Boost.Asio and the WebSocket protocol specification described in __rfc6455__.
|
|
]
|
|
|
|
|
|
|
|
|
|
[section:creation Creation]
|
|
|
|
The interface to Beast's WebSocket implementation is a single template
|
|
class [link beast.ref.websocket__stream `beast::websocket::stream`] which
|
|
wraps a "next layer" object. The next layer object must meet the requirements
|
|
of [link beast.ref.streams.SyncStream [*`SyncReadStream`]] if synchronous
|
|
operations are performed, or
|
|
[link beast.ref.streams.AsyncStream [*`AsyncStream`]] if asynchronous
|
|
operations are performed, or both. Arguments supplied during construction are
|
|
passed to next layer's constructor. Here we declare a websocket stream over
|
|
a TCP/IP socket with ownership of the socket:
|
|
```
|
|
boost::asio::io_service ios;
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
```
|
|
|
|
[heading Using SSL]
|
|
|
|
To use WebSockets over SSL, choose an SSL stream for the next layer template
|
|
argument when constructing the stream.
|
|
```
|
|
#include <beast/websocket/ssl.hpp>
|
|
#include <beast/websocket.hpp>
|
|
#include <boost/asio/ssl.hpp>
|
|
|
|
boost::asio::io_service ios;
|
|
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
|
|
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ws{ios, ctx};
|
|
```
|
|
|
|
[note
|
|
When creating websocket stream objects using SSL, it is necessary
|
|
to include the file `<beast/websocket/ssl.hpp>`.
|
|
]
|
|
|
|
[heading Non-owning references]
|
|
|
|
For servers that can handshake in multiple protocols, it may be desired
|
|
to wrap an object that already exists. This socket can be moved in:
|
|
```
|
|
boost::asio::ip::tcp::socket&& sock;
|
|
...
|
|
beast::websocket::stream<boost::asio::ip::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:
|
|
```
|
|
boost::asio::ip::tcp::socket sock;
|
|
...
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
|
```
|
|
|
|
The layer being wrapped can be accessed through the websocket's "next layer",
|
|
permitting callers to interact directly with its interface.
|
|
```
|
|
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
|
|
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws{ios, ctx};
|
|
...
|
|
ws.next_layer().shutdown(); // ssl::stream shutdown
|
|
```
|
|
|
|
[warning
|
|
Initiating read and write operations on the next layer while
|
|
stream operations are being performed can break invariants, and
|
|
result in undefined behavior.
|
|
]
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:connections Making connections]
|
|
|
|
Connections are established by using the interfaces which already exist
|
|
for the next layer. For example, making an outgoing connection:
|
|
```
|
|
std::string const host = "mywebapp.com";
|
|
boost::asio::io_service ios;
|
|
boost::asio::ip::tcp::resolver r{ios};
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
boost::asio::connect(ws.next_layer(),
|
|
r.resolve(boost::asio::ip::tcp::resolver::query{host, "ws"}));
|
|
```
|
|
|
|
Accepting an incoming connection:
|
|
```
|
|
void do_accept(boost::asio::ip::tcp::acceptor& acceptor)
|
|
{
|
|
beast::websocket::stream<boost::asio::ip::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.
|
|
[link beast.ref.websocket__stream.handshake `handshake`] is used to send the
|
|
request with the required host and resource strings.
|
|
```
|
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
...
|
|
ws.set_option(beast::websocket::keep_alive(true));
|
|
ws.handshake("ws.example.com:80", "/cgi-bin/bitcoin-prices");
|
|
```
|
|
|
|
The [link beast.ref.websocket__stream `stream`] 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:
|
|
```
|
|
beast::websocket::stream<boost::asio::ip::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(boost::asio::ip::tcp::socket& sock)
|
|
{
|
|
boost::asio::streambuf sb;
|
|
boost::asio::read_until(sock, sb, "\r\n\r\n");
|
|
...
|
|
beast::websocket::stream<boost::asio::ip::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(boost::asio::ip::tcp::socket& sock)
|
|
{
|
|
boost::asio::streambuf sb;
|
|
beast::http::request<http::empty_body> request;
|
|
beast::http::read(sock, sb, request);
|
|
if(beast::http::is_upgrade(request))
|
|
{
|
|
websocket::stream<ip::tcp::socket&> ws{sock};
|
|
ws.accept(request);
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
[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(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
|
|
{
|
|
beast::streambuf sb;
|
|
beast::websocket::opcode::value op;
|
|
ws.read(op, sb);
|
|
|
|
ws.set_option(beast::websocket::message_type{op});
|
|
ws.write(sb.data());
|
|
sb.consume(sb.size());
|
|
}
|
|
```
|
|
|
|
[important
|
|
Calls to [link beast.ref.websocket__stream.set_option `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(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
|
|
{
|
|
beast::streambuf sb;
|
|
beast::websocket::frame_info fi;
|
|
for(;;)
|
|
{
|
|
ws.read_frame(fi, sb);
|
|
if(fi.fin)
|
|
break;
|
|
}
|
|
ws.set_option(beast::websocket::message_type{fi.op});
|
|
beast::consuming_buffers<
|
|
beast::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:control Control Frames]
|
|
|
|
Control frames are small (less than 128 bytes) messages entirely contained
|
|
in an individual WebSocket frame. They may be sent at any time by either
|
|
peer on an established connection, and can appear in between continuation
|
|
frames for a message. There are three types of control frames: ping, pong,
|
|
and close.
|
|
|
|
A sent ping indicates a request that the sender wants to receive a pong. A
|
|
pong is a response to a ping. Pongs may be sent unsolicited, at any time.
|
|
One use for an unsolicited pong is to inform the remote peer that the
|
|
session is still active after a long period of inactivity. A close frame
|
|
indicates that the remote peer wishes to close the WebSocket connection.
|
|
The connection is considered gracefully closed when each side has sent
|
|
and received a close frame.
|
|
|
|
During read operations, Beast automatically reads and processes control
|
|
frames. Pings are replied to as soon as possible with a pong, received
|
|
pongs are delivered to the pong callback. The receipt of a close frame
|
|
initiates the WebSocket close procedure, eventually resulting in the error
|
|
code [link beast.ref.websocket__error `error::closed`] being delivered
|
|
to the caller in a subsequent read operation, assuming no other error
|
|
takes place.
|
|
|
|
A consequence of this automatic behavior is that caller-initiated read
|
|
operations can cause socket writes. However, these writes will not
|
|
compete with caller-initiated write operations. For the purposes of
|
|
correctness with respect to the stream invariants, caller-initiated
|
|
read operations still only count as a read. This means that callers can
|
|
have a simultaneous active read and write operation in progress, while
|
|
the implementation also automatically handles control frames.
|
|
|
|
[heading Ping and Pong Frames]
|
|
|
|
Ping and pong messages are control frames which may be sent at any time
|
|
by either peer on an established WebSocket connection. They are sent
|
|
using the functions
|
|
[link beast.ref.websocket__stream.ping `ping`] and
|
|
[link beast.ref.websocket__stream.pong `pong`].
|
|
|
|
To receive pong control frames, callers may register a "pong callback" using
|
|
[link beast.ref.websocket__stream.set_option `set_option`]. The object provided
|
|
with this option should be callable with the following signature:
|
|
```
|
|
void on_pong(ping_data const& payload);
|
|
...
|
|
ws.set_option(pong_callback{&on_pong});
|
|
```
|
|
|
|
When a pong callback is registered, any pongs received through either
|
|
synchronous read functions or asynchronous read functions will invoke the
|
|
pong callback, passing the payload in the pong message as the argument.
|
|
|
|
Unlike regular completion handlers used in calls to asynchronous initiation
|
|
functions, the pong callback only needs to be set once. The callback is not
|
|
reset when a pong is received. The same callback is used for both synchronous
|
|
and asynchronous reads. The pong callback is passive; in order to receive
|
|
pongs, a synchronous or asynchronous stream read function must be active.
|
|
|
|
[note
|
|
When an asynchronous read function receives a pong, the the pong
|
|
callback is invoked in the same manner as that used to invoke the
|
|
final completion handler of the corresponding read function.
|
|
]
|
|
|
|
[heading Close Frames]
|
|
|
|
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
|
|
the [link beast.ref.websocket__stream.close `close`] function:
|
|
```
|
|
ws.close();
|
|
```
|
|
|
|
When the remote peer initiates a close by sending a close frame, Beast
|
|
will handle it for you by causing the next read to return `error::closed`.
|
|
When this error code is delivered, it indicates to the application that
|
|
the WebSocket connection has been closed cleanly, and that the TCP/IP
|
|
connection has been closed. After initiating a close, it is necessary to
|
|
continue reading messages until receiving the error `error::closed`. This
|
|
is because the remote peer may still be sending message and control frames
|
|
before it receives and responds to the close frame.
|
|
|
|
[important
|
|
To receive the [link beast.ref.websocket__error `error::closed`]
|
|
error, a read operation is required.
|
|
]
|
|
|
|
[heading Auto-fragment]
|
|
|
|
To ensure timely delivery of control frames, large messages can be broken up
|
|
into smaller sized frames. The automatic fragment option turns on this
|
|
feature, and the write buffer size option determines the maximum size of
|
|
the fragments:
|
|
```
|
|
...
|
|
ws.set_option(beast::websocket::auto_fragment{true});
|
|
ws.set_option(beast::websocket::write_buffer_size{16384});
|
|
```
|
|
|
|
[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 [link beast.ref.DynamicBuffer [*`DynamicBuffer`]]. This concept is modeled on
|
|
[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/basic_streambuf.html `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:
|
|
```
|
|
websocket::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(websocket::stream<ip::tcp::socket>& ws,
|
|
boost::asio::yield_context yield)
|
|
{
|
|
ws.async_read(sb, yield);
|
|
std::future<websocket::error_code> fut =
|
|
ws.async_write, sb.data(), boost::use_future);
|
|
...
|
|
}
|
|
```
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:io_service The io_service]
|
|
|
|
The creation and operation of the
|
|
[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/io_service.html `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.WebSocket itself does not
|
|
use or require threads.
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:threads Thread Safety]
|
|
|
|
Like a regular asio socket, a [link beast.ref.websocket__stream `stream`] 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]
|