feat(HTTPClientSession): HTTPClientSession source IP address #2271

This commit is contained in:
Alex Fabijanic
2022-05-20 14:38:23 -07:00
parent cf4dc753f0
commit be7006af2f
6 changed files with 162 additions and 21 deletions

View File

@@ -116,6 +116,9 @@ public:
HTTPClientSession(const std::string& host, Poco::UInt16 port, const ProxyConfig& proxyConfig);
/// Creates a HTTPClientSession using the given host, port and proxy configuration.
HTTPClientSession(const StreamSocket& socket, const ProxyConfig& proxyConfig);
/// Creates a HTTPClientSession using the given socket and proxy configuration.
virtual ~HTTPClientSession();
/// Destroys the HTTPClientSession and closes
/// the underlying socket.
@@ -138,6 +141,32 @@ public:
Poco::UInt16 getPort() const;
/// Returns the port number of the target HTTP server.
void setSourceAddress(const SocketAddress& address);
/// Sets the source IP address and source port for the HTTPClientSession
/// socket.
///
/// Function can be called repeatedly to set one source address value for
/// IPv4 and one for IPv6, in the case where it is not known ahead of time
/// which type of address family the target host is part of.
///
/// The source address must not be changed once there
/// is an open connection to the server.
///
/// Note: Both the source IP address and source port can be set
/// using this function, but the typical client use is to set
/// the source IP address only and the source port portion
/// would normally be passed as 0 meaning that any port value
/// can be used on the source side of the socket.
const SocketAddress& getSourceAddress();
/// Returns the last source address set with setSourceAddress
const SocketAddress& getSourceAddress4();
/// Returns the last IPv4 source address set with setSourceAddress
const SocketAddress& getSourceAddress6();
/// Returns the last IPV6 source address set with setSourceAddress
void setProxy(const std::string& host, Poco::UInt16 port = HTTPSession::HTTP_PORT);
/// Sets the proxy host name and port number.
@@ -335,8 +364,14 @@ protected:
/// to the HTTPClientSession.
private:
using OStreamPtr = Poco::SharedPtr<std::ostream>;
using IStreamPtr = Poco::SharedPtr<std::istream>;
std::string _host;
Poco::UInt16 _port;
SocketAddress _sourceAddress;
SocketAddress _sourceAddress4;
SocketAddress _sourceAddress6;
ProxyConfig _proxyConfig;
Poco::Timespan _keepAliveTimeout;
Poco::Timestamp _lastRequest;
@@ -344,8 +379,8 @@ private:
bool _mustReconnect;
bool _expectResponseBody;
bool _responseReceived;
Poco::SharedPtr<std::ostream> _pRequestStream;
Poco::SharedPtr<std::istream> _pResponseStream;
OStreamPtr _pRequestStream;
IStreamPtr _pResponseStream;
HTTPBasicCredentials _proxyBasicCreds;
HTTPDigestCredentials _proxyDigestCreds;
HTTPNTLMCredentials _proxyNTLMCreds;

View File

@@ -165,6 +165,10 @@ protected:
/// Connects the underlying socket to the given address
/// and sets the socket's receive timeout.
void connect(const SocketAddress& targetAddress, const SocketAddress& sourceAddress);
/// Binds the underlying socket to the source address
/// and connects to the targetAddress.
void attachSocket(const StreamSocket& socket);
/// Attaches a socket to the session, replacing the
/// previously attached socket.

View File

@@ -128,6 +128,27 @@ public:
/// the TCP server at the given address. Prior to opening the
/// connection the socket is set to nonblocking mode.
void bind(const SocketAddress& address, bool reuseAddress = false, bool ipV6Only = false);
/// Bind a local address to the socket.
///
/// This is usually only done when establishing a server
/// socket.
///
/// TCP clients normally do not bind to a local address,
/// but in some special advanced cases it may be useful to have
/// this type of functionality. (e.g. in multihoming situations
/// where the traffic will be sent through a particular interface;
/// or in computer clustered environments with active/standby
/// servers and it is desired to make the traffic from either
/// active host present the same source IP address).
///
/// Note: Practical use of client source IP address binding
/// may require OS networking setup outside the scope of
/// the Poco library.
///
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
void shutdownReceive();
/// Shuts down the receiving part of the socket connection.

View File

