mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 05:17:26 +02:00
Documentation work
This commit is contained in:
@ -7,13 +7,13 @@
|
||||
|
||||
[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]
|
||||
request for
|
||||
[@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 HTTP Upgrade request must include the
|
||||
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]
|
||||
@ -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.async_handshake.overload1 `async_handshake`]
|
||||
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:
|
||||
|
||||
[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
|
||||
the HTTP Upgrade request as needed. This example sets a subprotocol
|
||||
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:
|
||||
|
||||
@ -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
|
||||
[link beast.ref.websocket__response_type `response_type`]
|
||||
as follows:
|
||||
```
|
||||
websocket::response_type res;
|
||||
ws.handshake(res, "localhost", "/");
|
||||
if(! res.exists("Sec-WebSocket-Protocol"))
|
||||
throw std::invalid_argument("missing subprotocols");
|
||||
```
|
||||
|
||||
[ws_snippet_10]
|
||||
|
||||
[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
|
||||
similarly. If the handshake fails, an error is returned or exception thrown:
|
||||
```
|
||||
...
|
||||
ws.accept();
|
||||
```
|
||||
|
||||
[ws_snippet_11]
|
||||
|
||||
Successful WebSocket Upgrade responses generated by the implementation will
|
||||
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
|
||||
the HTTP Upgrade request as needed. This example sets the Server
|
||||
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
|
||||
[[Serialized Octets][Description]]
|
||||
@ -77,7 +69,7 @@ The HTTP Upgrade response produced by the previous call will look thusly:
|
||||
|
||||
Undefined behavior results when the upgrade request is successful
|
||||
and the decorator modifies the fields specific to perform the
|
||||
WebSocket Upgrade , such as the Upgrade and Connection fields.
|
||||
WebSocket Upgrade, such as the Upgrade and Connection fields.
|
||||
]]]
|
||||
|
||||
[heading Passing HTTP Requests]
|
||||
@ -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
|
||||
in using the HTTP algorithms, and then passed to a newly constructed
|
||||
stream:
|
||||
```
|
||||
void handle_connection(boost::asio::ip::tcp::socket& sock)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[ws_snippet_13]
|
||||
|
||||
[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
|
||||
specified dynamic buffer as an octet sequence in the buffer's output
|
||||
area, and later uses those octets to attempt an HTTP WebSocket Upgrade:
|
||||
```
|
||||
void do_accept(boost::asio::ip::tcp::socket& sock)
|
||||
{
|
||||
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());
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
[ws_snippet_14]
|
||||
|
||||
[endsect]
|
||||
|
@ -10,19 +10,8 @@
|
||||
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:
|
||||
```
|
||||
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.write(b.data());
|
||||
b.consume(b.size());
|
||||
}
|
||||
```
|
||||
[ws_snippet_15]
|
||||
|
||||
[important
|
||||
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
|
||||
example reads and echoes a complete message using this interface:
|
||||
```
|
||||
template<class NextLayer>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[ws_snippet_16]
|
||||
|
||||
[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`].
|
||||
The object provided with this option should be callable with the following
|
||||
signature:
|
||||
```
|
||||
void on_ping(bool is_pong, websocket::ping_data const& payload);
|
||||
...
|
||||
ws.set_option(ping_callback{&on_ping});
|
||||
```
|
||||
|
||||
[ws_snippet_17]
|
||||
|
||||
When a ping callback is registered, all pings and pongs received through
|
||||
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
|
||||
connection is performed automatically. To manually initiate a close, use
|
||||
the
|
||||
[link beast.ref.websocket__stream.close `close`]
|
||||
function:
|
||||
```
|
||||
ws.close();
|
||||
```
|
||||
[link beast.ref.websocket__stream.close `close`] function:
|
||||
|
||||
[ws_snippet_18]
|
||||
|
||||
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`.
|
||||
@ -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
|
||||
feature, and the write buffer size option determines the maximum size of
|
||||
the fragments:
|
||||
```
|
||||
...
|
||||
ws.set_option(websocket::auto_fragment{true});
|
||||
ws.set_option(websocket::write_buffer_size{16384});
|
||||
```
|
||||
|
||||
[ws_snippet_19]
|
||||
|
||||
[endsect]
|
||||
|
@ -20,28 +20,14 @@ of the underlying TCP/IP connection.
|
||||
[heading Asynchronous Operations]
|
||||
|
||||
Asynchronous versions are available for all functions:
|
||||
```
|
||||
websocket::opcode op;
|
||||
ws.async_read(op, sb,
|
||||
[](boost::system::error_code const& ec)
|
||||
{
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
[ws_snippet_20]
|
||||
|
||||
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:
|
||||
```
|
||||
void echo(websocket::stream<ip::tcp::socket>& ws,
|
||||
boost::asio::yield_context yield)
|
||||
{
|
||||
ws.async_read(sb, yield);
|
||||
std::future<websocket::error_code> fut =
|
||||
ws.async_write, sb.data(), boost::use_future);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
[ws_snippet_21]
|
||||
|
||||
[heading The io_service]
|
||||
|
||||
|
@ -196,6 +196,16 @@ public:
|
||||
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.
|
||||
bool
|
||||
exists(string_view name) const
|
||||
|
@ -7,16 +7,20 @@
|
||||
|
||||
#include <beast/core.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/asio/use_future.hpp>
|
||||
#include <algorithm>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
using namespace beast;
|
||||
|
||||
//[ws_snippet_1
|
||||
#include <beast/websocket.hpp>
|
||||
using namespace beast::websocket;
|
||||
//]
|
||||
|
||||
using namespace beast;
|
||||
|
||||
namespace doc_ws_snippets {
|
||||
|
||||
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()
|
||||
|
||||
//[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
|
||||
|
Reference in New Issue
Block a user