WIP: proxy_support #3
2 changed files with 450 additions and 0 deletions
|
@ -157,6 +157,261 @@ struct StartTlsRequest {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename Socket>
|
||||||
|
auto ConnectViaProxy(Socket& socket, const HttpProxy& param_proxy, std::string_view host, std::uint16_t port)
|
||||||
|
-> boost::asio::awaitable<void> {
|
||||||
|
constexpr char kHttpVersion[] = "HTTP/1.1";
|
||||||
|
constexpr unsigned int kSuccessStatusCode = 200;
|
||||||
|
constexpr char kEndOfHeaders[] = "\r\n\r\n";
|
||||||
|
|
||||||
|
// HTTP CONNECT запрос
|
||||||
|
std::string request = std::format("CONNECT {}:{} {}\r\nHost: {}:{}\r\n\r\n", host, port, kHttpVersion, host, port);
|
||||||
|
|
||||||
|
co_await boost::asio::async_write(socket, boost::asio::buffer(request), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
|
|
||||||
|
// ответ от прокси-сервера
|
||||||
|
boost::asio::streambuf response;
|
||||||
|
std::size_t bytes_transferred = co_await boost::asio::async_read_until(socket, response, kEndOfHeaders, boost::asio::use_awaitable);
|
||||||
|
|||||||
|
|
||||||
|
// статус ответа
|
||||||
sha512sum
commented
Meaningless comments, variable names reflect what they contain. No need to duplicate. And use English for comments in code Meaningless comments, variable names reflect what they contain. No need to duplicate. And use English for comments in code
|
|||||||
|
std::istream response_stream(&response);
|
||||||
|
std::string http_version;
|
||||||
sha512sum
commented
camelCase camelCase
|
|||||||
|
unsigned int status_code;
|
||||||
|
std::string status_message;
|
||||||
|
|
||||||
|
response_stream >> http_version >> status_code;
|
||||||
|
std::getline(response_stream, status_message);
|
||||||
|
|
||||||
|
if(!response_stream || http_version.substr(0, 5) != "HTTP/") {
|
||||||
|
throw std::runtime_error("Invalid HTTP response from proxy");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(status_code != kSuccessStatusCode) {
|
||||||
|
std::ostringstream error_stream;
|
||||||
|
error_stream << http_version << " " << status_code << " " << status_message;
|
||||||
|
throw std::runtime_error("HTTP proxy CONNECT failed: " + error_stream.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Socket>
|
||||||
|
auto ConnectViaProxy(Socket& socket, Socks5Proxy& socksProxy, std::string_view address, std::uint16_t port)
|
||||||
|
-> boost::asio::awaitable<void> {
|
||||||
|
constexpr std::array kSocks5RequestStart = {std::byte{0x05}, std::byte{0x01}, std::byte{0x00}, std::byte{0x03}};
|
||||||
|
|
||||||
|
constexpr std::size_t kSocks5RequestMaxSize = 257;
|
||||||
|
|
||||||
|
constexpr std::size_t kSocks5ReplyTypeSize = 10;
|
||||||
|
|
||||||
|
constexpr std::array kHandshakeRequest{std::byte{0x05}, std::byte{0x01}, std::byte{0x00}};
|
||||||
|
|
||||||
|
// auto executor = co_await boost::asio::this_coro::executor;
|
||||||
|
// boost::asio::ip::tcp::resolver resolver{executor};
|
||||||
|
// auto resolved = co_await resolver.async_resolve({std::move(socksProxy.hostname), std::to_string(socksProxy.port)},
|
||||||
|
// boost::asio::use_awaitable);
|
||||||
|
// boost::asio::ip::tcp::socket socket{executor};
|
||||||
|
// co_await socket.async_connect(*resolved, boost::asio::use_awaitable);
|
||||||
|
|
||||||
|
std::array<std::byte, 2> handshakeResponse; // NOLINT
|
||||||
|
|
||||||
|
co_await boost::asio::async_write(
|
||||||
|
socket, boost::asio::buffer(kHandshakeRequest), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
|
co_await boost::asio::async_read(socket, boost::asio::buffer(handshakeResponse), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
|
if(handshakeResponse[0] != std::byte{0x05} || handshakeResponse[1] != std::byte{0x00}) { // NOLINT
|
||||||
|
throw std::exception{};
|
||||||
|
};
|
||||||
|
const auto [connectRequest, size] = [&] {
|
||||||
|
const auto size = static_cast<std::byte>(address.size());
|
||||||
|
const auto htonsPort = std::bit_cast<std::array<std::byte, 2>>(htons(port));
|
||||||
|
auto range = std::array{std::span{kSocks5RequestStart.begin(), kSocks5RequestStart.size()},
|
||||||
|
std::span{&size, 1},
|
||||||
|
std::span{utils::StartLifetimeAsArray<const std::byte>(address.data(), address.size()), address.size()},
|
||||||
|
std::span{htonsPort.data(), 2}} |
|
||||||
|
std::views::join;
|
||||||
|
std::array<std::byte, kSocks5RequestMaxSize> response; // NOLINT
|
||||||
|
auto sizee = std::ranges::copy(range, response.begin()).out - response.begin();
|
||||||
|
return std::pair{response, sizee};
|
||||||
|
}();
|
||||||
|
co_await boost::asio::async_write(
|
||||||
|
socket, boost::asio::buffer(connectRequest.begin(), size), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
|
std::array<std::byte, kSocks5ReplyTypeSize> connectReplyType; // NOLINT
|
||||||
|
co_await boost::asio::async_read(socket, boost::asio::buffer(connectReplyType), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
|
if(connectReplyType[1] != std::byte{0x00}) {
|
||||||
|
throw std::exception{};
|
||||||
|
};
|
||||||
|
co_return;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Socket>
|
||||||
|
auto ConnectViaProxyV(Socket& socket, const Socks5Proxy& proxy, std::string_view target_hostname, std::uint16_t target_port)
|
||||||
|
-> boost::asio::awaitable<void> {
|
||||||
|
constexpr std::uint8_t kSocks5Version = 0x05; // Version 5
|
||||||
|
constexpr std::uint8_t kNoAuthMethod = 0x00; // No auth required
|
||||||
|
constexpr std::uint8_t kRsv = 0x00; // Reserved
|
||||||
|
constexpr std::uint8_t kConnectCommand = 0x01; // Command CONNECT
|
||||||
|
constexpr std::uint8_t kDomainNameType = 0x03; // Address type: Domain name
|
||||||
|
constexpr std::uint8_t kIpv4 = 0x01; // Address type: IPv4
|
||||||
|
constexpr std::uint8_t kIpv6 = 0x04; // Address type: IPv6
|
||||||
|
constexpr std::uint8_t kMaxAddressL = 255; // Max address length
|
||||||
|
constexpr std::size_t kMaxRequestSize = 257;
|
||||||
|
|
||||||
|
if(target_hostname.size() > kMaxAddressL) {
|
||||||
|
throw std::runtime_error("Hostname too long for SOCKS5");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array greeting = {kSocks5Version, kConnectCommand, kRsv, kDomainNameType};
|
||||||
|
co_await boost::asio::async_write(socket, boost::asio::buffer(greeting), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
|
|
||||||
|
std::array<std::uint8_t, 2> response{};
|
||||||
sha512sum
commented
camelCase camelCase
|
|||||||
|
co_await boost::asio::async_read(socket, boost::asio::buffer(response), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
|
if(response[0] != kSocks5Version || response[1] != kNoAuthMethod) {
|
||||||
|
throw std::runtime_error("SOCKS5 proxy authentication failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array header{
|
||||||
|
kSocks5Version, kConnectCommand, kNoAuthMethod, kDomainNameType}; // 4 байта для заголовка, до 255 байт для адреса, 2 байта для порта
|
||||||
|
auto hostnameLength = static_cast<std::uint8_t>(target_hostname.size());
|
||||||
|
|
||||||
|
auto portBytes = std::bit_cast<std::array<std::uint8_t, 2>>(htons(target_port));
|
||||||
|
|
||||||
|
auto request =
|
||||||
|
std::array{std::span<const std::uint8_t>(header),
|
||||||
|
std::span<const std::uint8_t>(&hostnameLength, 1),
|
||||||
|
std::span<const std::uint8_t>(utils::StartLifetimeAsArray<uint8_t>(target_hostname.data(), target_hostname.size()),
|
||||||
|
target_hostname.size()),
|
||||||
|
std::span<const std::uint8_t>(portBytes)} |
|
||||||
|
std::views::join;
|
||||||
|
|
||||||
|
std::array<std::uint8_t, kMaxRequestSize> requestBuffer{};
|
||||||
|
|
||||||
|
auto it = std::ranges::copy(request, requestBuffer.begin()).out;
|
||||||
|
size_t requestSize = std::distance(requestBuffer.begin(), it);
|
||||||
|
|
||||||
|
// Отправляем запрос
|
||||||
|
co_await boost::asio::async_write(
|
||||||
|
socket, boost::asio::buffer(requestBuffer.data(), requestSize), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
|
|
||||||
|
// ответ сервера
|
||||||
|
std::array<std::uint8_t, 4> reply{};
|
||||||
|
co_await boost::asio::async_read(socket, boost::asio::buffer(reply), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
|
|
||||||
|
if(reply[0] != kSocks5Version || reply[1] != kNoAuthMethod) {
|
||||||
|
throw std::runtime_error("SOCKS5 proxy connection failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint8_t addr_type = reply[3];
|
||||||
|
size_t addr_len = 0;
|
||||||
|
if(addr_type == kIpv4) {
|
||||||
|
// IPv4
|
||||||
|
addr_len = 4;
|
||||||
|
} else if(addr_type == kDomainNameType) {
|
||||||
|
// Domain name
|
||||||
|
std::uint8_t len{};
|
||||||
|
co_await boost::asio::async_read(socket, boost::asio::buffer(&len, 1), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
|
addr_len = len;
|
||||||
|
} else if(addr_type == kIpv6) {
|
||||||
|
// IPv6
|
||||||
|
addr_len = 16;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Unknown address type in SOCKS5 reply");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<std::uint8_t, 18> addr{}; // Максимальный размер для IPv6 адреса + порт
|
||||||
|
co_await boost::asio::async_read(
|
||||||
|
socket, boost::asio::buffer(addr.data(), addr_len + 2), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
|
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Socket>
|
||||||
|
auto ConnectViaProxy(Socket&, const NoProxy&, std::string_view, std::uint16_t) -> boost::asio::awaitable<void> {
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ProxyType>
|
||||||
|
auto GetProxySettings(const char* proxyEnv, std::uint16_t port) -> Proxy {
|
||||||
|
std::string_view rawProxyStr = proxyEnv;
|
||||||
|
|
||||||
|
const std::size_t protocolNamePos = rawProxyStr.find("://");
|
||||||
|
std::string_view proxyStr = protocolNamePos == std::string_view::npos ? rawProxyStr : rawProxyStr.substr(protocolNamePos + 3);
|
||||||
|
|
||||||
|
const std::size_t portPos = proxyStr.find(':');
|
||||||
|
|
||||||
|
if(portPos == std::string_view::npos) {
|
||||||
|
return ProxyType{std::string(proxyStr), port};
|
||||||
|
}
|
||||||
|
auto host = std::string(proxyStr.substr(0, portPos));
|
||||||
|
auto portStr = proxyStr.substr(portPos + 1);
|
||||||
|
auto portOpt = ToInt<uint16_t>(portStr);
|
||||||
|
|
||||||
|
if(!portOpt) {
|
||||||
|
throw std::runtime_error("Invalid port number in proxy settings");
|
||||||
|
}
|
||||||
|
return ProxyType{host, portOpt.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto GetSystemProxySettings() -> Proxy {
|
||||||
|
if(const char* proxyEnv = std::getenv("http_proxy")) {
|
||||||
|
constexpr std::uint16_t kHttpPort = 8080;
|
||||||
|
return GetProxySettings<HttpProxy>(proxyEnv, kHttpPort);
|
||||||
|
}
|
||||||
|
if(const char* proxy_env = std::getenv("socks_proxy")) {
|
||||||
|
constexpr std::uint16_t kSocksPort = 1080;
|
||||||
|
return GetProxySettings<Socks5Proxy>(proxy_env, kSocksPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NoProxy{};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Socket>
|
||||||
|
auto ConnectViaProxy(Socket& socket, const SystemConfiguredProxy&, std::string_view host, std::uint16_t port)
|
||||||
|
-> boost::asio::awaitable<void> {
|
||||||
|
auto proxy_opt = GetSystemProxySettings();
|
||||||
|
|
||||||
|
co_await std::visit(
|
||||||
|
[&](auto&& proxy_variant) -> boost::asio::awaitable<void> {
|
||||||
|
co_await ConnectViaProxy(socket, proxy_variant, host, port);
|
||||||
|
},
|
||||||
|
proxy_opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Socket, typename ProxyType>
|
||||||
|
auto ConnectToServer(Socket& socket, const ProxyType& proxy, std::string_view host, std::uint16_t port) -> boost::asio::awaitable<void> {
|
||||||
|
auto executor = co_await boost::asio::this_coro::executor;
|
||||||
|
boost::asio::ip::tcp::resolver resolver(executor);
|
||||||
|
auto endpoints = co_await resolver.async_resolve(proxy.hostname, std::to_string(proxy.port), boost::asio::use_awaitable);
|
||||||
|
co_await boost::asio::async_connect(socket, endpoints, boost::asio::use_awaitable);
|
||||||
|
|
||||||
|
if constexpr(!std::same_as<ProxyType, NoProxy>) {
|
||||||
|
co_await ConnectViaProxy(socket, proxy, host, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Socket>
|
||||||
|
auto ConnectToServer(Socket& socket, const SystemConfiguredProxy& proxy, std::string_view host, std::uint16_t port)
|
||||||
|
-> boost::asio::awaitable<void> {
|
||||||
|
auto executor = co_await boost::asio::this_coro::executor;
|
||||||
|
boost::asio::ip::tcp::resolver resolver(executor);
|
||||||
|
auto endpoints = co_await resolver.async_resolve(host, std::to_string(port), boost::asio::use_awaitable);
|
||||||
|
co_await boost::asio::async_connect(socket, endpoints, boost::asio::use_awaitable);
|
||||||
|
|
||||||
|
co_await ConnectViaProxy(socket, proxy, host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Socket>
|
||||||
|
auto ConnectWithProxy(Socket& socket, const Proxy& proxy, std::string_view host, std::uint16_t port) -> boost::asio::awaitable<void> {
|
||||||
|
co_await std::visit(
|
||||||
|
[&socket, host, port](const auto& proxy_variant) -> boost::asio::awaitable<void> {
|
||||||
|
co_await ConnectToServer(socket, proxy_variant, host, port);
|
||||||
|
},
|
||||||
|
proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto GetAuthData(const PlainUserAccount& account) -> std::string {
|
||||||
|
return EncodeBase64('\0' + account.jid.username + '\0' + account.password);
|
||||||
|
}
|
||||||
|
|
||||||
struct ClientCreateVisitor {
|
struct ClientCreateVisitor {
|
||||||
UserAccount account;
|
UserAccount account;
|
||||||
const Options& options;
|
const Options& options;
|
||||||
|
|
195
tests/proxy.cpp
Normal file
195
tests/proxy.cpp
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <larra/client/client.hpp>
|
||||||
|
#include <larra/impl/mock_socket.hpp>
|
||||||
|
#include <larra/proxy.hpp>
|
||||||
|
|
||||||
|
using namespace larra::xmpp;
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
namespace asio = boost::asio;
|
||||||
|
|
||||||
|
class ProxyTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
boost::asio::io_context io_context;
|
||||||
|
larra::xmpp::impl::MockSocket mock_socket{io_context.get_executor()};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test 1: Connect via HTTP proxy with successful server response
|
||||||
|
TEST_F(ProxyTest, ConnectViaHttpProxy_SuccessfulResponse) {
|
||||||
|
HttpProxy proxy{"proxy_host", 8080};
|
||||||
|
|
||||||
|
std::string target_host = "target_host";
|
||||||
|
uint16_t target_port = 80;
|
||||||
|
|
||||||
|
std::string expected_request =
|
||||||
|
std::format("CONNECT {}:{} HTTP/1.1\r\nHost: {}:{}\r\n\r\n", target_host, target_port, target_host, target_port);
|
||||||
|
|
||||||
|
std::string proxy_response = "HTTP/1.1 200 Connection established\r\n\r\n";
|
||||||
|
|
||||||
|
mock_socket.AddReceivedData(proxy_response);
|
||||||
|
|
||||||
|
bool connect_successful = false;
|
||||||
sha512sum
commented
camelCase camelCase
|
|||||||
|
|
||||||
|
asio::co_spawn(
|
||||||
|
io_context,
|
||||||
|
[&]() -> asio::awaitable<void> {
|
||||||
|
try {
|
||||||
|
co_await client::impl::ConnectViaProxy(mock_socket, proxy, target_host, target_port);
|
||||||
|
connect_successful = true;
|
||||||
|
} catch(...) {
|
||||||
|
connect_successful = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
asio::detached);
|
||||||
|
|
||||||
|
io_context.run();
|
||||||
|
|
||||||
|
std::string sent_data = mock_socket.GetSentData();
|
||||||
|
|
||||||
|
EXPECT_EQ(sent_data, expected_request);
|
||||||
|
EXPECT_TRUE(connect_successful);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: Connect via SOCKS proxy
|
||||||
|
TEST(Socks5ProxyTest, ConnectViaProxy) {
|
||||||
|
boost::asio::io_context io;
|
||||||
|
auto executor = io.get_executor();
|
||||||
|
|
||||||
|
larra::xmpp::impl::MockSocket socket{executor};
|
||||||
|
|
||||||
|
// expected server responses
|
||||||
|
std::string server_response;
|
||||||
sha512sum
commented
camelCase camelCase
|
|||||||
|
server_response += "\x05\x00"; // VER, METHOD
|
||||||
|
server_response += "\x05\x00\x00\x01"; // VER, REP, RSV, ATYP (IPv4)
|
||||||
|
server_response += "\x7F\x00\x00\x01"; // BND.ADDR (127.0.0.1)
|
||||||
|
server_response += "\x1F\x90"; // BND.PORT (8080)
|
||||||
|
|
||||||
|
socket.AddReceivedData(server_response);
|
||||||
|
|
||||||
|
Socks5Proxy proxy{"proxy.example.com", 1080};
|
||||||
|
std::string target_hostname = "target.example.com";
|
||||||
|
std::uint16_t target_port = 80;
|
||||||
|
|
||||||
|
boost::asio::co_spawn(
|
||||||
|
executor,
|
||||||
|
[&]() -> boost::asio::awaitable<void> {
|
||||||
|
co_await client::impl::ConnectViaProxy(socket, proxy, target_hostname, target_port);
|
||||||
|
|
||||||
|
auto sent_data = socket.GetSentData();
|
||||||
|
|
||||||
|
// Expected client greeting
|
||||||
|
std::string expected_greeting = "\x05\x01\x00";
|
||||||
|
|
||||||
|
// Expected CONNECT request
|
||||||
|
std::array<std::uint8_t, 262> expected_request{};
|
||||||
|
std::size_t req_len = 0;
|
||||||
|
|
||||||
|
expected_request[req_len++] = 0x05; // VER
|
||||||
|
expected_request[req_len++] = 0x01; // CMD: CONNECT
|
||||||
|
expected_request[req_len++] = 0x00; // RSV
|
||||||
|
expected_request[req_len++] = 0x03; // ATYP: DOMAINNAME
|
||||||
|
|
||||||
|
expected_request[req_len++] = static_cast<std::uint8_t>(target_hostname.size()); // domain length
|
||||||
|
|
||||||
|
std::memcpy(&expected_request[req_len], target_hostname.data(), target_hostname.size());
|
||||||
|
req_len += target_hostname.size();
|
||||||
|
|
||||||
|
std::uint16_t network_order_port = htons(target_port);
|
||||||
|
expected_request[req_len++] = static_cast<std::uint8_t>((network_order_port >> 8) & 0xFF);
|
||||||
|
expected_request[req_len++] = static_cast<std::uint8_t>(network_order_port & 0xFF);
|
||||||
|
|
||||||
|
std::string expected_data = expected_greeting;
|
||||||
|
expected_data.append(reinterpret_cast<const char*>(expected_request.data()), req_len);
|
||||||
|
|
||||||
|
EXPECT_EQ(sent_data, expected_data);
|
||||||
|
|
||||||
|
co_return;
|
||||||
|
},
|
||||||
|
boost::asio::detached);
|
||||||
|
|
||||||
|
io.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Test 3: Connect via system configured proxy
|
||||||
|
TEST_F(ProxyTest, ConnectViaSystemProxy_HttpProxy) {
|
||||||
|
// Set the environment variable for the system proxy
|
||||||
|
const char* original_http_proxy = std::getenv("http_proxy");
|
||||||
|
setenv("http_proxy", "http://proxy_host:8080", 1);
|
||||||
|
|
||||||
|
std::string target_host = "target_host";
|
||||||
|
uint16_t target_port = 80;
|
||||||
|
|
||||||
|
std::string expected_request = std::format(
|
||||||
|
"CONNECT {}:{} HTTP/1.1\r\nHost: {}:{}\r\n\r\n",
|
||||||
sha512sum marked this conversation as resolved
Outdated
sha512sum
commented
camelCase camelCase
|
|||||||
|
target_host, target_port, target_host, target_port);
|
||||||
|
|
||||||
|
std::string proxy_response = "HTTP/1.1 200 Connection established\r\n\r\n";
|
||||||
|
|
||||||
|
mock_socket.AddReceivedData(proxy_response);
|
||||||
|
|
||||||
|
bool connect_successful = false;
|
||||||
|
|
||||||
|
asio::co_spawn(io_context, [&]() -> asio::awaitable<void> {
|
||||||
|
try {
|
||||||
|
SystemConfiguredProxy proxy;
|
||||||
|
co_await client::impl::ConnectToServer(mock_socket, proxy, target_host, target_port);
|
||||||
|
connect_successful = true;
|
||||||
|
} catch (...) {
|
||||||
|
connect_successful = false;
|
||||||
|
}
|
||||||
|
}, asio::detached);
|
||||||
|
|
||||||
|
io_context.run();
|
||||||
|
|
||||||
|
std::string sent_data = mock_socket.GetSentData();
|
||||||
|
|
||||||
|
EXPECT_EQ(sent_data, expected_request);
|
||||||
|
EXPECT_TRUE(connect_successful);
|
||||||
|
|
||||||
|
// Restore the original environment variable
|
||||||
|
if (original_http_proxy) {
|
||||||
|
setenv("http_proxy", original_http_proxy, 1);
|
||||||
|
} else {
|
||||||
|
unsetenv("http_proxy");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 4: Incorrect proxy data
|
||||||
|
TEST_F(ProxyTest, ConnectViaHttpProxy_IncorrectData) {
|
||||||
|
HttpProxy proxy{"proxy_host", 8080};
|
||||||
|
|
||||||
|
std::string target_host = "target_host";
|
||||||
|
uint16_t target_port = 80;
|
||||||
|
|
||||||
|
std::string expected_request = std::format(
|
||||||
|
"CONNECT {}:{} HTTP/1.1\r\nHost: {}:{}\r\n\r\n",
|
||||||
sha512sum
commented
camelCase camelCase
|
|||||||
|
target_host, target_port, target_host, target_port);
|
||||||
|
|
||||||
|
// Simulate an incorrect response from the proxy server
|
||||||
|
std::string proxy_response = "HTTP/1.1 400 Bad Request\r\n\r\n";
|
||||||
|
|
||||||
|
mock_socket.AddReceivedData(proxy_response);
|
||||||
|
|
||||||
|
bool connect_successful = false;
|
||||||
|
std::string error_message;
|
||||||
|
|
||||||
|
asio::co_spawn(io_context, [&]() -> asio::awaitable<void> {
|
||||||
|
try {
|
||||||
|
co_await client::impl::ConnectToServer(mock_socket, proxy, target_host, target_port);
|
||||||
|
connect_successful = true;
|
||||||
|
} catch (const std::runtime_error& e) {
|
||||||
|
connect_successful = false;
|
||||||
|
error_message = e.what();
|
||||||
|
}
|
||||||
|
}, asio::detached);
|
||||||
|
|
||||||
|
io_context.run();
|
||||||
|
|
||||||
|
std::string sent_data = mock_socket.GetSentData();
|
||||||
|
|
||||||
|
EXPECT_EQ(sent_data, expected_request);
|
||||||
|
EXPECT_FALSE(connect_successful);
|
||||||
|
EXPECT_FALSE(error_message.empty());
|
||||||
|
}
|
||||||
|
*/
|
Loading…
Reference in a new issue
Code style. Use camelCase