From ab9a4c66e0cae58c7ded86224491545f3197cf5b Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 25 Feb 2019 17:14:04 -0800 Subject: [PATCH] Doc work --- CHANGELOG.md | 1 + doc/qbk/06_websocket/01_connecting.qbk | 34 +- doc/qbk/06_websocket/02_handshaking.qbk | 155 +++++++++ doc/qbk/06_websocket/03_client.qbk | 67 ---- .../{05_decorator.qbk => 03_decorator.qbk} | 25 +- doc/qbk/06_websocket/04_messages.qbk | 122 +++++++ doc/qbk/06_websocket/04_server.qbk | 107 ------ .../{07_control.qbk => 05_control_frames.qbk} | 6 +- doc/qbk/06_websocket/06_messages.qbk | 83 ----- doc/qbk/06_websocket/06_timeouts.qbk | 14 + .../{08_teardown.qbk => 07_teardown.qbk} | 4 +- .../{09_notes.qbk => 08_notes.qbk} | 8 +- doc/qbk/06_websocket/_websocket.qbk | 60 ++-- doc/qbk/main.qbk | 10 +- doc/xsl/class_detail.xsl | 16 +- .../beast/_experimental/test/impl/stream.hpp | 16 +- .../beast/_experimental/unit_test/suite.hpp | 2 +- include/boost/beast/websocket/stream_base.hpp | 2 +- test/doc/CMakeLists.txt | 11 +- test/doc/Jamfile | 19 +- test/doc/websocket.cpp | 78 +++-- test/doc/websocket_1_connecting.cpp | 89 +++++ test/doc/websocket_2_handshaking.cpp | 154 +++++++++ test/doc/websocket_3_decorator.cpp | 145 +++++++++ test/doc/websocket_3_handshake.cpp | 236 -------------- test/doc/websocket_4_messages.cpp | 146 +++++++++ test/doc/websocket_5_control_frames.cpp | 78 +++++ test/doc/websocket_6_timeouts.cpp | 54 ++++ test/doc/websocket_7_teardown.cpp | 113 +++++++ test/doc/websocket_8_notes.cpp | 100 ++++++ test/doc/websocket_common.ipp | 20 ++ test/doc/websocket_snippets.cpp | 306 ------------------ 32 files changed, 1360 insertions(+), 921 deletions(-) create mode 100644 doc/qbk/06_websocket/02_handshaking.qbk delete mode 100644 doc/qbk/06_websocket/03_client.qbk rename doc/qbk/06_websocket/{05_decorator.qbk => 03_decorator.qbk} (78%) create mode 100644 doc/qbk/06_websocket/04_messages.qbk delete mode 100644 doc/qbk/06_websocket/04_server.qbk rename doc/qbk/06_websocket/{07_control.qbk => 05_control_frames.qbk} (98%) delete mode 100644 doc/qbk/06_websocket/06_messages.qbk create mode 100644 doc/qbk/06_websocket/06_timeouts.qbk rename doc/qbk/06_websocket/{08_teardown.qbk => 07_teardown.qbk} (98%) rename doc/qbk/06_websocket/{09_notes.qbk => 08_notes.qbk} (97%) create mode 100644 test/doc/websocket_1_connecting.cpp create mode 100644 test/doc/websocket_2_handshaking.cpp create mode 100644 test/doc/websocket_3_decorator.cpp delete mode 100644 test/doc/websocket_3_handshake.cpp create mode 100644 test/doc/websocket_4_messages.cpp create mode 100644 test/doc/websocket_5_control_frames.cpp create mode 100644 test/doc/websocket_6_timeouts.cpp create mode 100644 test/doc/websocket_7_teardown.cpp create mode 100644 test/doc/websocket_8_notes.cpp create mode 100644 test/doc/websocket_common.ipp delete mode 100644 test/doc/websocket_snippets.cpp 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