diff --git a/CHANGELOG.md b/CHANGELOG.md
index 52f78da0..59559b54 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@ Version 221:
* Rename to async_base, stable_async_base
* role_type is in boost/beast/core/role.hpp (API Change)
+* Cleanup in test::stream internals
Actions Required:
diff --git a/doc/qbk/06_websocket/01_connecting.qbk b/doc/qbk/06_websocket/01_connecting.qbk
index ab4b9a03..12954a5c 100644
--- a/doc/qbk/06_websocket/01_connecting.qbk
+++ b/doc/qbk/06_websocket/01_connecting.qbk
@@ -7,29 +7,27 @@
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
-object. For example, to make an outgoing connection using a standard TCP/IP
-socket:
+Before messages can be exchanged, a websocket stream first needs to be
+connected, and then to have the websocket handshake performed. The stream
+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
-socket, pass the next layer object to the acceptor:
+To accept incoming connections, an acceptor is used. The websocket stream
+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
-TCP/IP socket, multiple calls to retrieve the next layer may be required.
-In this example, the websocket stream wraps the SSL stream which wraps
-the TCP/IP socket:
+Alternatively, the incoming connection may be accepted directly into
+the socket owned by the websocket stream, using this overload of the
+acceptor member function.
-[wss_snippet_3]
-
-[note
- Examples use synchronous interfaces for clarity of exposition;
- signatures for asynchronous operations are also provided.
-]
+[code_websocket_1_3]
[endsect]
diff --git a/doc/qbk/06_websocket/02_handshaking.qbk b/doc/qbk/06_websocket/02_handshaking.qbk
new file mode 100644
index 00000000..efb663ce
--- /dev/null
+++ b/doc/qbk/06_websocket/02_handshaking.qbk
@@ -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]
diff --git a/doc/qbk/06_websocket/03_client.qbk b/doc/qbk/06_websocket/03_client.qbk
deleted file mode 100644
index 3a829f30..00000000
--- a/doc/qbk/06_websocket/03_client.qbk
+++ /dev/null
@@ -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]
diff --git a/doc/qbk/06_websocket/05_decorator.qbk b/doc/qbk/06_websocket/03_decorator.qbk
similarity index 78%
rename from doc/qbk/06_websocket/05_decorator.qbk
rename to doc/qbk/06_websocket/03_decorator.qbk
index 1fbe258f..f3e00863 100644
--- a/doc/qbk/06_websocket/05_decorator.qbk
+++ b/doc/qbk/06_websocket/03_decorator.qbk
@@ -7,7 +7,9 @@
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
request, the outgoing WebSocket HTTP Upgrade response, or both, the stream
@@ -20,13 +22,13 @@ uses:
[table WebSocket Decorator Interface
[[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
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
represent HTTP Upgrade response.
@@ -46,29 +48,34 @@ uses:
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
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
not incur a memory allocation. The follow code declares and sets a function
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:
-[code_websocket_3_decorator_3]
+[code_websocket_3_4]
It also possible for a single decorator to handle both requests and
responses, if it is overloaded for both types either as a generic
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
Undefined behavior results if the decorator modifies the fields
diff --git a/doc/qbk/06_websocket/04_messages.qbk b/doc/qbk/06_websocket/04_messages.qbk
new file mode 100644
index 00000000..32f6acd8
--- /dev/null
+++ b/doc/qbk/06_websocket/04_messages.qbk
@@ -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]
diff --git a/doc/qbk/06_websocket/04_server.qbk b/doc/qbk/06_websocket/04_server.qbk
deleted file mode 100644
index dc2a1607..00000000
--- a/doc/qbk/06_websocket/04_server.qbk
+++ /dev/null
@@ -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]
diff --git a/doc/qbk/06_websocket/07_control.qbk b/doc/qbk/06_websocket/05_control_frames.qbk
similarity index 98%
rename from doc/qbk/06_websocket/07_control.qbk
rename to doc/qbk/06_websocket/05_control_frames.qbk
index 82864305..e4d69708 100644
--- a/doc/qbk/06_websocket/07_control.qbk
+++ b/doc/qbk/06_websocket/05_control_frames.qbk
@@ -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
signature:
-[ws_snippet_17]
+[code_websocket_5_1]
When a control callback is registered, it will be invoked for all pings,
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.async_close `async_close`]:
-[ws_snippet_18]
+[code_websocket_5_2]
The close function will send a close frame, read and discard incoming
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
maximum size of the fragments:
-[ws_snippet_19]
+[code_websocket_5_3]
[endsect]
diff --git a/doc/qbk/06_websocket/06_messages.qbk b/doc/qbk/06_websocket/06_messages.qbk
deleted file mode 100644
index c4b4be13..00000000
--- a/doc/qbk/06_websocket/06_messages.qbk
+++ /dev/null
@@ -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]
diff --git a/doc/qbk/06_websocket/06_timeouts.qbk b/doc/qbk/06_websocket/06_timeouts.qbk
new file mode 100644
index 00000000..5c31ff32
--- /dev/null
+++ b/doc/qbk/06_websocket/06_timeouts.qbk
@@ -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]
diff --git a/doc/qbk/06_websocket/08_teardown.qbk b/doc/qbk/06_websocket/07_teardown.qbk
similarity index 98%
rename from doc/qbk/06_websocket/08_teardown.qbk
rename to doc/qbk/06_websocket/07_teardown.qbk
index 5a889f8a..cb46410a 100644
--- a/doc/qbk/06_websocket/08_teardown.qbk
+++ b/doc/qbk/06_websocket/07_teardown.qbk
@@ -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
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
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
take effect:
-[ws_snippet_23]
+[code_websocket_7_2]
[endsect]
diff --git a/doc/qbk/06_websocket/09_notes.qbk b/doc/qbk/06_websocket/08_notes.qbk
similarity index 97%
rename from doc/qbk/06_websocket/09_notes.qbk
rename to doc/qbk/06_websocket/08_notes.qbk
index 40332394..10226b5d 100644
--- a/doc/qbk/06_websocket/09_notes.qbk
+++ b/doc/qbk/06_websocket/08_notes.qbk
@@ -23,13 +23,13 @@ of the underlying TCP/IP connection.
Asynchronous versions are available for all functions:
-[ws_snippet_20]
+[code_websocket_8_1]
Calls to asynchronous initiation functions support the extensible asynchronous
model developed by the Boost.Asio author, allowing for traditional completion
handlers, stackful or stackless coroutines, and even futures:
-[ws_snippet_21]
+[code_websocket_8_1f]
The example programs that come with the library demonstrate the usage of
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
program is attempting to perform two simultaneous reads:
-[ws_snippet_24]
+[code_websocket_8_2]
However, this code is correct:
-[ws_snippet_25]
+[code_websocket_8_3]
The implementation uses composed asynchronous operations; although
some individual operations can perform both reads and writes, this
diff --git a/doc/qbk/06_websocket/_websocket.qbk b/doc/qbk/06_websocket/_websocket.qbk
index f901de4e..fa4dfa14 100644
--- a/doc/qbk/06_websocket/_websocket.qbk
+++ b/doc/qbk/06_websocket/_websocket.qbk
@@ -98,6 +98,18 @@ strand to invoke all completion handlers:
[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]
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
wrapped stream's constructor:
-[code_websocket_3f]
+[code_websocket_5f]
[important
Code which declares websocket stream objects using Asio SSL types
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
-constructed around the already existing socket by invoking the move
-constructor signature:
+[code_websocket_6f]
-[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
-this case, the caller is responsible for managing the lifetime of the
-underlying socket being wrapped:
+[code_websocket_7f]
-[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]
Please note that websocket streams do not support non-blocking modes.
-
+[/-----------------------------------------------------------------------------]
[include 01_connecting.qbk]
-[include 03_client.qbk]
-[include 04_server.qbk]
-[include 05_decorator.qbk]
-[include 06_messages.qbk]
-[include 07_control.qbk]
-[include 08_teardown.qbk]
-[include 09_notes.qbk]
+[include 02_handshaking.qbk]
+[include 03_decorator.qbk]
+[include 04_messages.qbk]
+[include 05_control_frames.qbk]
+[include 06_timeouts.qbk]
+[include 07_teardown.qbk]
+[include 08_notes.qbk]
[endsect]
diff --git a/doc/qbk/main.qbk b/doc/qbk/main.qbk
index b610eece..92535c82 100644
--- a/doc/qbk/main.qbk
+++ b/doc/qbk/main.qbk
@@ -144,8 +144,16 @@
[import ../../test/doc/core_3_timeouts.cpp]
[import ../../test/doc/core_4_layers.cpp]
[import ../../test/doc/http_10_custom_parser.cpp]
+[import ../../test/doc/websocket_common.ipp]
[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 ../../test/beast/core/rate_policy.cpp]
diff --git a/doc/xsl/class_detail.xsl b/doc/xsl/class_detail.xsl
index 69f2dc0b..3c51b725 100644
--- a/doc/xsl/class_detail.xsl
+++ b/doc/xsl/class_detail.xsl
@@ -1,6 +1,6 @@
- class ``[link beast.concepts.streams.AsyncStream [*AsyncStream]]``
+ class __AsyncStream__
class __AsyncReadStream__
@@ -12,11 +12,11 @@
class __Body__
- class ``[link beast.concepts.BufferSequence [*BufferSequence]]``
+ class __BufferSequence__
- ``[link beast.concepts.BufferSequence [*BufferSequence]]``
+ __BufferSequence__
class __CompletionCondition__
@@ -34,7 +34,7 @@
class __ConstBufferSequence__
- class ``[link beast.concepts.DynamicBuffer [*DynamicBuffer]]``
+ class __DynamicBuffer__
class __EndpointSequence__
@@ -46,7 +46,7 @@
class __Executor__
- class ``[link beast.concepts.Fields [*Fields]]``
+ class __Fields__
class __Handler__
@@ -64,16 +64,16 @@
class __RangeConnectHandler__
- class ``[link beast.concepts.RatePolicy [*RatePolicy]]``
+ class __RatePolicy__
class __ReadHandler__
- class ``[link beast.concepts.streams.Stream [*Stream]]``
+ class __Stream__
- class ``[link beast.concepts.streams.SyncStream [*SyncStream]]``
+ class __SyncStream__
class __SyncReadStream__
diff --git a/include/boost/beast/_experimental/test/impl/stream.hpp b/include/boost/beast/_experimental/test/impl/stream.hpp
index b272ed90..62fa57e2 100644
--- a/include/boost/beast/_experimental/test/impl/stream.hpp
+++ b/include/boost/beast/_experimental/test/impl/stream.hpp
@@ -160,7 +160,7 @@ struct stream::run_write_op
"WriteHandler type requirements not met");
++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(
in_->ioc.get_executor(),
@@ -171,22 +171,16 @@ struct stream::run_write_op
error_code ec;
std::size_t n = 0;
if(in_->fc && in_->fc->fail(ec))
- {
- return complete_op(ec, n);
- }
+ return upcall(ec, n);
// A request to write 0 bytes to a stream is a no-op.
if(buffer_size(buffers) == 0)
- {
- return complete_op(ec, n);
- }
+ return upcall(ec, n);
// connection closed
auto out = out_.lock();
if(! out)
- {
- return complete_op(net::error::connection_reset, n);
- }
+ return upcall(net::error::connection_reset, n);
// copy buffers
n = std::min(
@@ -198,7 +192,7 @@ struct stream::run_write_op
out->notify_read();
}
BOOST_ASSERT(! ec);
- complete_op(ec, n);
+ upcall(ec, n);
}
};
diff --git a/include/boost/beast/_experimental/unit_test/suite.hpp b/include/boost/beast/_experimental/unit_test/suite.hpp
index ef76756f..3cda8fe0 100644
--- a/include/boost/beast/_experimental/unit_test/suite.hpp
+++ b/include/boost/beast/_experimental/unit_test/suite.hpp
@@ -644,7 +644,7 @@ run(runner& r)
// detail:
// This inserts the suite with the given manual flag
#define BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,manual) \
- static beast::unit_test::detail::insert_suite \
+ static ::boost::beast::unit_test::detail::insert_suite \
Library ## Module ## Class ## _test_instance( \
#Class, #Module, #Library, manual)
diff --git a/include/boost/beast/websocket/stream_base.hpp b/include/boost/beast/websocket/stream_base.hpp
index df511830..51600b40 100644
--- a/include/boost/beast/websocket/stream_base.hpp
+++ b/include/boost/beast/websocket/stream_base.hpp
@@ -61,7 +61,7 @@ struct stream_base
template
decorator(Decorator&& f)
- : d_(f)
+ : d_(std::forward(f))
{
}
};
diff --git a/test/doc/CMakeLists.txt b/test/doc/CMakeLists.txt
index 7c645a4c..b52400e3 100644
--- a/test/doc/CMakeLists.txt
+++ b/test/doc/CMakeLists.txt
@@ -26,9 +26,16 @@ add_executable (tests-doc
http_10_custom_parser.cpp
http_examples.cpp
http_snippets.cpp
+ websocket_common.ipp
websocket.cpp
- websocket_3_handshake.cpp
- websocket_snippets.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
exemplars.cpp
)
diff --git a/test/doc/Jamfile b/test/doc/Jamfile
index c1df501a..b5461751 100644
--- a/test/doc/Jamfile
+++ b/test/doc/Jamfile
@@ -18,14 +18,20 @@ project
alias run-tests :
[ compile core_snippets.cpp ]
[ compile http_snippets.cpp ]
- [ compile websocket_snippets.cpp ]
[ run core_1_refresher.cpp $(TEST_MAIN) ]
[ run core_3_timeouts.cpp $(TEST_MAIN) ]
[ run core_4_layers.cpp $(TEST_MAIN) ]
[ run http_10_custom_parser.cpp $(TEST_MAIN) ]
[ run http_examples.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 :
@@ -36,7 +42,14 @@ exe fat-tests :
http_10_custom_parser.cpp
http_examples.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 ;
diff --git a/test/doc/websocket.cpp b/test/doc/websocket.cpp
index f0ac60ca..133e3a85 100644
--- a/test/doc/websocket.cpp
+++ b/test/doc/websocket.cpp
@@ -7,8 +7,6 @@
// Official repository: https://github.com/boostorg/beast
//
-#include "snippets.hpp"
-
#include
#ifdef BOOST_MSVC
@@ -17,26 +15,20 @@
#endif
//[code_websocket_1a
-#include
-#include
+
+#include
#include
-#include
+#include
+#include
+
//]
namespace {
-//[code_websocket_1b
-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);
-
-//]
+#include "websocket_common.ipp"
void
-websocket_snippets()
+snippets()
{
{
//[code_websocket_1f
@@ -52,8 +44,8 @@ websocket_snippets()
{
//[code_websocket_2f
- // The `tcp_stream` will be constructed with a new strand which
- // uses the specified I/O context.
+ // The `tcp_stream` will be constructed with a new
+ // strand which uses the specified I/O context.
stream ws(make_strand(ioc));
@@ -63,34 +55,60 @@ websocket_snippets()
{
//[code_websocket_3f
+ // Ownership of the `tcp_stream` is transferred to the websocket stream
+
+ stream ws(std::move(sock));
+
+ //]
+ }
+
+ {
+ 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
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)
-
-namespace boost {
-namespace beast {
-
-struct websocket_snippets_test
- : public beast::unit_test::suite
+struct doc_websocket_test
+ : public boost::beast::unit_test::suite
{
void
run() override
{
- BEAST_EXPECT(&websocket_snippets);
- pass();
+ BEAST_EXPECT(&snippets);
}
};
-BEAST_DEFINE_TESTSUITE(beast,doc,websocket_snippets);
+BEAST_DEFINE_TESTSUITE(beast,doc,doc_websocket);
-} // beast
-} // boost
+} // (anon)
#ifdef BOOST_MSVC
#pragma warning(pop)
diff --git a/test/doc/websocket_1_connecting.cpp b/test/doc/websocket_1_connecting.cpp
new file mode 100644
index 00000000..fbf35a64
--- /dev/null
+++ b/test/doc/websocket_1_connecting.cpp
@@ -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
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable: 4459) // declaration hides global declaration
+#endif
+
+#include
+#include
+#include
+#include
+
+namespace {
+
+#include "websocket_common.ipp"
+
+void
+snippets()
+{
+ {
+ //[code_websocket_1_1
+
+ 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 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 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
diff --git a/test/doc/websocket_2_handshaking.cpp b/test/doc/websocket_2_handshaking.cpp
new file mode 100644
index 00000000..d16c8e4b
--- /dev/null
+++ b/test/doc/websocket_2_handshaking.cpp
@@ -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
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable: 4459) // declaration hides global declaration
+#endif
+
+#include
+#include
+#include
+#include
+
+namespace {
+
+#include "websocket_common.ipp"
+
+void
+snippets()
+{
+ {
+ //[code_websocket_2_1
+
+ 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 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 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 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
diff --git a/test/doc/websocket_3_decorator.cpp b/test/doc/websocket_3_decorator.cpp
new file mode 100644
index 00000000..6ea485d3
--- /dev/null
+++ b/test/doc/websocket_3_decorator.cpp
@@ -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
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable: 4459) // declaration hides global declaration
+#endif
+
+#include
+#include
+#include
+#include
+
+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 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 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 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("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
diff --git a/test/doc/websocket_3_handshake.cpp b/test/doc/websocket_3_handshake.cpp
deleted file mode 100644
index 24fef815..00000000
--- a/test/doc/websocket_3_handshake.cpp
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace boost {
-namespace beast {
-namespace websocket {
-
-namespace {
-
-void
-websocket_3_handshake_snippets()
-{
- #include "snippets.ipp"
- stream 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 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 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 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
diff --git a/test/doc/websocket_4_messages.cpp b/test/doc/websocket_4_messages.cpp
new file mode 100644
index 00000000..2c324577
--- /dev/null
+++ b/test/doc/websocket_4_messages.cpp
@@ -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
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable: 4459) // declaration hides global declaration
+#endif
+
+#include
+#include
+#include
+#include
+
+namespace {
+
+#include "websocket_common.ipp"
+
+void
+snippets()
+{
+ 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 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
diff --git a/test/doc/websocket_5_control_frames.cpp b/test/doc/websocket_5_control_frames.cpp
new file mode 100644
index 00000000..e0cae607
--- /dev/null
+++ b/test/doc/websocket_5_control_frames.cpp
@@ -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
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable: 4459) // declaration hides global declaration
+#endif
+
+#include
+#include
+#include
+#include
+
+namespace {
+
+#include "websocket_common.ipp"
+
+void
+snippets()
+{
+ 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
diff --git a/test/doc/websocket_6_timeouts.cpp b/test/doc/websocket_6_timeouts.cpp
new file mode 100644
index 00000000..bed65896
--- /dev/null
+++ b/test/doc/websocket_6_timeouts.cpp
@@ -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
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable: 4459) // declaration hides global declaration
+#endif
+
+#include
+#include
+#include
+#include
+
+namespace {
+
+#include "websocket_common.ipp"
+
+void
+snippets()
+{
+ 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
diff --git a/test/doc/websocket_7_teardown.cpp b/test/doc/websocket_7_teardown.cpp
new file mode 100644
index 00000000..363c742b
--- /dev/null
+++ b/test/doc/websocket_7_teardown.cpp
@@ -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
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable: 4459) // declaration hides global declaration
+#endif
+
+#include
+#include
+#include
+#include
+
+namespace {
+
+#include "websocket_common.ipp"
+
+//[code_websocket_7_1
+
+struct custom_stream;
+
+void
+teardown(
+ role_type role,
+ custom_stream& stream,
+ error_code& ec);
+
+template
+void
+async_teardown(
+ role_type role,
+ custom_stream& stream,
+ TeardownHandler&& handler);
+
+//]
+
+//[code_websocket_7_2
+
+template
+struct custom_wrapper
+{
+ NextLayer next_layer;
+
+ template
+ explicit
+ custom_wrapper(Args&&... args)
+ : next_layer(std::forward(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
+ 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(handler));
+ }
+};
+
+//]
+
+void
+snippets()
+{
+ //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
diff --git a/test/doc/websocket_8_notes.cpp b/test/doc/websocket_8_notes.cpp
new file mode 100644
index 00000000..796ae992
--- /dev/null
+++ b/test/doc/websocket_8_notes.cpp
@@ -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
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable: 4459) // declaration hides global declaration
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+namespace {
+
+#include "websocket_common.ipp"
+
+void
+snippets()
+{
+ 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& ws,
+ multi_buffer& buffer, net::yield_context yield)
+{
+ ws.async_read(buffer, yield);
+ std::future 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
diff --git a/test/doc/websocket_common.ipp b/test/doc/websocket_common.ipp
new file mode 100644
index 00000000..7b6bbc28
--- /dev/null
+++ b/test/doc/websocket_common.ipp
@@ -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);
+
+//]
diff --git a/test/doc/websocket_snippets.cpp b/test/doc/websocket_snippets.cpp
deleted file mode 100644
index 5c43434e..00000000
--- a/test/doc/websocket_snippets.cpp
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-using namespace boost::beast;
-
-#include
-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 ws{std::move(sock)};
-//]
-}
-
-{
-//[ws_snippet_4
- stream 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 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 ws{acceptor.get_executor()};
- acceptor.accept(get_lowest_layer(ws).socket());
-//]
-}
-
-{
- stream 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 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 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 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& ws,
- multi_buffer& buffer, net::yield_context yield)
-{
- ws.async_read(buffer, yield);
- std::future 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
-void
-async_teardown(
- role_type role,
- custom_stream& stream,
- TeardownHandler&& handler);
-
-//]
-
-//[ws_snippet_23
-
-template
-struct custom_wrapper
-{
- NextLayer next_layer;
-
- template
- explicit
- custom_wrapper(Args&&... args)
- : next_layer(std::forward(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
- 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(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> 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