mirror of
https://github.com/boostorg/beast.git
synced 2025-08-01 22:04:34 +02:00
Documentation work
This commit is contained in:
@@ -7,13 +7,13 @@
|
|||||||
|
|
||||||
[section:client Handshaking (Clients)]
|
[section:client Handshaking (Clients)]
|
||||||
|
|
||||||
A WebSocket session begins when a client sends the HTTP
|
A WebSocket session begins when a client sends the HTTP/1
|
||||||
[@https://tools.ietf.org/html/rfc7230#section-6.7 Upgrade]
|
[@https://tools.ietf.org/html/rfc7230#section-6.7 Upgrade]
|
||||||
request for
|
request for
|
||||||
[@https://tools.ietf.org/html/rfc6455#section-1.3 websocket],
|
[@https://tools.ietf.org/html/rfc6455#section-1.3 websocket],
|
||||||
and the server sends an appropriate HTTP response indicating that
|
and the server sends an appropriate response indicating that
|
||||||
the request was accepted and that the connection has been upgraded.
|
the request was accepted and that the connection has been upgraded.
|
||||||
The HTTP Upgrade request must include the
|
The Upgrade request must include the
|
||||||
[@https://tools.ietf.org/html/rfc7230#section-5.4 Host]
|
[@https://tools.ietf.org/html/rfc7230#section-5.4 Host]
|
||||||
field, and the
|
field, and the
|
||||||
[@https://tools.ietf.org/html/rfc7230#section-5.3 target]
|
[@https://tools.ietf.org/html/rfc7230#section-5.3 target]
|
||||||
@@ -21,13 +21,10 @@ of the resource to request. The stream member functions
|
|||||||
[link beast.ref.websocket__stream.handshake.overload1 `handshake`] and
|
[link beast.ref.websocket__stream.handshake.overload1 `handshake`] and
|
||||||
[link beast.ref.websocket__stream.async_handshake.overload1 `async_handshake`]
|
[link beast.ref.websocket__stream.async_handshake.overload1 `async_handshake`]
|
||||||
are used to send the request with the required host and target strings.
|
are used to send the request with the required host and target strings.
|
||||||
```
|
|
||||||
...
|
|
||||||
ws.set_option(websocket::keep_alive(true));
|
|
||||||
ws.handshake("localhost", "/");
|
|
||||||
```
|
|
||||||
|
|
||||||
The implementation will create and send an HTTP request that typically
|
[ws_snippet_8]
|
||||||
|
|
||||||
|
The implementation will create and send a request that typically
|
||||||
looks like this:
|
looks like this:
|
||||||
|
|
||||||
[table WebSocket Upgrade HTTP Request
|
[table WebSocket Upgrade HTTP Request
|
||||||
@@ -58,15 +55,8 @@ are provided which allow an additional function object, called a
|
|||||||
['decorator], to be passed. The decorator is invoked to modify
|
['decorator], to be passed. The decorator is invoked to modify
|
||||||
the HTTP Upgrade request as needed. This example sets a subprotocol
|
the HTTP Upgrade request as needed. This example sets a subprotocol
|
||||||
on the request:
|
on the request:
|
||||||
```
|
|
||||||
void decorate(websocket::request_type& req)
|
|
||||||
{
|
|
||||||
req.insert("Sec-WebSocket-Protocol", "xmpp;ws-chat");
|
|
||||||
}
|
|
||||||
...
|
|
||||||
ws.handshake_ex("localhost", "/", &decorate);
|
|
||||||
|
|
||||||
```
|
[ws_snippet_9]
|
||||||
|
|
||||||
The HTTP Upgrade request produced by the previous call will look thusly:
|
The HTTP Upgrade request produced by the previous call will look thusly:
|
||||||
|
|
||||||
@@ -99,11 +89,7 @@ overloads of the handshake member function allow the caller to store the
|
|||||||
received HTTP message in an output reference argument as
|
received HTTP message in an output reference argument as
|
||||||
[link beast.ref.websocket__response_type `response_type`]
|
[link beast.ref.websocket__response_type `response_type`]
|
||||||
as follows:
|
as follows:
|
||||||
```
|
|
||||||
websocket::response_type res;
|
[ws_snippet_10]
|
||||||
ws.handshake(res, "localhost", "/");
|
|
||||||
if(! res.exists("Sec-WebSocket-Protocol"))
|
|
||||||
throw std::invalid_argument("missing subprotocols");
|
|
||||||
```
|
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
@@ -17,10 +17,8 @@ setting, the connection may remain open for a subsequent handshake attempt.
|
|||||||
|
|
||||||
Performing a handshake for an incoming websocket upgrade request operates
|
Performing a handshake for an incoming websocket upgrade request operates
|
||||||
similarly. If the handshake fails, an error is returned or exception thrown:
|
similarly. If the handshake fails, an error is returned or exception thrown:
|
||||||
```
|
|
||||||
...
|
[ws_snippet_11]
|
||||||
ws.accept();
|
|
||||||
```
|
|
||||||
|
|
||||||
Successful WebSocket Upgrade responses generated by the implementation will
|
Successful WebSocket Upgrade responses generated by the implementation will
|
||||||
typically look like this:
|
typically look like this:
|
||||||
@@ -49,16 +47,10 @@ are provided which allow an additional function object, called a
|
|||||||
['decorator], to be passed. The decorator is invoked to modify
|
['decorator], to be passed. The decorator is invoked to modify
|
||||||
the HTTP Upgrade request as needed. This example sets the Server
|
the HTTP Upgrade request as needed. This example sets the Server
|
||||||
field on the response:
|
field on the response:
|
||||||
```
|
|
||||||
ws.accept_ex(
|
|
||||||
[](websocket::response_type& res)
|
|
||||||
{
|
|
||||||
res.insert("Server", "AcmeServer");
|
|
||||||
});
|
|
||||||
|
|
||||||
```
|
[ws_snippet_12]
|
||||||
|
|
||||||
The HTTP Upgrade response produced by the previous call will look thusly:
|
The HTTP Upgrade response produced by the previous call looks like this:
|
||||||
|
|
||||||
[table Decorated WebSocket Upgrade HTTP Request
|
[table Decorated WebSocket Upgrade HTTP Request
|
||||||
[[Serialized Octets][Description]]
|
[[Serialized Octets][Description]]
|
||||||
@@ -97,19 +89,8 @@ are provided which receive the entire HTTP request header as an object
|
|||||||
to perform the handshake. In this example, the request is first read
|
to perform the handshake. In this example, the request is first read
|
||||||
in using the HTTP algorithms, and then passed to a newly constructed
|
in using the HTTP algorithms, and then passed to a newly constructed
|
||||||
stream:
|
stream:
|
||||||
```
|
|
||||||
void handle_connection(boost::asio::ip::tcp::socket& sock)
|
[ws_snippet_13]
|
||||||
{
|
|
||||||
flat_buffer buffer;
|
|
||||||
http::request<http::string_body> req;
|
|
||||||
http::read(sock, buffer, req);
|
|
||||||
if(websocket::is_upgrade(req))
|
|
||||||
{
|
|
||||||
websocket::stream<decltype(sock)> ws{std::move(sock)};
|
|
||||||
ws.accept(req);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[heading Buffered Handshakes]
|
[heading Buffered Handshakes]
|
||||||
|
|
||||||
@@ -129,16 +110,7 @@ them as part of the handshake.
|
|||||||
In this example, the server reads the initial HTTP message into the
|
In this example, the server reads the initial HTTP message into the
|
||||||
specified dynamic buffer as an octet sequence in the buffer's output
|
specified dynamic buffer as an octet sequence in the buffer's output
|
||||||
area, and later uses those octets to attempt an HTTP WebSocket Upgrade:
|
area, and later uses those octets to attempt an HTTP WebSocket Upgrade:
|
||||||
```
|
|
||||||
void do_accept(boost::asio::ip::tcp::socket& sock)
|
[ws_snippet_14]
|
||||||
{
|
|
||||||
boost::asio::streambuf sb;
|
|
||||||
boost::asio::read_until(sock, sb, "\r\n\r\n");
|
|
||||||
...
|
|
||||||
websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
|
||||||
ws.accept(sb.data());
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
@@ -10,19 +10,8 @@
|
|||||||
After the WebSocket handshake is accomplished, callers may send and receive
|
After the WebSocket handshake is accomplished, callers may send and receive
|
||||||
messages using the message oriented interface. This interface requires that
|
messages using the message oriented interface. This interface requires that
|
||||||
all of the buffers representing the message are known ahead of time:
|
all of the buffers representing the message are known ahead of time:
|
||||||
```
|
|
||||||
template<class NextLayer>
|
|
||||||
void echo(websocket::stream<NextLayer>& ws)
|
|
||||||
{
|
|
||||||
multi_buffer b;
|
|
||||||
websocket::opcode::value op;
|
|
||||||
ws.read(op, b);
|
|
||||||
|
|
||||||
ws.set_option(websocket::message_type{op});
|
[ws_snippet_15]
|
||||||
ws.write(b.data());
|
|
||||||
b.consume(b.size());
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[important
|
[important
|
||||||
Calls to [link beast.ref.websocket__stream.set_option `set_option`]
|
Calls to [link beast.ref.websocket__stream.set_option `set_option`]
|
||||||
@@ -41,37 +30,7 @@ message ahead of time:
|
|||||||
|
|
||||||
For these cases, the frame oriented interface may be used. This
|
For these cases, the frame oriented interface may be used. This
|
||||||
example reads and echoes a complete message using this interface:
|
example reads and echoes a complete message using this interface:
|
||||||
```
|
|
||||||
template<class NextLayer>
|
[ws_snippet_16]
|
||||||
void echo(websocket::stream<NextLayer>& ws)
|
|
||||||
{
|
|
||||||
multi_buffer b;
|
|
||||||
websocket::frame_info fi;
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
ws.read_frame(fi, b);
|
|
||||||
if(fi.fin)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ws.set_option(websocket::message_type{fi.op});
|
|
||||||
consuming_buffers<
|
|
||||||
multi_buffer::const_buffers_type> cb{b.data()};
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
using boost::asio::buffer_size;
|
|
||||||
std::size_t size = std::min(buffer_size(cb));
|
|
||||||
if(size > 512)
|
|
||||||
{
|
|
||||||
ws.write_frame(false, prepare_buffers(512, cb));
|
|
||||||
cb.consume(512);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ws.write_frame(true, cb);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
@@ -49,11 +49,8 @@ To be notified of ping and pong control frames, callers may register a
|
|||||||
"ping callback" using [link beast.ref.websocket__stream.set_option `set_option`].
|
"ping callback" using [link beast.ref.websocket__stream.set_option `set_option`].
|
||||||
The object provided with this option should be callable with the following
|
The object provided with this option should be callable with the following
|
||||||
signature:
|
signature:
|
||||||
```
|
|
||||||
void on_ping(bool is_pong, websocket::ping_data const& payload);
|
[ws_snippet_17]
|
||||||
...
|
|
||||||
ws.set_option(ping_callback{&on_ping});
|
|
||||||
```
|
|
||||||
|
|
||||||
When a ping callback is registered, all pings and pongs received through
|
When a ping callback is registered, all pings and pongs received through
|
||||||
either synchronous read functions or asynchronous read functions will
|
either synchronous read functions or asynchronous read functions will
|
||||||
@@ -80,11 +77,9 @@ The WebSocket protocol defines a procedure and control message for initiating
|
|||||||
a close of the session. Handling of close initiated by the remote end of the
|
a close of the session. Handling of close initiated by the remote end of the
|
||||||
connection is performed automatically. To manually initiate a close, use
|
connection is performed automatically. To manually initiate a close, use
|
||||||
the
|
the
|
||||||
[link beast.ref.websocket__stream.close `close`]
|
[link beast.ref.websocket__stream.close `close`] function:
|
||||||
function:
|
|
||||||
```
|
[ws_snippet_18]
|
||||||
ws.close();
|
|
||||||
```
|
|
||||||
|
|
||||||
When the remote peer initiates a close by sending a close frame, Beast
|
When the remote peer initiates a close by sending a close frame, Beast
|
||||||
will handle it for you by causing the next read to return `error::closed`.
|
will handle it for you by causing the next read to return `error::closed`.
|
||||||
@@ -107,10 +102,7 @@ To ensure timely delivery of control frames, large messages can be broken up
|
|||||||
into smaller sized frames. The automatic fragment option turns on this
|
into smaller sized frames. The automatic fragment option turns on this
|
||||||
feature, and the write buffer size option determines the maximum size of
|
feature, and the write buffer size option determines the maximum size of
|
||||||
the fragments:
|
the fragments:
|
||||||
```
|
|
||||||
...
|
[ws_snippet_19]
|
||||||
ws.set_option(websocket::auto_fragment{true});
|
|
||||||
ws.set_option(websocket::write_buffer_size{16384});
|
|
||||||
```
|
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
@@ -20,28 +20,14 @@ of the underlying TCP/IP connection.
|
|||||||
[heading Asynchronous Operations]
|
[heading Asynchronous Operations]
|
||||||
|
|
||||||
Asynchronous versions are available for all functions:
|
Asynchronous versions are available for all functions:
|
||||||
```
|
|
||||||
websocket::opcode op;
|
[ws_snippet_20]
|
||||||
ws.async_read(op, sb,
|
|
||||||
[](boost::system::error_code const& ec)
|
|
||||||
{
|
|
||||||
...
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Calls to asynchronous initiation functions support the extensible asynchronous
|
Calls to asynchronous initiation functions support the extensible asynchronous
|
||||||
model developed by the Boost.Asio author, allowing for traditional completion
|
model developed by the Boost.Asio author, allowing for traditional completion
|
||||||
handlers, stackful or stackless coroutines, and even futures:
|
handlers, stackful or stackless coroutines, and even futures:
|
||||||
```
|
|
||||||
void echo(websocket::stream<ip::tcp::socket>& ws,
|
[ws_snippet_21]
|
||||||
boost::asio::yield_context yield)
|
|
||||||
{
|
|
||||||
ws.async_read(sb, yield);
|
|
||||||
std::future<websocket::error_code> fut =
|
|
||||||
ws.async_write, sb.data(), boost::use_future);
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[heading The io_service]
|
[heading The io_service]
|
||||||
|
|
||||||
|
@@ -196,6 +196,16 @@ public:
|
|||||||
return list_.cend();
|
return list_.cend();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return `true` if the specified field exists.
|
||||||
|
bool
|
||||||
|
exists(field f) const
|
||||||
|
{
|
||||||
|
// VFALCO Should we throw here?
|
||||||
|
if(f == field::unknown)
|
||||||
|
return false;
|
||||||
|
return set_.find(to_string(f), less{}) != set_.end();
|
||||||
|
}
|
||||||
|
|
||||||
/// Return `true` if the specified field exists.
|
/// Return `true` if the specified field exists.
|
||||||
bool
|
bool
|
||||||
exists(string_view name) const
|
exists(string_view name) const
|
||||||
|
@@ -7,16 +7,20 @@
|
|||||||
|
|
||||||
#include <beast/core.hpp>
|
#include <beast/core.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/spawn.hpp>
|
||||||
|
#include <boost/asio/use_future.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <future>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
using namespace beast;
|
|
||||||
|
|
||||||
//[ws_snippet_1
|
//[ws_snippet_1
|
||||||
#include <beast/websocket.hpp>
|
#include <beast/websocket.hpp>
|
||||||
using namespace beast::websocket;
|
using namespace beast::websocket;
|
||||||
//]
|
//]
|
||||||
|
|
||||||
|
using namespace beast;
|
||||||
|
|
||||||
namespace doc_ws_snippets {
|
namespace doc_ws_snippets {
|
||||||
|
|
||||||
void fxx() {
|
void fxx() {
|
||||||
@@ -67,6 +71,166 @@ boost::asio::ip::tcp::socket sock{ios};
|
|||||||
//]
|
//]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
||||||
|
//[ws_snippet_8
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[ws_snippet_9
|
||||||
|
ws.handshake_ex("localhost", "/",
|
||||||
|
[](request_type& req)
|
||||||
|
{
|
||||||
|
req.insert(http::field::sec_websocket_protocol, "xmpp;ws-chat");
|
||||||
|
});
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[ws_snippet_10
|
||||||
|
response_type res;
|
||||||
|
ws.handshake(res, "localhost", "/");
|
||||||
|
if(! res.exists(http::field::sec_websocket_protocol))
|
||||||
|
throw std::invalid_argument("missing subprotocols");
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[ws_snippet_11
|
||||||
|
ws.accept();
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[ws_snippet_12
|
||||||
|
ws.accept_ex(
|
||||||
|
[](response_type& res)
|
||||||
|
{
|
||||||
|
res.insert(http::field::server, "MyServer");
|
||||||
|
});
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[ws_snippet_13]
|
||||||
|
// Buffer 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<boost::asio::ip::tcp::socket> ws{std::move(sock)};
|
||||||
|
|
||||||
|
// Accept the request from our message. Clients SHOULD NOT
|
||||||
|
// begin sending WebSocket frames until the server has
|
||||||
|
// provided a response, but just in case they did, we pass
|
||||||
|
// any leftovers in the buffer to the accept function.
|
||||||
|
//
|
||||||
|
ws.accept(req, buffer.data());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Its not a WebSocket upgrade, so
|
||||||
|
// handle it like a normal HTTP request.
|
||||||
|
}
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
||||||
|
//[ws_snippet_14
|
||||||
|
// Read into our buffer until we reach the end of the HTTP request.
|
||||||
|
// No parsing takes place here, we are just accumulating data.
|
||||||
|
boost::asio::streambuf buffer;
|
||||||
|
boost::asio::read_until(sock, buffer, "\r\n\r\n");
|
||||||
|
|
||||||
|
// Now accept the connection, using the buffered data.
|
||||||
|
ws.accept(buffer.data());
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
||||||
|
//[ws_snippet_15
|
||||||
|
multi_buffer buffer;
|
||||||
|
opcode op;
|
||||||
|
ws.read(op, buffer);
|
||||||
|
|
||||||
|
ws.set_option(message_type{op});
|
||||||
|
ws.write(buffer.data());
|
||||||
|
buffer.consume(buffer.size());
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
||||||
|
//[ws_snippet_16
|
||||||
|
multi_buffer buffer;
|
||||||
|
frame_info fi;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
ws.read_frame(fi, buffer);
|
||||||
|
if(fi.fin)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ws.set_option(message_type{fi.op});
|
||||||
|
consuming_buffers<multi_buffer::const_buffers_type> cb{buffer.data()};
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
if(buffer_size(cb) > 512)
|
||||||
|
{
|
||||||
|
ws.write_frame(false, buffer_prefix(512, cb));
|
||||||
|
cb.consume(512);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ws.write_frame(true, cb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
||||||
|
//[ws_snippet_17
|
||||||
|
ws.set_option(ping_callback(
|
||||||
|
[](bool is_pong, ping_data const& payload)
|
||||||
|
{
|
||||||
|
// Do something with the payload
|
||||||
|
}));
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[ws_snippet_18
|
||||||
|
ws.close(close_code::normal);
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[ws_snippet_19
|
||||||
|
ws.set_option(auto_fragment{true});
|
||||||
|
ws.set_option(write_buffer_size{16384});
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[ws_snippet_20
|
||||||
|
opcode op;
|
||||||
|
multi_buffer buffer;
|
||||||
|
ws.async_read(op, buffer,
|
||||||
|
[](error_code ec)
|
||||||
|
{
|
||||||
|
// Do something with the buffer
|
||||||
|
});
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
} // fxx()
|
} // fxx()
|
||||||
|
|
||||||
|
//[ws_snippet_21
|
||||||
|
void echo(stream<boost::asio::ip::tcp::socket>& ws,
|
||||||
|
multi_buffer& buffer, boost::asio::yield_context yield)
|
||||||
|
{
|
||||||
|
opcode op;
|
||||||
|
ws.async_read(op, buffer, yield);
|
||||||
|
std::future<void> fut =
|
||||||
|
ws.async_write(buffer.data(), boost::asio::use_future);
|
||||||
|
}
|
||||||
|
//]
|
||||||
|
|
||||||
} // doc_ws_snippets
|
} // doc_ws_snippets
|
||||||
|
Reference in New Issue
Block a user