From e85a043425bc9a4666d5e0e2795d1b78f3c49ddd Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Tue, 22 Aug 2023 16:53:37 +0500 Subject: [PATCH 01/15] Use new docker compose, small cleanups --- create-and-run-containers.sh | 6 +++--- include/restc-cpp/logging.h | 4 ---- include/restc-cpp/restc-cpp.h | 4 ++-- stop-containers.sh | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/create-and-run-containers.sh b/create-and-run-containers.sh index ba68e84..df88d48 100755 --- a/create-and-run-containers.sh +++ b/create-and-run-containers.sh @@ -13,8 +13,8 @@ else exit -1 fi -docker-compose stop -docker-compose build -docker-compose up -d +docker compose stop +docker compose build +docker compose up -d docker ps popd diff --git a/include/restc-cpp/logging.h b/include/restc-cpp/logging.h index 7e2515a..54af752 100644 --- a/include/restc-cpp/logging.h +++ b/include/restc-cpp/logging.h @@ -143,10 +143,6 @@ inline void RestcCppTestStartLogger(const std::string& level = "info") { #elif defined RESTC_CPP_LOG_WITH_BOOST_LOG -#ifndef WIN32 -# define BOOST_LOG_DYN_LINK 1 -#endif - #include #include #include diff --git a/include/restc-cpp/restc-cpp.h b/include/restc-cpp/restc-cpp.h index 867035d..9c78059 100644 --- a/include/restc-cpp/restc-cpp.h +++ b/include/restc-cpp/restc-cpp.h @@ -160,7 +160,7 @@ class Request { class Properties { public: using ptr_t = std::shared_ptr; - using redirect_fn_t = std::function; using general_callback_t = std::function; @@ -417,7 +417,7 @@ class RestClient { done_handler.reset(); }); - return move(future); + return future; } /*! Process from within an existing coroutine */ diff --git a/stop-containers.sh b/stop-containers.sh index 77950e6..22b110f 100755 --- a/stop-containers.sh +++ b/stop-containers.sh @@ -1,5 +1,5 @@ #!/bin/bash pushd ci/mock-backends -docker-compose down +docker compose down popd From 33c9c54780ca815a5191db440f23bd9fb59d6a15 Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Fri, 25 Aug 2023 15:08:46 +0500 Subject: [PATCH 02/15] HTTPS CONNECT proxy support. No auth yet. --- include/restc-cpp/restc-cpp.h | 4 +- src/DataReaderStream.cpp | 4 +- src/RequestImpl.cpp | 169 +++++++++++++++++++++++++++------- 3 files changed, 142 insertions(+), 35 deletions(-) diff --git a/include/restc-cpp/restc-cpp.h b/include/restc-cpp/restc-cpp.h index 9c78059..fd25ed1 100644 --- a/include/restc-cpp/restc-cpp.h +++ b/include/restc-cpp/restc-cpp.h @@ -66,8 +66,6 @@ class RequestBody; class Connection; class ConnectionPool; class Socket; -class Request; -class Reply; class Context; class DataWriter; @@ -136,7 +134,7 @@ class Request { }; struct Proxy { - enum class Type { NONE, HTTP, SOCKS5 }; + enum class Type { NONE, HTTP, HTTPS, SOCKS5 }; Type type = Type::NONE; std::string address; diff --git a/src/DataReaderStream.cpp b/src/DataReaderStream.cpp index 5d23cc1..ab534bf 100644 --- a/src/DataReaderStream.cpp +++ b/src/DataReaderStream.cpp @@ -71,6 +71,7 @@ DataReaderStream::GetData(size_t maxBytes) { void DataReaderStream::ReadServerResponse(Reply::HttpResponse& response) { + static const string http_1_0{"HTTP/1.0"}; //some proxies use HTTP/1.0 static const string http_1_1{"HTTP/1.1"}; constexpr size_t max_version_len = 16; constexpr size_t max_phrase_len = 256; @@ -91,7 +92,8 @@ void DataReaderStream::ReadServerResponse(Reply::HttpResponse& response) if (value.empty()) { throw ProtocolException("ReadHeaders(): No HTTP version"); } - if (ciEqLibC()(value, http_1_1)) { + if (ciEqLibC()(value, http_1_1) || + ciEqLibC()(value, http_1_0)) { ; // Do nothing HTTP 1.1 is the default value } else { throw ProtocolException( diff --git a/src/RequestImpl.cpp b/src/RequestImpl.cpp index 1139209..1ff4d96 100644 --- a/src/RequestImpl.cpp +++ b/src/RequestImpl.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -15,6 +16,7 @@ #include "restc-cpp/error.h" #include "restc-cpp/url_encode.h" #include "restc-cpp/RequestBody.h" +#include "restc-cpp/DataReaderStream.h" #include "ReplyImpl.h" using namespace std; @@ -66,8 +68,8 @@ boost::asio::ip::address make_address(const char* str, namespace restc_cpp { const std::string& Request::Proxy::GetName() { - static const array names = { - "NONE", "HTTP", "SOCKS5" + static const array names = { + "NONE", "HTTP", "HTTPS", "SOCKS5" }; return names.at(static_cast(type)); @@ -122,7 +124,7 @@ pair ParseAddress(const std::string addr) { } /*! Parse the address and write the socks5 connect request */ -void ParseAddressIntoSocke5ConnectRequest(const std::string& addr, +void ParseAddressIntoSocks5ConnectRequest(const std::string& addr, vector& out) { out.push_back(SOCKS5_VERSION); @@ -221,13 +223,13 @@ size_t ValidateCompleteSocks5ConnectReply(uint8_t *buf, size_t len) { return hdr_len; } -void DoSocks5Handshake(Connection& connection, +void DoSocks5Handshake(Connection::ptr_t connection, const Url& url, - const Request::Properties properties, + const Request::Properties::ptr_t& properties, Context& ctx) { - assert(properties.proxy.type == Request::Proxy::Type::SOCKS5); - auto& sck = connection.GetSocket(); + assert(properties->proxy.type == Request::Proxy::Type::SOCKS5); + auto& sck = connection->GetSocket(); // Send no-auth handshake { @@ -255,7 +257,7 @@ void DoSocks5Handshake(Connection& connection, auto addr = url.GetHost().to_string() + ":" + to_string(url.GetPort()); - ParseAddressIntoSocke5ConnectRequest(addr, params); + ParseAddressIntoSocks5ConnectRequest(addr, params); RESTC_CPP_LOG_TRACE_("DoSocks5Handshake - saying connect to " << url.GetHost().to_string() << ":" << url.GetPort()); sck.AsyncWriteT(params, ctx.GetYield()); } @@ -283,6 +285,93 @@ void DoSocks5Handshake(Connection& connection, } RESTC_CPP_LOG_TRACE_("DoSocks5Handshake - done"); } + + +void DoProxyConnect(Connection::ptr_t connection, + const Url& url, + const Request::Properties::ptr_t& properties, + Context& ctx) { + + assert(properties->proxy.type == Request::Proxy::Type::HTTPS); + static const string crlf{"\r\n"}; + auto& sck = connection->GetSocket(); + + { + const string host(url.GetHost().to_string() + ":" + to_string(url.GetPort())); + ostringstream request_buffer; + request_buffer << "CONNECT "; + request_buffer << host; + request_buffer << " HTTP/1.1" << crlf; + request_buffer << "Host: " << host << crlf << crlf; + RESTC_CPP_LOG_DEBUG_("DoProxyConnect - send CONNECT " << host << " HTTP/1.1"); + sck.AsyncWriteT(request_buffer.str(), ctx.GetYield()); + } + { + RESTC_CPP_LOG_TRACE_("DoProxyConnect: starting receiving from proxy"); + //struct { http_version=HTTP_1_1; int status_code=0; string reason_phrase; } + Reply::HttpResponse proxy_response; + + try { + DataReader::ReadConfig cfg; //one element struct + cfg.msReadTimeout = properties->recvTimeout;//1000*21 + + //make_unique(connection, ctx, cfg); + unique_ptr io_reader = DataReader::CreateIoReader(connection, ctx, cfg); + + //get from reply->StartReceiveFromServer(io_reader); + auto timer = IoTimer::Create("ReceivingFromProxy"s, + properties->replyTimeoutMs,//1000*21 + connection); + + auto stream = make_unique(move(io_reader)); + + //sets status_code, reason_phrase in response + stream->ReadServerResponse(proxy_response); + + connection.reset(); + + } catch (const exception& ex) { + RESTC_CPP_LOG_DEBUG_("DoProxyConnect: exception from ReceivingFromProxy: " << ex.what()); + throw; + } + + int status_code = proxy_response.status_code; + RESTC_CPP_LOG_DEBUG_("DoProxyConnect: Returned from ReceivingFromProxy. code=" << status_code); + + //check for response code 200 + RESTC_CPP_LOG_TRACE_("DoProxyConnect: validating proxy reply"); + constexpr auto magic_2 = 2; + constexpr auto magic_100 = 100; + constexpr auto http_401 = 401; + constexpr auto http_403 = 403; + constexpr auto http_404 = 404; + constexpr auto http_405 = 405; + constexpr auto http_406 = 406; + constexpr auto http_407 = 407; + constexpr auto http_408 = 408; + + if ((status_code / magic_100) > magic_2) switch(status_code) { + case http_401: + throw HttpAuthenticationException(proxy_response); + case http_403: + throw HttpForbiddenException(proxy_response); + case http_404: + throw HttpNotFoundException(proxy_response); + case http_405: + throw HttpMethodNotAllowedException(proxy_response); + case http_406: + throw HttpNotAcceptableException(proxy_response); + case http_407: + throw HttpProxyAuthenticationRequiredException(proxy_response); + case http_408: + throw HttpRequestTimeOutException(proxy_response); + default: + throw RequestFailedWithErrorException(proxy_response); + } + } + RESTC_CPP_LOG_TRACE_("DoProxyConnect - done"); +} + } // anonumous ns class RequestImpl : public Request { @@ -407,13 +496,13 @@ class RequestImpl : public Request { try { return DoExecute((ctx)); } catch(const RedirectException& ex) { - + auto url = ex.GetUrl(); - + if (properties_->redirectFn) { properties_->redirectFn(ex.GetCode(), url, ex.GetRedirectReply()); } - + if ((properties_->maxRedirects >= 0) && (++redirects > properties_->maxRedirects)) { throw ConstraintException("Too many redirects."); @@ -527,7 +616,8 @@ class RequestImpl : public Request { return request_buffer.str(); } - boost::asio::ip::tcp::resolver::query GetRequestEndpoint() { + //returns {protocol_type, host, service} instead of deprecated ip::resolver::query + tuple GetRequestEndpoint() { const auto proxy_type = properties_->proxy.type; if (proxy_type == Request::Proxy::Type::SOCKS5) { @@ -539,22 +629,36 @@ class RequestImpl : public Request { << " Proxy at: " << host << ':' << port); - return {host, to_string(port)}; + // what connection type should we use for SOCKS tunnel? + return { (parsed_url_.GetProtocol() == Url::Protocol::HTTPS) + ? Connection::Type::HTTPS + : Connection::Type::HTTP, + host, to_string(port) }; } - if (proxy_type == Request::Proxy::Type::HTTP) { + if ( (proxy_type == Request::Proxy::Type::HTTP && + parsed_url_.GetProtocol() == Url::Protocol::HTTP) || + (proxy_type == Request::Proxy::Type::HTTPS && + parsed_url_.GetProtocol() == Url::Protocol::HTTPS) ) { Url proxy {properties_->proxy.address.c_str()}; RESTC_CPP_LOG_TRACE_("Using " << properties_->proxy.GetName() + << ((proxy_type == Request::Proxy::Type::HTTPS) ? "(CONNECT)":"") << " Proxy at: " << proxy.GetHost() << ':' << proxy.GetPort()); - return { proxy.GetHost().to_string(), - proxy.GetPort().to_string()}; + return { (proxy.GetProtocol() == Url::Protocol::HTTPS) + ? Connection::Type::HTTPS + : Connection::Type::HTTP, + proxy.GetHost().to_string(), + proxy.GetPort().to_string() }; } - return { parsed_url_.GetHost().to_string(), - parsed_url_.GetPort().to_string()}; + return { (parsed_url_.GetProtocol() == Url::Protocol::HTTPS) + ? Connection::Type::HTTPS + : Connection::Type::HTTP, + parsed_url_.GetHost().to_string(), + parsed_url_.GetPort().to_string() }; } /* If we are redirected, we need to reset the body @@ -604,10 +708,9 @@ class RequestImpl : public Request { return {protocol, static_cast(port_num)}; } - boost::asio::ip::tcp::resolver::query q{host, port}; boost::asio::ip::tcp::resolver resolver(owner_.GetIoService()); - auto ep = resolver.async_resolve(q, ctx.GetYield()); + auto ep = resolver.async_resolve(host, port, ctx.GetYield()); const decltype(ep) addr_end; for(; ep != addr_end; ++ep) if (ep != addr_end) { @@ -655,19 +758,15 @@ class RequestImpl : public Request { auto prot_filter = GetBindProtocols(properties_->bindToLocalAddress, ctx); - const Connection::Type protocol_type = - (parsed_url_.GetProtocol() == Url::Protocol::HTTPS) - ? Connection::Type::HTTPS - : Connection::Type::HTTP; - boost::asio::ip::tcp::resolver resolver(owner_.GetIoService()); // Resolve the hostname - const auto query = GetRequestEndpoint(); + const auto ep_tuple = GetRequestEndpoint(); //{protocol_type, host, service=port} - RESTC_CPP_LOG_TRACE_("Resolving " << query.host_name() << ":" - << query.service_name()); + RESTC_CPP_LOG_TRACE_("Resolving " << get<1>(ep_tuple) << ":" + << get<2>(ep_tuple)); - auto address_it = resolver.async_resolve(query, + auto address_it = resolver.async_resolve(/*host*/ get<1>(ep_tuple), + /*port*/ get<2>(ep_tuple), ctx.GetYield()); const decltype(address_it) addr_end; @@ -684,7 +783,7 @@ class RequestImpl : public Request { for(size_t retries = 0; retries < 8; ++retries) { // Get a connection from the pool auto connection = owner_.GetConnectionPool()->GetConnection( - endpoint, protocol_type); + endpoint, /*protocol_type*/ get<0>(ep_tuple)); // Connect if the connection is new. if (connection->GetSocket().IsOpen()) { @@ -746,10 +845,18 @@ class RequestImpl : public Request { connection->GetSocket().SetAfterConnectCallback([&]() { RESTC_CPP_LOG_TRACE_("RequestImpl::Connect: In Socks5 callback"); - DoSocks5Handshake(*connection, parsed_url_, *properties_, ctx); + DoSocks5Handshake(connection, parsed_url_, properties_, ctx); RESTC_CPP_LOG_TRACE_("RequestImpl::Connect: Leaving Socks5 callback"); }); + } else if (properties_->proxy.type == Proxy::Type::HTTPS) { + connection->GetSocket().SetAfterConnectCallback([&]() { + RESTC_CPP_LOG_TRACE_("RequestImpl::Connect: In Https(connect)-proxy callback"); + + DoProxyConnect(connection, parsed_url_, properties_, ctx); + + RESTC_CPP_LOG_TRACE_("RequestImpl::Connect: Leaving Https(connect)-proxy callback"); + }); } RESTC_CPP_LOG_TRACE_("RequestImpl::Connect: calling AsyncConnect --> " << endpoint); From 5f34973f539cc8e9892bb3cfd2ffc0009f0cf661 Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Fri, 25 Aug 2023 15:20:50 +0500 Subject: [PATCH 03/15] Fix connection passing --- src/RequestImpl.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/RequestImpl.cpp b/src/RequestImpl.cpp index 1ff4d96..d6bb611 100644 --- a/src/RequestImpl.cpp +++ b/src/RequestImpl.cpp @@ -223,7 +223,7 @@ size_t ValidateCompleteSocks5ConnectReply(uint8_t *buf, size_t len) { return hdr_len; } -void DoSocks5Handshake(Connection::ptr_t connection, +void DoSocks5Handshake(const Connection::ptr_t& connection, const Url& url, const Request::Properties::ptr_t& properties, Context& ctx) { @@ -287,7 +287,7 @@ void DoSocks5Handshake(Connection::ptr_t connection, } -void DoProxyConnect(Connection::ptr_t connection, +void DoProxyConnect(const Connection::ptr_t& connection, const Url& url, const Request::Properties::ptr_t& properties, Context& ctx) { @@ -328,8 +328,6 @@ void DoProxyConnect(Connection::ptr_t connection, //sets status_code, reason_phrase in response stream->ReadServerResponse(proxy_response); - connection.reset(); - } catch (const exception& ex) { RESTC_CPP_LOG_DEBUG_("DoProxyConnect: exception from ReceivingFromProxy: " << ex.what()); throw; From 048340917a32a046f47a7162b64d1f776a360500 Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Tue, 29 Aug 2023 14:07:25 +0500 Subject: [PATCH 04/15] Implement sending plain data over tls socket before handshake. --- include/restc-cpp/url_encode.h | 4 ---- src/ConnectionPoolImpl.cpp | 4 +++- src/RequestImpl.cpp | 42 +++++++++++++++++----------------- src/SocketImpl.h | 2 +- src/TlsSocketImpl.h | 21 ++++++++++++++--- 5 files changed, 43 insertions(+), 30 deletions(-) diff --git a/include/restc-cpp/url_encode.h b/include/restc-cpp/url_encode.h index 6e93e62..d4f8537 100644 --- a/include/restc-cpp/url_encode.h +++ b/include/restc-cpp/url_encode.h @@ -1,8 +1,5 @@ #pragma once -#ifndef RESTC_CPP_URL_ENCODE_H_ -#define RESTC_CPP_URL_ENCODE_H_ - #include "restc-cpp.h" #include @@ -13,4 +10,3 @@ std::string url_encode(const boost::string_view& src); } // namespace -#endif // RESTC_CPP_URL_ENCODE_H_ diff --git a/src/ConnectionPoolImpl.cpp b/src/ConnectionPoolImpl.cpp index c2627be..c803d5c 100644 --- a/src/ConnectionPoolImpl.cpp +++ b/src/ConnectionPoolImpl.cpp @@ -365,7 +365,9 @@ class ConnectionPoolImpl } else { #ifdef RESTC_CPP_WITH_TLS - socket = make_unique(owner_.GetIoService(), owner_.GetTLSContext()); + socket = make_unique(owner_.GetIoService(), owner_.GetTLSContext(), + /*can_send_over_unupgraded_socket to send plain data over tls socket before handshake*/ + properties_->proxy.type == Request::Proxy::Type::HTTPS); #else throw NotImplementedException( "restc_cpp is compiled without TLS support"); diff --git a/src/RequestImpl.cpp b/src/RequestImpl.cpp index d6bb611..64b37ee 100644 --- a/src/RequestImpl.cpp +++ b/src/RequestImpl.cpp @@ -327,6 +327,10 @@ void DoProxyConnect(const Connection::ptr_t& connection, //sets status_code, reason_phrase in response stream->ReadServerResponse(proxy_response); + stream->ReadHeaderLines( + [](std::string&& name, std::string&& value) { + RESTC_CPP_LOG_TRACE_("Read proxy header: " << name); + }); } catch (const exception& ex) { RESTC_CPP_LOG_DEBUG_("DoProxyConnect: exception from ReceivingFromProxy: " << ex.what()); @@ -614,8 +618,8 @@ class RequestImpl : public Request { return request_buffer.str(); } - //returns {protocol_type, host, service} instead of deprecated ip::resolver::query - tuple GetRequestEndpoint() { + //returns {host, service} instead of deprecated ip::resolver::query + tuple GetRequestEndpoint() { const auto proxy_type = properties_->proxy.type; if (proxy_type == Request::Proxy::Type::SOCKS5) { @@ -627,11 +631,7 @@ class RequestImpl : public Request { << " Proxy at: " << host << ':' << port); - // what connection type should we use for SOCKS tunnel? - return { (parsed_url_.GetProtocol() == Url::Protocol::HTTPS) - ? Connection::Type::HTTPS - : Connection::Type::HTTP, - host, to_string(port) }; + return { host, to_string(port) }; } if ( (proxy_type == Request::Proxy::Type::HTTP && @@ -645,17 +645,11 @@ class RequestImpl : public Request { << " Proxy at: " << proxy.GetHost() << ':' << proxy.GetPort()); - return { (proxy.GetProtocol() == Url::Protocol::HTTPS) - ? Connection::Type::HTTPS - : Connection::Type::HTTP, - proxy.GetHost().to_string(), + return { proxy.GetHost().to_string(), proxy.GetPort().to_string() }; } - return { (parsed_url_.GetProtocol() == Url::Protocol::HTTPS) - ? Connection::Type::HTTPS - : Connection::Type::HTTP, - parsed_url_.GetHost().to_string(), + return { parsed_url_.GetHost().to_string(), parsed_url_.GetPort().to_string() }; } @@ -756,15 +750,21 @@ class RequestImpl : public Request { auto prot_filter = GetBindProtocols(properties_->bindToLocalAddress, ctx); + const Connection::Type protocol_type = + (parsed_url_.GetProtocol() == Url::Protocol::HTTPS || + properties_->proxy.type == Request::Proxy::Type::HTTPS) + ? Connection::Type::HTTPS + : Connection::Type::HTTP; + boost::asio::ip::tcp::resolver resolver(owner_.GetIoService()); // Resolve the hostname - const auto ep_tuple = GetRequestEndpoint(); //{protocol_type, host, service=port} + const auto ep_tuple = GetRequestEndpoint(); //{host, service=port} - RESTC_CPP_LOG_TRACE_("Resolving " << get<1>(ep_tuple) << ":" - << get<2>(ep_tuple)); + RESTC_CPP_LOG_TRACE_("Resolving " << get<0>(ep_tuple) << ":" + << get<1>(ep_tuple)); - auto address_it = resolver.async_resolve(/*host*/ get<1>(ep_tuple), - /*port*/ get<2>(ep_tuple), + auto address_it = resolver.async_resolve(/*host*/ get<0>(ep_tuple), + /*port*/ get<1>(ep_tuple), ctx.GetYield()); const decltype(address_it) addr_end; @@ -781,7 +781,7 @@ class RequestImpl : public Request { for(size_t retries = 0; retries < 8; ++retries) { // Get a connection from the pool auto connection = owner_.GetConnectionPool()->GetConnection( - endpoint, /*protocol_type*/ get<0>(ep_tuple)); + endpoint, protocol_type); // Connect if the connection is new. if (connection->GetSocket().IsOpen()) { diff --git a/src/SocketImpl.h b/src/SocketImpl.h index 747debf..f5ea3a0 100644 --- a/src/SocketImpl.h +++ b/src/SocketImpl.h @@ -56,7 +56,7 @@ class SocketImpl : public Socket, protected ExceptionWrapper { } void AsyncConnect(const boost::asio::ip::tcp::endpoint& ep, - const std::string &host, + const std::string &host, bool tcpNodelay, boost::asio::yield_context& yield) override { return WrapException([&] { diff --git a/src/TlsSocketImpl.h b/src/TlsSocketImpl.h index a7033f5..6463f69 100644 --- a/src/TlsSocketImpl.h +++ b/src/TlsSocketImpl.h @@ -24,9 +24,12 @@ class TlsSocketImpl : public Socket, protected ExceptionWrapper { using ssl_socket_t = boost::asio::ssl::stream; - TlsSocketImpl(boost::asio::io_service& io_service, shared_ptr ctx) + TlsSocketImpl(boost::asio::io_service& io_service, + shared_ptr ctx, + bool can_send_over_unupgraded_socket = false) { ssl_socket_ = std::make_unique(io_service, *ctx); + can_send_over_unupgraded_socket_ = can_send_over_unupgraded_socket; } boost::asio::ip::tcp::socket& GetSocket() override { @@ -42,6 +45,8 @@ class TlsSocketImpl : public Socket, protected ExceptionWrapper { std::size_t AsyncReadSome(boost::asio::mutable_buffers_1 buffers, boost::asio::yield_context& yield) override { return WrapException([&] { + if (can_send_over_unupgraded_socket_) + return ssl_socket_->next_layer().async_read_some(buffers, yield); return ssl_socket_->async_read_some(buffers, yield); }); } @@ -49,19 +54,27 @@ class TlsSocketImpl : public Socket, protected ExceptionWrapper { std::size_t AsyncRead(boost::asio::mutable_buffers_1 buffers, boost::asio::yield_context& yield) override { return WrapException([&] { + if (can_send_over_unupgraded_socket_) + return boost::asio::async_read(ssl_socket_->next_layer(), buffers, yield); return boost::asio::async_read(*ssl_socket_, buffers, yield); }); } void AsyncWrite(const boost::asio::const_buffers_1& buffers, boost::asio::yield_context& yield) override { - boost::asio::async_write(*ssl_socket_, buffers, yield); + if (can_send_over_unupgraded_socket_) + boost::asio::async_write(ssl_socket_->next_layer(), buffers, yield); + else + boost::asio::async_write(*ssl_socket_, buffers, yield); } void AsyncWrite(const write_buffers_t& buffers, boost::asio::yield_context& yield) override { return WrapException([&] { - boost::asio::async_write(*ssl_socket_, buffers, yield); + if (can_send_over_unupgraded_socket_) + boost::asio::async_write(ssl_socket_->next_layer(), buffers, yield); + else + boost::asio::async_write(*ssl_socket_, buffers, yield); }); } @@ -90,6 +103,7 @@ class TlsSocketImpl : public Socket, protected ExceptionWrapper { RESTC_CPP_LOG_TRACE_("AsyncConnect - Calling async_handshake"); ssl_socket_->async_handshake(boost::asio::ssl::stream_base::client, yield); + can_send_over_unupgraded_socket_ = false; RESTC_CPP_LOG_TRACE_("AsyncConnect - Done"); }); @@ -135,6 +149,7 @@ class TlsSocketImpl : public Socket, protected ExceptionWrapper { private: std::unique_ptr ssl_socket_; + bool can_send_over_unupgraded_socket_; }; } // restc_cpp From bdda9e07050ee4b4b39b1b3f28fe00749bd17a02 Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Tue, 29 Aug 2023 14:19:49 +0500 Subject: [PATCH 05/15] Documentation update --- README.md | 4 ++-- doc/Tutorial.md | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 956a673..24754f7 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,7 @@ Please refer to the [tutorial](doc/Tutorial.md) for more examples. - Write directly to the outgoing DataWriter when data is required. - Just provide a C++ object and let the library serialize it directly to the wire. - HTTP Proxy support -- SOCKS5 Proxy support (naive implementatin for now, no support for authentication). +- HTTPS Proxy (using CONNECT method), SOCKS5 Proxy support (naive implementatin for now, no support for authentication). # Current Status The project has been in public BETA since April 11th 2017. @@ -262,7 +262,7 @@ These are the operating systems where my Continues Integration (Jenkins) servers - Ubuntu Xenial (LTS) - Ubuntu Bionic (LTS) -Support for MacOS has been removed after Apples announcement that their love for privacy was just +Support for MacOS has been removed after Apples announcement that their love for privacy was just a marketing gimmick. Fedora is currently disabled in my CI because of failures to start their Docker containers. (Work in progress). Ubuntu Jammy don't work in docker with my Jenkins CI pipeline, so I have no reliable way to test it. Windows 11 cannot be run on my KVM /QEMU system, because it don't support "secure" boot, so I have no way to test it. diff --git a/doc/Tutorial.md b/doc/Tutorial.md index affbb85..dfd2cf1 100644 --- a/doc/Tutorial.md +++ b/doc/Tutorial.md @@ -288,10 +288,12 @@ use the *RequestBuilder*'s *Argument()* method for this. ``` -## Send a request going trough a HTTP Proxy +## Send a request going trough a HTTP or HTTPS Proxy ```C++ // Add the proxy information to the properties used by the client Request::Properties properties; + // Use proxy type Request::Proxy::Type::HTTP for simple HTTP proxing, + // Request::Proxy::Type::HTTPS for HTTPS proxing over CONNECT method properties.proxy.type = Request::Proxy::Type::HTTP; properties.proxy.address = "http://127.0.0.1:3003"; @@ -313,7 +315,7 @@ use the *RequestBuilder*'s *Argument()* method for this. }).get(); ``` -## Use an existing thread in stead of a new worker thread +## Use an existing thread instead of a new worker thread This example is slightly more advanced. Here we take responsibility to run the io-service used internally by From 21217ab3716940629650c3a1eb9e000521fd1ef2 Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Tue, 29 Aug 2023 15:31:13 +0500 Subject: [PATCH 06/15] Fix SNI host for tls protocol over proxy --- src/RequestImpl.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/RequestImpl.cpp b/src/RequestImpl.cpp index 64b37ee..58dd797 100644 --- a/src/RequestImpl.cpp +++ b/src/RequestImpl.cpp @@ -857,9 +857,13 @@ class RequestImpl : public Request { }); } + const string sni_host = (protocol_type == Connection::Type::HTTPS) + ? parsed_url_.GetHost().to_string() + : address_it->host_name(); + RESTC_CPP_LOG_TRACE_("RequestImpl::Connect: calling AsyncConnect --> " << endpoint); connection->GetSocket().AsyncConnect( - endpoint, address_it->host_name(), + endpoint, sni_host, properties_->tcpNodelay, ctx.GetYield()); RESTC_CPP_LOG_TRACE_("RequestImpl::Connect: OK AsyncConnect --> " << endpoint); return connection; @@ -869,7 +873,7 @@ class RequestImpl : public Request { connection->GetSocket().GetSocket().close(); if (ex.code() == boost::system::errc::resource_unavailable_try_again) { - if ( retries < 8) { + if ( retries < 8 ) { RESTC_CPP_LOG_DEBUG_( "RequestImpl::Connect:: Caught boost::system::system_error exception: \"" << ex.what() From 6efc0e1e818df8ac7177ed33097636e68ca77b94 Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Wed, 30 Aug 2023 10:47:01 +0500 Subject: [PATCH 07/15] Optimize code --- src/ConnectionPoolImpl.cpp | 2 +- src/RequestImpl.cpp | 178 ++++++++++++++++--------------------- 2 files changed, 77 insertions(+), 103 deletions(-) diff --git a/src/ConnectionPoolImpl.cpp b/src/ConnectionPoolImpl.cpp index c803d5c..39998ed 100644 --- a/src/ConnectionPoolImpl.cpp +++ b/src/ConnectionPoolImpl.cpp @@ -240,7 +240,7 @@ class ConnectionPoolImpl idle_.erase(current); } else { RESTC_CPP_LOG_TRACE_("Keeping << " << *current->second->GetConnection() - << " expieres in " + << " expires in " << std::chrono::duration_cast(expires - now).count() << " seconds "); } diff --git a/src/RequestImpl.cpp b/src/RequestImpl.cpp index 58dd797..ea3a75f 100644 --- a/src/RequestImpl.cpp +++ b/src/RequestImpl.cpp @@ -287,6 +287,39 @@ void DoSocks5Handshake(const Connection::ptr_t& connection, } +void _throwHttpExceptionWhenNot200(const Reply::HttpResponse& response) { + // Silence the cursed clang tidy! + constexpr auto magic_2 = 2; + constexpr auto magic_100 = 100; + constexpr auto http_401 = 401; + constexpr auto http_403 = 403; + constexpr auto http_404 = 404; + constexpr auto http_405 = 405; + constexpr auto http_406 = 406; + constexpr auto http_407 = 407; + constexpr auto http_408 = 408; + + if ((response.status_code / magic_100) > magic_2) switch(response.status_code) { + case http_401: + throw HttpAuthenticationException(response); + case http_403: + throw HttpForbiddenException(response); + case http_404: + throw HttpNotFoundException(response); + case http_405: + throw HttpMethodNotAllowedException(response); + case http_406: + throw HttpNotAcceptableException(response); + case http_407: + throw HttpProxyAuthenticationRequiredException(response); + case http_408: + throw HttpRequestTimeOutException(response); + default: + throw RequestFailedWithErrorException(response); + } +} + + void DoProxyConnect(const Connection::ptr_t& connection, const Url& url, const Request::Properties::ptr_t& properties, @@ -296,81 +329,51 @@ void DoProxyConnect(const Connection::ptr_t& connection, static const string crlf{"\r\n"}; auto& sck = connection->GetSocket(); - { - const string host(url.GetHost().to_string() + ":" + to_string(url.GetPort())); - ostringstream request_buffer; - request_buffer << "CONNECT "; - request_buffer << host; - request_buffer << " HTTP/1.1" << crlf; - request_buffer << "Host: " << host << crlf << crlf; - RESTC_CPP_LOG_DEBUG_("DoProxyConnect - send CONNECT " << host << " HTTP/1.1"); - sck.AsyncWriteT(request_buffer.str(), ctx.GetYield()); + const string host(url.GetHost().to_string() + ":" + to_string(url.GetPort())); + ostringstream request_buffer; + request_buffer << "CONNECT "; + request_buffer << host; + request_buffer << " HTTP/1.1" << crlf; + request_buffer << "Host: " << host << crlf << crlf; + RESTC_CPP_LOG_DEBUG_("DoProxyConnect - send CONNECT " << host << " HTTP/1.1"); + sck.AsyncWriteT(request_buffer.str(), ctx.GetYield()); + + RESTC_CPP_LOG_TRACE_("DoProxyConnect: starting receiving from proxy"); + //struct { http_version=HTTP_1_1; int status_code=0; string reason_phrase; } + Reply::HttpResponse proxy_response; + + try { + DataReader::ReadConfig cfg; //one element struct + cfg.msReadTimeout = properties->recvTimeout;//1000*21 + + //make_unique(connection, ctx, cfg); + unique_ptr io_reader = DataReader::CreateIoReader(connection, ctx, cfg); + + //get from reply->StartReceiveFromServer(io_reader); + auto timer = IoTimer::Create("ReceivingFromProxy"s, + properties->replyTimeoutMs,//1000*21 + connection); + + auto stream = make_unique(move(io_reader)); + + //sets status_code, reason_phrase in response + stream->ReadServerResponse(proxy_response); + stream->ReadHeaderLines( + [](std::string&& name, std::string&& value) { + RESTC_CPP_LOG_TRACE_("Read proxy header: " << name); + }); + + } catch (const exception& ex) { + RESTC_CPP_LOG_DEBUG_("DoProxyConnect: exception from ReceivingFromProxy: " << ex.what()); + throw; } - { - RESTC_CPP_LOG_TRACE_("DoProxyConnect: starting receiving from proxy"); - //struct { http_version=HTTP_1_1; int status_code=0; string reason_phrase; } - Reply::HttpResponse proxy_response; - - try { - DataReader::ReadConfig cfg; //one element struct - cfg.msReadTimeout = properties->recvTimeout;//1000*21 - //make_unique(connection, ctx, cfg); - unique_ptr io_reader = DataReader::CreateIoReader(connection, ctx, cfg); + RESTC_CPP_LOG_DEBUG_("DoProxyConnect: Returned from ReceivingFromProxy. code=" << proxy_response.status_code); - //get from reply->StartReceiveFromServer(io_reader); - auto timer = IoTimer::Create("ReceivingFromProxy"s, - properties->replyTimeoutMs,//1000*21 - connection); + //check for response code 200 + RESTC_CPP_LOG_TRACE_("DoProxyConnect: validating proxy reply"); + _throwHttpExceptionWhenNot200(proxy_response); - auto stream = make_unique(move(io_reader)); - - //sets status_code, reason_phrase in response - stream->ReadServerResponse(proxy_response); - stream->ReadHeaderLines( - [](std::string&& name, std::string&& value) { - RESTC_CPP_LOG_TRACE_("Read proxy header: " << name); - }); - - } catch (const exception& ex) { - RESTC_CPP_LOG_DEBUG_("DoProxyConnect: exception from ReceivingFromProxy: " << ex.what()); - throw; - } - - int status_code = proxy_response.status_code; - RESTC_CPP_LOG_DEBUG_("DoProxyConnect: Returned from ReceivingFromProxy. code=" << status_code); - - //check for response code 200 - RESTC_CPP_LOG_TRACE_("DoProxyConnect: validating proxy reply"); - constexpr auto magic_2 = 2; - constexpr auto magic_100 = 100; - constexpr auto http_401 = 401; - constexpr auto http_403 = 403; - constexpr auto http_404 = 404; - constexpr auto http_405 = 405; - constexpr auto http_406 = 406; - constexpr auto http_407 = 407; - constexpr auto http_408 = 408; - - if ((status_code / magic_100) > magic_2) switch(status_code) { - case http_401: - throw HttpAuthenticationException(proxy_response); - case http_403: - throw HttpForbiddenException(proxy_response); - case http_404: - throw HttpNotFoundException(proxy_response); - case http_405: - throw HttpMethodNotAllowedException(proxy_response); - case http_406: - throw HttpNotAcceptableException(proxy_response); - case http_407: - throw HttpProxyAuthenticationRequiredException(proxy_response); - case http_408: - throw HttpRequestTimeOutException(proxy_response); - default: - throw RequestFailedWithErrorException(proxy_response); - } - } RESTC_CPP_LOG_TRACE_("DoProxyConnect - done"); } @@ -526,36 +529,7 @@ class RequestImpl : public Request { private: void ValidateReply(const Reply& reply) { - // Silence the cursed clang tidy! - constexpr auto magic_2 = 2; - constexpr auto magic_100 = 100; - constexpr auto http_401 = 401; - constexpr auto http_403 = 403; - constexpr auto http_404 = 404; - constexpr auto http_405 = 405; - constexpr auto http_406 = 406; - constexpr auto http_407 = 407; - constexpr auto http_408 = 408; - - const auto& response = reply.GetHttpResponse(); - if ((response.status_code / magic_100) > magic_2) switch(response.status_code) { - case http_401: - throw HttpAuthenticationException(response); - case http_403: - throw HttpForbiddenException(response); - case http_404: - throw HttpNotFoundException(response); - case http_405: - throw HttpMethodNotAllowedException(response); - case http_406: - throw HttpNotAcceptableException(response); - case http_407: - throw HttpProxyAuthenticationRequiredException(response); - case http_408: - throw HttpRequestTimeOutException(response); - default: - throw RequestFailedWithErrorException(response); - } + _throwHttpExceptionWhenNot200(reply.GetHttpResponse()); } std::string BuildOutgoingRequest() { @@ -1086,7 +1060,7 @@ class RequestImpl : public Request { std::uint64_t bytes_sent_ = 0; bool dirty_ = false; bool add_url_args_ = true; -}; +}; //class RequestImpl std::unique_ptr From 1e57b6171990cac93dad2e19042303692564732f Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Wed, 30 Aug 2023 11:05:51 +0500 Subject: [PATCH 08/15] string_view reverted to string_ref (req for boost 1.58) --- include/restc-cpp/Url.h | 22 +++++++++++----------- include/restc-cpp/url_encode.h | 4 ++-- src/Url.cpp | 14 +++++++------- src/url_encode.cpp | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/restc-cpp/Url.h b/include/restc-cpp/Url.h index dd2b003..7a814cd 100644 --- a/include/restc-cpp/Url.h +++ b/include/restc-cpp/Url.h @@ -3,7 +3,7 @@ #ifndef RESTC_CPP_URL_H_ #define RESTC_CPP_URL_H_ -#include +#include namespace restc_cpp { @@ -20,19 +20,19 @@ namespace restc_cpp { Url& operator = (const char *url); - boost::string_view GetProtocolName() const { return protocol_name_; } - boost::string_view GetHost() const { return host_; } - boost::string_view GetPort() const { return port_; } - boost::string_view GetPath() const { return path_; } - boost::string_view GetArgs() const { return args_; } + boost::string_ref GetProtocolName() const { return protocol_name_; } + boost::string_ref GetHost() const { return host_; } + boost::string_ref GetPort() const { return port_; } + boost::string_ref GetPath() const { return path_; } + boost::string_ref GetArgs() const { return args_; } Protocol GetProtocol() const { return protocol_; } private: - boost::string_view protocol_name_; - boost::string_view host_; - boost::string_view port_; - boost::string_view path_ = "/"; - boost::string_view args_; + boost::string_ref protocol_name_; + boost::string_ref host_; + boost::string_ref port_; + boost::string_ref path_ = "/"; + boost::string_ref args_; Protocol protocol_ = Protocol::UNKNOWN; }; diff --git a/include/restc-cpp/url_encode.h b/include/restc-cpp/url_encode.h index d4f8537..d4415ef 100644 --- a/include/restc-cpp/url_encode.h +++ b/include/restc-cpp/url_encode.h @@ -2,11 +2,11 @@ #include "restc-cpp.h" -#include +#include namespace restc_cpp { -std::string url_encode(const boost::string_view& src); +std::string url_encode(const boost::string_ref& src); } // namespace diff --git a/src/Url.cpp b/src/Url.cpp index 448938c..4064ee8 100644 --- a/src/Url.cpp +++ b/src/Url.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include "restc-cpp/restc-cpp.h" #include "restc-cpp/Url.h" #include "restc-cpp/error.h" @@ -28,18 +28,18 @@ Url& Url::operator = (const char *url) { constexpr auto magic_7 = 7; assert(url != nullptr && "A valid URL is required"); - protocol_name_ = boost::string_view(url); + protocol_name_ = boost::string_ref(url); if (protocol_name_.find("https://") == 0) { - protocol_name_ = boost::string_view(url, magic_8); + protocol_name_ = boost::string_ref(url, magic_8); protocol_ = Protocol::HTTPS; } else if (protocol_name_.find("http://") == 0) { - protocol_name_ = boost::string_view(url, magic_7); + protocol_name_ = boost::string_ref(url, magic_7); protocol_ = Protocol::HTTP; } else { throw ParseException("Invalid protocol in url. Must be 'http[s]://'"); } - auto remains = boost::string_view(protocol_name_.end()); + auto remains = boost::string_ref(protocol_name_.end()); const auto args_start = remains.find('?'); if (args_start != string::npos) { args_ = {remains.begin() + args_start + 1, @@ -55,8 +55,8 @@ Url& Url::operator = (const char *url) { if (remains.length() <= static_cast(port_start + 2)) { throw ParseException("Invalid host (no port after column)"); } - //port_ = boost::string_view(&remains[port_start+1]); - //host_ = boost::string_view(host_.data(), port_start); + //port_ = boost::string_ref(&remains[port_start+1]); + //host_ = boost::string_ref(host_.data(), port_start); host_ = {remains.begin(), port_start}; remains = {remains.begin() + port_start + 1, remains.size() - (port_start + 1)}; diff --git a/src/url_encode.cpp b/src/url_encode.cpp index 5a34c71..479a1de 100644 --- a/src/url_encode.cpp +++ b/src/url_encode.cpp @@ -35,7 +35,7 @@ allchars_t get_normal_ch() { } // anonymous namespace -std::string url_encode(const boost::string_view& src) { +std::string url_encode(const boost::string_ref& src) { constexpr auto magic_4 = 4; constexpr auto magic_0x0f = 0x0f; From 961f59e8ec1731ebb411047d8d0a5069e9eee6ff Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Wed, 30 Aug 2023 17:14:58 +0500 Subject: [PATCH 09/15] Added simple https proxy test --- ci/mock-backends/docker-compose.yml | 1 + ci/mock-backends/nginx/Dockerfile | 3 + ci/mock-backends/nginx/create_cert | 5 ++ ci/mock-backends/nginx/proxy-https.conf.bin | 62 +++++++++++++++++++++ ci/mock-backends/nginx/test-key.pem | 52 +++++++++++++++++ ci/mock-backends/nginx/test.pem | 32 +++++++++++ tests/functional/ProxyTests.cpp | 22 ++++++++ tests/mock-nginx/Dockerfile | 3 + tests/mock-nginx/create_cert | 5 ++ tests/mock-nginx/proxy-https.conf.bin | 62 +++++++++++++++++++++ tests/mock-nginx/test-key.pem | 52 +++++++++++++++++ tests/mock-nginx/test.pem | 32 +++++++++++ 12 files changed, 331 insertions(+) create mode 100755 ci/mock-backends/nginx/create_cert create mode 100644 ci/mock-backends/nginx/proxy-https.conf.bin create mode 100644 ci/mock-backends/nginx/test-key.pem create mode 100644 ci/mock-backends/nginx/test.pem create mode 100755 tests/mock-nginx/create_cert create mode 100644 tests/mock-nginx/proxy-https.conf.bin create mode 100644 tests/mock-nginx/test-key.pem create mode 100644 tests/mock-nginx/test.pem diff --git a/ci/mock-backends/docker-compose.yml b/ci/mock-backends/docker-compose.yml index 33b9acd..197e5c5 100644 --- a/ci/mock-backends/docker-compose.yml +++ b/ci/mock-backends/docker-compose.yml @@ -8,6 +8,7 @@ services: build: nginx ports: - "3001:80" + - "3002:443" links: - "json:api" squid: diff --git a/ci/mock-backends/nginx/Dockerfile b/ci/mock-backends/nginx/Dockerfile index 48cc9e7..a6e1c2c 100644 --- a/ci/mock-backends/nginx/Dockerfile +++ b/ci/mock-backends/nginx/Dockerfile @@ -1,7 +1,10 @@ FROM nginx RUN rm /etc/nginx/conf.d/default.conf COPY proxy.conf.bin /etc/nginx/conf.d/proxy.conf +COPY proxy-https.conf.bin /etc/nginx/conf.d/proxy-https.conf COPY htpasswd.bin /etc/nginx/htpasswd +COPY test.pem /etc/nginx/ssl/test.pem +COPY test-key.pem /etc/nginx/ssl/test-key.pem RUN mkdir -p /etc/nginx/html/upload RUN chmod 777 /etc/nginx/html/upload diff --git a/ci/mock-backends/nginx/create_cert b/ci/mock-backends/nginx/create_cert new file mode 100755 index 0000000..d383304 --- /dev/null +++ b/ci/mock-backends/nginx/create_cert @@ -0,0 +1,5 @@ +#!/bin/bash + +openssl req -x509 -newkey rsa:4096 -keyout test-key.pem -out test.pem -sha256 -days 3650 -nodes -subj "/C=XX/ST=Test/L=Test/OU=Test/O=Test/CN=localhost" + +#openssl x509 -in test.pem -text diff --git a/ci/mock-backends/nginx/proxy-https.conf.bin b/ci/mock-backends/nginx/proxy-https.conf.bin new file mode 100644 index 0000000..3092d25 --- /dev/null +++ b/ci/mock-backends/nginx/proxy-https.conf.bin @@ -0,0 +1,62 @@ +server { + listen 443 ssl; + server_name localhost; + gzip on; + gzip_proxied any; + ssl_certificate /etc/nginx/ssl/test.pem; + ssl_certificate_key /etc/nginx/ssl/test-key.pem; + ssl_protocols TLSv1.2 TLSv1.3; + + + location ~ ^/cookies(/?)(.*)$ { + add_header Content-Type "application/json; charset=utf-8"; + add_header Set-Cookie test1=yes; + add_header Set-Cookie test2=maybe; + add_header Set-Cookie test3=no; + return 200 '{}'; + } + + location ~ ^/normal(/?)(.*)$ { + proxy_pass http://json/$2$is_args$args; + } + + location ~ ^/close(/?)(.*)$ { + proxy_pass http://json/$2$is_args$args; + keepalive_timeout 0; + } + + # Force nginx to resolve 'json' + location /dns_workaround { + proxy_pass http://json; + } + + location ~ ^/loop(/?)(.*)$ { + return 301 $scheme://$host:3001/loop/$2$is_args$args; + } + + location ~ ^/redirect(/?)(.*)$ { + return 301 $scheme://$host:3001/normal/$2$is_args$args; + } + + location ~ ^/reredirect(/?)(.*)$ { + return 301 $scheme://$host:3001/redirect/$2$is_args$args; + } + + location ~ ^/restricted(/?)(.*)$ { + auth_basic "Restricted Area"; + auth_basic_user_file /etc/nginx/htpasswd; + proxy_pass http://json/$2$is_args$args; + } + + location ~ ^/upload_raw(/?)(.*)$ { + limit_except POST { deny all; } + return 200 "OK"; + } + + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} + diff --git a/ci/mock-backends/nginx/test-key.pem b/ci/mock-backends/nginx/test-key.pem new file mode 100644 index 0000000..8d01ea1 --- /dev/null +++ b/ci/mock-backends/nginx/test-key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +stBsKJU1QibpCwO3/NmBYwjRitw1E2c= +-----END PRIVATE KEY----- diff --git a/ci/mock-backends/nginx/test.pem b/ci/mock-backends/nginx/test.pem new file mode 100644 index 0000000..2fd997a --- /dev/null +++ b/ci/mock-backends/nginx/test.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFmzCCA4OgAwIBAgIUJFXivxxXEIY3rp6fz2wgE3rsQzYwDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCWFgxDTALBgNVBAgMBFRlc3QxDTALBgNVBAcMBFRlc3Qx +DTALBgNVBAsMBFRlc3QxDTALBgNVBAoMBFRlc3QxEjAQBgNVBAMMCWxvY2FsaG9z +dDAeFw0yMzA4MzAxMTM2MjJaFw0zMzA4MjcxMTM2MjJaMF0xCzAJBgNVBAYTAlhY +MQ0wCwYDVQQIDARUZXN0MQ0wCwYDVQQHDARUZXN0MQ0wCwYDVQQLDARUZXN0MQ0w +CwYDVQQKDARUZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDPlSJHl/g59nyiHvVblS5eWCTRxFFueCIEbYGfUdeL ++Jc+YP20fJs3u5Tq7gCvuYqGTMztXfERRk3HNrHy1uXndh0fGVUusn5hYz4XNB8A +mRfZf1wDTpd4SjVe26Da52sIGppj1bGgPJWR9KK+7zDPNeEVQ4HPYnh8rTfHBG0v +YlfHnHR4EGwS6znKA3YIgwHqHIrdm/cOmMK3hDMmjG8l01U5YHIBOadTh1QZvqhp +EToJXnV949CvN12HiyawSQ3nG3HTC94PnrV7UtOLGf3rh+OxN8D9d+QgBVSAI8d7 +VqT+7xxU8wvzL5gWcQLuhY208cZW3Y6gC4U6ZpfHxeqUtAUw4ZK4bJmKr6HFdasX +aPkKPsUZsppeiGYtXRwt6yuHJuI3I1JTwcuASYCHEhus5biMyRZWZo5PLbCX1sXY +bbAEt/qVpg1jeTwrkeZ62lC79lAKjd0runTENMvZYKZwnJntSUz6vq6uMYoM0BzS +MSioFu2fqWBFH9263Tje/wwz/aNNAKx2+3SNavC7//gSM/0gl/9bVRgsWLyxc5qx +CTU8uQ1P5zNx07YNmv6C8UI44craA7KdxcYgqgdUStKDzpDB48eOvvbRRAZO55KA +YzCS/1dgfFUcsugP7DklblI3kTNHTIjarYYXJjUNl0Tpcx3Tk6hfgBBMX1Re/Gtv +TwIDAQABo1MwUTAdBgNVHQ4EFgQUtH5zl0rC27e1JxtdMbIxwqkg0NQwHwYDVR0j +BBgwFoAUtH5zl0rC27e1JxtdMbIxwqkg0NQwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAgEAw7sQ1KIyHCYHJUzYdX9I/l0ylbzrKpfJUABq3OOmgjUM +k6InH6bXvAEP7bj4zC3UDKj1hf5WVHQM0yUeLx8ZmzHzsgfS6C8MiSt+47V0NboQ +5aGHY21ceMivlNDihf0cjPg+h4NsAZkzMWR771Nxe3WLT20Wo1ehKqCjIC7HnCJ3 +IAKbholWJwaiu1Y9tLn21sNMC0Z75hx/JWokP8OuZUyLxsp5RDLLvPA4N41z1Tep +CXpBct6jdRVzq7b5bUgPIQJ4XVdnaIYtdWIZVavulbr0vo4P7dayhf7ZWGcFVyPh +GmjHsPiurZy5BShEOgyYlwrSf9guDJfLR0gclKysKF1I4O9EWysRntvxuDMg0bys +jdUkp25EjQzIP/gA5J6NhTUFYnt04cLIebxs8CU/ZqZ0QZ11wT3VnvTm9ukTsaT4 +lKzmBjlejZVLwlki6r3IAj/CD2v9pJddt950usj9/I9C6quw1XpWG6TYZlCyM9lb +ucUfPttVknOM8c/eenXYAECR/f9MqlFBHO3Bj9YfO4hGTZtE7qaiY2f6yhcLRCOt +Bd649G/bfKT0KvLp+6SdTJxMYFC70FUqoqniEfxT+AhsKP+2k0MfU4MvJ/neLcGA +l/fM+TeIJWTzadl/DCZeE0FyQXAzeqHICnNQOieH6spMpjiXd6cv0aPO1zSqpV4= +-----END CERTIFICATE----- diff --git a/tests/functional/ProxyTests.cpp b/tests/functional/ProxyTests.cpp index cda65f9..77c4691 100644 --- a/tests/functional/ProxyTests.cpp +++ b/tests/functional/ProxyTests.cpp @@ -15,6 +15,7 @@ using namespace restc_cpp; static const string defunct_proxy_address = GetDockerUrl("http://localhost:0"); static const string http_proxy_address = GetDockerUrl("http://localhost:3003"); +static const string https_proxy_address = GetDockerUrl("http://localhost:3003"); static const string socks5_proxy_address = GetDockerUrl("localhost:3004"); TEST(Proxy, FailToConnect) @@ -57,6 +58,27 @@ TEST(Proxy, WithHttpProxy) EXPECT_NO_THROW(f.get()); } +TEST(Proxy, WithHttpsProxy) +{ + Request::Properties properties; + properties.proxy.type = Request::Proxy::Type::HTTPS; + properties.proxy.address = https_proxy_address; + + // Create the client with our configuration + auto rest_client = RestClient::Create(properties); + + auto f = rest_client->ProcessWithPromise([&](Context& ctx) { + auto reply = RequestBuilder(ctx) + .Get("https://api.example.com/normal/posts/1") + .Execute(); + + EXPECT_HTTP_OK(reply->GetResponseCode()); + cout << "Got: " << reply->GetBodyAsString() << endl; + }); + + EXPECT_NO_THROW(f.get()); +} + TEST(Proxy, WithSocks5Proxy) { Request::Properties properties; diff --git a/tests/mock-nginx/Dockerfile b/tests/mock-nginx/Dockerfile index 48cc9e7..a6e1c2c 100644 --- a/tests/mock-nginx/Dockerfile +++ b/tests/mock-nginx/Dockerfile @@ -1,7 +1,10 @@ FROM nginx RUN rm /etc/nginx/conf.d/default.conf COPY proxy.conf.bin /etc/nginx/conf.d/proxy.conf +COPY proxy-https.conf.bin /etc/nginx/conf.d/proxy-https.conf COPY htpasswd.bin /etc/nginx/htpasswd +COPY test.pem /etc/nginx/ssl/test.pem +COPY test-key.pem /etc/nginx/ssl/test-key.pem RUN mkdir -p /etc/nginx/html/upload RUN chmod 777 /etc/nginx/html/upload diff --git a/tests/mock-nginx/create_cert b/tests/mock-nginx/create_cert new file mode 100755 index 0000000..d383304 --- /dev/null +++ b/tests/mock-nginx/create_cert @@ -0,0 +1,5 @@ +#!/bin/bash + +openssl req -x509 -newkey rsa:4096 -keyout test-key.pem -out test.pem -sha256 -days 3650 -nodes -subj "/C=XX/ST=Test/L=Test/OU=Test/O=Test/CN=localhost" + +#openssl x509 -in test.pem -text diff --git a/tests/mock-nginx/proxy-https.conf.bin b/tests/mock-nginx/proxy-https.conf.bin new file mode 100644 index 0000000..3092d25 --- /dev/null +++ b/tests/mock-nginx/proxy-https.conf.bin @@ -0,0 +1,62 @@ +server { + listen 443 ssl; + server_name localhost; + gzip on; + gzip_proxied any; + ssl_certificate /etc/nginx/ssl/test.pem; + ssl_certificate_key /etc/nginx/ssl/test-key.pem; + ssl_protocols TLSv1.2 TLSv1.3; + + + location ~ ^/cookies(/?)(.*)$ { + add_header Content-Type "application/json; charset=utf-8"; + add_header Set-Cookie test1=yes; + add_header Set-Cookie test2=maybe; + add_header Set-Cookie test3=no; + return 200 '{}'; + } + + location ~ ^/normal(/?)(.*)$ { + proxy_pass http://json/$2$is_args$args; + } + + location ~ ^/close(/?)(.*)$ { + proxy_pass http://json/$2$is_args$args; + keepalive_timeout 0; + } + + # Force nginx to resolve 'json' + location /dns_workaround { + proxy_pass http://json; + } + + location ~ ^/loop(/?)(.*)$ { + return 301 $scheme://$host:3001/loop/$2$is_args$args; + } + + location ~ ^/redirect(/?)(.*)$ { + return 301 $scheme://$host:3001/normal/$2$is_args$args; + } + + location ~ ^/reredirect(/?)(.*)$ { + return 301 $scheme://$host:3001/redirect/$2$is_args$args; + } + + location ~ ^/restricted(/?)(.*)$ { + auth_basic "Restricted Area"; + auth_basic_user_file /etc/nginx/htpasswd; + proxy_pass http://json/$2$is_args$args; + } + + location ~ ^/upload_raw(/?)(.*)$ { + limit_except POST { deny all; } + return 200 "OK"; + } + + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} + diff --git a/tests/mock-nginx/test-key.pem b/tests/mock-nginx/test-key.pem new file mode 100644 index 0000000..8d01ea1 --- /dev/null +++ b/tests/mock-nginx/test-key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +stBsKJU1QibpCwO3/NmBYwjRitw1E2c= +-----END PRIVATE KEY----- diff --git a/tests/mock-nginx/test.pem b/tests/mock-nginx/test.pem new file mode 100644 index 0000000..2fd997a --- /dev/null +++ b/tests/mock-nginx/test.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFmzCCA4OgAwIBAgIUJFXivxxXEIY3rp6fz2wgE3rsQzYwDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCWFgxDTALBgNVBAgMBFRlc3QxDTALBgNVBAcMBFRlc3Qx +DTALBgNVBAsMBFRlc3QxDTALBgNVBAoMBFRlc3QxEjAQBgNVBAMMCWxvY2FsaG9z +dDAeFw0yMzA4MzAxMTM2MjJaFw0zMzA4MjcxMTM2MjJaMF0xCzAJBgNVBAYTAlhY +MQ0wCwYDVQQIDARUZXN0MQ0wCwYDVQQHDARUZXN0MQ0wCwYDVQQLDARUZXN0MQ0w +CwYDVQQKDARUZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDPlSJHl/g59nyiHvVblS5eWCTRxFFueCIEbYGfUdeL ++Jc+YP20fJs3u5Tq7gCvuYqGTMztXfERRk3HNrHy1uXndh0fGVUusn5hYz4XNB8A +mRfZf1wDTpd4SjVe26Da52sIGppj1bGgPJWR9KK+7zDPNeEVQ4HPYnh8rTfHBG0v +YlfHnHR4EGwS6znKA3YIgwHqHIrdm/cOmMK3hDMmjG8l01U5YHIBOadTh1QZvqhp +EToJXnV949CvN12HiyawSQ3nG3HTC94PnrV7UtOLGf3rh+OxN8D9d+QgBVSAI8d7 +VqT+7xxU8wvzL5gWcQLuhY208cZW3Y6gC4U6ZpfHxeqUtAUw4ZK4bJmKr6HFdasX +aPkKPsUZsppeiGYtXRwt6yuHJuI3I1JTwcuASYCHEhus5biMyRZWZo5PLbCX1sXY +bbAEt/qVpg1jeTwrkeZ62lC79lAKjd0runTENMvZYKZwnJntSUz6vq6uMYoM0BzS +MSioFu2fqWBFH9263Tje/wwz/aNNAKx2+3SNavC7//gSM/0gl/9bVRgsWLyxc5qx +CTU8uQ1P5zNx07YNmv6C8UI44craA7KdxcYgqgdUStKDzpDB48eOvvbRRAZO55KA +YzCS/1dgfFUcsugP7DklblI3kTNHTIjarYYXJjUNl0Tpcx3Tk6hfgBBMX1Re/Gtv +TwIDAQABo1MwUTAdBgNVHQ4EFgQUtH5zl0rC27e1JxtdMbIxwqkg0NQwHwYDVR0j +BBgwFoAUtH5zl0rC27e1JxtdMbIxwqkg0NQwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAgEAw7sQ1KIyHCYHJUzYdX9I/l0ylbzrKpfJUABq3OOmgjUM +k6InH6bXvAEP7bj4zC3UDKj1hf5WVHQM0yUeLx8ZmzHzsgfS6C8MiSt+47V0NboQ +5aGHY21ceMivlNDihf0cjPg+h4NsAZkzMWR771Nxe3WLT20Wo1ehKqCjIC7HnCJ3 +IAKbholWJwaiu1Y9tLn21sNMC0Z75hx/JWokP8OuZUyLxsp5RDLLvPA4N41z1Tep +CXpBct6jdRVzq7b5bUgPIQJ4XVdnaIYtdWIZVavulbr0vo4P7dayhf7ZWGcFVyPh +GmjHsPiurZy5BShEOgyYlwrSf9guDJfLR0gclKysKF1I4O9EWysRntvxuDMg0bys +jdUkp25EjQzIP/gA5J6NhTUFYnt04cLIebxs8CU/ZqZ0QZ11wT3VnvTm9ukTsaT4 +lKzmBjlejZVLwlki6r3IAj/CD2v9pJddt950usj9/I9C6quw1XpWG6TYZlCyM9lb +ucUfPttVknOM8c/eenXYAECR/f9MqlFBHO3Bj9YfO4hGTZtE7qaiY2f6yhcLRCOt +Bd649G/bfKT0KvLp+6SdTJxMYFC70FUqoqniEfxT+AhsKP+2k0MfU4MvJ/neLcGA +l/fM+TeIJWTzadl/DCZeE0FyQXAzeqHICnNQOieH6spMpjiXd6cv0aPO1zSqpV4= +-----END CERTIFICATE----- From fb22c59d6235f8d93ef072de90cd436ceb013700 Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Thu, 31 Aug 2023 12:03:42 +0500 Subject: [PATCH 10/15] Add more proxy tests and update squid config --- ci/mock-backends/squid/squid.conf.bin | 6 +- tests/functional/ProxyTests.cpp | 88 +++++++++++++++++++++++---- tests/mock-squid/squid.conf.bin | 6 +- 3 files changed, 86 insertions(+), 14 deletions(-) diff --git a/ci/mock-backends/squid/squid.conf.bin b/ci/mock-backends/squid/squid.conf.bin index 5e1aedf..2ce71e2 100644 --- a/ci/mock-backends/squid/squid.conf.bin +++ b/ci/mock-backends/squid/squid.conf.bin @@ -1,5 +1,9 @@ - +reply_header_add Test-proxy "THIS IS TEST PROXY" all +acl CONNECT method CONNECT +acl connect_denied_ports port 444 http_access allow localhost manager http_access deny manager +http_access deny CONNECT connect_denied_ports http_access allow all http_port 3128 + diff --git a/tests/functional/ProxyTests.cpp b/tests/functional/ProxyTests.cpp index 77c4691..e7f563e 100644 --- a/tests/functional/ProxyTests.cpp +++ b/tests/functional/ProxyTests.cpp @@ -13,12 +13,12 @@ using namespace std; using namespace restc_cpp; -static const string defunct_proxy_address = GetDockerUrl("http://localhost:0"); +static const string defunct_proxy_address = GetDockerUrl("http://localhost:3777"); static const string http_proxy_address = GetDockerUrl("http://localhost:3003"); static const string https_proxy_address = GetDockerUrl("http://localhost:3003"); static const string socks5_proxy_address = GetDockerUrl("localhost:3004"); -TEST(Proxy, FailToConnect) +TEST(Proxy, FailToConnect_HTTP_Proxy) { Request::Properties properties; properties.proxy.type = Request::Proxy::Type::HTTP; @@ -34,10 +34,11 @@ TEST(Proxy, FailToConnect) .Execute(); }); - EXPECT_ANY_THROW(f.get()); + //EXPECT_ANY_THROW(f.get()); + EXPECT_THROW(f.get(), FailedToConnectException); } -TEST(Proxy, WithHttpProxy) +TEST(Proxy, With_HTTP_Proxy) { Request::Properties properties; properties.proxy.type = Request::Proxy::Type::HTTP; @@ -51,14 +52,34 @@ TEST(Proxy, WithHttpProxy) .Get("http://api.example.com/normal/posts/1") .Execute(); - EXPECT_HTTP_OK(reply->GetResponseCode()); - cout << "Got: " << reply->GetBodyAsString() << endl; + EXPECT_HTTP_OK(reply->GetResponseCode()); + cout << "Got: " << reply->GetBodyAsString() << endl; }); EXPECT_NO_THROW(f.get()); } -TEST(Proxy, WithHttpsProxy) + +TEST(Proxy, FailToConnect_HTTPSCONNECT_Proxy) +{ + Request::Properties properties; + properties.proxy.type = Request::Proxy::Type::HTTPS; + properties.proxy.address = defunct_proxy_address; + + // Create the client with our configuration + auto rest_client = RestClient::Create(properties); + + + auto f = rest_client->ProcessWithPromise([&](Context& ctx) { + auto reply = RequestBuilder(ctx) + .Get("https://api.example.com/normal/posts/1") + .Execute(); + }); + + EXPECT_THROW(f.get(), FailedToConnectException); +} + +TEST(Proxy, With_HTTPSCONNECT_ProxyToHttps) { Request::Properties properties; properties.proxy.type = Request::Proxy::Type::HTTPS; @@ -72,14 +93,57 @@ TEST(Proxy, WithHttpsProxy) .Get("https://api.example.com/normal/posts/1") .Execute(); - EXPECT_HTTP_OK(reply->GetResponseCode()); - cout << "Got: " << reply->GetBodyAsString() << endl; + EXPECT_HTTP_OK(reply->GetResponseCode()); + cout << "Got: " << reply->GetBodyAsString() << endl; + }); + + EXPECT_NO_THROW(f.get()); +} + +// WARNING: passing plain http over a CONNECT tunnel is denied by default in squid +// we enabled it in squid.conf for the sake of our test +TEST(Proxy, With_HTTPSCONNECT_ProxyToHttp) +{ + Request::Properties properties; + properties.proxy.type = Request::Proxy::Type::HTTPS; + properties.proxy.address = https_proxy_address; + + // Create the client with our configuration + auto rest_client = RestClient::Create(properties); + + auto f = rest_client->ProcessWithPromise([&](Context& ctx) { + auto reply = RequestBuilder(ctx) + .Get("http://api.example.com/normal/posts/1") + .Execute(); + + EXPECT_HTTP_OK(reply->GetResponseCode()); + cout << "Got: " << reply->GetBodyAsString() << endl; }); EXPECT_NO_THROW(f.get()); } -TEST(Proxy, WithSocks5Proxy) +// we denied CONNECT method to port 444 in our squid.conf +TEST(Proxy, With_HTTPSCONNECT_ProxyToDeniedPort) +{ + Request::Properties properties; + properties.proxy.type = Request::Proxy::Type::HTTPS; + properties.proxy.address = https_proxy_address; + + // Create the client with our configuration + auto rest_client = RestClient::Create(properties); + + auto f = rest_client->ProcessWithPromise([&](Context& ctx) { + auto reply = RequestBuilder(ctx) + .Get("https://api.example.com:444/normal/posts/1") + .Execute(); + }); + + EXPECT_THROW(f.get(), FailedToConnectException); +} + + +TEST(Proxy, With_SOCKS5_Proxy) { Request::Properties properties; properties.proxy.type = Request::Proxy::Type::SOCKS5; @@ -93,8 +157,8 @@ TEST(Proxy, WithSocks5Proxy) .Get("http://api.example.com/normal/posts/1") .Execute(); - EXPECT_HTTP_OK(reply->GetResponseCode()); - cout << "Got: " << reply->GetBodyAsString() << endl; + EXPECT_HTTP_OK(reply->GetResponseCode()); + cout << "Got: " << reply->GetBodyAsString() << endl; }); EXPECT_NO_THROW(f.get()); diff --git a/tests/mock-squid/squid.conf.bin b/tests/mock-squid/squid.conf.bin index 5e1aedf..2ce71e2 100644 --- a/tests/mock-squid/squid.conf.bin +++ b/tests/mock-squid/squid.conf.bin @@ -1,5 +1,9 @@ - +reply_header_add Test-proxy "THIS IS TEST PROXY" all +acl CONNECT method CONNECT +acl connect_denied_ports port 444 http_access allow localhost manager http_access deny manager +http_access deny CONNECT connect_denied_ports http_access allow all http_port 3128 + From 967f6a237439001e9dc3a2a2480247a3ada1b134 Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Thu, 31 Aug 2023 12:06:58 +0500 Subject: [PATCH 11/15] Fix some proxy usage corner cases --- src/RequestImpl.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/RequestImpl.cpp b/src/RequestImpl.cpp index ea3a75f..6ce0eaf 100644 --- a/src/RequestImpl.cpp +++ b/src/RequestImpl.cpp @@ -596,6 +596,10 @@ class RequestImpl : public Request { tuple GetRequestEndpoint() { const auto proxy_type = properties_->proxy.type; + //https connections via http proxy is not possible + assert(!(proxy_type == Request::Proxy::Type::HTTP + && parsed_url_.GetProtocol() == Url::Protocol::HTTPS)); + if (proxy_type == Request::Proxy::Type::SOCKS5) { string host; uint16_t port = 0; @@ -608,10 +612,9 @@ class RequestImpl : public Request { return { host, to_string(port) }; } - if ( (proxy_type == Request::Proxy::Type::HTTP && - parsed_url_.GetProtocol() == Url::Protocol::HTTP) || - (proxy_type == Request::Proxy::Type::HTTPS && - parsed_url_.GetProtocol() == Url::Protocol::HTTPS) ) { + if ( (proxy_type == Request::Proxy::Type::HTTP + && parsed_url_.GetProtocol() == Url::Protocol::HTTP) + || proxy_type == Request::Proxy::Type::HTTPS ) { Url proxy {properties_->proxy.address.c_str()}; RESTC_CPP_LOG_TRACE_("Using " << properties_->proxy.GetName() @@ -726,7 +729,8 @@ class RequestImpl : public Request { const Connection::Type protocol_type = (parsed_url_.GetProtocol() == Url::Protocol::HTTPS || - properties_->proxy.type == Request::Proxy::Type::HTTPS) + (properties_->proxy.type == Request::Proxy::Type::HTTPS + && parsed_url_.GetProtocol() == Url::Protocol::HTTPS)) ? Connection::Type::HTTPS : Connection::Type::HTTP; @@ -861,6 +865,15 @@ class RequestImpl : public Request { << ex.what() << "\" while connecting to " << endpoint); break; // Go to the next endpoint + } catch(const RequestFailedWithErrorException& ex) { + RESTC_CPP_LOG_WARN_("Connect to " + << endpoint + << " failed with HTTP protocol exception type: " + << typeid(ex).name() + << ", message: " << ex.what()); + + connection->GetSocket().GetSocket().close(); + break; // Go to the next endpoint } catch(const exception& ex) { RESTC_CPP_LOG_WARN_("Connect to " << endpoint From 099015d47cd546b3a284c1cb36b8e7d816288c83 Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Thu, 31 Aug 2023 12:08:29 +0500 Subject: [PATCH 12/15] Improve parameter spelling --- src/ConnectionPoolImpl.cpp | 2 +- src/TlsSocketImpl.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ConnectionPoolImpl.cpp b/src/ConnectionPoolImpl.cpp index 39998ed..28182cf 100644 --- a/src/ConnectionPoolImpl.cpp +++ b/src/ConnectionPoolImpl.cpp @@ -366,7 +366,7 @@ class ConnectionPoolImpl else { #ifdef RESTC_CPP_WITH_TLS socket = make_unique(owner_.GetIoService(), owner_.GetTLSContext(), - /*can_send_over_unupgraded_socket to send plain data over tls socket before handshake*/ + /*allow_sending_over_unupgraded_socket to send plain data over tls socket before handshake*/ properties_->proxy.type == Request::Proxy::Type::HTTPS); #else throw NotImplementedException( diff --git a/src/TlsSocketImpl.h b/src/TlsSocketImpl.h index 6463f69..aece0ef 100644 --- a/src/TlsSocketImpl.h +++ b/src/TlsSocketImpl.h @@ -26,10 +26,10 @@ class TlsSocketImpl : public Socket, protected ExceptionWrapper { TlsSocketImpl(boost::asio::io_service& io_service, shared_ptr ctx, - bool can_send_over_unupgraded_socket = false) + bool allow_sending_over_unupgraded_socket = false) { ssl_socket_ = std::make_unique(io_service, *ctx); - can_send_over_unupgraded_socket_ = can_send_over_unupgraded_socket; + can_send_over_unupgraded_socket_ = allow_sending_over_unupgraded_socket; } boost::asio::ip::tcp::socket& GetSocket() override { From 1f250b45ef2793cd144176750557dab31989677c Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Thu, 31 Aug 2023 16:22:23 +0500 Subject: [PATCH 13/15] Add proxy detection function proxy.detect() --- include/restc-cpp/restc-cpp.h | 1 + src/RequestImpl.cpp | 29 ++++++++++++- tests/unit/CMakeLists.txt | 12 +++++ tests/unit/ProxyDetectionTests.cpp | 70 ++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 tests/unit/ProxyDetectionTests.cpp diff --git a/include/restc-cpp/restc-cpp.h b/include/restc-cpp/restc-cpp.h index fd25ed1..0383070 100644 --- a/include/restc-cpp/restc-cpp.h +++ b/include/restc-cpp/restc-cpp.h @@ -139,6 +139,7 @@ class Request { std::string address; const std::string& GetName(); + Type detect(); }; using args_t = std::deque; diff --git a/src/RequestImpl.cpp b/src/RequestImpl.cpp index 6ce0eaf..06ed538 100644 --- a/src/RequestImpl.cpp +++ b/src/RequestImpl.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include @@ -75,6 +77,27 @@ const std::string& Request::Proxy::GetName() { return names.at(static_cast(type)); } +Request::Proxy::Type Request::Proxy::detect() { + char* p; + + if ( (p = std::getenv("https_proxy")) + || (p = std::getenv("HTTPS_PROXY")) + && strlen(p) > 0 ) { + type = Request::Proxy::Type::HTTPS; + address = p; + return type; + } + if ( (p = std::getenv("http_proxy")) + || (p = std::getenv("HTTP_PROXY")) + && strlen(p) > 0 ) { + type = Request::Proxy::Type::HTTP; + address = p; + return type; + } + address.clear(); + return Request::Proxy::Type::NONE; +} + namespace { constexpr char SOCKS5_VERSION = 0x05; @@ -865,7 +888,8 @@ class RequestImpl : public Request { << ex.what() << "\" while connecting to " << endpoint); break; // Go to the next endpoint - } catch(const RequestFailedWithErrorException& ex) { + + } catch (const RequestFailedWithErrorException& ex) { RESTC_CPP_LOG_WARN_("Connect to " << endpoint << " failed with HTTP protocol exception type: " @@ -874,7 +898,8 @@ class RequestImpl : public Request { connection->GetSocket().GetSocket().close(); break; // Go to the next endpoint - } catch(const exception& ex) { + + } catch (const exception& ex) { RESTC_CPP_LOG_WARN_("Connect to " << endpoint << " failed with exception type: " diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index bcc2580..b2616bb 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -85,3 +85,15 @@ target_link_libraries(request_builder_tests add_dependencies(request_builder_tests restc-cpp ${DEPENDS_GTEST}) ADD_AND_RUN_UNITTEST(REQUEST_BUILDER_TESTS request_builder_tests) + +# ====================================== + +add_executable(proxy_detection_tests ProxyDetectionTests.cpp) +target_link_libraries(proxy_detection_tests + ${GTEST_LIBRARIES} + restc-cpp + ${DEFAULT_LIBRARIES} +) +add_dependencies(proxy_detection_tests restc-cpp ${DEPENDS_GTEST}) +ADD_AND_RUN_UNITTEST(PROXY_DETECTION_TESTS proxy_detection_tests) + diff --git a/tests/unit/ProxyDetectionTests.cpp b/tests/unit/ProxyDetectionTests.cpp new file mode 100644 index 0000000..a88b594 --- /dev/null +++ b/tests/unit/ProxyDetectionTests.cpp @@ -0,0 +1,70 @@ +// Include before boost::log headers +#include "restc-cpp/logging.h" + +#include "restc-cpp/restc-cpp.h" + +#include "gtest/gtest.h" +#include "restc-cpp/test_helper.h" + +#include + +using namespace std; +using namespace restc_cpp; + +void clear_env() { + unsetenv("https_proxy"); + unsetenv("HTTPS_PROXY"); + unsetenv("http_proxy"); + unsetenv("HTTP_PROXY"); +} + +TEST(ProxyDetection, ProxyTypeHTTP) +{ + clear_env(); + setenv("HTTP_PROXY", "http://1.2.3.4:8088", 1); + + Request::Properties properties; + EXPECT_EQ(Request::Proxy::Type::HTTP, properties.proxy.detect()); + EXPECT_EQ(Request::Proxy::Type::HTTP, properties.proxy.type); + EXPECT_EQ("http://1.2.3.4:8088", properties.proxy.address); + + setenv("http_proxy", "http://1.2.3.4:8089", 1); + EXPECT_EQ(Request::Proxy::Type::HTTP, properties.proxy.detect()); + EXPECT_EQ(Request::Proxy::Type::HTTP, properties.proxy.type); + EXPECT_EQ("http://1.2.3.4:8089", properties.proxy.address); +} + +TEST(ProxyDetection, ProxyTypeHTTPS) +{ + clear_env(); + setenv("HTTP_PROXY", "http://1.2.3.4:8088", 1); + setenv("HTTPS_PROXY", "http://1.2.3.4:8090", 1); + + Request::Properties properties; + EXPECT_EQ(Request::Proxy::Type::HTTPS, properties.proxy.detect()); + EXPECT_EQ(Request::Proxy::Type::HTTPS, properties.proxy.type); + EXPECT_EQ("http://1.2.3.4:8090", properties.proxy.address); + + setenv("https_proxy", "http://1.2.3.4:8091", 1); + EXPECT_EQ(Request::Proxy::Type::HTTPS, properties.proxy.detect()); + EXPECT_EQ(Request::Proxy::Type::HTTPS, properties.proxy.type); + EXPECT_EQ("http://1.2.3.4:8091", properties.proxy.address); +} + +TEST(ProxyDetection, ProxyTypeNONE) +{ + clear_env(); + + Request::Properties properties; + EXPECT_EQ(Request::Proxy::Type::NONE, properties.proxy.detect()); + EXPECT_EQ(Request::Proxy::Type::NONE, properties.proxy.type); + EXPECT_EQ("", properties.proxy.address); +} + + +int main( int argc, char * argv[] ) +{ + RESTC_CPP_TEST_LOGGING_SETUP("debug"); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 4b56c94c3b10436a9b4cae6066d94efc1a0cfeff Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Fri, 1 Sep 2023 17:27:18 +0500 Subject: [PATCH 14/15] revert deprecated resolver::query back for boost1.58 --- src/RequestImpl.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/RequestImpl.cpp b/src/RequestImpl.cpp index 06ed538..ef580f2 100644 --- a/src/RequestImpl.cpp +++ b/src/RequestImpl.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -615,8 +614,9 @@ class RequestImpl : public Request { return request_buffer.str(); } - //returns {host, service} instead of deprecated ip::resolver::query - tuple GetRequestEndpoint() { + //boost1.83: returns {host, service} instead of deprecated ip::resolver::query + //boost1.83: tuple GetRequestEndpoint() { + boost::asio::ip::tcp::resolver::query GetRequestEndpoint() { const auto proxy_type = properties_->proxy.type; //https connections via http proxy is not possible @@ -700,9 +700,10 @@ class RequestImpl : public Request { return {protocol, static_cast(port_num)}; } + boost::asio::ip::tcp::resolver::query q{host, port}; boost::asio::ip::tcp::resolver resolver(owner_.GetIoService()); - auto ep = resolver.async_resolve(host, port, ctx.GetYield()); + auto ep = resolver.async_resolve(q, ctx.GetYield()); const decltype(ep) addr_end; for(; ep != addr_end; ++ep) if (ep != addr_end) { @@ -759,13 +760,14 @@ class RequestImpl : public Request { boost::asio::ip::tcp::resolver resolver(owner_.GetIoService()); // Resolve the hostname - const auto ep_tuple = GetRequestEndpoint(); //{host, service=port} + const auto query = GetRequestEndpoint(); //{host, service=port} - RESTC_CPP_LOG_TRACE_("Resolving " << get<0>(ep_tuple) << ":" - << get<1>(ep_tuple)); + RESTC_CPP_LOG_TRACE_("Resolving " << query.host_name() << ":" + << query.service_name()); - auto address_it = resolver.async_resolve(/*host*/ get<0>(ep_tuple), - /*port*/ get<1>(ep_tuple), + auto address_it = resolver.async_resolve(query, + /*boost1.83: host get<0>(ep_tuple),*/ + /*boost1.83: port get<1>(ep_tuple),*/ ctx.GetYield()); const decltype(address_it) addr_end; From a589a53dd5f8ac80e84eb62b6be14bef68f70a48 Mon Sep 17 00:00:00 2001 From: Ural Khasanov Date: Fri, 1 Sep 2023 17:43:02 +0500 Subject: [PATCH 15/15] Improve cert scripts naming --- ci/mock-backends/nginx/{create_cert => create_cert.sh} | 0 tests/mock-nginx/{create_cert => create_cert.sh} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename ci/mock-backends/nginx/{create_cert => create_cert.sh} (100%) rename tests/mock-nginx/{create_cert => create_cert.sh} (100%) diff --git a/ci/mock-backends/nginx/create_cert b/ci/mock-backends/nginx/create_cert.sh similarity index 100% rename from ci/mock-backends/nginx/create_cert rename to ci/mock-backends/nginx/create_cert.sh diff --git a/tests/mock-nginx/create_cert b/tests/mock-nginx/create_cert.sh similarity index 100% rename from tests/mock-nginx/create_cert rename to tests/mock-nginx/create_cert.sh