1[/ 2 Copyright (c) 2016-2019 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 Official repository: https://github.com/boostorg/beast 8] 9 10[section Comparison to Zaphoyd Studios WebSocket++] 11 12[variablelist 13 14[[ 15 How does this compare to [@https://www.zaphoyd.com/websocketpp websocketpp], 16 an alternate header-only WebSocket implementation? 17][ 18[variablelist 19 20[[1. Synchronous Interface][ 21 22Beast offers full support for WebSockets using a synchronous interface. It 23uses the same style of interfaces found in Boost.Asio: versions that throw 24exceptions, or versions that return the error code in a reference parameter: 25 26[table 27 [ 28 [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]] 29 [websocketpp] 30 ][ 31 [``` 32 template<class DynamicBuffer> 33 void 34 read(DynamicBuffer& dynabuf) 35 ```] 36 [ 37 /<not available>/ 38 ] 39]]]] 40 41[[2. Connection Model][ 42 43websocketpp supports multiple transports by utilizing a trait, the `config::transport_type` 44([@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/connection.hpp#L60 asio transport example]) 45To get an idea of the complexity involved with implementing a transport, 46compare the asio transport to the 47[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59 `iostream` transport] 48(a layer that allows websocket communication over a `std::iostream`). 49 50In contrast, Beast abstracts the transport by defining just one [*`NextLayer`] 51template argument The type requirements for [*`NextLayer`] are 52already familiar to users as they are documented in Asio: 53__AsyncReadStream__, __AsyncWriteStream__, __SyncReadStream__, __SyncWriteStream__. 54 55The type requirements for instantiating `beast::websocket::stream` versus 56`websocketpp::connection` with user defined types are vastly reduced 57(18 functions versus 2). Note that websocketpp connections are passed by 58`shared_ptr`. Beast does not use `shared_ptr` anywhere in its public interface. 59A `beast::websocket::stream` is constructible and movable in a manner identical 60to a `boost::asio::ip::tcp::socket`. Callers can put such objects in a 61`shared_ptr` if they want to, but there is no requirement to do so. 62 63[table 64 [ 65 [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp Beast]] 66 [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L234 websocketpp]] 67 ][ 68 [``` 69 template<class NextLayer> 70 class stream 71 { 72 NextLayer next_layer_; 73 ... 74 } 75 ```] 76 [``` 77 template <typename config> 78 class connection 79 : public config::transport_type::transport_con_type 80 , public config::connection_base 81 { 82 public: 83 typedef lib::shared_ptr<type> ptr; 84 ... 85 } 86 ```] 87]]]] 88 89[[3. Client and Server Role][ 90 91websocketpp provides multi-role support through a hierarchy of 92different classes. A `beast::websocket::stream` is role-agnostic, it 93offers member functions to perform both client and server handshakes 94in the same class. The same types are used for client and server 95streams. 96 97[table 98 [ 99 [Beast] 100 [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/server_endpoint.hpp#L39 websocketpp], 101 [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/client_endpoint.hpp#L42 also]] 102 ][ 103 [ 104 /<not needed>/ 105 ] 106 [``` 107 template <typename config> 108 class client : public endpoint<connection<config>,config>; 109 template <typename config> 110 class server : public endpoint<connection<config>,config>; 111 ```] 112]]]] 113 114[[4. Thread Safety][ 115 116websocketpp uses mutexes to protect shared data from concurrent 117access. In contrast, Beast does not use mutexes anywhere in its 118implementation. Instead, it follows the Asio pattern. Calls to 119asynchronous initiation functions use the same method to invoke 120intermediate handlers as the method used to invoke the final handler, 121through the __asio_handler_invoke__ mechanism. 122 123The only requirement in Beast is that calls to asynchronous initiation 124functions are made from the same implicit or explicit strand. For 125example, if the `io_context` associated with a `beast::websocket::stream` 126is single threaded, this counts as an implicit strand and no performance 127costs associated with mutexes are incurred. 128 129[table 130 [ 131 [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/read_frame_op.ipp#L118 Beast]] 132 [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L706 websocketpp]] 133 ][ 134 [``` 135 template <class Function> 136 friend 137 void asio_handler_invoke(Function&& f, read_frame_op* op) 138 { 139 return boost_asio_handler_invoke_helpers::invoke(f, op->d_->h); 140 } 141 ```] 142 [``` 143 mutex_type m_read_mutex; 144 ```] 145]]]] 146 147[[5. Callback Model][ 148 149websocketpp requires a one-time call to set the handler for each event 150in its interface (for example, upon message receipt). The handler is 151represented by a `std::function` equivalent. Its important to recognize 152that the websocketpp interface performs type-erasure on this handler. 153 154In comparison, Beast handlers are specified in a manner identical to 155Boost.Asio. They are function objects which can be copied or moved but 156most importantly they are not type erased. The compiler can see 157through the type directly to the implementation, permitting 158optimization. Furthermore, Beast follows the Asio rules for treatment 159of handlers. It respects any allocation, continuation, or invocation 160customizations associated with the handler through the use of argument 161dependent lookup overloads of functions such as `asio_handler_allocate`. 162 163The Beast completion handler is provided at the call site. For each 164call to an asynchronous initiation function, it is guaranteed that 165there will be exactly one final call to the handler. This functions 166exactly the same way as the asynchronous initiation functions found in 167Boost.Asio, allowing the composition of higher level abstractions. 168 169[table 170 [ 171 [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L834 Beast]] 172 [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L281 websocketpp], 173 [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L473 also]] 174 ][ 175 [``` 176 template< 177 class DynamicBuffer, // Supports user defined types 178 class ReadHandler // Handler is NOT type-erased 179 > 180 typename async_completion< // Return value customization 181 ReadHandler, // supports futures and coroutines 182 void(error_code) 183 >::result_type 184 async_read( 185 DynamicBuffer& dynabuf, 186 ReadHandler&& handler); 187 ```] 188 [``` 189 typedef lib::function< 190 void(connection_hdl,message_ptr) 191 > message_handler; 192 void set_message_handler(message_handler h); 193 ```] 194]]]] 195 196[[6. Extensible Asynchronous Model][ 197 198Beast fully supports the 199[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf Extensible Asynchronous Model] 200developed by Christopher Kohlhoff, author of Boost.Asio (see Section 8). 201 202Beast websocket asynchronous interfaces may be used seamlessly with 203`std::future` stackful/stackless coroutines, or user defined customizations. 204 205[table 206 [ 207 [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/stream.ipp#L378 Beast]] 208 [websocketpp] 209 ][ 210 [``` 211 beast::async_completion< 212 ReadHandler, 213 void(error_code)> completion{handler}; 214 read_op< 215 DynamicBuffer, decltype(completion.handler)>{ 216 completion.handler, *this, op, buffer}; 217 218 return completion.result.get(); // Customization point 219 ```] 220 [ 221 /<not available>/ 222 ] 223]]]] 224 225[[7. Message Buffering][ 226 227websocketpp defines a message buffer, passed in arguments by 228`shared_ptr`, and an associated message manager which permits 229aggregation and reuse of memory. The implementation of 230`websocketpp::message` uses a `std::string` to hold the payload. If an 231incoming message is broken up into multiple frames, the string may be 232reallocated for each continuation frame. The `std::string` always uses 233the standard allocator, it is not possible to customize the choice of 234allocator. 235 236Beast allows callers to specify the object for receiving the message 237or frame data, which is of any type meeting the requirements of 238__DynamicBuffer__ (modeled after `boost::asio::streambuf`). 239 240Beast comes with the class __basic_multi_buffer__, an efficient 241implementation of the __DynamicBuffer__ concept which makes use of multiple 242allocated octet arrays. If an incoming message is broken up into 243multiple pieces, no reallocation occurs. Instead, new allocations are 244appended to the sequence when existing allocations are filled. Beast 245does not impose any particular memory management model on callers. The 246__basic_multi_buffer__ provided by beast supports standard allocators through 247a template argument. Use the __DynamicBuffer__ that comes with beast, 248customize the allocator if you desire, or provide your own type that 249meets the requirements. 250 251[table 252 [ 253 [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]] 254 [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/message_buffer/message.hpp#L78 websocketpp]] 255 ][ 256 [``` 257 template<class DynamicBuffer> 258 read(DynamicBuffer& dynabuf); 259 ```] 260 [``` 261 template <template<class> class con_msg_manager> 262 class message { 263 public: 264 typedef lib::shared_ptr<message> ptr; 265 ... 266 std::string m_payload; 267 ... 268 }; 269 ```] 270]]]] 271 272[[8. Sending Messages][ 273 274When sending a message, websocketpp requires that the payload is 275packaged in a `websocketpp::message` object using `std::string` as the 276storage, or it requires a copy of the caller provided buffer by 277constructing a new message object. Messages are placed onto an 278outgoing queue. An asynchronous write operation runs in the background 279to clear the queue. No user facing handler can be registered to be 280notified when messages or frames have completed sending. 281 282Beast doesn't allocate or make copies of buffers when sending data. The 283caller's buffers are sent in-place. You can use any object meeting the 284requirements of __ConstBufferSequence, permitting efficient scatter-gather I/O. 285 286The [*ConstBufferSequence] interface allows callers to send data from 287memory-mapped regions (not possible in websocketpp). Callers can also 288use the same buffers to send data to multiple streams, for example 289broadcasting common subscription data to many clients at once. For 290each call to `async_write` the completion handler is called once when 291the data finishes sending, in a manner identical to `boost::asio::async_write`. 292 293[table 294 [ 295 [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1048 Beast]] 296 [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L672 websocketpp]] 297 ][ 298 [``` 299 template<class ConstBufferSequence> 300 void 301 write(ConstBufferSequence const& buffers); 302 ```] 303 [``` 304 lib::error_code send(std::string const & payload, 305 frame::opcode::value op = frame::opcode::text); 306 ... 307 lib::error_code send(message_ptr msg); 308 ```] 309]]]] 310 311[[9. Streaming Messages][ 312 313websocketpp requires that the entire message fit into memory, and that 314the size is known ahead of time. 315 316Beast allows callers to compose messages in individual frames. This is 317useful when the size of the data is not known ahead of time or if it 318is not desired to buffer the entire message in memory at once before 319sending it. For example, sending periodic output of a database query 320running on a coroutine. Or sending the contents of a file in pieces, 321without bringing it all into memory. 322 323[table 324 [ 325 [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1151 Beast]] 326 [websocketpp] 327 ][ 328 [``` 329 template<class ConstBufferSequence> 330 void 331 write_some(bool fin, 332 ConstBufferSequence const& buffers); 333 ```] 334 [ 335 /<not available>/ 336 ] 337]]]] 338 339[[10. Flow Control][ 340 341The websocketpp read implementation continuously reads asynchronously 342from the network and buffers message data. To prevent unbounded growth 343and leverage TCP/IP's flow control mechanism, callers can periodically 344turn this 'read pump' off and back on. 345 346In contrast a `beast::websocket::stream` does not independently begin 347background activity, nor does it buffer messages. It receives data only 348when there is a call to an asynchronous initiation function (for 349example `beast::websocket::stream::async_read`) with an associated handler. 350Applications do not need to implement explicit logic to regulate the 351flow of data. Instead, they follow the traditional model of issuing a 352read, receiving a read completion, processing the message, then 353issuing a new read and repeating the process. 354 355[table 356 [ 357 [Beast] 358 [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L728 websocketpp]] 359 ][ 360 [ 361 /<implicit>/ 362 ] 363 [``` 364 lib::error_code pause_reading(); 365 lib::error_code resume_reading(); 366 ```] 367]]]] 368 369[[11. Connection Establishment][ 370 371websocketpp offers the `endpoint` class which can handle binding and 372listening to a port, and spawning connection objects. 373 374Beast does not reinvent the wheel here, callers use the interfaces 375already in `boost::asio` for receiving incoming connections resolving 376host names, or establishing outgoing connections. After the socket (or 377`boost::asio::ssl::stream`) is connected, the `beast::websocket::stream` 378is constructed around it and the WebSocket handshake can be performed. 379 380Beast users are free to implement their own "connection manager", but 381there is no requirement to do so. 382 383[table 384 [ 385 [[@http://www.boost.org/doc/html/boost_asio/reference/async_connect.html Beast], 386 [@http://www.boost.org/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept.html also]] 387 [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/endpoint.hpp#L52 websocketpp]] 388 ][ 389 [``` 390 #include <boost/asio.hpp> 391 ```] 392 [``` 393 template <typename config> 394 class endpoint : public config::socket_type; 395 ```] 396]]]] 397 398[[12. WebSocket Handshaking][ 399 400Callers invoke `beast::websocket::accept` to perform the WebSocket 401handshake, but there is no requirement to use this function. Advanced 402users can perform the WebSocket handshake themselves. Beast WebSocket 403provides the tools for composing the request or response, and the 404Beast HTTP interface provides the container and algorithms for sending 405and receiving HTTP/1 messages including the necessary HTTP Upgrade 406request for establishing the WebSocket session. 407 408Beast allows the caller to pass the incoming HTTP Upgrade request for 409the cases where the caller has already received an HTTP message. 410This flexibility permits novel and robust implementations. For example, 411a listening socket that can handshake in multiple protocols on the 412same port. 413 414Sometimes callers want to read some bytes on the socket before reading 415the WebSocket HTTP Upgrade request. Beast allows these already-received 416bytes to be supplied to an overload of the accepting function to permit 417sophisticated features. For example, a listening socket that can 418accept both regular WebSocket and Secure WebSocket (SSL) connections. 419 420[table 421 [ 422 [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L501 Beast], 423 [@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L401 also]] 424 [websocketpp] 425 ][ 426 [``` 427 template<class ConstBufferSequence> 428 void 429 accept(ConstBufferSequence const& buffers); 430 431 template<class Allocator> 432 void 433 accept(http::header<true, http::basic_fields<Allocator>> const& req); 434 ```] 435 [ 436 /<not available>/ 437 ] 438]]]] 439 440] 441]] 442 443] 444 445[endsect] 446