This commit is contained in:
Vinnie Falco
2019-02-25 17:14:04 -08:00
parent e9ba521c60
commit ab9a4c66e0
32 changed files with 1360 additions and 921 deletions

View File

@@ -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:

View File

@@ -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]

View 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]

View File

@@ -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]

View File

@@ -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

View 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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View 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]

View File

@@ -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]

View File

@@ -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

View File

@@ -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]

View File

@@ -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]

View File

@@ -1,6 +1,6 @@
<!-- CLASS_DETAIL_TEMPLATE BEGIN -->
<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 test="type = 'class AsyncReadStream'">
<xsl:text>class __AsyncReadStream__</xsl:text>
@@ -12,11 +12,11 @@
<xsl:text>class __Body__</xsl:text>
</xsl:when>
<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 test="(type = 'class' or type = 'class...') and declname = 'BufferSequence'">
<xsl:value-of select="type"/>
<xsl:text> ``[link beast.concepts.BufferSequence [*BufferSequence]]``</xsl:text>
<xsl:text> __BufferSequence__</xsl:text>
</xsl:when>
<xsl:when test="declname = 'CompletionCondition' or type = 'class CompletionCondition'">
<xsl:text>class __CompletionCondition__</xsl:text>
@@ -34,7 +34,7 @@
<xsl:text>class __ConstBufferSequence__</xsl:text>
</xsl:when>
<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 test="type = 'class EndpointSequence'">
<xsl:text>class __EndpointSequence__</xsl:text>
@@ -46,7 +46,7 @@
<xsl:text>class __Executor__</xsl:text>
</xsl:when>
<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 test="declname = 'Handler' or type = 'class Handler'">
<xsl:text>class __Handler__</xsl:text>
@@ -64,16 +64,16 @@
<xsl:text>class __RangeConnectHandler__</xsl:text>
</xsl:when>
<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 test="declname = 'ReadHandler' or type = 'class ReadHandler'">
<xsl:text>class __ReadHandler__</xsl:text>
</xsl:when>
<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 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 test="declname = 'SyncReadStream' or type = 'class SyncReadStream'">
<xsl:text>class __SyncReadStream__</xsl:text>

View File

@@ -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<std::size_t>(
@@ -198,7 +192,7 @@ struct stream::run_write_op
out->notify_read();
}
BOOST_ASSERT(! ec);
complete_op(ec, n);
upcall(ec, n);
}
};

View File

@@ -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 <Class##_test> \
static ::boost::beast::unit_test::detail::insert_suite <Class##_test> \
Library ## Module ## Class ## _test_instance( \
#Class, #Module, #Library, manual)

View File

@@ -61,7 +61,7 @@ struct stream_base
template<class Decorator>
decorator(Decorator&& f)
: d_(f)
: d_(std::forward<Decorator>(f))
{
}
};

View File

@@ -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
)

View File

@@ -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 ;

View File

@@ -7,8 +7,6 @@
// Official repository: https://github.com/boostorg/beast
//
#include "snippets.hpp"
#include <boost/beast/_experimental/unit_test/suite.hpp>
#ifdef BOOST_MSVC
@@ -17,26 +15,20 @@
#endif
//[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/websocket.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
//]
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<tcp_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<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
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)
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)

View 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

View 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

View 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

View File

@@ -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

View 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

View 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

View 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

View 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

View 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

View 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);
//]

View File

@@ -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