Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example of raw transport for websocket #2914

Open
ValentinNikin opened this issue Aug 2, 2024 · 6 comments
Open

Example of raw transport for websocket #2914

ValentinNikin opened this issue Aug 2, 2024 · 6 comments

Comments

@ValentinNikin
Copy link

I have a single thread application which can process TCP connections (some implementation of Reactor pattern).
I want to add possibility to process websocket connections.
Let's assume that I have established TCP connection in which I can send data and from which I can receive data through onRecv-callback.

I want to implement some custom transport-adapter to use it TCP-connection with boost::beast websocket.
I managed to do this using the websocketpp (https://github.com/zaphoyd/websocketpp) library.
But later I came across an article on the official boost site (https://www.boost.org/doc/libs/1_66_0/libs/beast/doc/html/beast/design_choices/comparison_to_zaphoyd_studios_we.html) in which says that boost::beast can do the same thing but only better.
But I have not found a single example that would confirm this. It seems I should implement AsyncReadStream, AsyncWriteStream, but also no examples of how to do this.

Is it possible to impement custom transport with for beast websocket? And if so, can you share some examples?

@vinniefalco
Copy link
Member

Yes it is possible to implement a custom transport. No, I cannot share an example. It is expected that users are able to read the concept requirements and be able to create a working implementation from those requirements. For reference, here are the requirements that you must implement:

https://www.boost.org/doc/libs/1_85_0/doc/html/boost_asio/reference/AsyncReadStream.html
https://www.boost.org/doc/libs/1_85_0/doc/html/boost_asio/reference/AsyncWriteStream.html

You will also need to provide an overload of the functions teardown and async_teardown as explained in the documentation:

https://www.boost.org/doc/libs/1_85_0/libs/beast/doc/html/beast/using_websocket/teardown.html

If you get stuck and you have specific questions, consider asking in the Official C++ Language Slack Workspace (https://cpp.al/slack)

@ValentinNikin
Copy link
Author

ValentinNikin commented Aug 6, 2024

Thank you for the answer. I already tried to implement AsyncReadStream/AsyncWriteStream interfaces. But seems it is not enough for custom transport class.

Below is an example of a maximally simplified class where the necessary methods are declared.

class NextLayerImpl_ {
public:
    using executor_type = boost::asio::any_io_executor;
    any_io_executor get_executor() const { return ex; }

    template<class MB, class Handler>
    void async_write_some(MB mb, Handler&& func)
    {

    }

    template<class MB, class Handler>
    void async_read_some(MB mb, Handler&& func)
    {

    }

private:
    any_io_executor ex;
};

template<class TeardownHandler>
void async_teardown(
    boost::beast::role_type role,
    NextLayerImpl_& stream,
    TeardownHandler&& handler) {

    }

void fail(beast::error_code ec, char const* what)
{
    std::cerr << what << ": " << ec.message() << "\n";
}

void on_accept(beast::error_code ec)
{
    if(ec)
        return fail(ec, "accept");

    // Read a message
    std::cout << "On accept called" << std::endl;
}

int main(int argc, char* argv[])
{
    websocket::stream<NextLayerImpl_> ws_;

    ws_.async_accept(
        beast::bind_front_handler(
                &on_accept)
    );

    return EXIT_SUCCESS;
}

But I got compilation error. Probably the class must satisfy some other requirements. Are these additional requirements written somewhere?

...
/usr/local/include/boost/beast/websocket/impl/accept.hpp:568:26:   required from ‘auto boost::beast::websocket::stream< <template-parameter-1-1>, <anonymous> >::async_accept(AcceptHandler&&) [with AcceptHandler = boost::beast::detail::bind_front_wrapper<void (*)(boost::system::error_code)>; NextLayer = NextLayerImpl_; bool deflateSupported = true]’
/home/nikin/workspace/cpp-test/beast/main.cpp:156:21:   required from here
/usr/local/include/boost/beast/core/stream_traits.hpp:459:27: error: no matching function for call to ‘beast_close_socket(NextLayerImpl_&)’
  459 |         beast_close_socket(t);
      |         ~~~~~~~~~~~~~~~~~~^~~
/usr/local/include/boost/beast/core/stream_traits.hpp:442:1: note: candidate: ‘template<class Protocol, class Executor> void boost::beast::beast_close_socket(boost::asio::basic_socket<Protocol, Executor>&)’
  442 | beast_close_socket(
      | ^~~~~~~~~~~~~~~~~~
/usr/local/include/boost/beast/core/stream_traits.hpp:442:1: note:   template argument deduction/substitution failed:
/usr/local/include/boost/beast/core/stream_traits.hpp:459:27: note:   ‘NextLayerImpl_’ is not derived from ‘boost::asio::basic_socket<Protocol, Executor>’
  459 |         beast_close_socket(t);
      |         ~~~~~~~~~~~~~~~~~~^~~

@ashtum
Copy link
Collaborator

ashtum commented Aug 6, 2024

You need to provide an overload of the function beast_close_socket too:

void
beast_close_socket(NextLayerImpl_&)
{
}

@ValentinNikin
Copy link
Author

ValentinNikin commented Aug 6, 2024

@ashtum Thank you, this helped to compile the application.

Could you tell me concrete types for MB and Handler? I need to save this parameters as class fields. I need this to fill them out later, when I got data from inner transport infrastructure.

template<class MB, class Handler>
auto async_read_some(MB buf, Handler&& func)

I tried to use boost::beast::detail::BufferSequenceboost::asio::mutable_buffer instead of MB, but got a compilation error

/usr/local/include/boost/beast/websocket/impl/accept.hpp:568:26:   required from ‘auto boost::beast::websocket::stream< <template-parameter-1-1>, <anonymous> >::async_accept(AcceptHandler&&) [with AcceptHandler = boost::beast::detail::bind_front_wrapper<void (*)(boost::system::error_code)>; NextLayer = NextLayerImpl_; bool deflateSupported = true]’
/home/nikin/workspace/cpp-test/beast/main.cpp:204:21:   required from here
/usr/local/include/boost/beast/http/impl/read.hpp:600:48: error: static assertion failed: AsyncReadStream type requirements not met
  600 |         is_async_read_stream<AsyncReadStream>::value,
      |                                                ^~~~~
/usr/local/include/boost/beast/http/impl/read.hpp:600:48: note: ‘std::integral_constant<bool, false>::value’ evaluates to false

@ashtum
Copy link
Collaborator

ashtum commented Aug 6, 2024

The Handler is not a regular function object; it is an Asio Completion Token. You can only use it through async_initiate. You can use any_completion_handler for type-erasing the handler, but it still needs to go through async_initiate first. Here is an example: https://github.com/boostorg/asio/tree/develop/example/cpp11/type_erasure.

Regarding the buffer sequence types, there is no concrete type for them. They have different types in different operations, and you need to create a type-erased wrapper for the Mutable buffer sequence and Constant buffer sequence concepts.

@ValentinNikin
Copy link
Author

@ashtum I will try this, thank you for the reply

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants