Add HTTP & SOCKS5 proxy support (#4321)
This can be configured using the `CHATTERINO2_PROXY_URL` environment variable. The behaviour is similar to curl's CURLOPT_PROXY
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
#include "providers/NetworkConfigurationProvider.hpp"
|
||||
|
||||
#include "common/Env.hpp"
|
||||
#include "common/QLogging.hpp"
|
||||
|
||||
#include <QNetworkProxy>
|
||||
#include <QSslConfiguration>
|
||||
#include <QUrl>
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* Creates a QNetworkProxy from a given URL.
|
||||
*
|
||||
* Creates an HTTP proxy by default, a Socks5 will be created if the scheme is 'socks5'.
|
||||
*/
|
||||
QNetworkProxy createProxyFromUrl(const QUrl &url)
|
||||
{
|
||||
QNetworkProxy proxy;
|
||||
proxy.setHostName(url.host(QUrl::FullyEncoded));
|
||||
proxy.setUser(url.userName(QUrl::FullyEncoded));
|
||||
proxy.setPassword(url.password(QUrl::FullyEncoded));
|
||||
proxy.setPort(url.port(1080));
|
||||
|
||||
if (url.scheme().compare(QStringLiteral("socks5"), Qt::CaseInsensitive) ==
|
||||
0)
|
||||
{
|
||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||
}
|
||||
else
|
||||
{
|
||||
proxy.setType(QNetworkProxy::HttpProxy);
|
||||
if (!proxy.user().isEmpty() || !proxy.password().isEmpty())
|
||||
{
|
||||
// for some reason, Qt doesn't set the Proxy-Authorization header
|
||||
const auto auth = proxy.user() + ":" + proxy.password();
|
||||
const auto base64 = auth.toUtf8().toBase64();
|
||||
proxy.setRawHeader("Proxy-Authorization",
|
||||
QByteArray("Basic ").append(base64));
|
||||
}
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to apply the proxy specified by `url` as the application proxy.
|
||||
*/
|
||||
void applyProxy(const QString &url)
|
||||
{
|
||||
auto proxyUrl = QUrl(url);
|
||||
if (!proxyUrl.isValid() || proxyUrl.isEmpty())
|
||||
{
|
||||
qCDebug(chatterinoNetwork)
|
||||
<< "Invalid or empty proxy url: " << proxyUrl;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto proxy = createProxyFromUrl(proxyUrl);
|
||||
|
||||
QNetworkProxy::setApplicationProxy(proxy);
|
||||
qCDebug(chatterinoNetwork) << "Set application proxy to" << proxy;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
void NetworkConfigurationProvider::applyFromEnv(const Env &env)
|
||||
{
|
||||
if (env.proxyUrl)
|
||||
{
|
||||
applyProxy(env.proxyUrl.get());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/QLogging.hpp"
|
||||
|
||||
#include <QNetworkProxy>
|
||||
#include <websocketpp/connection.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class Env;
|
||||
|
||||
/** This class manipulates the global network configuration (e.g. proxies). */
|
||||
class NetworkConfigurationProvider
|
||||
{
|
||||
public:
|
||||
/** This class should never be instantiated. */
|
||||
NetworkConfigurationProvider() = delete;
|
||||
|
||||
/**
|
||||
* Applies the configuration requested from the environment variables.
|
||||
*
|
||||
* Currently a proxy is applied if configured.
|
||||
*/
|
||||
static void applyFromEnv(const Env &env);
|
||||
|
||||
template <class C>
|
||||
static void applyToWebSocket(
|
||||
const std::shared_ptr<websocketpp::connection<C>> &connection)
|
||||
{
|
||||
const auto applicationProxy = QNetworkProxy::applicationProxy();
|
||||
if (applicationProxy.type() != QNetworkProxy::HttpProxy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::string url = "http://";
|
||||
url += applicationProxy.hostName().toStdString();
|
||||
url += ":";
|
||||
url += std::to_string(applicationProxy.port());
|
||||
websocketpp::lib::error_code ec;
|
||||
connection->set_proxy(url, ec);
|
||||
if (ec)
|
||||
{
|
||||
qCDebug(chatterinoNetwork)
|
||||
<< "Couldn't set websocket proxy:" << ec.value();
|
||||
return;
|
||||
}
|
||||
|
||||
connection->set_proxy_basic_auth(
|
||||
applicationProxy.user().toStdString(),
|
||||
applicationProxy.password().toStdString(), ec);
|
||||
if (ec)
|
||||
{
|
||||
qCDebug(chatterinoNetwork)
|
||||
<< "Couldn't set websocket proxy auth:" << ec.value();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "common/Version.hpp"
|
||||
#include "providers/liveupdates/BasicPubSubClient.hpp"
|
||||
#include "providers/liveupdates/BasicPubSubWebsocket.hpp"
|
||||
#include "providers/NetworkConfigurationProvider.hpp"
|
||||
#include "providers/twitch/PubSubHelpers.hpp"
|
||||
#include "util/DebugCount.hpp"
|
||||
#include "util/ExponentialBackoff.hpp"
|
||||
@@ -336,6 +337,8 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkConfigurationProvider::applyToWebSocket(con);
|
||||
|
||||
this->websocketClient_.connect(con);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "providers/twitch/PubSubManager.hpp"
|
||||
|
||||
#include "common/QLogging.hpp"
|
||||
#include "providers/NetworkConfigurationProvider.hpp"
|
||||
#include "providers/twitch/PubSubActions.hpp"
|
||||
#include "providers/twitch/PubSubClient.hpp"
|
||||
#include "providers/twitch/PubSubHelpers.hpp"
|
||||
@@ -514,6 +515,8 @@ void PubSub::addClient()
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkConfigurationProvider::applyToWebSocket(con);
|
||||
|
||||
this->websocketClient.connect(con);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user