Re: [boost] New Lib "Beast", HTTP + WebSocket protocols
On Fri, Apr 22, 2016 at 8:13 PM, David Stone
How does this compare to www.zaphoyd.com/websocketpp
We have experience with websocketpp as it is the solution we have been
using in our open source peer to peer software
(http://github.com/ripple/rippled) for the last five years. It is that
experience that drove the development of this new library as its
replacement. Since the WebSocket protocol starts with a HTTP
handshake, we also developed Beast.HTTP as an added bonus - think of
it as a double feature at the drive-in.
Beast.WebSocket has these things going for it:
* Construct a stream from a socket or ssl::stream that's already
connected or accepted:
boost::asio::ip::tcp::socket sock(ios);
websocket::streamboost::asio::ip::tcp::socket ws(std::move(sock));
* Accept a WebSocket handshake request with bytes that have already
been read from the socket:
template<class ConstBufferSequence>
void websocket::stream::accept(ConstBufferSequence const& buffers);
* Accept a WebSocket HTTP request handshake that was already parsed
(e.g. from Beast.HTTP):
template
On Fri, Apr 22, 2016 at 8:13 PM, David Stone
wrote: How does this compare to www.zaphoyd.com/websocketpp
We have experience with websocketpp as it is the solution we have been using in our open source peer to peer software (http://github.com/ripple/rippled) for the last five years. It is that experience that drove the development of this new library as its replacement. Since the WebSocket protocol starts with a HTTP handshake, we also developed Beast.HTTP as an added bonus - think of it as a double feature at the drive-in.
Thanks for the lengthy explanation of your library features. However to get back to the question originally asked (which you haven't really answered): 'How does this compare to www.zaphoyd.com/websocketpp'? Why should I use it and not websocketpp? Thanks! Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu
Beast.WebSocket has these things going for it:
* Construct a stream from a socket or ssl::stream that's already connected or accepted:
boost::asio::ip::tcp::socket sock(ios); websocket::streamboost::asio::ip::tcp::socket ws(std::move(sock));
* Accept a WebSocket handshake request with bytes that have already been read from the socket:
template<class ConstBufferSequence> void websocket::stream::accept(ConstBufferSequence const& buffers);
* Accept a WebSocket HTTP request handshake that was already parsed (e.g. from Beast.HTTP):
template
void websocket::stream::accept(http::request const& request); * Wraps any object meeting these type requirements, use your own type if you want:
websocket::stream<YourTypeHere> ws(...);
The type requirements (sync, async, or both):
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncRea dStream.html
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWri teStream.html
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncRe adStream.html
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWr iteStream.html
* Receive functions accept any object meeting the requirements of Streambuf, permitting custom memory management strategies including the use of statically sized buffers. For example games using messages of fixed length.
Streambuf type requirements: http://vinniefalco.github.io/beast/beast/types/Streambuf.html
* In the Asio doc, Christopher Kohlhoff alludes to alternate implementation strategies for boost::asio::streambuf using multiple discontiguous buffers of varying sizes. We fulfill this idea with an optimized stream buffer that follows the strategy he described, providing a useful building block for sending and receiving variable size data. See:
https://github.com/vinniefalco/Beast/blob/master/include/beast/basic_strea mbuf.hpp
* Interfaces are designed to closely resemble Asio in every possible way. This includes support for the extensible asynchronous model - support for std::future and stackful/stackless coroutines right out of the box. The library is designed to eliminate the learning curve for those familiar with Asio:
extern void handle_read(boost::system::error_code ec);
websocket::streamboost::asio::ip::tcp::socket ws(ios); ... websocket::opcode op; beast::streambuf sb; ws.async_read(op, sb, std::bind(&handle_read, std::placeholders::_1));
* All functions are template driven so everything is transparent to the compiler, including completion handlers passed to asynchronous initiation functions and objects used as buffers.
By design, Beast.WebSocket avoids doing these things:
* Managing the io_service * Managing a listening socket * Managing threads or a thread pool * Requiring the use of exceptions in certain places * Imposing a particular message buffering strategy * Using std::function for asynchronous callbacks * Requiring callers to know the size of the message in advance.
The last point above is especially important for applications that produce data incrementally. For example, a huge JSON reply that comes from a database query.
Planned improvements for Beast.WebSocket include:
* Explicit proxy support with basic authentication * Tools for parsing subprotocols and message headers * permessage compression: https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-28
Years of experience working with Asio and and WebSockets have gone into the development of this library. It tries to be a natural extension of Asio, in a manner that is as narrow and lean as possible to provide maximum flexibility and performance.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On Fri, Apr 29, 2016 at 8:31 AM, Hartmut Kaiser
How does this compare to www.zaphoyd.com/websocketpp'? Why should I use it and not websocketpp?
You should use Beast instead of websocketpp if: Beast has the features
you need (compression and subprotocols planned), and you care about
any of the library differences in the section that follows.
First, websocketpp is a great library; its clear the author paid a lot
of attention to implementing broad support for websocket including
Hixie-76 which is not a feature found in Beast. That library has great
documentation, support, and quite a few years of presence. In this
author's opinion it is one of the best available WebSocket
implementation in C++, and by a wide margin.
Features already in websocketpp but planned for Beast include
per-message compression and utilities for analyzing subprotocols. The
section that follows provides a feature by feature comparison of
websocketpp and Beast, with links to relevant source material and then
exposition.
1. Synchronous Interfaces
websocketpp:
<not available>
beast:
template<class Streambuf>
void
read(opcode& op, Streambuf& streambuf)
https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea5...
Beast offers full support for websocket using a synchronous interface.
It uses the same style of synchronous interfaces found in Boost.Asio:
versions that throw exceptions, or versions that return the error code
in a reference parameter.
2. Connection Model
websocketpp:
template <typename config>
class connection
: public config::transport_type::transport_con_type
, public config::connection_base
{
public:
typedef lib::shared_ptr<type> ptr;
...
https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1...
beast:
template<class NextLayer>
class stream : public detail::stream_base
{
NextLayer next_layer_;
...
https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea5...
websocketpp supports multiple transports by utilizing a trait, the
config::transport_type. An example of the implementation of this
concept is the asio transport, declared here:
https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1...
To get an idea of the complexity involved with implementing a
transport, compare the asio transport to the iostream transport (a
layer that allows websocket communication over a std iostream):
https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1...
In contrast, beast abstracts the transport by defining just one
template argument, the NextLayer. The type requirements for NextLayer
are already familiar to users as they are documented in Asio:
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadS...
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWrite...
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncRead...
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWrit...
The type requirements for instantiating beast::websocket::stream
versus websocketpp::connection with user defined types are vastly
reduced (18 functions versus 2).
Note that websocketpp connections are passed by shared_ptr. Beast does
not use shared_ptr anywhere in its public interface. A
beast::websocket::stream is constructible and movable in a manner
identical to a boost::asio::ip::socket. Callers can put such objects
in a shared_ptr if they want to, but there is no requirement to do so.
3. Client and Server Roles
websocketpp:
template <typename config>
class client : public endpoint
Thanks for the extensive explanations! Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu
-----Original Message----- From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of Vinnie Falco Sent: Friday, April 29, 2016 11:49 AM To: boost@lists.boost.org Subject: Re: [boost] New Lib "Beast", HTTP + WebSocket protocols
On Fri, Apr 29, 2016 at 8:31 AM, Hartmut Kaiser
wrote: How does this compare to www.zaphoyd.com/websocketpp'? Why should I use it and not websocketpp?
You should use Beast instead of websocketpp if: Beast has the features you need (compression and subprotocols planned), and you care about any of the library differences in the section that follows.
First, websocketpp is a great library; its clear the author paid a lot of attention to implementing broad support for websocket including Hixie-76 which is not a feature found in Beast. That library has great documentation, support, and quite a few years of presence. In this author's opinion it is one of the best available WebSocket implementation in C++, and by a wide margin.
Features already in websocketpp but planned for Beast include per-message compression and utilities for analyzing subprotocols. The section that follows provides a feature by feature comparison of websocketpp and Beast, with links to relevant source material and then exposition.
1. Synchronous Interfaces
websocketpp: <not available>
beast: template<class Streambuf> void read(opcode& op, Streambuf& streambuf)
https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffe a57ebc99/include/beast/websocket/stream.hpp#L774
Beast offers full support for websocket using a synchronous interface. It uses the same style of synchronous interfaces found in Boost.Asio: versions that throw exceptions, or versions that return the error code in a reference parameter.
2. Connection Model
websocketpp: template <typename config> class connection : public config::transport_type::transport_con_type , public config::connection_base { public: typedef lib::shared_ptr<type> ptr; ...
https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/connection.hpp#L234
beast: template<class NextLayer> class stream : public detail::stream_base { NextLayer next_layer_; ...
https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffe a57ebc99/include/beast/websocket/stream.hpp
websocketpp supports multiple transports by utilizing a trait, the config::transport_type. An example of the implementation of this concept is the asio transport, declared here: https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/transport/asio/connection.hpp#L60 To get an idea of the complexity involved with implementing a transport, compare the asio transport to the iostream transport (a layer that allows websocket communication over a std iostream): https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59
In contrast, beast abstracts the transport by defining just one template argument, the NextLayer. The type requirements for NextLayer are already familiar to users as they are documented in Asio: http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncRea dStream.html http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWri teStream.html http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncRe adStream.html http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWr iteStream.html
The type requirements for instantiating beast::websocket::stream versus websocketpp::connection with user defined types are vastly reduced (18 functions versus 2).
Note that websocketpp connections are passed by shared_ptr. Beast does not use shared_ptr anywhere in its public interface. A beast::websocket::stream is constructible and movable in a manner identical to a boost::asio::ip::socket. Callers can put such objects in a shared_ptr if they want to, but there is no requirement to do so.
3. Client and Server Roles
websocketpp: template <typename config> class client : public endpoint
; template <typename config> class server : public endpoint { https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/roles/server_endpoint.hpp#L39 https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/roles/client_endpoint.hpp#L42
beast: <not applicable>
websocketpp provides multi-role support through a hierarchy of different classes. A beast::websocket::stream is role-agnostic, it offers member functions to perform both client and server handshakes in the same class. The same types are used for client and server streams.
4. Thread Safety
websocketpp: mutex_type m_read_mutex;
https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/transport/iostream/connection.hpp#L706
beast: template <class Function> friend void asio_handler_invoke(Function&& f, read_frame_op* op) { return boost_asio_handler_invoke_helpers::invoke(f, op->d_->h); }
https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffe a57ebc99/include/beast/websocket/impl/read_frame_op.ipp#L118
websocketpp uses mutexes to protect shared data from concurrent access. In contrast, Beast does not use mutexes anywhere in its implementation. Instead, it follows the Asio pattern. Calls to asynchronous initiation functions use the same method to invoke intermediate handlers as the method used to invoke the final handler, through the asio_handler_invoke mechanism: http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/asio_ha ndler_invoke.html
The only requirement in Beast is that calls to asynchronous initiation functions are made from the same implicit or explicit strand. For example, if the io_service associated with a beast::websocket::stream's value for NextLayer is single threaded, this counts as an implicit strand and no performance costs associated with mutexes are incurred.
5. Callback Model
websocketpp: typedef lib::function
message_handler; void set_message_handler(message_handler h); https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/connection.hpp#L281 https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/connection.hpp#L473
beast: template
typename async_completion ::result_type async_read(opcode& op, Streambuf& streambuf, ReadHandler&& handler); https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffe a57ebc99/include/beast/websocket/stream.hpp#L834
websocketpp requires a one-time call to set the handler for each event in its interface (for example, upon message receipt). The handler is represented by a std::function equivalent. Its important to recognize that the websocketpp interface performs type-erasure on this handler.
In comparison, Beast handlers are specified in a manner identical to Boost.Asio. They are function objects which can be copied or moved but most importantly they are not type erased. The compiler can see through the type directly to the implementation, permitting optimization. Furthermore, Beast follows the Asio rules for treatment of handlers. It respects any allocation customizations, continuation customization, or invoke customization associated with the handler through the use of argument dependent lookup overloads of functions such as asio_handler_allocate.
The Beast completion handler is provided at the call site. For each call to an asynchronous initiation function, it is guaranteed that there will be exactly one final call to the handler. This functions exactly the same way as the asynchronous initiation functions found in Boost.Asio, allowing the composition of higher level abstractions.
6. Extensible Asynchronous Model
websocketpp: <not available>
beast: ... beast::async_completion
completion(handler); read_op { completion.handler, *this, op, streambuf}; return completion.result.get(); https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffe a57ebc99/include/beast/websocket/impl/stream.ipp#L378
Beast fully supports the Extensible Asynchronous Model developed by Christopher Kohlhoff, author of Boost.Asio. See Section 8 in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf
This means that Beast websocket asynchronous interface can be used with std::future, stackful/stackless coroutines, or user defined customizations.
7. Message Buffering
websocketpp: template class message { public: typedef lib::shared_ptr<message> ptr; ... std::string m_payload;
https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/message_buffer/message.hpp#L78
beast: template<class Streambuf>
https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffe a57ebc99/include/beast/websocket/stream.hpp#L834 http://vinniefalco.github.io/beast/beast/types/Streambuf.html
websocketpp defines a message buffer, passed in arguments by shared_ptr, and an associated message manager which permits aggregation and memory reuse of memory. The implementation of websocketpp::message uses a std::string to hold the payload. If an incoming message is broken up into multiple frames, the string may be reallocated for each continuation frame. The std::string always uses the standard allocator, it is not possible to customize the choice of allocator.
Beast allows callers to specify the object for receiving the message or frame data, which is of any type meeting the requirements of Streambuf (modeled after boost::asio::streambuf) and described here: http://vinniefalco.github.io/beast/beast/types/Streambuf.html
Beast comes with the class beast::basic_streambuf, an efficient implementation of the Streambuf concept which makes use of multiple allocated octet arrays. If an incoming message is broken up into multiple pieces, no reallocation occurs. Instead, new allocations are appended to the sequence when existing allocations are filled. Beast does not impose any particular memory management model on callers. The basic_streambuf provided by beast supports standard allocators through a template argument. Use the Streambuf that comes with beast, customize the allocator if you desire, or provide your own type that meets the requirements: https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffe a57ebc99/include/beast/basic_streambuf.hpp#L21
8. Sending Messages
websocketpp: lib::error_code send(std::string const & payload, frame::opcode::value op = frame::opcode::text); ... lib::error_code send(message_ptr msg);
https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/connection.hpp#L672
beast: template
typename async_completion ::result_type async_write(ConstBufferSequence const& buffers, WriteHandler&& handler); https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffe a57ebc99/include/beast/websocket/stream.hpp#L1048
When sending a message, websocketpp requires that the payload is packaged in a websocketpp::message object using std::string as the storage, or it makes a copy of the caller provided buffer by constructing a new message object. Messages are placed onto an outgoing queue. An asynchronous write operation runs in the background to clear the queue. No user facing handler can be registered to be notified when messages or frames have completed sending.
Beast doesn't allocate and copy buffers when sending data. The callers buffers are sent in-place. You can use any object meeting the requirements of ConstBufferSequence, permitting efficient scatter-gather I/O: http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBu fferSequence.html
The ConstBufferSequence interface allows callers to send data from memory-mapped regions (not possible in websocketpp). Callers can also use the same buffers to send data to multiple streams, for example broadcasting common subscription data to many clients at once. For each call to async_write the completion handler is called once when the data finishes sending, in a manner identical to boost::asio::async_write.
9. Streaming Messages
websocketpp: <not available>
beast: template
typename async_completion ::result_type async_write_frame(bool fin, ConstBufferSequence const& buffers, WriteHandler&& handler); https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffe a57ebc99/include/beast/websocket/stream.hpp#L1151
websocketpp requires that the entire message fit into memory, and that the size is known ahead of time.
Beast allows callers to compose messages in individual frames. This is useful when the size of the data is not known ahead of time or if it is not desired to buffer the entire message in memory at once before sending it. For example, sending periodic output of a database query running on a coroutine. Or sending the contents of a file in pieces, without bringing it all into memory.
10. Flow Control
websocketpp: lib::error_code pause_reading();
https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/connection.hpp#L728
beast: <implicit>
The websocketpp read implementation continuously reads asynchronously from the network and buffers message data. To prevent unbounded growth and leverage TCP/IP's flow control mechanism, callers can periodically turn off the read pump. In contrast a beast::websocket::stream does not independently begin background activity, nor does it buffer messages. It receives data only when there is a call to an asynchronous initiation function (for example beast::websocket::stream::async_read) with an associated handler. Applications do not need to implement explicit logic to regulate the flow of data. Instead, they follow the traditional model of issuing a read, receiving a read completion, processing the message, then issuing a new read and repeating the process.
11. Connection Establishment
websocketpp: template <typename config> class endpoint : public config::socket_type;
https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944b f1f640ccc3/websocketpp/transport/asio/endpoint.hpp#L52
beast: boost::asio
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/async_c onnect.html http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/basic_s ocket_acceptor/async_accept.html
websocketpp offers the endpoint class which can handle binding and listening to a port, and spawning connection objects
Beast does not reinvent the wheel here, callers use the interfaces already in boost::asio for receiving incoming connections resolving host names, or establishing outgoing connections. After the socket (or boost::asio::ssl::stream) is connected, the beast::websocket::stream is constructed around it and the websocket handshake can be performed.
Beast users are free to implement their own "connection manager", but there is no requirement to do so.
---
The design choices of Beast.WebSocket were made to give users the familiar Asio interface while leveraging its strengths, to create a library that is lean, easy to understand, and doesn't duplicate functionality already possible in Asio. We hope that we've succeeded in this goal.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (2)
-
Hartmut Kaiser
-
Vinnie Falco