Add a proxy fallback, and read proxy from env

This commit is contained in:
Bartosz Wieczorek 2025-06-09 13:01:08 +02:00
parent 4d456c56a8
commit 5d163800c3
5 changed files with 110 additions and 34 deletions

View File

@ -1,4 +1,3 @@
#include "rublon/bits.hpp"
#include <security/pam_appl.h>
#include <security/pam_client.h>
#include <security/pam_ext.h>
@ -6,6 +5,7 @@
#include <security/pam_modules.h>
#include <syslog.h>
#include <rublon/bits.hpp>
#include <rublon/check_application.hpp>
#include <rublon/error.hpp>
#include <rublon/error_handler.hpp>

View File

@ -1,5 +1,6 @@
#pragma once
#include "tl/expected.hpp"
#include <rublon/error.hpp>
#include <rublon/memory.hpp>
#include <rublon/static_string.hpp>
@ -147,6 +148,64 @@ class ConfigurationReader {
return std::nullopt;
};
auto parseProxyURL = [&](const char * envValue) -> bool {
// Very simple parser: scheme://[user[:pass]@]host[:port]
std::string_view url = envValue;
std::string_view scheme{};
std::string_view auth{};
std::string_view hostport{};
auto scheme_pos = url.find("://");
if(scheme_pos != std::string_view::npos) {
scheme = url.substr(0, scheme_pos);
url = url.substr(scheme_pos + 3);
} else {
scheme = "http"; // default
}
auto at_pos = url.find('@');
if(at_pos != std::string_view::npos) {
auth = url.substr(0, at_pos);
hostport = url.substr(at_pos + 1);
} else {
hostport = url;
}
std::string_view host = hostport;
std::string_view port_str{};
auto colon_pos = hostport.rfind(':');
if(colon_pos != std::string_view::npos && colon_pos < hostport.size() - 1) {
host = hostport.substr(0, colon_pos);
port_str = hostport.substr(colon_pos + 1);
}
config.proxyEnabled = true;
config.proxyType = scheme;
config.proxyServer = host;
if(!port_str.empty()) {
config.proxyPort = conv::to_uint32opt(port_str);
if(not config.proxyPort) {
log(LogLevel::Error, "Invalid proxy port in environment variable");
return false;
}
}
if(!auth.empty()) {
auto colon = auth.find(':');
if(colon != std::string_view::npos) {
config.proxyUsername = auth.substr(0, colon);
config.proxyPass = auth.substr(colon + 1);
} else {
config.proxyUsername = auth;
}
config.proxyAuthRequired = true;
}
return true;
};
/// NOTE:
// getStringOpt can return a valid empty string, for example configuration entry like
// option=
@ -183,6 +242,22 @@ class ConfigurationReader {
config.proxyEnabled = getBool("proxyEnabled").value_or(false);
config.proxyType = getStringOpt("proxyType");
config.proxyServer = getStringOpt("proxyServer");
// Apply fallback if no config is set
if (config.proxyEnabled && (!config.proxyType || config.proxyType->empty()) && (!config.proxyServer || config.proxyServer->empty())) {
log(LogLevel::Info, "Proxy is enabled but no configuration for it is provided, trying to read from env");
if (const char* https_proxy = std::getenv("https_proxy"); https_proxy && *https_proxy) {
if (parseProxyURL(https_proxy)) {
log(LogLevel::Info, "Loaded proxy config from HTTPS_PROXY");
}
} else if (const char* http_proxy = std::getenv("http_proxy"); http_proxy && *http_proxy) {
if (parseProxyURL(http_proxy)) {
log(LogLevel::Info, "Loaded proxy config from HTTP_PROXY");
}
}
}
if(config.proxyEnabled) {
if(not config.proxyType or config.proxyType->empty()) {
log(LogLevel::Error, "Proxy is enabled but proxy type is not present or empty");

View File

@ -99,7 +99,7 @@ class CURL {
proxyUrl = *conf().proxyType;
proxyUrl += "://";
proxyUrl += *conf().proxyServer;
if(conf().proxyPort > 0) {
if(conf().proxyPort.value_or(0) > 0) {
proxyUrl += ":";
proxyUrl += std::to_string(*conf().proxyPort);
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <charconv>
#include <optional>
#include <rublon/memory.hpp>
#include <rublon/static_string.hpp>
@ -150,22 +151,22 @@ namespace conv {
return strcmp(buf.data(), "true") == 0;
}
inline tl::expected< std::uint32_t, Error > to_uint32(std::string_view userinput) noexcept {
try {
return std::stoi(userinput.data());
} catch(const std::invalid_argument & e) {
return tl::make_unexpected(Error::NotANumber);
} catch(const std::out_of_range & e) {
return tl::make_unexpected(Error::OutOfRange);
}
}
inline std::optional< std::uint32_t> to_uint32opt(std::string_view userinput) noexcept {
try {
return std::stoi(userinput.data());
} catch(...) {
int out;
const std::from_chars_result result = std::from_chars(userinput.data(), userinput.data() + userinput.size(), out);
if(result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range) {
return std::nullopt;
}
return out;
}
inline tl::expected< std::uint32_t, Error > to_uint32(std::string_view userinput) noexcept {
int out;
const std::from_chars_result result = std::from_chars(userinput.data(), userinput.data() + userinput.size(), out);
if(result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range) {
return tl::make_unexpected(Error::NotANumber);
}
return out;
}
} // namespace conv

View File

@ -93,7 +93,7 @@ class WebSocket {
}
proxyUrl += *cfg.proxyServer;
if(cfg.proxyPort > 0) {
if(cfg.proxyPort.value_or(0) > 0) {
proxyUrl += ":";
proxyUrl += std::to_string(*cfg.proxyPort);
}