fix(websocket): try connecting to all hosts (#6631)

This commit is contained in:
pajlada
2025-12-07 17:26:43 +01:00
committed by GitHub
parent 74b4674c6b
commit bd21db125f
3 changed files with 69 additions and 11 deletions
+1
View File
@@ -25,6 +25,7 @@
- Bugfix: Fixed settings occasionally not opening when clicking on "Manage Accounts" in the account switcher. (#6543)
- Bugfix: Fixed font change not resulting in forced layout update. (#6536)
- Bugfix: Fixed scrollbar rect computation potentially resulting in overflows. (#6547)
- Bugfix: Fixed websocket connections failing if the DNS resolving gave bad hosts. (#6631)
- Bugfix: Forward query params to websocket URLs. (#6141)
- Bugfix: Fixed Lua errors from handlers of HTTP requests not being logged. (#6452)
- Bugfix: Fixed restore button not showing on Windows. (#6565)
@@ -7,6 +7,8 @@
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/websocket/ssl.hpp>
using namespace std::literals::string_view_literals;
namespace chatterino::ws::detail {
namespace asio = boost::asio;
@@ -93,30 +95,61 @@ void WebSocketConnectionHelper<Derived, Inner>::onResolve(
return;
}
qCDebug(chatterinoWebsocket) << *this << "Resolved host";
this->resolvedEndpoints = results;
this->tryConnect(this->resolvedEndpoints.begin());
}
template <typename Derived, typename Inner>
void WebSocketConnectionHelper<Derived, Inner>::tryConnect(
boost::asio::ip::tcp::resolver::results_type::const_iterator
endpointIterator)
{
if (endpointIterator == this->resolvedEndpoints.end())
{
this->fail("Ran out of resolved endpoints"sv, u"connect");
return;
}
const auto &endpoint = endpointIterator->endpoint();
qCDebug(chatterinoWebsocket)
<< *this << "connect to" << endpoint.address().to_string();
beast::get_lowest_layer(this->stream)
.expires_after(std::chrono::seconds{30});
beast::get_lowest_layer(this->stream)
.async_connect(results, beast::bind_front_handler(
&WebSocketConnectionHelper::onTcpHandshake,
this->shared_from_this()));
.async_connect(endpoint,
beast::bind_front_handler(
&WebSocketConnectionHelper::onTcpHandshake,
this->shared_from_this(), endpointIterator));
}
template <typename Derived, typename Inner>
void WebSocketConnectionHelper<Derived, Inner>::onTcpHandshake(
boost::system::error_code ec,
const asio::ip::tcp::resolver::endpoint_type &ep)
boost::asio::ip::tcp::resolver::results_type::const_iterator
endpointIterator,
boost::system::error_code ec)
{
const auto &ep = endpointIterator->endpoint();
if (ec)
{
this->fail(ec, u"TCP handshake");
qCDebug(chatterinoWebsocket)
<< *this << "error in tcp handshake" << ep.address().to_string()
<< ec.message();
this->tryConnect(++endpointIterator);
return;
}
qCDebug(chatterinoWebsocket) << *this << "TCP handshake done";
qCDebug(chatterinoWebsocket)
<< *this << "TCP handshake done" << ep.address().to_string();
this->options.url.setPort(ep.port());
// We are done with the endpoints, we can clear the range.
this->resolvedEndpoints = {};
this->derived()->afterTcpHandshake();
}
@@ -319,9 +352,16 @@ void WebSocketConnectionHelper<Derived, Inner>::closeImpl()
template <typename Derived, typename Inner>
void WebSocketConnectionHelper<Derived, Inner>::fail(
boost::system::error_code ec, QStringView op)
{
this->fail(ec.message(), op);
}
template <typename Derived, typename Inner>
void WebSocketConnectionHelper<Derived, Inner>::fail(std::string_view ec,
QStringView op)
{
qCWarning(chatterinoWebsocket)
<< *this << "Failed:" << op << QUtf8StringView(ec.message());
<< *this << "Failed:" << op << QUtf8StringView(ec);
if (this->stream.is_open())
{
this->closeImpl();
@@ -41,6 +41,7 @@ protected:
Derived *derived();
void fail(boost::system::error_code ec, QStringView op);
void fail(std::string_view ec, QStringView op);
void doWsHandshake();
void closeImpl();
@@ -57,15 +58,31 @@ private:
void onResolve(boost::system::error_code ec,
const boost::asio::ip::tcp::resolver::results_type &results);
/// Initialize a TCP connection to the given endpoint iterator in `resolvedEndpoints`.
///
/// If we failed to connect, try the next iterator.
///
/// If the iterator is invalid, we have run out of endpoints to try, and deem this
/// connection a failure.
void tryConnect(boost::asio::ip::tcp::resolver::results_type::const_iterator
endpointIterator);
void onTcpHandshake(
boost::system::error_code ec,
const boost::asio::ip::tcp::resolver::endpoint_type &ep);
boost::asio::ip::tcp::resolver::results_type::const_iterator
endpointIterator,
boost::system::error_code ec);
void onWsHandshake(boost::system::error_code ec);
void onReadDone(boost::system::error_code ec, size_t bytesRead);
void onWriteDone(boost::system::error_code ec, size_t bytesWritten);
friend Derived;
/// A range of endpoints from the `onResolve` function.
///
/// When we successfully resolve the host, we try to connect by
/// iterating over these results.
boost::asio::ip::tcp::resolver::results_type resolvedEndpoints;
};
/// A WebSocket connection over TLS (wss://).