mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
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:
parent
98c2ff5607
commit
c9a9e44e1f
|
@ -12,6 +12,7 @@
|
||||||
- Minor: Added link to streamlink docs for easier user setup. (#4217)
|
- Minor: Added link to streamlink docs for easier user setup. (#4217)
|
||||||
- Minor: Added setting to turn off rendering of reply context. (#4224)
|
- Minor: Added setting to turn off rendering of reply context. (#4224)
|
||||||
- Minor: Added setting to select which channels to log. (#4302)
|
- Minor: Added setting to select which channels to log. (#4302)
|
||||||
|
- Minor: Added support for HTTP and Socks5 proxies through environment variables. (#4321)
|
||||||
- Minor: Remove sending part of the multipart emoji workaround (#4361)
|
- Minor: Remove sending part of the multipart emoji workaround (#4361)
|
||||||
- Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271)
|
- Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271)
|
||||||
- Bugfix: Fixed highlight sounds not reloading on change properly. (#4194)
|
- Bugfix: Fixed highlight sounds not reloading on change properly. (#4194)
|
||||||
|
|
|
@ -195,6 +195,8 @@ set(SOURCE_FILES
|
||||||
providers/IvrApi.hpp
|
providers/IvrApi.hpp
|
||||||
providers/LinkResolver.cpp
|
providers/LinkResolver.cpp
|
||||||
providers/LinkResolver.hpp
|
providers/LinkResolver.hpp
|
||||||
|
providers/NetworkConfigurationProvider.cpp
|
||||||
|
providers/NetworkConfigurationProvider.hpp
|
||||||
providers/RecentMessagesApi.cpp
|
providers/RecentMessagesApi.cpp
|
||||||
providers/RecentMessagesApi.hpp
|
providers/RecentMessagesApi.hpp
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,17 @@ namespace {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<QString> readOptionalStringEnv(const char *envName)
|
||||||
|
{
|
||||||
|
auto envString = std::getenv(envName);
|
||||||
|
if (envString != nullptr)
|
||||||
|
{
|
||||||
|
return QString(envString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t readPortEnv(const char *envName, uint16_t defaultValue)
|
uint16_t readPortEnv(const char *envName, uint16_t defaultValue)
|
||||||
{
|
{
|
||||||
auto envString = std::getenv(envName);
|
auto envString = std::getenv(envName);
|
||||||
|
@ -89,6 +100,7 @@ Env::Env()
|
||||||
readStringEnv("CHATTERINO2_TWITCH_SERVER_HOST", "irc.chat.twitch.tv"))
|
readStringEnv("CHATTERINO2_TWITCH_SERVER_HOST", "irc.chat.twitch.tv"))
|
||||||
, twitchServerPort(readPortEnv("CHATTERINO2_TWITCH_SERVER_PORT", 443))
|
, twitchServerPort(readPortEnv("CHATTERINO2_TWITCH_SERVER_PORT", 443))
|
||||||
, twitchServerSecure(readBoolEnv("CHATTERINO2_TWITCH_SERVER_SECURE", true))
|
, twitchServerSecure(readBoolEnv("CHATTERINO2_TWITCH_SERVER_SECURE", true))
|
||||||
|
, proxyUrl(readOptionalStringEnv("CHATTERINO2_PROXY_URL"))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
@ -16,6 +17,7 @@ public:
|
||||||
const QString twitchServerHost;
|
const QString twitchServerHost;
|
||||||
const uint16_t twitchServerPort;
|
const uint16_t twitchServerPort;
|
||||||
const bool twitchServerSecure;
|
const bool twitchServerSecure;
|
||||||
|
const boost::optional<QString> proxyUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -28,6 +28,7 @@ Q_LOGGING_CATEGORY(chatterinoMain, "chatterino.main", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoMessage, "chatterino.message", logThreshold);
|
Q_LOGGING_CATEGORY(chatterinoMessage, "chatterino.message", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoNativeMessage, "chatterino.nativemessage",
|
Q_LOGGING_CATEGORY(chatterinoNativeMessage, "chatterino.nativemessage",
|
||||||
logThreshold);
|
logThreshold);
|
||||||
|
Q_LOGGING_CATEGORY(chatterinoNetwork, "chatterino.network", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoNotification, "chatterino.notification",
|
Q_LOGGING_CATEGORY(chatterinoNotification, "chatterino.notification",
|
||||||
logThreshold);
|
logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoNuulsuploader, "chatterino.nuulsuploader",
|
Q_LOGGING_CATEGORY(chatterinoNuulsuploader, "chatterino.nuulsuploader",
|
||||||
|
|
|
@ -22,6 +22,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoLiveupdates);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoMain);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoMain);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoMessage);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoMessage);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNativeMessage);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoNativeMessage);
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoNetwork);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNotification);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoNotification);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNuulsuploader);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoNuulsuploader);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoPubSub);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoPubSub);
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
#include "BrowserExtension.hpp"
|
#include "BrowserExtension.hpp"
|
||||||
#include "common/Args.hpp"
|
#include "common/Args.hpp"
|
||||||
|
#include "common/Env.hpp"
|
||||||
#include "common/Modes.hpp"
|
#include "common/Modes.hpp"
|
||||||
#include "common/QLogging.hpp"
|
#include "common/QLogging.hpp"
|
||||||
#include "common/Version.hpp"
|
#include "common/Version.hpp"
|
||||||
#include "providers/IvrApi.hpp"
|
#include "providers/IvrApi.hpp"
|
||||||
|
#include "providers/NetworkConfigurationProvider.hpp"
|
||||||
#include "providers/twitch/api/Helix.hpp"
|
#include "providers/twitch/api/Helix.hpp"
|
||||||
#include "RunGui.hpp"
|
#include "RunGui.hpp"
|
||||||
#include "singletons/Paths.hpp"
|
#include "singletons/Paths.hpp"
|
||||||
|
@ -81,6 +83,8 @@ int main(int argc, char **argv)
|
||||||
attachToConsole();
|
attachToConsole();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkConfigurationProvider::applyFromEnv(Env::get());
|
||||||
|
|
||||||
IvrApi::initialize();
|
IvrApi::initialize();
|
||||||
Helix::initialize();
|
Helix::initialize();
|
||||||
|
|
||||||
|
|
76
src/providers/NetworkConfigurationProvider.cpp
Normal file
76
src/providers/NetworkConfigurationProvider.cpp
Normal file
|
@ -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
|
61
src/providers/NetworkConfigurationProvider.hpp
Normal file
61
src/providers/NetworkConfigurationProvider.hpp
Normal file
|
@ -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 "common/Version.hpp"
|
||||||
#include "providers/liveupdates/BasicPubSubClient.hpp"
|
#include "providers/liveupdates/BasicPubSubClient.hpp"
|
||||||
#include "providers/liveupdates/BasicPubSubWebsocket.hpp"
|
#include "providers/liveupdates/BasicPubSubWebsocket.hpp"
|
||||||
|
#include "providers/NetworkConfigurationProvider.hpp"
|
||||||
#include "providers/twitch/PubSubHelpers.hpp"
|
#include "providers/twitch/PubSubHelpers.hpp"
|
||||||
#include "util/DebugCount.hpp"
|
#include "util/DebugCount.hpp"
|
||||||
#include "util/ExponentialBackoff.hpp"
|
#include "util/ExponentialBackoff.hpp"
|
||||||
|
@ -336,6 +337,8 @@ private:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkConfigurationProvider::applyToWebSocket(con);
|
||||||
|
|
||||||
this->websocketClient_.connect(con);
|
this->websocketClient_.connect(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "providers/twitch/PubSubManager.hpp"
|
#include "providers/twitch/PubSubManager.hpp"
|
||||||
|
|
||||||
#include "common/QLogging.hpp"
|
#include "common/QLogging.hpp"
|
||||||
|
#include "providers/NetworkConfigurationProvider.hpp"
|
||||||
#include "providers/twitch/PubSubActions.hpp"
|
#include "providers/twitch/PubSubActions.hpp"
|
||||||
#include "providers/twitch/PubSubClient.hpp"
|
#include "providers/twitch/PubSubClient.hpp"
|
||||||
#include "providers/twitch/PubSubHelpers.hpp"
|
#include "providers/twitch/PubSubHelpers.hpp"
|
||||||
|
@ -514,6 +515,8 @@ void PubSub::addClient()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkConfigurationProvider::applyToWebSocket(con);
|
||||||
|
|
||||||
this->websocketClient.connect(con);
|
this->websocketClient.connect(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue