WIP: proxy_support #3

Draft
sectapunterx wants to merge 6 commits from proxy_support into main
4 changed files with 53 additions and 54 deletions
Showing only changes of commit 760bc2ab68 - Show all commits

View file

@ -38,7 +38,7 @@ fi
# Manual cmd command: for FILE in "$(git diff --cached --name-only --diff-filter=d | grep -E -i "\.(sh)$") .githooks/pre-commit"; do shellcheck -S warning $FILE; done # Manual cmd command: for FILE in "$(git diff --cached --name-only --diff-filter=d | grep -E -i "\.(sh)$") .githooks/pre-commit"; do shellcheck -S warning $FILE; done
# #
SHELLCHECK_RES=0 SHELLCHECK_RES=0
# for FILE in $GIT_SCRIPT_FILES; do shellcheck -S warning $FILE; RET_CODE=$?; SHELLCHECK_RES=$(( RET_CODE + SHELLCHECK_RES )); done for FILE in $GIT_SCRIPT_FILES; do shellcheck -S warning $FILE; RET_CODE=$?; SHELLCHECK_RES=$(( RET_CODE + SHELLCHECK_RES )); done
if [[ $SHELLCHECK_RES != 0 ]]; then if [[ $SHELLCHECK_RES != 0 ]]; then
printf "\n\t ${RED}[ERROR] shell scripts check FAILED!${NC} Fix above errors before commiting your changes. (check .githooks/pre-commit for additional info)\n" printf "\n\t ${RED}[ERROR] shell scripts check FAILED!${NC} Fix above errors before commiting your changes. (check .githooks/pre-commit for additional info)\n"
@ -62,7 +62,7 @@ printf "\n\tBuild GTests to check (takes up to 30 seconds)"
cmake --build ${GTEST_FOLDER} --target larra_xmpp_tests --parallel "$(nproc)" cmake --build ${GTEST_FOLDER} --target larra_xmpp_tests --parallel "$(nproc)"
printf "\n\tLaunch GTests to check\n" printf "\n\tLaunch GTests to check\n"
# ./larra_xmpp_tests --gtest_brief=1 ./larra_xmpp_tests --gtest_brief=1
GTEST_RES=$? GTEST_RES=$?
cd ${PROJECT_FOLDER} && rm -rf ${GTEST_FOLDER?} cd ${PROJECT_FOLDER} && rm -rf ${GTEST_FOLDER?}

1
.gitignore vendored
View file

@ -40,7 +40,6 @@ temp*
#/.idea/codeStyles/Project.xml #/.idea/codeStyles/Project.xml
#/.idea/vcs.xml #/.idea/vcs.xml
/.idea/ /.idea/
.githooks/
sha512sum marked this conversation as resolved
Review

Why .githooks ???

Why .githooks ???

View file

@ -171,25 +171,25 @@ auto ConnectViaProxy(Socket& socket, const HttpProxy& param_proxy, std::string_v
// ответ от прокси-сервера // ответ от прокси-сервера
boost::asio::streambuf response; boost::asio::streambuf response;
std::size_t bytes_transferred = co_await boost::asio::async_read_until(socket, response, kEndOfHeaders, boost::asio::use_awaitable); std::size_t bytesTransferred = co_await boost::asio::async_read_until(socket, response, kEndOfHeaders, boost::asio::use_awaitable);
// статус ответа // статус ответа
Review

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::istream responseStream(&response);
std::string http_version; std::string httpVersion;
unsigned int status_code = 0; unsigned int statusCode = 0;
std::string status_message; std::string statusMessage;
response_stream >> http_version >> status_code; responseStream >> httpVersion >> statusCode;
std::getline(response_stream, status_message); std::getline(responseStream, statusMessage);
if(!response_stream || http_version.substr(0, kEndOfHttpSubstring) != "HTTP/") { if(!responseStream || httpVersion.substr(0, kEndOfHttpSubstring) != "HTTP/") {
throw std::runtime_error("Invalid HTTP response from proxy"); throw std::runtime_error("Invalid HTTP response from proxy");
} }
if(status_code != kSuccessStatusCode) { if(statusCode != kSuccessStatusCode) {
std::ostringstream error_stream; std::ostringstream errorStream;
error_stream << http_version << " " << status_code << " " << status_message; errorStream << httpVersion << " " << statusCode << " " << statusMessage;
throw std::runtime_error("HTTP proxy CONNECT failed: " + error_stream.str()); throw std::runtime_error("HTTP proxy CONNECT failed: " + errorStream.str());
} }
co_return; co_return;

View file

@ -18,36 +18,36 @@ class ProxyTest : public ::testing::Test {
TEST_F(ProxyTest, ConnectViaHttpProxy_SuccessfulResponse) { TEST_F(ProxyTest, ConnectViaHttpProxy_SuccessfulResponse) {
HttpProxy proxy{"proxy_host", 8080}; HttpProxy proxy{"proxy_host", 8080};
std::string target_host = "target_host"; std::string targetHost = "target_host";
uint16_t target_port = 80; uint16_t targetPort = 80;
std::string expected_request = std::string expectedRequest =
std::format("CONNECT {}:{} HTTP/1.1\r\nHost: {}:{}\r\n\r\n", target_host, target_port, target_host, target_port); std::format("CONNECT {}:{} HTTP/1.1\r\nHost: {}:{}\r\n\r\n", targetHost, targetPort, targetHost, targetPort);
std::string proxy_response = "HTTP/1.1 200 Connection established\r\n\r\n"; std::string proxyResponse = "HTTP/1.1 200 Connection established\r\n\r\n";
mock_socket.AddReceivedData(proxy_response); mock_socket.AddReceivedData(proxyResponse);
bool connect_successful = false; bool connectSuccessful = false;
asio::co_spawn( asio::co_spawn(
io_context, io_context,
[&]() -> asio::awaitable<void> { [&]() -> asio::awaitable<void> {
try { try {
co_await client::impl::ConnectViaProxy(mock_socket, proxy, target_host, target_port); co_await client::impl::ConnectViaProxy(mock_socket, proxy, targetHost, targetPort);
connect_successful = true; connectSuccessful = true;
} catch(...) { } catch(...) {
connect_successful = false; connectSuccessful = false;
} }
}, },
asio::detached); asio::detached);
io_context.run(); io_context.run();
std::string sent_data = mock_socket.GetSentData(); std::string sentData = mock_socket.GetSentData();
EXPECT_EQ(sent_data, expected_request); EXPECT_EQ(sentData, expectedRequest);
EXPECT_TRUE(connect_successful); EXPECT_TRUE(connectSuccessful);
} }
// Test 2: Connect via SOCKS proxy // Test 2: Connect via SOCKS proxy
@ -58,53 +58,53 @@ TEST(Socks5ProxyTest, ConnectViaProxy) {
larra::xmpp::impl::MockSocket socket{executor}; larra::xmpp::impl::MockSocket socket{executor};
// expected server responses // expected server responses
std::string server_response; std::string serverResponse;
server_response += "\x05\x00"; // VER, METHOD serverResponse += "\x05\x00"; // VER, METHOD
server_response += "\x05\x00\x00\x01"; // VER, REP, RSV, ATYP (IPv4) serverResponse += "\x05\x00\x00\x01"; // VER, REP, RSV, ATYP (IPv4)
server_response += "\x7F\x00\x00\x01"; // BND.ADDR (127.0.0.1) serverResponse += "\x7F\x00\x00\x01"; // BND.ADDR (127.0.0.1)
server_response += "\x1F\x90"; // BND.PORT (8080) serverResponse += "\x1F\x90"; // BND.PORT (8080)
socket.AddReceivedData(server_response); socket.AddReceivedData(serverResponse);
Socks5Proxy proxy{"proxy.example.com", 1080}; Socks5Proxy proxy{"proxy.example.com", 1080};
std::string target_hostname = "target.example.com"; std::string targetHostname = "target.example.com";
std::uint16_t target_port = 80; std::uint16_t targetPort = 80;
boost::asio::co_spawn( boost::asio::co_spawn(
executor, executor,
[&]() -> boost::asio::awaitable<void> { [&]() -> boost::asio::awaitable<void> {
co_await client::impl::ConnectViaProxy(socket, proxy, target_hostname, target_port); co_await client::impl::ConnectViaProxy(socket, proxy, targetHostname, targetPort);
auto sent_data = socket.GetSentData(); auto sentData = socket.GetSentData();
// Expected client greeting // Expected client greeting
std::string expected_greeting = "\x05\x01\x00"; std::string expectedGreeting = "\x05\x01\x00";
// Expected CONNECT request // Expected CONNECT request
std::array<std::uint8_t, 262> expected_request{}; std::array<std::uint8_t, 262> expectedRequest{};
std::size_t req_len = 0; std::size_t req_len = 0;
expected_request[req_len++] = 0x05; // VER expectedRequest[req_len++] = 0x05; // VER
expected_request[req_len++] = 0x01; // CMD: CONNECT expectedRequest[req_len++] = 0x01; // CMD: CONNECT
expected_request[req_len++] = 0x00; // RSV expectedRequest[req_len++] = 0x00; // RSV
expected_request[req_len++] = 0x03; // ATYP: DOMAINNAME expectedRequest[req_len++] = 0x03; // ATYP: DOMAINNAME
expected_request[req_len++] = static_cast<std::uint8_t>(target_hostname.size()); // domain length expectedRequest[req_len++] = static_cast<std::uint8_t>(targetHostname.size()); // domain length
std::memcpy(&expected_request[req_len], target_hostname.data(), target_hostname.size()); std::memcpy(&expectedRequest[req_len], targetHostname.data(), targetHostname.size());
req_len += target_hostname.size(); req_len += targetHostname.size();
std::uint16_t network_order_port = htons(target_port); std::uint16_t networkOrderPort = htons(targetPort);
expected_request[req_len++] = static_cast<std::uint8_t>((network_order_port >> 8) & 0xFF); expectedRequest[req_len++] = static_cast<std::uint8_t>((networkOrderPort >> 8) & 0xFF);
expected_request[req_len++] = static_cast<std::uint8_t>(network_order_port & 0xFF); expectedRequest[req_len++] = static_cast<std::uint8_t>(networkOrderPort & 0xFF);
std::string expected_data = expected_greeting; std::string expectedData = expectedGreeting;
auto transformed_view = expected_request | std::views::take(req_len) | std::views::transform([](std::uint8_t byte) { auto transformed_view = expectedRequest | std::views::take(req_len) | std::views::transform([](std::uint8_t byte) {
return static_cast<char>(byte); return static_cast<char>(byte);
}); });
expected_data.append(std::ranges::to<std::string>(transformed_view)); expectedData.append(std::ranges::to<std::string>(transformed_view));
EXPECT_EQ(sent_data, expected_data); EXPECT_EQ(sentData, expectedData);
co_return; co_return;
}, },