]> git.proxmox.com Git - ceph.git/blob - ceph/src/Beast/doc/websocket.qbk
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / Beast / doc / websocket.qbk
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
25 The WebSocket Protocol enables two-way communication between a client
26 running untrusted code in a controlled environment to a remote host that has
27 opted-in to communications from that code. The protocol consists of an opening
28 handshake followed by basic message framing, layered over TCP. The goal of
29 this technology is to provide a mechanism for browser-based applications that
30 need two-way communication with servers that does not rely on opening multiple
31 HTTP connections.
32
33 Beast.WebSocket provides developers with a robust WebSocket implementation
34 built on Boost.Asio with a consistent asynchronous model using a modern
35 C++ approach.
36
37 The 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
50 The interface to Beast's WebSocket implementation is a single template
51 class [link beast.ref.websocket__stream `beast::websocket::stream`] which
52 wraps a "next layer" object. The next layer object must meet the requirements
53 of [link beast.ref.streams.SyncStream [*`SyncStream`]] if synchronous
54 operations are performed, or
55 [link beast.ref.streams.AsyncStream [*`AsyncStream`]] if asynchronous
56 operations are performed, or both. Arguments supplied during construction are
57 passed to next layer's constructor. Here we declare a websocket stream over
58 a TCP/IP socket with ownership of the socket:
59 ```
60 boost::asio::io_service ios;
61 beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
62 ```
63
64 [heading Using SSL]
65
66 To use WebSockets over SSL, choose an SSL stream for the next layer template
67 argument when constructing the stream.
68 ```
69 #include <beast/websocket/ssl.hpp>
70 #include <beast/websocket.hpp>
71 #include <boost/asio/ssl.hpp>
72
73 boost::asio::io_service ios;
74 boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
75 beast::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
85 For servers that can handshake in multiple protocols, it may be desired
86 to 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
93 Or, the wrapper can be constructed with a non-owning reference. In
94 this case, the caller is responsible for managing the lifetime of the
95 underlying 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
102 The layer being wrapped can be accessed through the websocket's "next layer",
103 permitting 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
123 Connections are established by using the interfaces which already exist
124 for 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
134 Accepting an incoming connection:
135 ```
136 void 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
153 A WebSocket session begins when one side sends the HTTP Upgrade request
154 for websocket, and the other side sends an appropriate HTTP response
155 indicating that the request was accepted and that the connection has
156 been upgraded. The HTTP Upgrade request must include the Host HTTP field,
157 and the URI of the resource to request.
158 [link beast.ref.websocket__stream.handshake `handshake`] is used to send the
159 request 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
167 The [link beast.ref.websocket__stream `stream`] automatically
168 handles receiving and processing the HTTP response to the handshake request.
169 The call to handshake is successful if a HTTP response is received with the
170 101 "Switching Protocols" status code. On failure, an error is returned or an
171 exception is thrown. Depending on the keep alive setting, the socket may remain
172 open for a subsequent handshake attempt
173
174 Performing a handshake for an incoming websocket upgrade request operates
175 similarly. 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
182 Servers that can handshake in multiple protocols may have already read data
183 on the connection, or might have already received an entire HTTP request
184 containing the upgrade request. Overloads of `accept` allow callers to
185 pass in this additional buffered handshake data.
186 ```
187 void 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
198 Alternatively, the caller can pass an entire HTTP request if it was
199 obtained elsewhere:
200 ```
201 void 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
221 After the WebSocket handshake is accomplished, callers may send and receive
222 messages using the message oriented interface. This interface requires that
223 all of the buffers representing the message are known ahead of time:
224 ```
225 void 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
249 Some use-cases make it impractical or impossible to buffer the entire
250 message 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
256 For these cases, the frame oriented interface may be used. This
257 example reads and echoes a complete message using this interface:
258 ```
259 void 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
296 Control frames are small (less than 128 bytes) messages entirely contained
297 in an individual WebSocket frame. They may be sent at any time by either
298 peer on an established connection, and can appear in between continuation
299 frames for a message. There are three types of control frames: ping, pong,
300 and close.
301
302 A sent ping indicates a request that the sender wants to receive a pong. A
303 pong is a response to a ping. Pongs may be sent unsolicited, at any time.
304 One use for an unsolicited pong is to inform the remote peer that the
305 session is still active after a long period of inactivity. A close frame
306 indicates that the remote peer wishes to close the WebSocket connection.
307 The connection is considered gracefully closed when each side has sent
308 and received a close frame.
309
310 During read operations, Beast automatically reads and processes control
311 frames. Pings are replied to as soon as possible with a pong, received
312 ping and pongs are delivered to the ping callback. The receipt of a close
313 frame initiates the WebSocket close procedure, eventually resulting in the
314 error code [link beast.ref.websocket__error `error::closed`] being delivered
315 to the caller in a subsequent read operation, assuming no other error
316 takes place.
317
318 A consequence of this automatic behavior is that caller-initiated read
319 operations can cause socket writes. However, these writes will not
320 compete with caller-initiated write operations. For the purposes of
321 correctness with respect to the stream invariants, caller-initiated
322 read operations still only count as a read. This means that callers can
323 have a simultaneously active read, write, and ping operation in progress,
324 while the implementation also automatically handles control frames.
325
326 [heading Ping and Pong Frames]
327
328 Ping and pong messages are control frames which may be sent at any time
329 by either peer on an established WebSocket connection. They are sent
330 using the functions
331 [link beast.ref.websocket__stream.ping `ping`] and
332 [link beast.ref.websocket__stream.pong `pong`].
333
334 To 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`].
336 The object provided with this option should be callable with the following
337 signature:
338 ```
339 void on_ping(bool is_pong, ping_data const& payload);
340 ...
341 ws.set_option(ping_callback{&on_ping});
342 ```
343
344 When a ping callback is registered, all pings and pongs received through
345 either synchronous read functions or asynchronous read functions will
346 invoke the ping callback, with the value of `is_pong` set to `true` if a
347 pong was received else `false` if a ping was received. The payload of
348 the ping or pong control frame is passed in the payload argument.
349
350 Unlike regular completion handlers used in calls to asynchronous initiation
351 functions, the ping callback only needs to be set once. The callback is not
352 reset when a ping or pong is received. The same callback is used for both
353 synchronous and asynchronous reads. The ping callback is passive; in order
354 to receive pings and pongs, a synchronous or asynchronous stream read
355 function 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
365 The WebSocket protocol defines a procedure and control message for initiating
366 a close of the session. Handling of close initiated by the remote end of the
367 connection is performed automatically. To manually initiate a close, use
368 the [link beast.ref.websocket__stream.close `close`] function:
369 ```
370 ws.close();
371 ```
372
373 When the remote peer initiates a close by sending a close frame, Beast
374 will handle it for you by causing the next read to return `error::closed`.
375 When this error code is delivered, it indicates to the application that
376 the WebSocket connection has been closed cleanly, and that the TCP/IP
377 connection has been closed. After initiating a close, it is necessary to
378 continue reading messages until receiving the error `error::closed`. This
379 is because the remote peer may still be sending message and control frames
380 before 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
389 To ensure timely delivery of control frames, large messages can be broken up
390 into smaller sized frames. The automatic fragment option turns on this
391 feature, and the write buffer size option determines the maximum size of
392 the 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
405 Because calls to read data may return a variable amount of bytes, the
406 interface to calls that read data require an object that meets the requirements
407 of [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
410 The implementation does not perform queueing or buffering of messages. If
411 desired, these features should be provided by callers. The impact of this
412 design is that library users are in full control of the allocation strategy
413 used to store data and the back-pressure applied on the read and write side
414 of the underlying TCP/IP connection.
415
416 [endsect]
417
418
419
420 [section:async Asynchronous interface]
421
422 Asynchronous versions are available for all functions:
423 ```
424 websocket::opcode op;
425 ws.async_read(op, sb,
426 [](boost::system::error_code const& ec)
427 {
428 ...
429 });
430 ```
431
432 Calls to asynchronous initiation functions support the extensible asynchronous
433 model developed by the Boost.Asio author, allowing for traditional completion
434 handlers, stackful or stackless coroutines, and even futures:
435 ```
436 void 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
452 The 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`]
454 associated with the underlying stream is left to the callers, permitting any
455 implementation strategy including one that does not require threads for
456 environments where threads are unavailable. Beast.WebSocket itself does not
457 use or require threads.
458
459 [endsect]
460
461
462
463 [section:threads Thread Safety]
464
465 Like a regular asio socket, a [link beast.ref.websocket__stream `stream`] is
466 not thread safe. Callers are responsible for synchronizing operations on the
467 socket using an implicit or explicit strand, as per the Asio documentation.
468 The asynchronous interface supports one active read and one active write
469 simultaneously. Undefined behavior results if two or more reads or two or
470 more writes are attempted concurrently. Caller initiated WebSocket ping, pong,
471 and close operations each count as an active write.
472
473 The implementation uses composed asynchronous operations internally; a high
474 level read can cause both reads and writes to take place on the underlying
475 stream. This behavior is transparent to callers.
476
477 [endsect]
478
479
480
481 [endsect]
482
483 [include quickref.xml]