Documentation work

This commit is contained in:
Vinnie Falco
2017-06-08 15:23:30 -07:00
parent eaa820df30
commit 669b1feae1
7 changed files with 208 additions and 139 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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