How to find if the 'target' for websocket::stream::async_handshake() is not found?

427 views Asked by At

For HTTP calls, you can do:

http::async_read(m_stream, m_buffer, m_res, beast::bind_front_handler(&RestSession::on_read, shared_from_this())); 

// ... then
void on_read(beast::error_code ec, std::size_t /*bytes_transferred*/)
{
    if (ec)
        return ; // handle error_code

    if (m_res.result() == http::status::not_found)
        return ; // handle target not found
}

but I can't see how to do the equivalent for a websocket stream. If the stream target does not exist the on_read() is never called.

I tried getting the next stream from the websocket stream (m_websocketStream), but the lambda is never called, presumably because the websocket layer has already swallowed the HTTP response:

void on_handshake(beast::error_code ec)
{
    if(ec)
        return fail(ec, "handshake", m_callback);
    
    http::async_read(m_websockStream.next_layer(), m_buffer, m_httpRes, [this, self = shared_from_this()](beast::error_code ec, std::size_t)
    {
        if (m_httpResponse.result() == http::status::not_found)
            return fail("path not found", m_callback);
        else
            m_ws.async_read(m_buffer, beast::bind_front_handler(&WsSession::on_read, self->shared_from_this()));
    });
}

Is this possible?

1

There are 1 answers

1
sehe On

but I can't see how to do the equivalent for a websocket stream. If the stream target does not exist the on_read() is never called.

That makes a lot of sense, since the read won't ever be started. Instead the handshake fails, and the ec in fail(ec, "handshake", m_callback); will reflect that.


Getting HTTP response details

This is also supported: docs:

When a client receives an HTTP Upgrade response from the server indicating a successful upgrade, the caller may wish to perform additional validation on the received HTTP response message.

You can pass an additional response object to recive the details:

beast::error_code ec;
http::response<http::string_body> res;
s.handshake(res, host + ":" + port, path, ec);

std::cout << "handshake: " << ec.message() << " (" << ec.category().name()
          << ":" << ec.value() << ")" << std::endl;

std::cout << "Declined: " << std::boolalpha
          << (ec == websocket::error::upgrade_declined) << '\n';

std::cout << "Full response:" << res;

here's a very simple test:

#include <boost/asio/ip/tcp.hpp>
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/beast/websocket.hpp>
#include <istream>
#include "zlib.h"
#include <iostream>

namespace net       = boost::asio;
namespace beast     = boost::beast;
namespace http      = beast::http;
namespace websocket = beast::websocket;
using tcp           = net::ip::tcp;
using socket_t      = websocket::stream<tcp::socket>;

int main() {
    std::string host = "www.example.com";
    auto const port  = "80";
    auto const path  = "/this/path/will/not/exist";

    net::io_context ioc;
    tcp::resolver   resolver{ioc};

    socket_t s{ioc};
    net::connect(beast::get_lowest_layer(s), resolver.resolve(host, port));

    beast::error_code ec;
    http::response<http::string_body> res;
    s.handshake(res, host + ":" + port, path, ec);

    std::cout << "handshake: " << ec.message() << " (" << ec.category().name()
              << ":" << ec.value() << ")" << std::endl;

    std::cout << "Declined: " << std::boolalpha
              << (ec == websocket::error::upgrade_declined) << '\n';

    std::cout << "Full response:" << res;
}

Prints

handshake: The WebSocket handshake was declined by the remote peer (boost.beast.websocket:20)
Declined: true
Full response:HTTP/1.1 200 OK

I suppose most servers will simply decline, not send HTTP Not Found.