]> git.proxmox.com Git - ceph.git/blame - ceph/src/Beast/doc/websocket.qbk
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / Beast / doc / websocket.qbk
CommitLineData
7c673cae
FG
1[/
2 Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
3
4 Distributed under the Boost Software License, Version 1.0. (See accompanying
5 file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6]
7
8[section:websocket WebSocket]
9
10[block '''
11<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
12 <member><link linkend="beast.websocket.creation">Creation</link></member>
13 <member><link linkend="beast.websocket.connections">Making connections</link></member>
14 <member><link linkend="beast.websocket.handshaking">Handshaking</link></member>
15 <member><link linkend="beast.websocket.messages">Messages</link></member>
16 <member><link linkend="beast.websocket.frames">Frames</link></member>
17 <member><link linkend="beast.websocket.control">Control Frames</link></member>
18 <member><link linkend="beast.websocket.buffers">Buffers</link></member>
19 <member><link linkend="beast.websocket.async">Asynchronous interface</link></member>
20 <member><link linkend="beast.websocket.io_service">The io_service</link></member>
21 <member><link linkend="beast.websocket.threads">Thread Safety</link></member>
22</simplelist></entry></row></tbody></tgroup></informaltable>
23''']
24
25The WebSocket Protocol enables two-way communication between a client
26running untrusted code in a controlled environment to a remote host that has
27opted-in to communications from that code. The protocol consists of an opening
28handshake followed by basic message framing, layered over TCP. The goal of
29this technology is to provide a mechanism for browser-based applications that
30need two-way communication with servers that does not rely on opening multiple
31HTTP connections.
32
33Beast.WebSocket provides developers with a robust WebSocket implementation
34built on Boost.Asio with a consistent asynchronous model using a modern
35C++ approach.
36
37The WebSocket protocol is described fully in
38[@https://tools.ietf.org/html/rfc6455 rfc6455]
39
40[note
41 The following documentation assumes familiarity with both
42 Boost.Asio and the WebSocket protocol specification described in __rfc6455__.
43]
44
45
46
47
48[section:creation Creation]
49
50The interface to Beast's WebSocket implementation is a single template
51class [link beast.ref.websocket__stream `beast::websocket::stream`] which
52wraps a "next layer" object. The next layer object must meet the requirements
53of [link beast.ref.streams.SyncStream [*`SyncStream`]] if synchronous
54operations are performed, or
55[link beast.ref.streams.AsyncStream [*`AsyncStream`]] if asynchronous
56operations are performed, or both. Arguments supplied during construction are
57passed to next layer's constructor. Here we declare a websocket stream over
58a TCP/IP socket with ownership of the socket:
59```
60boost::asio::io_service ios;
61beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
62```
63
64[heading Using SSL]
65
66To use WebSockets over SSL, choose an SSL stream for the next layer template
67argument when constructing the stream.
68```
69#include <beast/websocket/ssl.hpp>
70#include <beast/websocket.hpp>
71#include <boost/asio/ssl.hpp>
72
73boost::asio::io_service ios;
74boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
75beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ws{ios, ctx};
76```
77
78[note
79 When creating websocket stream objects using SSL, it is necessary
80 to include the file `<beast/websocket/ssl.hpp>`.
81]
82
83[heading Non-owning references]
84
85For servers that can handshake in multiple protocols, it may be desired
86to wrap an object that already exists. This socket can be moved in:
87```
88 boost::asio::ip::tcp::socket&& sock;
89 ...
90 beast::websocket::stream<boost::asio::ip::tcp::socket> ws{std::move(sock)};
91```
92
93Or, the wrapper can be constructed with a non-owning reference. In
94this case, the caller is responsible for managing the lifetime of the
95underlying socket being wrapped:
96```
97 boost::asio::ip::tcp::socket sock;
98 ...
99 beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
100```
101
102The layer being wrapped can be accessed through the websocket's "next layer",
103permitting callers to interact directly with its interface.
104```
105 boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
106 beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws{ios, ctx};
107 ...
108 ws.next_layer().shutdown(); // ssl::stream shutdown
109```
110
111[warning
112 Initiating read and write operations on the next layer while
113 stream operations are being performed can break invariants, and
114 result in undefined behavior.
115]
116
117[endsect]
118
119
120
121[section:connections Making connections]
122
123Connections are established by using the interfaces which already exist
124for the next layer. For example, making an outgoing connection:
125```
126 std::string const host = "mywebapp.com";
127 boost::asio::io_service ios;
128 boost::asio::ip::tcp::resolver r{ios};
129 beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
130 boost::asio::connect(ws.next_layer(),
131 r.resolve(boost::asio::ip::tcp::resolver::query{host, "ws"}));
132```
133
134Accepting an incoming connection:
135```
136void do_accept(boost::asio::ip::tcp::acceptor& acceptor)
137{
138 beast::websocket::stream<boost::asio::ip::tcp::socket> ws{acceptor.get_io_service()};
139 acceptor.accept(ws.next_layer());
140}
141```
142
143[note
144 Examples use synchronous interfaces for clarity of exposition.
145]
146
147[endsect]
148
149
150
151[section:handshaking Handshaking]
152
153A WebSocket session begins when one side sends the HTTP Upgrade request
154for websocket, and the other side sends an appropriate HTTP response
155indicating that the request was accepted and that the connection has
156been upgraded. The HTTP Upgrade request must include the Host HTTP field,
157and the URI of the resource to request.
158[link beast.ref.websocket__stream.handshake `handshake`] is used to send the
159request with the required host and resource strings.
160```
161 beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
162 ...
163 ws.set_option(beast::websocket::keep_alive(true));
164 ws.handshake("ws.example.com:80", "/cgi-bin/bitcoin-prices");
165```
166
167The [link beast.ref.websocket__stream `stream`] automatically
168handles receiving and processing the HTTP response to the handshake request.
169The call to handshake is successful if a HTTP response is received with the
170101 "Switching Protocols" status code. On failure, an error is returned or an
171exception is thrown. Depending on the keep alive setting, the socket may remain
172open for a subsequent handshake attempt
173
174Performing a handshake for an incoming websocket upgrade request operates
175similarly. If the handshake fails, an error is returned or exception thrown:
176```
177 beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
178 ...
179 ws.accept();
180```
181
182Servers that can handshake in multiple protocols may have already read data
183on the connection, or might have already received an entire HTTP request
184containing the upgrade request. Overloads of `accept` allow callers to
185pass in this additional buffered handshake data.
186```
187void do_accept(boost::asio::ip::tcp::socket& sock)
188{
189 boost::asio::streambuf sb;
190 boost::asio::read_until(sock, sb, "\r\n\r\n");
191 ...
192 beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
193 ws.accept(sb.data());
194 ...
195}
196```
197
198Alternatively, the caller can pass an entire HTTP request if it was
199obtained elsewhere:
200```
201void do_accept(boost::asio::ip::tcp::socket& sock)
202{
203 boost::asio::streambuf sb;
204 beast::http::request<http::empty_body> request;
205 beast::http::read(sock, sb, request);
206 if(beast::http::is_upgrade(request))
207 {
208 websocket::stream<ip::tcp::socket&> ws{sock};
209 ws.accept(request);
210 ...
211 }
212}
213```
214
215[endsect]
216
217
218
219[section:messages Messages]
220
221After the WebSocket handshake is accomplished, callers may send and receive
222messages using the message oriented interface. This interface requires that
223all of the buffers representing the message are known ahead of time:
224```
225void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
226{
227 beast::streambuf sb;
228 beast::websocket::opcode::value op;
229 ws.read(op, sb);
230
231 ws.set_option(beast::websocket::message_type{op});
232 ws.write(sb.data());
233 sb.consume(sb.size());
234}
235```
236
237[important
238 Calls to [link beast.ref.websocket__stream.set_option `set_option`]
239 must be made from the same implicit or explicit strand as that used
240 to perform other operations.
241]
242
243[endsect]
244
245
246
247[section:frames Frames]
248
249Some use-cases make it impractical or impossible to buffer the entire
250message ahead of time:
251
252* Streaming multimedia to an endpoint.
253* Sending a message that does not fit in memory at once.
254* Providing incremental results as they become available.
255
256For these cases, the frame oriented interface may be used. This
257example reads and echoes a complete message using this interface:
258```
259void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
260{
261 beast::streambuf sb;
262 beast::websocket::frame_info fi;
263 for(;;)
264 {
265 ws.read_frame(fi, sb);
266 if(fi.fin)
267 break;
268 }
269 ws.set_option(beast::websocket::message_type{fi.op});
270 beast::consuming_buffers<
271 beast::streambuf::const_buffers_type> cb{sb.data()};
272 for(;;)
273 {
274 using boost::asio::buffer_size;
275 std::size_t size = std::min(buffer_size(cb));
276 if(size > 512)
277 {
278 ws.write_frame(false, beast::prepare_buffers(512, cb));
279 cb.consume(512);
280 }
281 else
282 {
283 ws.write_frame(true, cb);
284 break;
285 }
286 }
287}
288```
289
290[endsect]
291
292
293
294[section:control Control Frames]
295
296Control frames are small (less than 128 bytes) messages entirely contained
297in an individual WebSocket frame. They may be sent at any time by either
298peer on an established connection, and can appear in between continuation
299frames for a message. There are three types of control frames: ping, pong,
300and close.
301
302A sent ping indicates a request that the sender wants to receive a pong. A
303pong is a response to a ping. Pongs may be sent unsolicited, at any time.
304One use for an unsolicited pong is to inform the remote peer that the
305session is still active after a long period of inactivity. A close frame
306indicates that the remote peer wishes to close the WebSocket connection.
307The connection is considered gracefully closed when each side has sent
308and received a close frame.
309
310During read operations, Beast automatically reads and processes control
311frames. Pings are replied to as soon as possible with a pong, received
312ping and pongs are delivered to the ping callback. The receipt of a close
313frame initiates the WebSocket close procedure, eventually resulting in the
314error code [link beast.ref.websocket__error `error::closed`] being delivered
315to the caller in a subsequent read operation, assuming no other error
316takes place.
317
318A consequence of this automatic behavior is that caller-initiated read
319operations can cause socket writes. However, these writes will not
320compete with caller-initiated write operations. For the purposes of
321correctness with respect to the stream invariants, caller-initiated
322read operations still only count as a read. This means that callers can
323have a simultaneously active read, write, and ping operation in progress,
324while the implementation also automatically handles control frames.
325
326[heading Ping and Pong Frames]
327
328Ping and pong messages are control frames which may be sent at any time
329by either peer on an established WebSocket connection. They are sent
330using the functions
331 [link beast.ref.websocket__stream.ping `ping`] and
332 [link beast.ref.websocket__stream.pong `pong`].
333
334To be notified of ping and pong control frames, callers may register a
335"ping callback" using [link beast.ref.websocket__stream.set_option `set_option`].
336The object provided with this option should be callable with the following
337signature:
338```
339 void on_ping(bool is_pong, ping_data const& payload);
340 ...
341 ws.set_option(ping_callback{&on_ping});
342```
343
344When a ping callback is registered, all pings and pongs received through
345either synchronous read functions or asynchronous read functions will
346invoke the ping callback, with the value of `is_pong` set to `true` if a
347pong was received else `false` if a ping was received. The payload of
348the ping or pong control frame is passed in the payload argument.
349
350Unlike regular completion handlers used in calls to asynchronous initiation
351functions, the ping callback only needs to be set once. The callback is not
352reset when a ping or pong is received. The same callback is used for both
353synchronous and asynchronous reads. The ping callback is passive; in order
354to receive pings and pongs, a synchronous or asynchronous stream read
355function must be active.
356
357[note
358 When an asynchronous read function receives a ping or pong, the
359 ping callback is invoked in the same manner as that used to invoke
360 the final completion handler of the corresponding read function.
361]
362
363[heading Close Frames]
364
365The WebSocket protocol defines a procedure and control message for initiating
366a close of the session. Handling of close initiated by the remote end of the
367connection is performed automatically. To manually initiate a close, use
368the [link beast.ref.websocket__stream.close `close`] function:
369```
370 ws.close();
371```
372
373When the remote peer initiates a close by sending a close frame, Beast
374will handle it for you by causing the next read to return `error::closed`.
375When this error code is delivered, it indicates to the application that
376the WebSocket connection has been closed cleanly, and that the TCP/IP
377connection has been closed. After initiating a close, it is necessary to
378continue reading messages until receiving the error `error::closed`. This
379is because the remote peer may still be sending message and control frames
380before it receives and responds to the close frame.
381
382[important
383 To receive the [link beast.ref.websocket__error `error::closed`]
384 error, a read operation is required.
385]
386
387[heading Auto-fragment]
388
389To ensure timely delivery of control frames, large messages can be broken up
390into smaller sized frames. The automatic fragment option turns on this
391feature, and the write buffer size option determines the maximum size of
392the fragments:
393```
394 ...
395 ws.set_option(beast::websocket::auto_fragment{true});
396 ws.set_option(beast::websocket::write_buffer_size{16384});
397```
398
399[endsect]
400
401
402
403[section:buffers Buffers]
404
405Because calls to read data may return a variable amount of bytes, the
406interface to calls that read data require an object that meets the requirements
407of [link beast.ref.DynamicBuffer [*`DynamicBuffer`]]. This concept is modeled on
408[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/basic_streambuf.html `boost::asio::basic_streambuf`].
409
410The implementation does not perform queueing or buffering of messages. If
411desired, these features should be provided by callers. The impact of this
412design is that library users are in full control of the allocation strategy
413used to store data and the back-pressure applied on the read and write side
414of the underlying TCP/IP connection.
415
416[endsect]
417
418
419
420[section:async Asynchronous interface]
421
422Asynchronous versions are available for all functions:
423```
424websocket::opcode op;
425ws.async_read(op, sb,
426 [](boost::system::error_code const& ec)
427 {
428 ...
429 });
430```
431
432Calls to asynchronous initiation functions support the extensible asynchronous
433model developed by the Boost.Asio author, allowing for traditional completion
434handlers, stackful or stackless coroutines, and even futures:
435```
436void echo(websocket::stream<ip::tcp::socket>& ws,
437 boost::asio::yield_context yield)
438{
439 ws.async_read(sb, yield);
440 std::future<websocket::error_code> fut =
441 ws.async_write, sb.data(), boost::use_future);
442 ...
443}
444```
445
446[endsect]
447
448
449
450[section:io_service The io_service]
451
452The creation and operation of the
453[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/io_service.html `boost::asio::io_service`]
454associated with the underlying stream is left to the callers, permitting any
455implementation strategy including one that does not require threads for
456environments where threads are unavailable. Beast.WebSocket itself does not
457use or require threads.
458
459[endsect]
460
461
462
463[section:threads Thread Safety]
464
465Like a regular asio socket, a [link beast.ref.websocket__stream `stream`] is
466not thread safe. Callers are responsible for synchronizing operations on the
467socket using an implicit or explicit strand, as per the Asio documentation.
468The asynchronous interface supports one active read and one active write
469simultaneously. Undefined behavior results if two or more reads or two or
470more writes are attempted concurrently. Caller initiated WebSocket ping, pong,
471and close operations each count as an active write.
472
473The implementation uses composed asynchronous operations internally; a high
474level read can cause both reads and writes to take place on the underlying
475stream. This behavior is transparent to callers.
476
477[endsect]
478
479
480
481[endsect]
482
483[include quickref.xml]