@@ -40,6 +40,8 @@ HTTPClientSession::ProxyConfig HTTPClientSession::_globalProxyConfig;
HTTPClientSession::HTTPClientSession():
_port(HTTPSession::HTTP_PORT),
_sourceAddress4(IPAddress::wildcard(IPAddress::IPv4), 0),
_sourceAddress6(IPAddress::wildcard(IPAddress::IPv6), 0),
_proxyConfig(_globalProxyConfig),
_keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
_reconnect(false),
@@ -54,6 +56,8 @@ HTTPClientSession::HTTPClientSession():
HTTPClientSession::HTTPClientSession(const StreamSocket& socket):
HTTPSession(socket),
_port(HTTPSession::HTTP_PORT),
_sourceAddress4(IPAddress::wildcard(IPAddress::IPv4), 0),
_sourceAddress6(IPAddress::wildcard(IPAddress::IPv6), 0),
_proxyConfig(_globalProxyConfig),
_keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
_reconnect(false),
@@ -68,6 +72,8 @@ HTTPClientSession::HTTPClientSession(const StreamSocket& socket):
HTTPClientSession::HTTPClientSession(const SocketAddress& address):
_host(address.host().toString()),
_port(address.port()),
_sourceAddress4(IPAddress::wildcard(IPAddress::IPv4), 0),
_sourceAddress6(IPAddress::wildcard(IPAddress::IPv6), 0),
_proxyConfig(_globalProxyConfig),
_keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
_reconnect(false),
@@ -82,6 +88,8 @@ HTTPClientSession::HTTPClientSession(const SocketAddress& address):
HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port):
_host(host),
_port(port),
_sourceAddress4(IPAddress::wildcard(IPAddress::IPv4), 0),
_sourceAddress6(IPAddress::wildcard(IPAddress::IPv6), 0),
_proxyConfig(_globalProxyConfig),
_keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
_reconnect(false),
@@ -107,6 +115,21 @@ HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port,
}
HTTPClientSession::HTTPClientSession(const StreamSocket& socket, const ProxyConfig& proxyConfig):
HTTPSession(socket),
_port(HTTPSession::HTTP_PORT),
_sourceAddress4(IPAddress::wildcard(IPAddress::IPv4), 0),
_sourceAddress6(IPAddress::wildcard(IPAddress::IPv6), 0),
_proxyConfig(proxyConfig),
_keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
_reconnect(false),
_mustReconnect(false),
_expectResponseBody(false),
_responseReceived(false)
{
}
HTTPClientSession::~HTTPClientSession()
{
}
@@ -130,6 +153,39 @@ void HTTPClientSession::setPort(Poco::UInt16 port)
}
void HTTPClientSession::setSourceAddress(const SocketAddress& address)
{
if (!connected())
{
if (address.family() == IPAddress::IPv4)
_sourceAddress4 = address;
else
_sourceAddress6 = address;
_sourceAddress = address;
}
else
throw IllegalStateException("Cannot set the source address for an already connected session");
}
const SocketAddress& HTTPClientSession::getSourceAddress()
{
return _sourceAddress;
}
const SocketAddress& HTTPClientSession::getSourceAddress4()
{
return _sourceAddress4;
}
const SocketAddress& HTTPClientSession::getSourceAddress6()
{
return _sourceAddress6;
}
void HTTPClientSession::setProxy(const std::string& host, Poco::UInt16 port)
{
if (!connected())
@@ -400,16 +456,22 @@ int HTTPClientSession::write(const char* buffer, std::streamsize length)
void HTTPClientSession::reconnect()
{
if (_proxyConfig.host.empty() || bypassProxy())
{
SocketAddress addr(_host, _port);
connect(addr);
}
if (_proxyConfig.host.empty() || bypassProxy())
addr = SocketAddress(_host, _port);
else
addr = SocketAddress(_proxyConfig.host, _proxyConfig.port);
SocketAddress sourceAddr;
if (addr.family() == IPAddress::IPv4)
sourceAddr = _sourceAddress4;
else
sourceAddr = _sourceAddress6;
if ((!sourceAddr.host().isWildcard()) || (sourceAddr.port() != 0))
connect(addr, sourceAddr);
else
{
SocketAddress addr(_proxyConfig.host, _proxyConfig.port);
connect(addr);
}
}
@@ -536,6 +598,8 @@ StreamSocket HTTPClientSession::proxyConnect()
proxyRequest.set(HTTPRequest::HOST, getHost());
proxySession.proxyAuthenticateImpl(proxyRequest, _proxyConfig);
proxySession.setKeepAlive(true);
proxySession.setSourceAddress(_sourceAddress4);
proxySession.setSourceAddress(_sourceAddress6);
proxySession.sendRequest(proxyRequest);
proxySession.receiveResponse(proxyResponse);
if (proxyResponse.getStatus() != HTTPResponse::HTTP_OK)

View File

@@ -203,6 +203,13 @@ void HTTPSession::connect(const SocketAddress& address)
}
void HTTPSession::connect(const SocketAddress& targetAddress, const SocketAddress& sourceAddress)
{
_socket.bind(sourceAddress, true);
connect(targetAddress);
}
void HTTPSession::abort()
{
_socket.shutdown();

View File

@@ -112,6 +112,16 @@ StreamSocket& StreamSocket::operator = (StreamSocket&& socket)
#endif // POCO_NEW_STATE_ON_MOVE
void StreamSocket::bind(const SocketAddress& address, bool reuseAddress, bool ipV6Only)
{
if (address.family() == IPAddress::IPv4)
impl()->bind(address, reuseAddress);
else
impl()->bind6(address, reuseAddress, ipV6Only);
}
void StreamSocket::connect(const SocketAddress& address)
{
impl()->connect(address);