Boost logo

Boost :

Subject: Re: [boost] New Lib "Beast", HTTP + WebSocket protocols
From: Hartmut Kaiser (hartmut.kaiser_at_[hidden])
Date: 2016-04-29 17:49:57


Thanks for the extensive explanations!

Regards Hartmut
---------------
http://boost-spirit.com
http://stellar.cct.lsu.edu

> -----Original Message-----
> From: Boost [mailto:boost-bounces_at_[hidden]] On Behalf Of Vinnie
> Falco
> Sent: Friday, April 29, 2016 11:49 AM
> To: boost_at_[hidden]
> Subject: Re: [boost] New Lib "Beast", HTTP + WebSocket protocols
>
> On Fri, Apr 29, 2016 at 8:31 AM, Hartmut Kaiser
> <hartmut.kaiser_at_[hidden]> 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<connection<config>,config>;
> template <typename config>
> class server : public endpoint<connection<config>,config> {
>
> 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<void(connection_hdl,message_ptr)>
> 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<class Streambuf, class ReadHandler>
> typename async_completion<ReadHandler, void(error_code)>::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<ReadHandler, void(error_code)>
> completion(handler);
> read_op<Streambuf, decltype(completion.handler)>{
> 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 <template<class> class con_msg_manager>
> 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<class ConstBufferSequence, class WriteHandler>
> typename async_completion<WriteHandler, void(error_code)>::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<class ConstBufferSequence, class WriteHandler>
> typename async_completion<WriteHandler, void(error_code)>::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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk