mirror of
https://github.com/boostorg/beast.git
synced 2025-08-03 14:54:32 +02:00
Doc work
This commit is contained in:
@@ -2,6 +2,7 @@ Version 221:
|
|||||||
|
|
||||||
* Rename to async_base, stable_async_base
|
* Rename to async_base, stable_async_base
|
||||||
* role_type is in boost/beast/core/role.hpp (API Change)
|
* role_type is in boost/beast/core/role.hpp (API Change)
|
||||||
|
* Cleanup in test::stream internals
|
||||||
|
|
||||||
Actions Required:
|
Actions Required:
|
||||||
|
|
||||||
|
@@ -7,29 +7,27 @@
|
|||||||
Official repository: https://github.com/boostorg/beast
|
Official repository: https://github.com/boostorg/beast
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:establishing_connections Connecting]
|
[section:establishing_connections Connecting __new__]
|
||||||
|
|
||||||
Connections are established by invoking functions directly on the next layer
|
Before messages can be exchanged, a websocket stream first needs to be
|
||||||
object. For example, to make an outgoing connection using a standard TCP/IP
|
connected, and then to have the websocket handshake performed. The stream
|
||||||
socket:
|
delegates the task of establishing the connection to the next layers.
|
||||||
|
For example, if the next layer is a connectible stream or socket object,
|
||||||
|
it can be accessed to call the necessary function for connecting.
|
||||||
|
Here we make an outbound connection as a client would do.
|
||||||
|
|
||||||
[ws_snippet_6]
|
[code_websocket_1_1]
|
||||||
|
|
||||||
Similarly, to accept an incoming connection using a standard TCP/IP
|
To accept incoming connections, an acceptor is used. The websocket stream
|
||||||
socket, pass the next layer object to the acceptor:
|
may be constructed from the socket returned by the acceptor when an
|
||||||
|
incoming connection is established.
|
||||||
|
|
||||||
[ws_snippet_7]
|
[code_websocket_1_2]
|
||||||
|
|
||||||
When using SSL, which itself wraps a next layer object that is usually a
|
Alternatively, the incoming connection may be accepted directly into
|
||||||
TCP/IP socket, multiple calls to retrieve the next layer may be required.
|
the socket owned by the websocket stream, using this overload of the
|
||||||
In this example, the websocket stream wraps the SSL stream which wraps
|
acceptor member function.
|
||||||
the TCP/IP socket:
|
|
||||||
|
|
||||||
[wss_snippet_3]
|
[code_websocket_1_3]
|
||||||
|
|
||||||
[note
|
|
||||||
Examples use synchronous interfaces for clarity of exposition;
|
|
||||||
signatures for asynchronous operations are also provided.
|
|
||||||
]
|
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
155
doc/qbk/06_websocket/02_handshaking.qbk
Normal file
155
doc/qbk/06_websocket/02_handshaking.qbk
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
[/
|
||||||
|
Copyright (c) 2016-2019 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)
|
||||||
|
|
||||||
|
Official repository: https://github.com/boostorg/beast
|
||||||
|
]
|
||||||
|
|
||||||
|
[section Handshaking]
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[heading Client Role]
|
||||||
|
|
||||||
|
A WebSocket session begins when a client sends the HTTP/1.1
|
||||||
|
[@https://tools.ietf.org/html/rfc7230#section-6.7 Upgrade]
|
||||||
|
request for
|
||||||
|
[@https://tools.ietf.org/html/rfc6455#section-1.3 WebSocket]
|
||||||
|
on an established connection, and the server sends an appropriate response
|
||||||
|
indicating that the request was accepted and that the connection has been
|
||||||
|
upgraded. The Upgrade request must include the
|
||||||
|
[@https://tools.ietf.org/html/rfc7230#section-5.4 Host]
|
||||||
|
field, and the
|
||||||
|
[@https://tools.ietf.org/html/rfc7230#section-5.3 target]
|
||||||
|
of the resource to request.
|
||||||
|
A typical HTTP Upgrade request created and sent by the implementation
|
||||||
|
will look like this:
|
||||||
|
|
||||||
|
[table WebSocket HTTP Upgrade Request
|
||||||
|
[[Wire Format][Description]]
|
||||||
|
[[
|
||||||
|
```
|
||||||
|
GET / HTTP/1.1
|
||||||
|
Host: www.example.com
|
||||||
|
Upgrade: websocket
|
||||||
|
Connection: upgrade
|
||||||
|
Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA==
|
||||||
|
Sec-WebSocket-Version: 13
|
||||||
|
User-Agent: Boost.Beast/216
|
||||||
|
```
|
||||||
|
][
|
||||||
|
The host and target parameters become part of the Host field
|
||||||
|
and request-target in the resulting HTTP request. The key is
|
||||||
|
generated by the implementation. Callers who wish to add,
|
||||||
|
modify, or inspect fields may set the ['decorator] option
|
||||||
|
on the stream (described later).
|
||||||
|
]]]
|
||||||
|
|
||||||
|
The
|
||||||
|
[link beast.ref.boost__beast__websocket__stream `websocket::stream`]
|
||||||
|
member functions
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.handshake `handshake`] and
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.async_handshake `async_handshake`]
|
||||||
|
are used to send the request with the required host and target strings. This
|
||||||
|
code connects to the IP address returned from a hostname lookup, then performs
|
||||||
|
the WebSocket handshake in the client role.
|
||||||
|
|
||||||
|
[code_websocket_2_1]
|
||||||
|
|
||||||
|
When a client receives an HTTP Upgrade response from the server indicating
|
||||||
|
a successful upgrade, the caller may wish to perform additional validation
|
||||||
|
on the received HTTP response message. For example, to check that the
|
||||||
|
response to a basic authentication challenge is valid. To achieve this,
|
||||||
|
overloads of the handshake member function allow the caller to store the
|
||||||
|
received HTTP message in an output reference argument of type
|
||||||
|
[link beast.ref.boost__beast__websocket__response_type `response_type`]
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
[code_websocket_2_2]
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[heading Server Role]
|
||||||
|
|
||||||
|
For servers accepting incoming connections, the
|
||||||
|
[link beast.ref.boost__beast__websocket__stream `websocket::stream`]
|
||||||
|
can read the incoming upgrade request and automatically reply. If the handshake
|
||||||
|
meets the requirements, the stream sends back the upgrade response with a
|
||||||
|
[@https://tools.ietf.org/html/rfc6455#section-4.2.2 ['101 Switching Protocols]]
|
||||||
|
status code. If the handshake does not meet the requirements, or falls outside
|
||||||
|
the range of allowed parameters specified by stream options set previously by
|
||||||
|
the caller, the stream sends back an HTTP response with a status code indicating
|
||||||
|
an error. Depending on the keep alive setting, the connection may remain open
|
||||||
|
for a subsequent handshake attempt. A typical HTTP Upgrade response created and
|
||||||
|
sent by the implementation upon receiving an upgrade request handshake will
|
||||||
|
look like this:
|
||||||
|
|
||||||
|
[table WebSocket Upgrade HTTP Response
|
||||||
|
[[Wire Format][Description]]
|
||||||
|
[[
|
||||||
|
```
|
||||||
|
HTTP/1.1 101 Switching Protocols
|
||||||
|
Upgrade: websocket
|
||||||
|
Connection: upgrade
|
||||||
|
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
|
||||||
|
Server: Boost.Beast
|
||||||
|
```
|
||||||
|
][
|
||||||
|
The
|
||||||
|
[@https://tools.ietf.org/html/rfc6455#section-11.3.3 ['Sec-WebSocket-Accept]]
|
||||||
|
field value is generated from the request in a fashion specified by
|
||||||
|
the WebSocket protocol.
|
||||||
|
]]]
|
||||||
|
|
||||||
|
The
|
||||||
|
[link beast.ref.boost__beast__websocket__stream `stream`]
|
||||||
|
member functions
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.accept `accept`] and
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`]
|
||||||
|
are used to read the WebSocket HTTP Upgrade request handshake from a stream
|
||||||
|
already connected to an incoming peer, and then send the WebSocket HTTP
|
||||||
|
Upgrade response, as shown:
|
||||||
|
|
||||||
|
[code_websocket_2_3]
|
||||||
|
|
||||||
|
[heading Handshake Buffering]
|
||||||
|
|
||||||
|
It is possible for servers to read data from the stream and decide later
|
||||||
|
that the buffered bytes should be interpreted as a WebSocket upgrade
|
||||||
|
request. To address this usage, overloads of
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.accept `accept`] and
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`]
|
||||||
|
which accept an additional buffer sequence parameter are provided.
|
||||||
|
|
||||||
|
In this example, the server reads the initial HTTP request header into a
|
||||||
|
dynamic buffer, then later uses the buffered data to attempt a websocket
|
||||||
|
upgrade.
|
||||||
|
|
||||||
|
[code_websocket_2_4]
|
||||||
|
|
||||||
|
[heading Inspecting HTTP Requests]
|
||||||
|
|
||||||
|
When implementing an HTTP server that also supports WebSocket, the
|
||||||
|
server usually reads the HTTP request from the client. To detect when
|
||||||
|
the incoming HTTP request is a WebSocket Upgrade request, the function
|
||||||
|
[link beast.ref.boost__beast__websocket__is_upgrade `is_upgrade`] may be used.
|
||||||
|
|
||||||
|
Once the caller determines that the HTTP request is a WebSocket Upgrade,
|
||||||
|
additional overloads of
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.accept `accept`] and
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`]
|
||||||
|
are provided which receive the entire HTTP request header as an object
|
||||||
|
to perform the handshake. By reading the request manually, the program
|
||||||
|
can handle normal HTTP requests as well as upgrades. The program may
|
||||||
|
also enforce policies based on the HTTP fields, such as Basic
|
||||||
|
Authentication. In this example, the request is first read in
|
||||||
|
using the HTTP algorithms, and then passed to a newly constructed
|
||||||
|
stream:
|
||||||
|
|
||||||
|
[code_websocket_2_5]
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[endsect]
|
@@ -1,67 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2016-2019 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)
|
|
||||||
|
|
||||||
Official repository: https://github.com/boostorg/beast
|
|
||||||
]
|
|
||||||
|
|
||||||
[section Handshaking (Clients)]
|
|
||||||
|
|
||||||
A WebSocket session begins when a client sends the HTTP/1.1
|
|
||||||
[@https://tools.ietf.org/html/rfc7230#section-6.7 Upgrade]
|
|
||||||
request for
|
|
||||||
[@https://tools.ietf.org/html/rfc6455#section-1.3 websocket],
|
|
||||||
and the server sends an appropriate response indicating that
|
|
||||||
the request was accepted and that the connection has been upgraded.
|
|
||||||
The Upgrade request must include the
|
|
||||||
[@https://tools.ietf.org/html/rfc7230#section-5.4 Host]
|
|
||||||
field, and the
|
|
||||||
[@https://tools.ietf.org/html/rfc7230#section-5.3 target]
|
|
||||||
of the resource to request.
|
|
||||||
A typical HTTP Upgrade request created and sent by the implementation
|
|
||||||
will look like this:
|
|
||||||
|
|
||||||
[table WebSocket HTTP Upgrade Request
|
|
||||||
[[Serialized Octets][Description]]
|
|
||||||
[[
|
|
||||||
```
|
|
||||||
GET / HTTP/1.1
|
|
||||||
Host: www.example.com
|
|
||||||
Upgrade: websocket
|
|
||||||
Connection: upgrade
|
|
||||||
Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA==
|
|
||||||
Sec-WebSocket-Version: 13
|
|
||||||
User-Agent: Boost.Beast/216
|
|
||||||
```
|
|
||||||
][
|
|
||||||
The host and target parameters become part of the Host field
|
|
||||||
and request-target in the resulting HTTP request. The key is
|
|
||||||
generated by the implementation. Callers may add fields or
|
|
||||||
modify fields by providing a ['decorator], described later.
|
|
||||||
]]]
|
|
||||||
|
|
||||||
The
|
|
||||||
[link beast.ref.boost__beast__websocket__stream `stream`]
|
|
||||||
member functions
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.handshake `handshake`] and
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.async_handshake `async_handshake`]
|
|
||||||
are used to send the request with the required host and target strings. The
|
|
||||||
code below sends the WebSocket HTTP Upgrade request, then reads and processes
|
|
||||||
the response:
|
|
||||||
|
|
||||||
[code_websocket_3_client_1]
|
|
||||||
|
|
||||||
When a client receives an HTTP Upgrade response from the server indicating
|
|
||||||
a successful upgrade, the caller may wish to perform additional validation
|
|
||||||
on the received HTTP response message. For example, to check that the
|
|
||||||
response to a basic authentication challenge is valid. To achieve this,
|
|
||||||
overloads of the handshake member function allow the caller to store the
|
|
||||||
received HTTP message in an output reference argument of type
|
|
||||||
[link beast.ref.boost__beast__websocket__response_type `response_type`]
|
|
||||||
as follows:
|
|
||||||
|
|
||||||
[code_websocket_3_client_2]
|
|
||||||
|
|
||||||
[endsect]
|
|
@@ -7,7 +7,9 @@
|
|||||||
Official repository: https://github.com/boostorg/beast
|
Official repository: https://github.com/boostorg/beast
|
||||||
]
|
]
|
||||||
|
|
||||||
[section:decorator Custom HTTP Fields __new__]
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[section:decorator Decorator __new__]
|
||||||
|
|
||||||
For programs which need to modify either the outgoing WebSocket HTTP Upgrade
|
For programs which need to modify either the outgoing WebSocket HTTP Upgrade
|
||||||
request, the outgoing WebSocket HTTP Upgrade response, or both, the stream
|
request, the outgoing WebSocket HTTP Upgrade response, or both, the stream
|
||||||
@@ -20,13 +22,13 @@ uses:
|
|||||||
[table WebSocket Decorator Interface
|
[table WebSocket Decorator Interface
|
||||||
[[Name][Description]]
|
[[Name][Description]]
|
||||||
[[
|
[[
|
||||||
`request_type`
|
[link beast.ref.boost__beast__websocket__request_type `request_type`]
|
||||||
][
|
][
|
||||||
This is the type of the object passed to the decorator to
|
This is the type of the object passed to the decorator to
|
||||||
represent HTTP Upgrade requests.
|
represent HTTP Upgrade requests.
|
||||||
]]
|
]]
|
||||||
[[
|
[[
|
||||||
`response_type`
|
[link beast.ref.boost__beast__websocket__response_type `response_type`]
|
||||||
][
|
][
|
||||||
This is the type of the object passed to the decorator to
|
This is the type of the object passed to the decorator to
|
||||||
represent HTTP Upgrade response.
|
represent HTTP Upgrade response.
|
||||||
@@ -46,29 +48,34 @@ uses:
|
|||||||
|
|
||||||
This declares a normal function which decorates outgoing HTTP requests:
|
This declares a normal function which decorates outgoing HTTP requests:
|
||||||
|
|
||||||
[code_websocket_3_decorator_1b]
|
[code_websocket_3_1]
|
||||||
|
|
||||||
If a decorator is used, it must be set on the stream before any handshaking
|
When using a decorator, it must be set on the stream before any handshaking
|
||||||
takes place. This sets the decorator on the stream, to be used for all
|
takes place. This sets the decorator on the stream, to be used for all
|
||||||
subsequent calls to accept or handshake:
|
subsequent calls to accept or handshake:
|
||||||
|
|
||||||
[code_websocket_3_decorator_1]
|
[code_websocket_3_2]
|
||||||
|
|
||||||
Alternatively, a function object may be used. Small function objects will
|
Alternatively, a function object may be used. Small function objects will
|
||||||
not incur a memory allocation. The follow code declares and sets a function
|
not incur a memory allocation. The follow code declares and sets a function
|
||||||
object as a decorator:
|
object as a decorator:
|
||||||
|
|
||||||
[code_websocket_3_decorator_2]
|
[code_websocket_3_3]
|
||||||
|
|
||||||
A lambda may be used in place of a named function object:
|
A lambda may be used in place of a named function object:
|
||||||
|
|
||||||
[code_websocket_3_decorator_3]
|
[code_websocket_3_4]
|
||||||
|
|
||||||
It also possible for a single decorator to handle both requests and
|
It also possible for a single decorator to handle both requests and
|
||||||
responses, if it is overloaded for both types either as a generic
|
responses, if it is overloaded for both types either as a generic
|
||||||
lambda (C++14 and later) or as a class as shown here:
|
lambda (C++14 and later) or as a class as shown here:
|
||||||
|
|
||||||
[code_websocket_3_decorator_4]
|
[code_websocket_3_5]
|
||||||
|
|
||||||
|
The implementation takes ownership by decay-copy of the invocable object
|
||||||
|
used as the decorator. Move-only types are possible:
|
||||||
|
|
||||||
|
[code_websocket_3_6]
|
||||||
|
|
||||||
[important
|
[important
|
||||||
Undefined behavior results if the decorator modifies the fields
|
Undefined behavior results if the decorator modifies the fields
|
122
doc/qbk/06_websocket/04_messages.qbk
Normal file
122
doc/qbk/06_websocket/04_messages.qbk
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
[/
|
||||||
|
Copyright (c) 2016-2019 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)
|
||||||
|
|
||||||
|
Official repository: https://github.com/boostorg/beast
|
||||||
|
]
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[section Messages __new__]
|
||||||
|
|
||||||
|
Once a websocket session is established, messages can be sent unsolicited by
|
||||||
|
either peer at any time. A message is made up of one or more ['messages frames].
|
||||||
|
Each frame is prefixed with the size of the payload in bytes, followed by the
|
||||||
|
data. A frame also contains a flag (called 'fin') indicating whether or not it
|
||||||
|
is the last frame of the message. When a message is made up from only one frame,
|
||||||
|
it is possible to know immediately what the size of the message will be.
|
||||||
|
Otherwise, the total size of the message can only be determined once
|
||||||
|
the last frame is received.
|
||||||
|
|
||||||
|
The boundaries between frames of a multi-frame message are not not considered
|
||||||
|
part of the message. Intermediaries such as proxies which forward the websocket
|
||||||
|
traffic are free to "reframe" (split frames and combine them) the message in
|
||||||
|
arbitrary ways. These intermediaries include Beast, which can reframe messages
|
||||||
|
automatically in some cases depending on the options set on the stream.
|
||||||
|
|
||||||
|
[caution
|
||||||
|
An algorithm should never depend on the way that incoming or outgoing
|
||||||
|
messages are split up into frames.
|
||||||
|
]
|
||||||
|
|
||||||
|
Messages can be either text or binary. A message sent as text must contain
|
||||||
|
consist of valid utf8, while a message sent as binary may contain arbitrary
|
||||||
|
data. In addition to message frames, websocket provides ['control frames]
|
||||||
|
in the form of ping, pong, and close messages which have a small upper limit
|
||||||
|
on their payload size. Depending on how a message is framed, control frames
|
||||||
|
may have more opportunities to be sent in-between.
|
||||||
|
|
||||||
|
[heading Sending]
|
||||||
|
|
||||||
|
These stream members are used to write websocket messages:
|
||||||
|
|
||||||
|
[table WebSocket Stream Write Operations
|
||||||
|
[[Function][Description]]
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.write `write`],
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.async_write `async_write`]
|
||||||
|
][
|
||||||
|
Send a buffer sequence as a complete message.
|
||||||
|
]
|
||||||
|
][
|
||||||
|
[
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.write_some `write_some`],
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.async_write `async_write_some`]
|
||||||
|
][
|
||||||
|
Send a buffer sequence as part of a message.
|
||||||
|
]
|
||||||
|
]]
|
||||||
|
|
||||||
|
This example shows how to send a buffer sequence as a complete message.
|
||||||
|
|
||||||
|
[code_websocket_4_1]
|
||||||
|
|
||||||
|
The same message could be sent in two or more frames thusly.
|
||||||
|
|
||||||
|
[heading Receiving]
|
||||||
|
|
||||||
|
[table WebSocket Stream Read Operations
|
||||||
|
[[Function][Description]]
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.read `read`],
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.async_read `async_read`]
|
||||||
|
][
|
||||||
|
Read a complete message into a __DynamicBuffer__.
|
||||||
|
]
|
||||||
|
][
|
||||||
|
[
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.read_some.overload2 `read_some`],
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.async_read.overload1 `async_read_some`]
|
||||||
|
][
|
||||||
|
Read part of a message into a __DynamicBuffer__.
|
||||||
|
]
|
||||||
|
][
|
||||||
|
[
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.read_some.overload4 `read_some`],
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.async_read.overload2 `async_read_some`]
|
||||||
|
][
|
||||||
|
Read part of a message into a __MutableBufferSequence__.
|
||||||
|
]
|
||||||
|
]]
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
[code_websocket_4_2]
|
||||||
|
|
||||||
|
[important
|
||||||
|
[link beast.ref.boost__beast__websocket__stream `websocket::stream`]
|
||||||
|
is not thread-safe. Calls to stream member functions must
|
||||||
|
all be made from the same implicit or explicit strand.
|
||||||
|
]
|
||||||
|
|
||||||
|
[heading 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 partial data oriented interface may be used. This
|
||||||
|
example reads and echoes a complete message using this interface:
|
||||||
|
|
||||||
|
[ws_snippet_16]
|
||||||
|
|
||||||
|
[endsect]
|
@@ -1,107 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2016-2019 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)
|
|
||||||
|
|
||||||
Official repository: https://github.com/boostorg/beast
|
|
||||||
]
|
|
||||||
|
|
||||||
[section Handshaking (Servers)]
|
|
||||||
|
|
||||||
A
|
|
||||||
[link beast.ref.boost__beast__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
|
|
||||||
[@https://tools.ietf.org/html/rfc6455#section-4.2.2 ['101 Switching Protocols]]
|
|
||||||
status code. On failure, an error is returned or an exception is thrown.
|
|
||||||
Depending on the keep alive setting, the connection may remain open for
|
|
||||||
a subsequent handshake attempt.
|
|
||||||
A typical HTTP Upgrade response created and sent by the implementation
|
|
||||||
upon receiving an upgrade request handshake will look like this:
|
|
||||||
|
|
||||||
[table WebSocket Upgrade HTTP Response
|
|
||||||
[[Serialized Octets][Description]]
|
|
||||||
[[
|
|
||||||
```
|
|
||||||
HTTP/1.1 101 Switching Protocols
|
|
||||||
Upgrade: websocket
|
|
||||||
Connection: upgrade
|
|
||||||
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
|
|
||||||
Server: Boost.Beast/216
|
|
||||||
```
|
|
||||||
][
|
|
||||||
The
|
|
||||||
[@https://tools.ietf.org/html/rfc6455#section-11.3.3 ['Sec-WebSocket-Accept]]
|
|
||||||
field value is generated from the request in a fashion specified by
|
|
||||||
the WebSocket protocol.
|
|
||||||
]]]
|
|
||||||
|
|
||||||
The
|
|
||||||
[link beast.ref.boost__beast__websocket__stream `stream`]
|
|
||||||
member functions
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.accept `accept`] and
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`]
|
|
||||||
are used to read the WebSocket HTTP Upgrade request handshake from a stream
|
|
||||||
already connected to an incoming peer, and then send the WebSocket HTTP
|
|
||||||
Upgrade response, as shown:
|
|
||||||
|
|
||||||
[code_websocket_3_server_1]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[heading Passing HTTP Requests]
|
|
||||||
|
|
||||||
When implementing an HTTP server that also supports WebSocket, the
|
|
||||||
server usually reads the HTTP request from the client. To detect when
|
|
||||||
the incoming HTTP request is a WebSocket Upgrade request, the function
|
|
||||||
[link beast.ref.boost__beast__websocket__is_upgrade `is_upgrade`] may be used.
|
|
||||||
|
|
||||||
Once the caller determines that the HTTP request is a WebSocket Upgrade,
|
|
||||||
additional overloads of
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.accept `accept`] and
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`]
|
|
||||||
are provided which receive the entire HTTP request header as an object
|
|
||||||
to perform the handshake. By reading the request manually, the program
|
|
||||||
can handle normal HTTP requests as well as upgrades. The program may
|
|
||||||
also enforce policies based on the HTTP fields, such as Basic
|
|
||||||
Authentication. In this example, the request is first read in
|
|
||||||
using the HTTP algorithms, and then passed to a newly constructed
|
|
||||||
stream:
|
|
||||||
|
|
||||||
[code_websocket_3_server_1b]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[heading Buffered Handshakes]
|
|
||||||
|
|
||||||
Sometimes a server implementation wishes to read octets on the stream
|
|
||||||
in order to route the incoming request. For example, a server may read
|
|
||||||
the first 6 octets after accepting an incoming connection to determine
|
|
||||||
if a TLS protocol is being negotiated, and choose a suitable implementation
|
|
||||||
at run-time. In the case where the server wishes to accept the incoming
|
|
||||||
request as an HTTP WebSocket Upgrade request, additional overloads of
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.accept `accept`] and
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`]
|
|
||||||
are provided which receive the additional buffered octets and consume
|
|
||||||
them as part of the handshake.
|
|
||||||
|
|
||||||
In this example, the server reads the initial HTTP message into the
|
|
||||||
specified dynamic buffer as an octet sequence in the buffer's output
|
|
||||||
area, and later uses those octets to attempt an HTTP WebSocket Upgrade:
|
|
||||||
|
|
||||||
[code_websocket_3_server_2]
|
|
||||||
|
|
||||||
The implementation uses a fixed-size storage area to hold buffers passed
|
|
||||||
using these functions. If an application is reaching the limit of the
|
|
||||||
internal buffer size, then the websocket stream may be instantiated with
|
|
||||||
the next layer type of
|
|
||||||
[link beast.ref.boost__beast__buffered_read_stream `buffered_read_stream`]
|
|
||||||
to wrap the underlying stream. The buffered handshake data may be first
|
|
||||||
placed into the buffered read stream, which uses a dynamically sized
|
|
||||||
buffer.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[endsect]
|
|
@@ -56,7 +56,7 @@ To be notified of control frames, callers may register a
|
|||||||
The object provided with this option should be callable with the following
|
The object provided with this option should be callable with the following
|
||||||
signature:
|
signature:
|
||||||
|
|
||||||
[ws_snippet_17]
|
[code_websocket_5_1]
|
||||||
|
|
||||||
When a control callback is registered, it will be invoked for all pings,
|
When a control callback is registered, it will be invoked for all pings,
|
||||||
pongs, and close frames received through either synchronous read functions
|
pongs, and close frames received through either synchronous read functions
|
||||||
@@ -88,7 +88,7 @@ To request a close use a close function such as
|
|||||||
[link beast.ref.boost__beast__websocket__stream.close.overload2 `close`] or
|
[link beast.ref.boost__beast__websocket__stream.close.overload2 `close`] or
|
||||||
[link beast.ref.boost__beast__websocket__stream.async_close `async_close`]:
|
[link beast.ref.boost__beast__websocket__stream.async_close `async_close`]:
|
||||||
|
|
||||||
[ws_snippet_18]
|
[code_websocket_5_2]
|
||||||
|
|
||||||
The close function will send a close frame, read and discard incoming
|
The close function will send a close frame, read and discard incoming
|
||||||
message data until receiving a close frame, and then shut down the
|
message data until receiving a close frame, and then shut down the
|
||||||
@@ -114,6 +114,6 @@ be broken up into smaller sized frames. The automatic fragment option
|
|||||||
turns on this feature, and the write buffer size option determines the
|
turns on this feature, and the write buffer size option determines the
|
||||||
maximum size of the fragments:
|
maximum size of the fragments:
|
||||||
|
|
||||||
[ws_snippet_19]
|
[code_websocket_5_3]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
@@ -1,83 +0,0 @@
|
|||||||
[/
|
|
||||||
Copyright (c) 2016-2019 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)
|
|
||||||
|
|
||||||
Official repository: https://github.com/boostorg/beast
|
|
||||||
]
|
|
||||||
|
|
||||||
[section Send and Receive Messages]
|
|
||||||
|
|
||||||
Interfaces for transacting messages are structured into layers. The
|
|
||||||
highest layer provides ease of use, while lower layers provide
|
|
||||||
additional control and flexibility. The layers are arranged thusly:
|
|
||||||
|
|
||||||
[table
|
|
||||||
[[Level][Read/Write What][Description]]
|
|
||||||
[
|
|
||||||
[[*2]]
|
|
||||||
[
|
|
||||||
message
|
|
||||||
][
|
|
||||||
At the top layer, these functions allow for an entire
|
|
||||||
message to be sent or received. They are designed for
|
|
||||||
ease of use:
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.read.overload2 `read`],
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.write.overload2 `write`],
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.async_read `async_read`], and
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.async_write `async_write`].
|
|
||||||
]
|
|
||||||
][
|
|
||||||
[[*1]]
|
|
||||||
[
|
|
||||||
['partial]
|
|
||||||
][
|
|
||||||
These read functions enable partial message data to be
|
|
||||||
received into a __DynamicBuffer__. They can be configured
|
|
||||||
to perform bounded work:
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.read_some.overload2 `read_some`], and
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.async_read_some.overload1 `async_read_some`].
|
|
||||||
]
|
|
||||||
][
|
|
||||||
[[*0]]
|
|
||||||
[
|
|
||||||
['partial]
|
|
||||||
][
|
|
||||||
At the lowest level these read and write functions enable
|
|
||||||
partial message data to be transacted using a constant or
|
|
||||||
mutable buffer sequence:
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.read_some.overload4 `read_some`],
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.write_some.overload2 `write_some`],
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.async_read_some.overload2 `async_read_some`], and
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.async_write_some `async_write_some`].
|
|
||||||
]
|
|
||||||
]]
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
[ws_snippet_15]
|
|
||||||
|
|
||||||
[important
|
|
||||||
[link beast.ref.boost__beast__websocket__stream `websocket::stream`]
|
|
||||||
is not thread-safe. Calls to stream member functions must
|
|
||||||
all be made from the same implicit or explicit strand.
|
|
||||||
]
|
|
||||||
|
|
||||||
[heading 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 partial data oriented interface may be used. This
|
|
||||||
example reads and echoes a complete message using this interface:
|
|
||||||
|
|
||||||
[ws_snippet_16]
|
|
||||||
|
|
||||||
[endsect]
|
|
14
doc/qbk/06_websocket/06_timeouts.qbk
Normal file
14
doc/qbk/06_websocket/06_timeouts.qbk
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[/
|
||||||
|
Copyright (c) 2016-2019 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)
|
||||||
|
|
||||||
|
Official repository: https://github.com/boostorg/beast
|
||||||
|
]
|
||||||
|
|
||||||
|
[section Timeouts __new__]
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[endsect]
|
@@ -47,7 +47,7 @@ To provide overloads of teardown for a user-defined type, simply declare
|
|||||||
the two free functions with the correct signature, accepting a reference
|
the two free functions with the correct signature, accepting a reference
|
||||||
to the user-defined type as the stream parameter:
|
to the user-defined type as the stream parameter:
|
||||||
|
|
||||||
[ws_snippet_22]
|
[code_websocket_7_1]
|
||||||
|
|
||||||
When the implementation invokes the asynchronous teardown function, it
|
When the implementation invokes the asynchronous teardown function, it
|
||||||
always uses an invokable completion handler. It is not necessary
|
always uses an invokable completion handler. It is not necessary
|
||||||
@@ -61,6 +61,6 @@ into scope with a `using` statement. Then call the customization point
|
|||||||
without namespace qualification, allowing argument-dependent lookup to
|
without namespace qualification, allowing argument-dependent lookup to
|
||||||
take effect:
|
take effect:
|
||||||
|
|
||||||
[ws_snippet_23]
|
[code_websocket_7_2]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
@@ -23,13 +23,13 @@ of the underlying TCP/IP connection.
|
|||||||
|
|
||||||
Asynchronous versions are available for all functions:
|
Asynchronous versions are available for all functions:
|
||||||
|
|
||||||
[ws_snippet_20]
|
[code_websocket_8_1]
|
||||||
|
|
||||||
Calls to asynchronous initiation functions support the extensible asynchronous
|
Calls to asynchronous initiation functions support the extensible asynchronous
|
||||||
model developed by the Boost.Asio author, allowing for traditional completion
|
model developed by the Boost.Asio author, allowing for traditional completion
|
||||||
handlers, stackful or stackless coroutines, and even futures:
|
handlers, stackful or stackless coroutines, and even futures:
|
||||||
|
|
||||||
[ws_snippet_21]
|
[code_websocket_8_1f]
|
||||||
|
|
||||||
The example programs that come with the library demonstrate the usage of
|
The example programs that come with the library demonstrate the usage of
|
||||||
websocket stream operations with all asynchronous varieties.
|
websocket stream operations with all asynchronous varieties.
|
||||||
@@ -59,11 +59,11 @@ following operations to be active at the same time:
|
|||||||
For example, the following code is produces undefined behavior, because the
|
For example, the following code is produces undefined behavior, because the
|
||||||
program is attempting to perform two simultaneous reads:
|
program is attempting to perform two simultaneous reads:
|
||||||
|
|
||||||
[ws_snippet_24]
|
[code_websocket_8_2]
|
||||||
|
|
||||||
However, this code is correct:
|
However, this code is correct:
|
||||||
|
|
||||||
[ws_snippet_25]
|
[code_websocket_8_3]
|
||||||
|
|
||||||
The implementation uses composed asynchronous operations; although
|
The implementation uses composed asynchronous operations; although
|
||||||
some individual operations can perform both reads and writes, this
|
some individual operations can perform both reads and writes, this
|
@@ -98,6 +98,18 @@ strand to invoke all completion handlers:
|
|||||||
|
|
||||||
[code_websocket_2f]
|
[code_websocket_2f]
|
||||||
|
|
||||||
|
If the next layer supports move-construction, then the websocket stream can be
|
||||||
|
constructed from a moved-from object.
|
||||||
|
|
||||||
|
[code_websocket_3f]
|
||||||
|
|
||||||
|
The next layer may be accessed by calling
|
||||||
|
[link beast.ref.boost__beast__websocket__stream.next_layer.overload1 `stream::next_layer`].
|
||||||
|
|
||||||
|
[code_websocket_4f]
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
[heading Using SSL]
|
[heading Using SSL]
|
||||||
|
|
||||||
To use WebSockets over SSL, use an instance of the __ssl_stream__
|
To use WebSockets over SSL, use an instance of the __ssl_stream__
|
||||||
@@ -105,51 +117,41 @@ class template as the template type for the stream. The required
|
|||||||
__io_context__ and __ssl_context__ arguments are forwarded to the
|
__io_context__ and __ssl_context__ arguments are forwarded to the
|
||||||
wrapped stream's constructor:
|
wrapped stream's constructor:
|
||||||
|
|
||||||
[code_websocket_3f]
|
[code_websocket_5f]
|
||||||
|
|
||||||
[important
|
[important
|
||||||
Code which declares websocket stream objects using Asio SSL types
|
Code which declares websocket stream objects using Asio SSL types
|
||||||
must include the file [include_file boost/beast/websocket/ssl.hpp].
|
must include the file [include_file boost/beast/websocket/ssl.hpp].
|
||||||
]
|
]
|
||||||
|
|
||||||
[heading Non-owning References]
|
As before, the underlying SSL stream may be accessed by calling `next_layer`.
|
||||||
|
|
||||||
If a socket type supports move construction, a websocket stream may be
|
[code_websocket_6f]
|
||||||
constructed around the already existing socket by invoking the move
|
|
||||||
constructor signature:
|
|
||||||
|
|
||||||
[ws_snippet_3]
|
With multi-layered streams such as the one declared above, accessing an
|
||||||
|
individual layer can be cumbersome when using chained calls to `next_layer`.
|
||||||
|
The function
|
||||||
|
[link beast.ref.boost__beast__get_lowest_layer `get_lowest_layer`]
|
||||||
|
returns the last stream in a stack of layers in a layered stream. Here we
|
||||||
|
access the lowest layer to cancel all outstanding I/O.
|
||||||
|
|
||||||
Or, the wrapper can be constructed with a non-owning reference. In
|
[code_websocket_7f]
|
||||||
this case, the caller is responsible for managing the lifetime of the
|
|
||||||
underlying socket being wrapped:
|
|
||||||
|
|
||||||
[ws_snippet_4]
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
Once the WebSocket stream wrapper is created, the wrapped object may be
|
|
||||||
accessed by calling
|
|
||||||
[link beast.ref.boost__beast__websocket__stream.next_layer.overload1 `stream::next_layer`]:
|
|
||||||
|
|
||||||
[ws_snippet_5]
|
|
||||||
|
|
||||||
[warning
|
|
||||||
Initiating operations on the next layer while websocket
|
|
||||||
operations are being performed may result in undefined behavior.
|
|
||||||
]
|
|
||||||
|
|
||||||
[heading Non-Blocking Mode]
|
[heading Non-Blocking Mode]
|
||||||
|
|
||||||
Please note that websocket streams do not support non-blocking modes.
|
Please note that websocket streams do not support non-blocking modes.
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
[include 01_connecting.qbk]
|
[include 01_connecting.qbk]
|
||||||
[include 03_client.qbk]
|
[include 02_handshaking.qbk]
|
||||||
[include 04_server.qbk]
|
[include 03_decorator.qbk]
|
||||||
[include 05_decorator.qbk]
|
[include 04_messages.qbk]
|
||||||
[include 06_messages.qbk]
|
[include 05_control_frames.qbk]
|
||||||
[include 07_control.qbk]
|
[include 06_timeouts.qbk]
|
||||||
[include 08_teardown.qbk]
|
[include 07_teardown.qbk]
|
||||||
[include 09_notes.qbk]
|
[include 08_notes.qbk]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
@@ -144,8 +144,16 @@
|
|||||||
[import ../../test/doc/core_3_timeouts.cpp]
|
[import ../../test/doc/core_3_timeouts.cpp]
|
||||||
[import ../../test/doc/core_4_layers.cpp]
|
[import ../../test/doc/core_4_layers.cpp]
|
||||||
[import ../../test/doc/http_10_custom_parser.cpp]
|
[import ../../test/doc/http_10_custom_parser.cpp]
|
||||||
|
[import ../../test/doc/websocket_common.ipp]
|
||||||
[import ../../test/doc/websocket.cpp]
|
[import ../../test/doc/websocket.cpp]
|
||||||
[import ../../test/doc/websocket_3_handshake.cpp]
|
[import ../../test/doc/websocket_1_connecting.cpp]
|
||||||
|
[import ../../test/doc/websocket_2_handshaking.cpp]
|
||||||
|
[import ../../test/doc/websocket_3_decorator.cpp]
|
||||||
|
[import ../../test/doc/websocket_4_messages.cpp]
|
||||||
|
[import ../../test/doc/websocket_5_control_frames.cpp]
|
||||||
|
[import ../../test/doc/websocket_6_timeouts.cpp]
|
||||||
|
[import ../../test/doc/websocket_7_teardown.cpp]
|
||||||
|
[import ../../test/doc/websocket_8_notes.cpp]
|
||||||
|
|
||||||
[import ../../include/boost/beast/core/detect_ssl.hpp]
|
[import ../../include/boost/beast/core/detect_ssl.hpp]
|
||||||
[import ../../test/beast/core/rate_policy.cpp]
|
[import ../../test/beast/core/rate_policy.cpp]
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<!-- CLASS_DETAIL_TEMPLATE BEGIN -->
|
<!-- CLASS_DETAIL_TEMPLATE BEGIN -->
|
||||||
<xsl:when test="type = 'class AsyncStream'">
|
<xsl:when test="type = 'class AsyncStream'">
|
||||||
<xsl:text>class ``[link beast.concepts.streams.AsyncStream [*AsyncStream]]``</xsl:text>
|
<xsl:text>class __AsyncStream__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class AsyncReadStream'">
|
<xsl:when test="type = 'class AsyncReadStream'">
|
||||||
<xsl:text>class __AsyncReadStream__</xsl:text>
|
<xsl:text>class __AsyncReadStream__</xsl:text>
|
||||||
@@ -12,11 +12,11 @@
|
|||||||
<xsl:text>class __Body__</xsl:text>
|
<xsl:text>class __Body__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class BufferSequence'">
|
<xsl:when test="type = 'class BufferSequence'">
|
||||||
<xsl:text>class ``[link beast.concepts.BufferSequence [*BufferSequence]]``</xsl:text>
|
<xsl:text>class __BufferSequence__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="(type = 'class' or type = 'class...') and declname = 'BufferSequence'">
|
<xsl:when test="(type = 'class' or type = 'class...') and declname = 'BufferSequence'">
|
||||||
<xsl:value-of select="type"/>
|
<xsl:value-of select="type"/>
|
||||||
<xsl:text> ``[link beast.concepts.BufferSequence [*BufferSequence]]``</xsl:text>
|
<xsl:text> __BufferSequence__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'CompletionCondition' or type = 'class CompletionCondition'">
|
<xsl:when test="declname = 'CompletionCondition' or type = 'class CompletionCondition'">
|
||||||
<xsl:text>class __CompletionCondition__</xsl:text>
|
<xsl:text>class __CompletionCondition__</xsl:text>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
<xsl:text>class __ConstBufferSequence__</xsl:text>
|
<xsl:text>class __ConstBufferSequence__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'DynamicBuffer' or type = 'class DynamicBuffer'">
|
<xsl:when test="declname = 'DynamicBuffer' or type = 'class DynamicBuffer'">
|
||||||
<xsl:text>class ``[link beast.concepts.DynamicBuffer [*DynamicBuffer]]``</xsl:text>
|
<xsl:text>class __DynamicBuffer__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class EndpointSequence'">
|
<xsl:when test="type = 'class EndpointSequence'">
|
||||||
<xsl:text>class __EndpointSequence__</xsl:text>
|
<xsl:text>class __EndpointSequence__</xsl:text>
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
<xsl:text>class __Executor__</xsl:text>
|
<xsl:text>class __Executor__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class Fields' or substring(type, 1, 13) = 'class Fields '">
|
<xsl:when test="type = 'class Fields' or substring(type, 1, 13) = 'class Fields '">
|
||||||
<xsl:text>class ``[link beast.concepts.Fields [*Fields]]``</xsl:text>
|
<xsl:text>class __Fields__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'Handler' or type = 'class Handler'">
|
<xsl:when test="declname = 'Handler' or type = 'class Handler'">
|
||||||
<xsl:text>class __Handler__</xsl:text>
|
<xsl:text>class __Handler__</xsl:text>
|
||||||
@@ -64,16 +64,16 @@
|
|||||||
<xsl:text>class __RangeConnectHandler__</xsl:text>
|
<xsl:text>class __RangeConnectHandler__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class RatePolicy'">
|
<xsl:when test="type = 'class RatePolicy'">
|
||||||
<xsl:text>class ``[link beast.concepts.RatePolicy [*RatePolicy]]``</xsl:text>
|
<xsl:text>class __RatePolicy__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'ReadHandler' or type = 'class ReadHandler'">
|
<xsl:when test="declname = 'ReadHandler' or type = 'class ReadHandler'">
|
||||||
<xsl:text>class __ReadHandler__</xsl:text>
|
<xsl:text>class __ReadHandler__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'Stream' or type = 'class Stream'">
|
<xsl:when test="declname = 'Stream' or type = 'class Stream'">
|
||||||
<xsl:text>class ``[link beast.concepts.streams.Stream [*Stream]]``</xsl:text>
|
<xsl:text>class __Stream__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class SyncStream'">
|
<xsl:when test="type = 'class SyncStream'">
|
||||||
<xsl:text>class ``[link beast.concepts.streams.SyncStream [*SyncStream]]``</xsl:text>
|
<xsl:text>class __SyncStream__</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'SyncReadStream' or type = 'class SyncReadStream'">
|
<xsl:when test="declname = 'SyncReadStream' or type = 'class SyncReadStream'">
|
||||||
<xsl:text>class __SyncReadStream__</xsl:text>
|
<xsl:text>class __SyncReadStream__</xsl:text>
|
||||||
|
@@ -160,7 +160,7 @@ struct stream::run_write_op
|
|||||||
"WriteHandler type requirements not met");
|
"WriteHandler type requirements not met");
|
||||||
|
|
||||||
++in_->nwrite;
|
++in_->nwrite;
|
||||||
auto const complete_op = [&](error_code ec, std::size_t n)
|
auto const upcall = [&](error_code ec, std::size_t n)
|
||||||
{
|
{
|
||||||
net::post(
|
net::post(
|
||||||
in_->ioc.get_executor(),
|
in_->ioc.get_executor(),
|
||||||
@@ -171,22 +171,16 @@ struct stream::run_write_op
|
|||||||
error_code ec;
|
error_code ec;
|
||||||
std::size_t n = 0;
|
std::size_t n = 0;
|
||||||
if(in_->fc && in_->fc->fail(ec))
|
if(in_->fc && in_->fc->fail(ec))
|
||||||
{
|
return upcall(ec, n);
|
||||||
return complete_op(ec, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A request to write 0 bytes to a stream is a no-op.
|
// A request to write 0 bytes to a stream is a no-op.
|
||||||
if(buffer_size(buffers) == 0)
|
if(buffer_size(buffers) == 0)
|
||||||
{
|
return upcall(ec, n);
|
||||||
return complete_op(ec, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
// connection closed
|
// connection closed
|
||||||
auto out = out_.lock();
|
auto out = out_.lock();
|
||||||
if(! out)
|
if(! out)
|
||||||
{
|
return upcall(net::error::connection_reset, n);
|
||||||
return complete_op(net::error::connection_reset, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy buffers
|
// copy buffers
|
||||||
n = std::min<std::size_t>(
|
n = std::min<std::size_t>(
|
||||||
@@ -198,7 +192,7 @@ struct stream::run_write_op
|
|||||||
out->notify_read();
|
out->notify_read();
|
||||||
}
|
}
|
||||||
BOOST_ASSERT(! ec);
|
BOOST_ASSERT(! ec);
|
||||||
complete_op(ec, n);
|
upcall(ec, n);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -644,7 +644,7 @@ run(runner& r)
|
|||||||
// detail:
|
// detail:
|
||||||
// This inserts the suite with the given manual flag
|
// This inserts the suite with the given manual flag
|
||||||
#define BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,manual) \
|
#define BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,manual) \
|
||||||
static beast::unit_test::detail::insert_suite <Class##_test> \
|
static ::boost::beast::unit_test::detail::insert_suite <Class##_test> \
|
||||||
Library ## Module ## Class ## _test_instance( \
|
Library ## Module ## Class ## _test_instance( \
|
||||||
#Class, #Module, #Library, manual)
|
#Class, #Module, #Library, manual)
|
||||||
|
|
||||||
|
@@ -61,7 +61,7 @@ struct stream_base
|
|||||||
|
|
||||||
template<class Decorator>
|
template<class Decorator>
|
||||||
decorator(Decorator&& f)
|
decorator(Decorator&& f)
|
||||||
: d_(f)
|
: d_(std::forward<Decorator>(f))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -26,9 +26,16 @@ add_executable (tests-doc
|
|||||||
http_10_custom_parser.cpp
|
http_10_custom_parser.cpp
|
||||||
http_examples.cpp
|
http_examples.cpp
|
||||||
http_snippets.cpp
|
http_snippets.cpp
|
||||||
|
websocket_common.ipp
|
||||||
websocket.cpp
|
websocket.cpp
|
||||||
websocket_3_handshake.cpp
|
websocket_1_connecting.cpp
|
||||||
websocket_snippets.cpp
|
websocket_2_handshaking.cpp
|
||||||
|
websocket_3_decorator.cpp
|
||||||
|
websocket_4_messages.cpp
|
||||||
|
websocket_5_control_frames.cpp
|
||||||
|
websocket_6_timeouts.cpp
|
||||||
|
websocket_7_teardown.cpp
|
||||||
|
websocket_8_notes.cpp
|
||||||
exemplars.cpp
|
exemplars.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -18,14 +18,20 @@ project
|
|||||||
alias run-tests :
|
alias run-tests :
|
||||||
[ compile core_snippets.cpp ]
|
[ compile core_snippets.cpp ]
|
||||||
[ compile http_snippets.cpp ]
|
[ compile http_snippets.cpp ]
|
||||||
[ compile websocket_snippets.cpp ]
|
|
||||||
[ run core_1_refresher.cpp $(TEST_MAIN) ]
|
[ run core_1_refresher.cpp $(TEST_MAIN) ]
|
||||||
[ run core_3_timeouts.cpp $(TEST_MAIN) ]
|
[ run core_3_timeouts.cpp $(TEST_MAIN) ]
|
||||||
[ run core_4_layers.cpp $(TEST_MAIN) ]
|
[ run core_4_layers.cpp $(TEST_MAIN) ]
|
||||||
[ run http_10_custom_parser.cpp $(TEST_MAIN) ]
|
[ run http_10_custom_parser.cpp $(TEST_MAIN) ]
|
||||||
[ run http_examples.cpp $(TEST_MAIN) ]
|
[ run http_examples.cpp $(TEST_MAIN) ]
|
||||||
[ run websocket.cpp $(TEST_MAIN) ]
|
[ run websocket.cpp $(TEST_MAIN) ]
|
||||||
[ run websocket_3_handshake.cpp $(TEST_MAIN) ]
|
[ run websocket_1_connecting.cpp $(TEST_MAIN) ]
|
||||||
|
[ run websocket_2_handshaking.cpp $(TEST_MAIN) ]
|
||||||
|
[ run websocket_3_decorator.cpp $(TEST_MAIN) ]
|
||||||
|
[ run websocket_4_messages.cpp $(TEST_MAIN) ]
|
||||||
|
[ run websocket_5_control_frames.cpp $(TEST_MAIN) ]
|
||||||
|
[ run websocket_6_timeouts.cpp $(TEST_MAIN) ]
|
||||||
|
[ run websocket_7_teardown.cpp $(TEST_MAIN) ]
|
||||||
|
[ run websocket_8_notes.cpp $(TEST_MAIN) ]
|
||||||
;
|
;
|
||||||
|
|
||||||
exe fat-tests :
|
exe fat-tests :
|
||||||
@@ -36,7 +42,14 @@ exe fat-tests :
|
|||||||
http_10_custom_parser.cpp
|
http_10_custom_parser.cpp
|
||||||
http_examples.cpp
|
http_examples.cpp
|
||||||
websocket.cpp
|
websocket.cpp
|
||||||
websocket_3_handshake.cpp
|
websocket_1_connecting.cpp
|
||||||
|
websocket_2_handshaking.cpp
|
||||||
|
websocket_3_decorator.cpp
|
||||||
|
websocket_4_messages.cpp
|
||||||
|
websocket_5_control_frames.cpp
|
||||||
|
websocket_6_timeouts.cpp
|
||||||
|
websocket_7_teardown.cpp
|
||||||
|
websocket_8_notes.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
explicit fat-tests ;
|
explicit fat-tests ;
|
||||||
|
@@ -7,8 +7,6 @@
|
|||||||
// Official repository: https://github.com/boostorg/beast
|
// Official repository: https://github.com/boostorg/beast
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "snippets.hpp"
|
|
||||||
|
|
||||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
|
||||||
#ifdef BOOST_MSVC
|
#ifdef BOOST_MSVC
|
||||||
@@ -17,26 +15,20 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
//[code_websocket_1a
|
//[code_websocket_1a
|
||||||
#include <boost/beast/core.hpp>
|
|
||||||
#include <boost/beast/http.hpp>
|
#include <boost/beast.hpp>
|
||||||
#include <boost/beast/ssl.hpp>
|
#include <boost/beast/ssl.hpp>
|
||||||
#include <boost/beast/websocket.hpp>
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
|
||||||
//]
|
//]
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
//[code_websocket_1b
|
#include "websocket_common.ipp"
|
||||||
namespace net = boost::asio;
|
|
||||||
using namespace boost::beast;
|
|
||||||
using namespace boost::beast::websocket;
|
|
||||||
|
|
||||||
net::io_context ioc;
|
|
||||||
net::ssl::context ctx(net::ssl::context::sslv23);
|
|
||||||
|
|
||||||
//]
|
|
||||||
|
|
||||||
void
|
void
|
||||||
websocket_snippets()
|
snippets()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
//[code_websocket_1f
|
//[code_websocket_1f
|
||||||
@@ -52,8 +44,8 @@ websocket_snippets()
|
|||||||
{
|
{
|
||||||
//[code_websocket_2f
|
//[code_websocket_2f
|
||||||
|
|
||||||
// The `tcp_stream` will be constructed with a new strand which
|
// The `tcp_stream` will be constructed with a new
|
||||||
// uses the specified I/O context.
|
// strand which uses the specified I/O context.
|
||||||
|
|
||||||
stream<tcp_stream> ws(make_strand(ioc));
|
stream<tcp_stream> ws(make_strand(ioc));
|
||||||
|
|
||||||
@@ -63,34 +55,60 @@ websocket_snippets()
|
|||||||
{
|
{
|
||||||
//[code_websocket_3f
|
//[code_websocket_3f
|
||||||
|
|
||||||
|
// Ownership of the `tcp_stream` is transferred to the websocket stream
|
||||||
|
|
||||||
|
stream<tcp_stream> ws(std::move(sock));
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<tcp_stream> ws(ioc);
|
||||||
|
//[code_websocket_4f
|
||||||
|
|
||||||
|
// Calls `close` on the underlying `beast::tcp_stream`
|
||||||
|
ws.next_layer().close();
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_5f
|
||||||
|
|
||||||
// The WebSocket stream will use SSL and a new strand
|
// The WebSocket stream will use SSL and a new strand
|
||||||
stream<ssl_stream<tcp_stream>> wss(make_strand(ioc), ctx);
|
stream<ssl_stream<tcp_stream>> wss(make_strand(ioc), ctx);
|
||||||
|
|
||||||
//
|
//]
|
||||||
|
|
||||||
|
//[code_websocket_6f
|
||||||
|
|
||||||
|
// Perform the SSL handshake in the client role
|
||||||
|
wss.next_layer().handshake(net::ssl::stream_base::client);
|
||||||
|
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[code_websocket_7f
|
||||||
|
|
||||||
|
// Cancel all pending I/O on the underlying `tcp_stream`
|
||||||
|
get_lowest_layer(wss).cancel();
|
||||||
|
|
||||||
//]
|
//]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // (anon)
|
struct doc_websocket_test
|
||||||
|
: public boost::beast::unit_test::suite
|
||||||
namespace boost {
|
|
||||||
namespace beast {
|
|
||||||
|
|
||||||
struct websocket_snippets_test
|
|
||||||
: public beast::unit_test::suite
|
|
||||||
{
|
{
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(&websocket_snippets);
|
BEAST_EXPECT(&snippets);
|
||||||
pass();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BEAST_DEFINE_TESTSUITE(beast,doc,websocket_snippets);
|
BEAST_DEFINE_TESTSUITE(beast,doc,doc_websocket);
|
||||||
|
|
||||||
} // beast
|
} // (anon)
|
||||||
} // boost
|
|
||||||
|
|
||||||
#ifdef BOOST_MSVC
|
#ifdef BOOST_MSVC
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
89
test/doc/websocket_1_connecting.cpp
Normal file
89
test/doc/websocket_1_connecting.cpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2019 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)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/beast
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4459) // declaration hides global declaration
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/beast.hpp>
|
||||||
|
#include <boost/beast/ssl.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#include "websocket_common.ipp"
|
||||||
|
|
||||||
|
void
|
||||||
|
snippets()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
//[code_websocket_1_1
|
||||||
|
|
||||||
|
stream<tcp_stream> ws(ioc);
|
||||||
|
net::ip::tcp::resolver resolver(ioc);
|
||||||
|
|
||||||
|
// Connect the socket to the IP address returned from performing a name lookup
|
||||||
|
get_lowest_layer(ws).connect(resolver.resolve("example.com", "ws"));
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_1_2
|
||||||
|
|
||||||
|
net::ip::tcp::acceptor acceptor(ioc);
|
||||||
|
acceptor.bind(net::ip::tcp::endpoint(net::ip::tcp::v4(), 0));
|
||||||
|
acceptor.listen();
|
||||||
|
|
||||||
|
// The socket returned by accept() will be forwarded to the tcp_stream,
|
||||||
|
// which uses it to perform a move-construction from the net::ip::tcp::socket.
|
||||||
|
|
||||||
|
stream<tcp_stream> ws(acceptor.accept());
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
net::ip::tcp::acceptor acceptor(ioc);
|
||||||
|
//[code_websocket_1_3
|
||||||
|
|
||||||
|
// The stream will use the strand for invoking all completion handlers
|
||||||
|
stream<tcp_stream> ws(make_strand(ioc));
|
||||||
|
|
||||||
|
// This overload of accept uses the socket provided for the new connection.
|
||||||
|
// The function `tcp_stream::socket` provides access to the low-level socket
|
||||||
|
// object contained in the tcp_stream.
|
||||||
|
|
||||||
|
acceptor.accept(get_lowest_layer(ws).socket());
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct doc_websocket_1_test
|
||||||
|
: public boost::beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(&snippets);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(beast,doc,doc_websocket_1);
|
||||||
|
|
||||||
|
} // (anon)
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
154
test/doc/websocket_2_handshaking.cpp
Normal file
154
test/doc/websocket_2_handshaking.cpp
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2019 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)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/beast
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4459) // declaration hides global declaration
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/beast.hpp>
|
||||||
|
#include <boost/beast/ssl.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#include "websocket_common.ipp"
|
||||||
|
|
||||||
|
void
|
||||||
|
snippets()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
//[code_websocket_2_1
|
||||||
|
|
||||||
|
stream<tcp_stream> ws(ioc);
|
||||||
|
net::ip::tcp::resolver resolver(ioc);
|
||||||
|
get_lowest_layer(ws).connect(resolver.resolve("www.example.com", "ws"));
|
||||||
|
|
||||||
|
// Do the websocket handshake in the client role, on the connected stream.
|
||||||
|
// The implementation only uses the Host parameter to set the HTTP "Host" field,
|
||||||
|
// it does not perform any DNS lookup. That must be done first, as shown above.
|
||||||
|
|
||||||
|
ws.handshake(
|
||||||
|
"www.example.com", // The Host field
|
||||||
|
"/" // The request-target
|
||||||
|
);
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
stream<tcp_stream> ws(ioc);
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_2_2
|
||||||
|
|
||||||
|
// This variable will receive the HTTP response from the server
|
||||||
|
response_type res;
|
||||||
|
|
||||||
|
// Perform the websocket handshake in the client role.
|
||||||
|
// On success, `res` will hold the complete HTTP response received.
|
||||||
|
|
||||||
|
ws.handshake(
|
||||||
|
res, // Receives the HTTP response
|
||||||
|
"www.example.com", // The Host field
|
||||||
|
"/" // The request-target
|
||||||
|
);
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_2_3
|
||||||
|
|
||||||
|
// Perform the websocket handshake in the server role.
|
||||||
|
// The stream must already be connected to the peer.
|
||||||
|
|
||||||
|
ws.accept();
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_2_4
|
||||||
|
|
||||||
|
// This buffer is required for reading HTTP messages
|
||||||
|
flat_buffer buffer;
|
||||||
|
|
||||||
|
// Read into our buffer until we reach the end of the HTTP request.
|
||||||
|
// No parsing takes place here, we are just accumulating data.
|
||||||
|
// We use beast::dynamic_buffer_ref to pass a lightweight, movable
|
||||||
|
// reference to our buffer, because Networking expects to take
|
||||||
|
// ownership, while Beast algorithms only use a reference.
|
||||||
|
|
||||||
|
net::read_until(sock, dynamic_buffer_ref(buffer), "\r\n\r\n");
|
||||||
|
|
||||||
|
// Now accept the connection, using the buffered data.
|
||||||
|
ws.accept(buffer.data());
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_2_5
|
||||||
|
|
||||||
|
// This buffer is required for reading HTTP messages
|
||||||
|
flat_buffer buffer;
|
||||||
|
|
||||||
|
// Read the HTTP request ourselves
|
||||||
|
http::request<http::string_body> req;
|
||||||
|
http::read(sock, buffer, req);
|
||||||
|
|
||||||
|
// See if its a WebSocket upgrade request
|
||||||
|
if(websocket::is_upgrade(req))
|
||||||
|
{
|
||||||
|
// Construct the stream, transferring ownership of the socket
|
||||||
|
stream<tcp_stream> ws(std::move(sock));
|
||||||
|
|
||||||
|
// Clients SHOULD NOT begin sending WebSocket
|
||||||
|
// frames until the server has provided a response.
|
||||||
|
BOOST_ASSERT(buffer.size() == 0);
|
||||||
|
|
||||||
|
// Accept the upgrade request
|
||||||
|
ws.accept(req);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Its not a WebSocket upgrade, so
|
||||||
|
// handle it like a normal HTTP request.
|
||||||
|
}
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct websocket_2_test
|
||||||
|
: public boost::beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(&snippets);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(beast,doc,websocket_2);
|
||||||
|
|
||||||
|
} // (anon)
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
145
test/doc/websocket_3_decorator.cpp
Normal file
145
test/doc/websocket_3_decorator.cpp
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2019 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)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/beast
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4459) // declaration hides global declaration
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/beast.hpp>
|
||||||
|
#include <boost/beast/ssl.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#include "websocket_common.ipp"
|
||||||
|
|
||||||
|
//[code_websocket_3_1
|
||||||
|
|
||||||
|
void set_user_agent(request_type& req)
|
||||||
|
{
|
||||||
|
// Set the User-Agent on the request
|
||||||
|
req.set(http::field::user_agent, "My User Agent");
|
||||||
|
}
|
||||||
|
|
||||||
|
//]
|
||||||
|
|
||||||
|
void
|
||||||
|
snippets()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
//[code_websocket_3_2
|
||||||
|
|
||||||
|
stream<tcp_stream> ws(ioc);
|
||||||
|
|
||||||
|
// The function `set_user_agent` will be invoked with
|
||||||
|
// every upgrade request before it is sent by the stream.
|
||||||
|
|
||||||
|
ws.set_option(stream_base::decorator(&set_user_agent));
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<tcp_stream> ws(ioc);
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_3_3
|
||||||
|
|
||||||
|
struct set_server
|
||||||
|
{
|
||||||
|
void operator()(response_type& res)
|
||||||
|
{
|
||||||
|
// Set the Server field on the response
|
||||||
|
res.set(http::field::user_agent, "My Server");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.set_option(stream_base::decorator(set_server{}));
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_3_4
|
||||||
|
|
||||||
|
ws.set_option(stream_base::decorator(
|
||||||
|
[](response_type& res)
|
||||||
|
{
|
||||||
|
// Set the Server field on the response
|
||||||
|
res.set(http::field::user_agent, "My Server");
|
||||||
|
}));
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_3_5
|
||||||
|
|
||||||
|
struct set_message_fields
|
||||||
|
{
|
||||||
|
void operator()(request_type& req)
|
||||||
|
{
|
||||||
|
// Set the User-Agent on the request
|
||||||
|
req.set(http::field::user_agent, "My User Agent");
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(response_type& res)
|
||||||
|
{
|
||||||
|
// Set the Server field on the response
|
||||||
|
res.set(http::field::user_agent, "My Server");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.set_option(stream_base::decorator(set_message_fields{}));
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_3_6
|
||||||
|
|
||||||
|
struct set_auth
|
||||||
|
{
|
||||||
|
std::unique_ptr<std::string> key;
|
||||||
|
|
||||||
|
void operator()(request_type& req)
|
||||||
|
{
|
||||||
|
// Set the authorization field
|
||||||
|
req.set(http::field::authorization, *key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The stream takes ownership of the decorator object
|
||||||
|
ws.set_option(stream_base::decorator(
|
||||||
|
set_auth{boost::make_unique<std::string>("Basic QWxhZGRpbjpPcGVuU2VzYW1l")}));
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct websocket_3_test
|
||||||
|
: public boost::beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(&snippets);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(beast,doc,websocket_3);
|
||||||
|
|
||||||
|
} // (anon)
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
@@ -1,236 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2016-2019 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)
|
|
||||||
//
|
|
||||||
// Official repository: https://github.com/boostorg/beast
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "snippets.hpp"
|
|
||||||
|
|
||||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
|
||||||
#include <boost/beast/_experimental/test/stream.hpp>
|
|
||||||
#include <boost/beast/core/async_base.hpp>
|
|
||||||
#include <boost/beast/core/error.hpp>
|
|
||||||
#include <boost/beast/core/stream_traits.hpp>
|
|
||||||
#include <boost/beast/websocket.hpp>
|
|
||||||
#include <boost/asio/async_result.hpp>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace boost {
|
|
||||||
namespace beast {
|
|
||||||
namespace websocket {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
void
|
|
||||||
websocket_3_handshake_snippets()
|
|
||||||
{
|
|
||||||
#include "snippets.ipp"
|
|
||||||
stream<net::ip::tcp::socket> ws(ioc);
|
|
||||||
{
|
|
||||||
//[code_websocket_3_client_1
|
|
||||||
|
|
||||||
// Note that the stream must already be connected, this
|
|
||||||
// function does not perform a DNS lookup on the host
|
|
||||||
// name, nor does it establish an outgoing connection.
|
|
||||||
|
|
||||||
// Perform the websocket handshake in the client role.
|
|
||||||
ws.handshake(
|
|
||||||
"www.example.com", // The Host field
|
|
||||||
"/", // The request-target
|
|
||||||
ec // Set to the error
|
|
||||||
);
|
|
||||||
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
//[code_websocket_3_client_2
|
|
||||||
|
|
||||||
// Note that the stream must already be connected, this
|
|
||||||
// function does not perform a DNS lookup on the host
|
|
||||||
// name, nor does it establish an outgoing connection.
|
|
||||||
|
|
||||||
// This variable will receive the HTTP response from the server
|
|
||||||
response_type res;
|
|
||||||
|
|
||||||
// Perform the websocket handshake in the client role.
|
|
||||||
ws.handshake(
|
|
||||||
res, // Receives the HTTP response
|
|
||||||
"www.example.com", // The Host field
|
|
||||||
"/" // The request-target
|
|
||||||
,ec // Set to the error, if any
|
|
||||||
);
|
|
||||||
|
|
||||||
// Upon success, `res` will be set to the complete
|
|
||||||
// response received from the server.
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
|
||||||
|
|
||||||
{
|
|
||||||
//[code_websocket_3_server_1
|
|
||||||
|
|
||||||
// Note that the stream must already be connected
|
|
||||||
// to an incoming remote peer.
|
|
||||||
|
|
||||||
// Perform the websocket handshake in the server role.
|
|
||||||
ws.accept(
|
|
||||||
ec // Set to the error, if any
|
|
||||||
);
|
|
||||||
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
//[code_websocket_3_server_2
|
|
||||||
// This buffer is required for reading HTTP messages
|
|
||||||
flat_buffer buffer;
|
|
||||||
|
|
||||||
// Read into our buffer until we reach the end of the HTTP request.
|
|
||||||
// No parsing takes place here, we are just accumulating data.
|
|
||||||
// We use beast::dynamic_buffer_ref to pass a lightweight, movable
|
|
||||||
// reference to our buffer, because Networking expects to take
|
|
||||||
// ownership unlike Beast algorithms which use a reference.
|
|
||||||
|
|
||||||
net::read_until(sock, dynamic_buffer_ref(buffer), "\r\n\r\n");
|
|
||||||
|
|
||||||
// Now accept the connection, using the buffered data.
|
|
||||||
ws.accept(buffer.data());
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
websocket_3_handshake_snippets_2()
|
|
||||||
{
|
|
||||||
using namespace boost::beast;
|
|
||||||
namespace net = boost::asio;
|
|
||||||
namespace ssl = boost::asio::ssl;
|
|
||||||
using tcp = net::ip::tcp;
|
|
||||||
error_code ec;
|
|
||||||
net::io_context ioc;
|
|
||||||
tcp::socket sock(ioc);
|
|
||||||
|
|
||||||
{
|
|
||||||
//[code_websocket_3_server_1b
|
|
||||||
// This buffer is required for reading HTTP messages
|
|
||||||
flat_buffer buffer;
|
|
||||||
|
|
||||||
// Read the HTTP request ourselves
|
|
||||||
http::request<http::string_body> req;
|
|
||||||
http::read(sock, buffer, req);
|
|
||||||
|
|
||||||
// See if its a WebSocket upgrade request
|
|
||||||
if(websocket::is_upgrade(req))
|
|
||||||
{
|
|
||||||
// Construct the stream, transferring ownership of the socket
|
|
||||||
stream<net::ip::tcp::socket> ws{std::move(sock)};
|
|
||||||
|
|
||||||
// Clients SHOULD NOT begin sending WebSocket
|
|
||||||
// frames until the server has provided a response.
|
|
||||||
BOOST_ASSERT(buffer.size() == 0);
|
|
||||||
|
|
||||||
// Accept the upgrade request
|
|
||||||
ws.accept(req);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Its not a WebSocket upgrade, so
|
|
||||||
// handle it like a normal HTTP request.
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//[code_websocket_3_decorator_1b
|
|
||||||
void set_user_agent(request_type& req)
|
|
||||||
{
|
|
||||||
// Set the User-Agent on the request
|
|
||||||
req.set(http::field::user_agent, "My User Agent");
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
|
|
||||||
void
|
|
||||||
websocket_3_handshake_snippets_3()
|
|
||||||
{
|
|
||||||
#include "snippets.ipp"
|
|
||||||
stream<net::ip::tcp::socket> ws(ioc);
|
|
||||||
{
|
|
||||||
//[code_websocket_3_decorator_1
|
|
||||||
|
|
||||||
ws.set_option(stream_base::decorator(&set_user_agent));
|
|
||||||
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
//[code_websocket_3_decorator_2
|
|
||||||
|
|
||||||
struct set_server
|
|
||||||
{
|
|
||||||
void operator()(response_type& res)
|
|
||||||
{
|
|
||||||
// Set the Server field on the response
|
|
||||||
res.set(http::field::user_agent, "My Server");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.set_option(stream_base::decorator(set_server{}));
|
|
||||||
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
//[code_websocket_3_decorator_3
|
|
||||||
|
|
||||||
ws.set_option(stream_base::decorator(
|
|
||||||
[](response_type& res)
|
|
||||||
{
|
|
||||||
// Set the Server field on the response
|
|
||||||
res.set(http::field::user_agent, "My Server");
|
|
||||||
}));
|
|
||||||
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
//[code_websocket_3_decorator_4
|
|
||||||
|
|
||||||
struct multi_decorator
|
|
||||||
{
|
|
||||||
void operator()(request_type& req)
|
|
||||||
{
|
|
||||||
// Set the User-Agent on the request
|
|
||||||
req.set(http::field::user_agent, "My User Agent");
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(response_type& res)
|
|
||||||
{
|
|
||||||
// Set the Server field on the response
|
|
||||||
res.set(http::field::user_agent, "My Server");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.set_option(stream_base::decorator(multi_decorator{}));
|
|
||||||
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
} // (anon)
|
|
||||||
|
|
||||||
struct websocket_3_handshake_test
|
|
||||||
: public beast::unit_test::suite
|
|
||||||
{
|
|
||||||
void
|
|
||||||
run() override
|
|
||||||
{
|
|
||||||
BEAST_EXPECT(&websocket_3_handshake_snippets);
|
|
||||||
BEAST_EXPECT(&websocket_3_handshake_snippets_2);
|
|
||||||
BEAST_EXPECT(&websocket_3_handshake_snippets_3);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BEAST_DEFINE_TESTSUITE(beast,doc,websocket_3_handshake);
|
|
||||||
|
|
||||||
} // websocket
|
|
||||||
} // beast
|
|
||||||
} // boost
|
|
146
test/doc/websocket_4_messages.cpp
Normal file
146
test/doc/websocket_4_messages.cpp
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2019 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)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/beast
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4459) // declaration hides global declaration
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/beast.hpp>
|
||||||
|
#include <boost/beast/ssl.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#include "websocket_common.ipp"
|
||||||
|
|
||||||
|
void
|
||||||
|
snippets()
|
||||||
|
{
|
||||||
|
stream<tcp_stream> ws(ioc);
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_4_1
|
||||||
|
|
||||||
|
net::const_buffer b("Hello, world!", 13);
|
||||||
|
|
||||||
|
// This sets all outgoing messages to be sent as text.
|
||||||
|
// Text messages must contain valid utf8, this is checked
|
||||||
|
// when reading but not when writing.
|
||||||
|
|
||||||
|
ws.text(true);
|
||||||
|
|
||||||
|
// Write the buffer as text
|
||||||
|
ws.write(b);
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_4_2
|
||||||
|
|
||||||
|
// This DynamicBuffer will hold the received message
|
||||||
|
flat_buffer buffer;
|
||||||
|
|
||||||
|
// Read a complete message into the buffer's input area
|
||||||
|
ws.read(buffer);
|
||||||
|
|
||||||
|
// Set text mode if the received message was also text,
|
||||||
|
// otherwise binary mode will be set.
|
||||||
|
ws.text(ws.got_text());
|
||||||
|
|
||||||
|
// Echo the received message back to the peer. If the received
|
||||||
|
// message was in text mode, the echoed message will also be
|
||||||
|
// in text mode, otherwise it will be in binary mode.
|
||||||
|
ws.write(buffer.data());
|
||||||
|
|
||||||
|
// Discard all of the bytes stored in the dynamic buffer,
|
||||||
|
// otherwise the next call to read will append to the existing
|
||||||
|
// data instead of building a fresh message.
|
||||||
|
buffer.consume(buffer.size());
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_4_3
|
||||||
|
|
||||||
|
// This DynamicBuffer will hold the received message
|
||||||
|
multi_buffer buffer;
|
||||||
|
|
||||||
|
// Read the next message in pieces
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Append up to 512 bytes of the message into the buffer
|
||||||
|
ws.read_some(buffer, 512);
|
||||||
|
}
|
||||||
|
while(! ws.is_message_done());
|
||||||
|
|
||||||
|
// At this point we have a complete message in the buffer, now echo it
|
||||||
|
|
||||||
|
// The echoed message will be sent in binary mode if the received
|
||||||
|
// message was in binary mode, otherwise we will send in text mode.
|
||||||
|
ws.binary(ws.got_binary());
|
||||||
|
|
||||||
|
// This buffer adaptor allows us to iterate through buffer in pieces
|
||||||
|
buffers_suffix<multi_buffer::const_buffers_type> cb{buffer.data()};
|
||||||
|
|
||||||
|
// Echo the received message in pieces.
|
||||||
|
// This will cause the message to be broken up into multiple frames.
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if(buffer_size(cb) > 512)
|
||||||
|
{
|
||||||
|
// There are more than 512 bytes left to send, just
|
||||||
|
// send the next 512 bytes. The value `false` informs
|
||||||
|
// the stream that the message is not complete.
|
||||||
|
ws.write_some(false, buffers_prefix(512, cb));
|
||||||
|
|
||||||
|
// This efficiently discards data from the adaptor by
|
||||||
|
// simply ignoring it, but does not actually affect the
|
||||||
|
// underlying dynamic buffer.
|
||||||
|
cb.consume(512);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Only 512 bytes or less remain, so write the whole
|
||||||
|
// thing and inform the stream that this piece represents
|
||||||
|
// the end of the message by passing `true`.
|
||||||
|
ws.write_some(true, cb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discard all of the bytes stored in the dynamic buffer,
|
||||||
|
// otherwise the next call to read will append to the existing
|
||||||
|
// data instead of building a fresh message.
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct websocket_4_test
|
||||||
|
: public boost::beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(&snippets);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(beast,doc,websocket_4);
|
||||||
|
|
||||||
|
} // (anon)
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
78
test/doc/websocket_5_control_frames.cpp
Normal file
78
test/doc/websocket_5_control_frames.cpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2019 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)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/beast
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4459) // declaration hides global declaration
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/beast.hpp>
|
||||||
|
#include <boost/beast/ssl.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#include "websocket_common.ipp"
|
||||||
|
|
||||||
|
void
|
||||||
|
snippets()
|
||||||
|
{
|
||||||
|
stream<tcp_stream> ws(ioc);
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_5_1
|
||||||
|
|
||||||
|
ws.control_callback(
|
||||||
|
[](frame_type kind, string_view payload)
|
||||||
|
{
|
||||||
|
// Do something with the payload
|
||||||
|
boost::ignore_unused(kind, payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_5_2
|
||||||
|
|
||||||
|
ws.close(close_code::normal);
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_5_3
|
||||||
|
|
||||||
|
ws.auto_fragment(true);
|
||||||
|
ws.write_buffer_size(16384);
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct websocket_5_test
|
||||||
|
: public boost::beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(&snippets);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(beast,doc,websocket_5);
|
||||||
|
|
||||||
|
} // (anon)
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
54
test/doc/websocket_6_timeouts.cpp
Normal file
54
test/doc/websocket_6_timeouts.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2019 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)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/beast
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4459) // declaration hides global declaration
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/beast.hpp>
|
||||||
|
#include <boost/beast/ssl.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#include "websocket_common.ipp"
|
||||||
|
|
||||||
|
void
|
||||||
|
snippets()
|
||||||
|
{
|
||||||
|
stream<tcp_stream> ws(ioc);
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_6_1
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct websocket_6_test
|
||||||
|
: public boost::beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(&snippets);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(beast,doc,websocket_6);
|
||||||
|
|
||||||
|
} // (anon)
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
113
test/doc/websocket_7_teardown.cpp
Normal file
113
test/doc/websocket_7_teardown.cpp
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2019 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)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/beast
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4459) // declaration hides global declaration
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/beast.hpp>
|
||||||
|
#include <boost/beast/ssl.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#include "websocket_common.ipp"
|
||||||
|
|
||||||
|
//[code_websocket_7_1
|
||||||
|
|
||||||
|
struct custom_stream;
|
||||||
|
|
||||||
|
void
|
||||||
|
teardown(
|
||||||
|
role_type role,
|
||||||
|
custom_stream& stream,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
template<class TeardownHandler>
|
||||||
|
void
|
||||||
|
async_teardown(
|
||||||
|
role_type role,
|
||||||
|
custom_stream& stream,
|
||||||
|
TeardownHandler&& handler);
|
||||||
|
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[code_websocket_7_2
|
||||||
|
|
||||||
|
template <class NextLayer>
|
||||||
|
struct custom_wrapper
|
||||||
|
{
|
||||||
|
NextLayer next_layer;
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
explicit
|
||||||
|
custom_wrapper(Args&&... args)
|
||||||
|
: next_layer(std::forward<Args>(args)...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void
|
||||||
|
teardown(
|
||||||
|
role_type role,
|
||||||
|
custom_wrapper& stream,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
using boost::beast::websocket::teardown;
|
||||||
|
teardown(role, stream.next_layer, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TeardownHandler>
|
||||||
|
friend
|
||||||
|
void
|
||||||
|
async_teardown(
|
||||||
|
role_type role,
|
||||||
|
custom_wrapper& stream,
|
||||||
|
TeardownHandler&& handler)
|
||||||
|
{
|
||||||
|
using boost::beast::websocket::async_teardown;
|
||||||
|
async_teardown(role, stream.next_layer, std::forward<TeardownHandler>(handler));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//]
|
||||||
|
|
||||||
|
void
|
||||||
|
snippets()
|
||||||
|
{
|
||||||
|
//stream<tcp_stream> ws(ioc);
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_7_1
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct websocket_7_test
|
||||||
|
: public boost::beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(&snippets);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(beast,doc,websocket_7);
|
||||||
|
|
||||||
|
} // (anon)
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
100
test/doc/websocket_8_notes.cpp
Normal file
100
test/doc/websocket_8_notes.cpp
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2019 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)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/beast
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4459) // declaration hides global declaration
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/beast.hpp>
|
||||||
|
#include <boost/beast/ssl.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#include <boost/asio/spawn.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#include "websocket_common.ipp"
|
||||||
|
|
||||||
|
void
|
||||||
|
snippets()
|
||||||
|
{
|
||||||
|
stream<tcp_stream> ws(ioc);
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_8_1
|
||||||
|
|
||||||
|
flat_buffer buffer;
|
||||||
|
|
||||||
|
ws.async_read(buffer,
|
||||||
|
[](error_code, std::size_t)
|
||||||
|
{
|
||||||
|
// Do something with the buffer
|
||||||
|
});
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
multi_buffer b;
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_8_2
|
||||||
|
|
||||||
|
ws.async_read(b, [](error_code, std::size_t){});
|
||||||
|
ws.async_read(b, [](error_code, std::size_t){});
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[code_websocket_8_3
|
||||||
|
|
||||||
|
ws.async_read(b, [](error_code, std::size_t){});
|
||||||
|
ws.async_write(b.data(), [](error_code, std::size_t){});
|
||||||
|
ws.async_ping({}, [](error_code){});
|
||||||
|
ws.async_close({}, [](error_code){});
|
||||||
|
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// workaround for https://github.com/chriskohlhoff/asio/issues/112
|
||||||
|
//#ifdef BOOST_MSVC
|
||||||
|
//[code_websocket_8_1f
|
||||||
|
|
||||||
|
void echo(stream<tcp_stream>& ws,
|
||||||
|
multi_buffer& buffer, net::yield_context yield)
|
||||||
|
{
|
||||||
|
ws.async_read(buffer, yield);
|
||||||
|
std::future<std::size_t> fut =
|
||||||
|
ws.async_write(buffer.data(), net::use_future);
|
||||||
|
}
|
||||||
|
|
||||||
|
//]
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
struct websocket_8_test
|
||||||
|
: public boost::beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(&snippets);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(beast,doc,websocket_8);
|
||||||
|
|
||||||
|
} // (anon)
|
||||||
|
|
||||||
|
#ifdef BOOST_MSVC
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
20
test/doc/websocket_common.ipp
Normal file
20
test/doc/websocket_common.ipp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2019 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)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/beast
|
||||||
|
//
|
||||||
|
|
||||||
|
//[code_websocket_1b
|
||||||
|
|
||||||
|
namespace net = boost::asio;
|
||||||
|
using namespace boost::beast;
|
||||||
|
using namespace boost::beast::websocket;
|
||||||
|
|
||||||
|
net::io_context ioc;
|
||||||
|
tcp_stream sock(ioc);
|
||||||
|
net::ssl::context ctx(net::ssl::context::sslv23);
|
||||||
|
|
||||||
|
//]
|
@@ -1,306 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2016-2019 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)
|
|
||||||
//
|
|
||||||
// Official repository: https://github.com/boostorg/beast
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <boost/beast/core.hpp>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <boost/asio/spawn.hpp>
|
|
||||||
#include <boost/asio/ssl.hpp>
|
|
||||||
#include <boost/asio/use_future.hpp>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <future>
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
using namespace boost::beast;
|
|
||||||
|
|
||||||
#include <boost/beast/websocket.hpp>
|
|
||||||
using namespace boost::beast::websocket;
|
|
||||||
|
|
||||||
namespace doc_ws_snippets {
|
|
||||||
|
|
||||||
void fxx() {
|
|
||||||
|
|
||||||
net::io_context ioc;
|
|
||||||
auto work = net::make_work_guard(ioc);
|
|
||||||
std::thread t{[&](){ ioc.run(); }};
|
|
||||||
error_code ec;
|
|
||||||
net::ip::tcp::socket sock{ioc};
|
|
||||||
boost::ignore_unused(ec);
|
|
||||||
|
|
||||||
{
|
|
||||||
//[ws_snippet_3
|
|
||||||
stream<net::ip::tcp::socket> ws{std::move(sock)};
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
//[ws_snippet_4
|
|
||||||
stream<net::ip::tcp::socket&> ws{sock};
|
|
||||||
//]
|
|
||||||
|
|
||||||
//[ws_snippet_5
|
|
||||||
ws.next_layer().shutdown(net::ip::tcp::socket::shutdown_send);
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
//[ws_snippet_6
|
|
||||||
std::string const host = "example.com";
|
|
||||||
net::ip::tcp::resolver r(ioc);
|
|
||||||
stream<tcp_stream> ws(ioc);
|
|
||||||
auto const results = r.resolve(host, "ws");
|
|
||||||
get_lowest_layer(ws).connect(results.begin(), results.end());
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
//[ws_snippet_7
|
|
||||||
net::ip::tcp::acceptor acceptor{ioc};
|
|
||||||
stream<tcp_stream> ws{acceptor.get_executor()};
|
|
||||||
acceptor.accept(get_lowest_layer(ws).socket());
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
stream<net::ip::tcp::socket> ws{ioc};
|
|
||||||
//[ws_snippet_15
|
|
||||||
// This DynamicBuffer will hold the received message
|
|
||||||
multi_buffer buffer;
|
|
||||||
|
|
||||||
// Read a complete message into the buffer's input area
|
|
||||||
ws.read(buffer);
|
|
||||||
|
|
||||||
// Set text mode if the received message was also text,
|
|
||||||
// otherwise binary mode will be set.
|
|
||||||
ws.text(ws.got_text());
|
|
||||||
|
|
||||||
// Echo the received message back to the peer. If the received
|
|
||||||
// message was in text mode, the echoed message will also be
|
|
||||||
// in text mode, otherwise it will be in binary mode.
|
|
||||||
ws.write(buffer.data());
|
|
||||||
|
|
||||||
// Discard all of the bytes stored in the dynamic buffer,
|
|
||||||
// otherwise the next call to read will append to the existing
|
|
||||||
// data instead of building a fresh message.
|
|
||||||
buffer.consume(buffer.size());
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
stream<net::ip::tcp::socket> ws{ioc};
|
|
||||||
//[ws_snippet_16
|
|
||||||
// This DynamicBuffer will hold the received message
|
|
||||||
multi_buffer buffer;
|
|
||||||
|
|
||||||
// Read the next message in pieces
|
|
||||||
do
|
|
||||||
{
|
|
||||||
// Append up to 512 bytes of the message into the buffer
|
|
||||||
ws.read_some(buffer, 512);
|
|
||||||
}
|
|
||||||
while(! ws.is_message_done());
|
|
||||||
|
|
||||||
// At this point we have a complete message in the buffer, now echo it
|
|
||||||
|
|
||||||
// The echoed message will be sent in binary mode if the received
|
|
||||||
// message was in binary mode, otherwise we will send in text mode.
|
|
||||||
ws.binary(ws.got_binary());
|
|
||||||
|
|
||||||
// This buffer adaptor allows us to iterate through buffer in pieces
|
|
||||||
buffers_suffix<multi_buffer::const_buffers_type> cb{buffer.data()};
|
|
||||||
|
|
||||||
// Echo the received message in pieces.
|
|
||||||
// This will cause the message to be broken up into multiple frames.
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
if(buffer_size(cb) > 512)
|
|
||||||
{
|
|
||||||
// There are more than 512 bytes left to send, just
|
|
||||||
// send the next 512 bytes. The value `false` informs
|
|
||||||
// the stream that the message is not complete.
|
|
||||||
ws.write_some(false, buffers_prefix(512, cb));
|
|
||||||
|
|
||||||
// This efficiently discards data from the adaptor by
|
|
||||||
// simply ignoring it, but does not actually affect the
|
|
||||||
// underlying dynamic buffer.
|
|
||||||
cb.consume(512);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Only 512 bytes or less remain, so write the whole
|
|
||||||
// thing and inform the stream that this piece represents
|
|
||||||
// the end of the message by passing `true`.
|
|
||||||
ws.write_some(true, cb);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discard all of the bytes stored in the dynamic buffer,
|
|
||||||
// otherwise the next call to read will append to the existing
|
|
||||||
// data instead of building a fresh message.
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
stream<net::ip::tcp::socket> ws{ioc};
|
|
||||||
//[ws_snippet_17
|
|
||||||
ws.control_callback(
|
|
||||||
[](frame_type kind, string_view payload)
|
|
||||||
{
|
|
||||||
// Do something with the payload
|
|
||||||
boost::ignore_unused(kind, payload);
|
|
||||||
});
|
|
||||||
//]
|
|
||||||
|
|
||||||
//[ws_snippet_18
|
|
||||||
ws.close(close_code::normal);
|
|
||||||
//]
|
|
||||||
|
|
||||||
//[ws_snippet_19
|
|
||||||
ws.auto_fragment(true);
|
|
||||||
ws.write_buffer_size(16384);
|
|
||||||
//]
|
|
||||||
|
|
||||||
//[ws_snippet_20
|
|
||||||
multi_buffer buffer;
|
|
||||||
ws.async_read(buffer,
|
|
||||||
[](error_code, std::size_t)
|
|
||||||
{
|
|
||||||
// Do something with the buffer
|
|
||||||
});
|
|
||||||
//]
|
|
||||||
|
|
||||||
{
|
|
||||||
multi_buffer b;
|
|
||||||
//[ws_snippet_24
|
|
||||||
ws.async_read(b, [](error_code, std::size_t){});
|
|
||||||
ws.async_read(b, [](error_code, std::size_t){});
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
multi_buffer b;
|
|
||||||
//[ws_snippet_25
|
|
||||||
ws.async_read(b, [](error_code, std::size_t){});
|
|
||||||
ws.async_write(b.data(), [](error_code, std::size_t){});
|
|
||||||
ws.async_ping({}, [](error_code){});
|
|
||||||
ws.async_close({}, [](error_code){});
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // fxx()
|
|
||||||
|
|
||||||
// workaround for https://github.com/chriskohlhoff/asio/issues/112
|
|
||||||
#ifdef BOOST_MSVC
|
|
||||||
//[ws_snippet_21
|
|
||||||
void echo(stream<net::ip::tcp::socket>& ws,
|
|
||||||
multi_buffer& buffer, net::yield_context yield)
|
|
||||||
{
|
|
||||||
ws.async_read(buffer, yield);
|
|
||||||
std::future<std::size_t> fut =
|
|
||||||
ws.async_write(buffer.data(), net::use_future);
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//[ws_snippet_22
|
|
||||||
|
|
||||||
struct custom_stream;
|
|
||||||
|
|
||||||
void
|
|
||||||
teardown(
|
|
||||||
role_type role,
|
|
||||||
custom_stream& stream,
|
|
||||||
error_code& ec);
|
|
||||||
|
|
||||||
template<class TeardownHandler>
|
|
||||||
void
|
|
||||||
async_teardown(
|
|
||||||
role_type role,
|
|
||||||
custom_stream& stream,
|
|
||||||
TeardownHandler&& handler);
|
|
||||||
|
|
||||||
//]
|
|
||||||
|
|
||||||
//[ws_snippet_23
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
struct custom_wrapper
|
|
||||||
{
|
|
||||||
NextLayer next_layer;
|
|
||||||
|
|
||||||
template<class... Args>
|
|
||||||
explicit
|
|
||||||
custom_wrapper(Args&&... args)
|
|
||||||
: next_layer(std::forward<Args>(args)...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
void
|
|
||||||
teardown(
|
|
||||||
role_type role,
|
|
||||||
custom_wrapper& stream,
|
|
||||||
error_code& ec)
|
|
||||||
{
|
|
||||||
using boost::beast::websocket::teardown;
|
|
||||||
teardown(role, stream.next_layer, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class TeardownHandler>
|
|
||||||
friend
|
|
||||||
void
|
|
||||||
async_teardown(
|
|
||||||
role_type role,
|
|
||||||
custom_wrapper& stream,
|
|
||||||
TeardownHandler&& handler)
|
|
||||||
{
|
|
||||||
using boost::beast::websocket::async_teardown;
|
|
||||||
async_teardown(role, stream.next_layer, std::forward<TeardownHandler>(handler));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//]
|
|
||||||
|
|
||||||
} // doc_ws_snippets
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace doc_wss_snippets {
|
|
||||||
|
|
||||||
void fxx() {
|
|
||||||
|
|
||||||
net::io_context ioc;
|
|
||||||
auto work = net::make_work_guard(ioc);
|
|
||||||
std::thread t{[&](){ ioc.run(); }};
|
|
||||||
error_code ec;
|
|
||||||
net::ip::tcp::socket sock{ioc};
|
|
||||||
|
|
||||||
{
|
|
||||||
//[wss_snippet_3
|
|
||||||
net::ip::tcp::endpoint ep;
|
|
||||||
net::ssl::context ctx{net::ssl::context::sslv23};
|
|
||||||
stream<net::ssl::stream<net::ip::tcp::socket>> ws{ioc, ctx};
|
|
||||||
|
|
||||||
// connect the underlying TCP/IP socket
|
|
||||||
ws.next_layer().next_layer().connect(ep);
|
|
||||||
|
|
||||||
// perform SSL handshake
|
|
||||||
ws.next_layer().handshake(net::ssl::stream_base::client);
|
|
||||||
|
|
||||||
// perform WebSocket handshake
|
|
||||||
ws.handshake("localhost", "/");
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
} // fxx()
|
|
||||||
|
|
||||||
} // doc_wss_snippets
|
|
Reference in New Issue
Block a user