Skip to content

Commit

Permalink
HTTPS proxy support
Browse files Browse the repository at this point in the history
  • Loading branch information
Jokser committed Jun 19, 2020
1 parent 3fc3e5f commit 9d23564
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 28 deletions.
40 changes: 37 additions & 3 deletions Net/include/Poco/Net/HTTPClientSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "Poco/Net/HTTPBasicCredentials.h"
#include "Poco/Net/HTTPDigestCredentials.h"
#include "Poco/Net/HTTPNTLMCredentials.h"
#include "Poco/Net/HTTPSessionFactory.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/SharedPtr.h"
#include <istream>
Expand Down Expand Up @@ -78,14 +79,21 @@ class Net_API HTTPClientSession: public HTTPSession
{
ProxyConfig():
port(HTTP_PORT),
authMethod(PROXY_AUTH_HTTP_BASIC)
authMethod(PROXY_AUTH_HTTP_BASIC),
protocol("http"),
tunnel(true)
{
}

std::string host;
/// Proxy server host name or IP address.
Poco::UInt16 port;
/// Proxy server TCP port.
std::string protocol;
/// Protocol to use (http or https).
bool tunnel;
/// Use proxy as tunnel (establish 2-way communication through CONNECT request).
/// If tunnel option is 'false' request will be send directly to proxy without CONNECT request.
std::string username;
/// Proxy server username.
std::string password;
Expand Down Expand Up @@ -138,21 +146,33 @@ class Net_API HTTPClientSession: public HTTPSession
Poco::UInt16 getPort() const;
/// Returns the port number of the target HTTP server.

void setProxy(const std::string& host, Poco::UInt16 port = HTTPSession::HTTP_PORT);
/// Sets the proxy host name and port number.
void setProxy(const std::string& host, Poco::UInt16 port = HTTPSession::HTTP_PORT, const std::string& protocol = "http", bool tunnel = true);
/// Sets the proxy host name, port number, protocol (http or https) and tunnel behaviour.

void setProxyHost(const std::string& host);
/// Sets the host name of the proxy server.

void setProxyPort(Poco::UInt16 port);
/// Sets the port number of the proxy server.

void setProxyProtocol(const std::string& protocol);
/// Sets the proxy protocol (http or https).

void setProxyTunnel(bool tunnel);
/// If 'true' proxy will be used as tunnel.

const std::string& getProxyHost() const;
/// Returns the proxy host name.

Poco::UInt16 getProxyPort() const;
/// Returns the proxy port number.

const std::string& getProxyProtocol() const;
/// Returns the proxy protocol.

bool isProxyTunnel() const;
/// Returns 'true' if proxy is configured as tunnel.

void setProxyCredentials(const std::string& username, const std::string& password);
/// Sets the username and password for proxy authentication.
/// Only Basic authentication is supported.
Expand Down Expand Up @@ -334,6 +354,8 @@ class Net_API HTTPClientSession: public HTTPSession
/// Calls proxyConnect() and attaches the resulting StreamSocket
/// to the HTTPClientSession.

HTTPSessionFactory _proxySessionFactory;
/// Factory to create HTTPClientSession to proxy.
private:
std::string _host;
Poco::UInt16 _port;
Expand Down Expand Up @@ -387,6 +409,18 @@ inline Poco::UInt16 HTTPClientSession::getProxyPort() const
}


inline const std::string& HTTPClientSession::getProxyProtocol() const
{
return _proxyConfig.protocol;
}


inline bool HTTPClientSession::isProxyTunnel() const
{
return _proxyConfig.tunnel;
}


inline const std::string& HTTPClientSession::getProxyUsername() const
{
return _proxyConfig.username;
Expand Down
58 changes: 48 additions & 10 deletions Net/src/HTTPClientSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@


#include "Poco/Net/HTTPClientSession.h"
#include "Poco/Net/HTTPSessionInstantiator.h"
#include "Poco/Net/HTTPRequest.h"
#include "Poco/Net/HTTPResponse.h"
#include "Poco/Net/HTTPHeaderStream.h"
Expand Down Expand Up @@ -48,6 +49,7 @@ HTTPClientSession::HTTPClientSession():
_responseReceived(false),
_ntlmProxyAuthenticated(false)
{
_proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator);
}


Expand All @@ -62,6 +64,7 @@ HTTPClientSession::HTTPClientSession(const StreamSocket& socket):
_responseReceived(false),
_ntlmProxyAuthenticated(false)
{
_proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator);
}


Expand All @@ -76,6 +79,7 @@ HTTPClientSession::HTTPClientSession(const SocketAddress& address):
_responseReceived(false),
_ntlmProxyAuthenticated(false)
{
_proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator);
}


Expand All @@ -90,6 +94,7 @@ HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port)
_responseReceived(false),
_ntlmProxyAuthenticated(false)
{
_proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator);
}


Expand All @@ -104,11 +109,13 @@ HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port,
_responseReceived(false),
_ntlmProxyAuthenticated(false)
{
_proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator);
}


HTTPClientSession::~HTTPClientSession()
{
_proxySessionFactory.unregisterProtocol("http");
}


Expand All @@ -130,14 +137,19 @@ void HTTPClientSession::setPort(Poco::UInt16 port)
}


void HTTPClientSession::setProxy(const std::string& host, Poco::UInt16 port)
void HTTPClientSession::setProxy(const std::string& host, Poco::UInt16 port, const std::string& protocol, bool tunnel)
{
if (protocol != "http" && protocol != "https")
throw IllegalStateException("Protocol must be either http or https");

if (!connected())
{
_proxyConfig.host = host;
_proxyConfig.port = port;
_proxyConfig.protocol = protocol;
_proxyConfig.tunnel = tunnel;
}
else throw IllegalStateException("Cannot set the proxy host and port for an already connected session");
else throw IllegalStateException("Cannot set the proxy host, port and protocol for an already connected session");
}


Expand All @@ -159,6 +171,27 @@ void HTTPClientSession::setProxyPort(Poco::UInt16 port)
}


void HTTPClientSession::setProxyProtocol(const std::string& protocol)
{
if (protocol != "http" && protocol != "https")
throw IllegalStateException("Protocol must be either http or https");

if (!connected())
_proxyConfig.protocol = protocol;
else
throw IllegalStateException("Cannot set the proxy port number for an already connected session");
}


void HTTPClientSession::setProxyTunnel(bool tunnel)
{
if (!connected())
_proxyConfig.tunnel = tunnel;
else
throw IllegalStateException("Cannot set the proxy tunnel for an already connected session");
}


void HTTPClientSession::setProxyCredentials(const std::string& username, const std::string& password)
{
_proxyConfig.username = username;
Expand Down Expand Up @@ -522,23 +555,28 @@ void HTTPClientSession::sendChallengeRequest(const HTTPRequest& request, HTTPRes

StreamSocket HTTPClientSession::proxyConnect()
{
ProxyConfig emptyProxyConfig;
HTTPClientSession proxySession(getProxyHost(), getProxyPort(), emptyProxyConfig);
proxySession.setTimeout(getTimeout());
URI proxyUri;
proxyUri.setScheme(getProxyProtocol());
proxyUri.setHost(getProxyHost());
proxyUri.setPort(getProxyPort());

SharedPtr<HTTPClientSession> proxySession (HTTPSessionFactory::defaultFactory().createClientSession(proxyUri));

proxySession->setTimeout(getTimeout());
std::string targetAddress(_host);
targetAddress.append(":");
NumberFormatter::append(targetAddress, _port);
HTTPRequest proxyRequest(HTTPRequest::HTTP_CONNECT, targetAddress, HTTPMessage::HTTP_1_1);
HTTPResponse proxyResponse;
proxyRequest.set(HTTPMessage::PROXY_CONNECTION, HTTPMessage::CONNECTION_KEEP_ALIVE);
proxyRequest.set(HTTPRequest::HOST, getHost());
proxyRequest.set(HTTPRequest::HOST, targetAddress);
proxySession.proxyAuthenticateImpl(proxyRequest, _proxyConfig);
proxySession.setKeepAlive(true);
proxySession.sendRequest(proxyRequest);
proxySession.receiveResponse(proxyResponse);
proxySession->setKeepAlive(true);
proxySession->sendRequest(proxyRequest);
proxySession->receiveResponse(proxyResponse);
if (proxyResponse.getStatus() != HTTPResponse::HTTP_OK)
throw HTTPException("Cannot establish proxy connection", proxyResponse.getReason());
return proxySession.detachSocket();
return proxySession->detachSocket();
}


Expand Down
46 changes: 31 additions & 15 deletions NetSSL_OpenSSL/src/HTTPSClientSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@


#include "Poco/Net/HTTPSClientSession.h"
#include "Poco/Net/HTTPSSessionInstantiator.h"
#include "Poco/Net/SecureStreamSocket.h"
#include "Poco/Net/SecureStreamSocketImpl.h"
#include "Poco/Net/SSLManager.h"
Expand All @@ -36,6 +37,7 @@ HTTPSClientSession::HTTPSClientSession():
_pContext(SSLManager::instance().defaultClientContext())
{
setPort(HTTPS_PORT);
_proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator);
}


Expand All @@ -44,6 +46,7 @@ HTTPSClientSession::HTTPSClientSession(const SecureStreamSocket& socket):
_pContext(socket.context())
{
setPort(HTTPS_PORT);
_proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator);
}


Expand All @@ -53,6 +56,7 @@ HTTPSClientSession::HTTPSClientSession(const SecureStreamSocket& socket, Session
_pSession(pSession)
{
setPort(HTTPS_PORT);
_proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator);
}


Expand All @@ -62,13 +66,15 @@ HTTPSClientSession::HTTPSClientSession(const std::string& host, Poco::UInt16 por
{
setHost(host);
setPort(port);
_proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator);
}


HTTPSClientSession::HTTPSClientSession(Context::Ptr pContext):
HTTPClientSession(SecureStreamSocket(pContext)),
_pContext(pContext)
{
_proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator(pContext));
}


Expand All @@ -77,6 +83,7 @@ HTTPSClientSession::HTTPSClientSession(Context::Ptr pContext, Session::Ptr pSess
_pContext(pContext),
_pSession(pSession)
{
_proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator(pContext));
}


Expand All @@ -86,6 +93,7 @@ HTTPSClientSession::HTTPSClientSession(const std::string& host, Poco::UInt16 por
{
setHost(host);
setPort(port);
_proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator(pContext));
}


Expand All @@ -96,11 +104,13 @@ HTTPSClientSession::HTTPSClientSession(const std::string& host, Poco::UInt16 por
{
setHost(host);
setPort(port);
_proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator(pContext));
}


HTTPSClientSession::~HTTPSClientSession()
{
_proxySessionFactory.unregisterProtocol("https");
}


Expand All @@ -126,7 +136,11 @@ X509Certificate HTTPSClientSession::serverCertificate()

std::string HTTPSClientSession::proxyRequestPrefix() const
{
return std::string();
std::string result("https://");
result.append(getHost());
result.append(":");
NumberFormatter::append(result, getPort());
return result;
}


Expand All @@ -137,12 +151,24 @@ void HTTPSClientSession::proxyAuthenticate(HTTPRequest& request)

void HTTPSClientSession::connect(const SocketAddress& address)
{
if (getProxyHost().empty() || bypassProxy())
bool useProxy = !getProxyHost().empty() && !bypassProxy();

if (useProxy && isProxyTunnel())
{
StreamSocket proxySocket(proxyConnect());
SecureStreamSocket secureSocket = SecureStreamSocket::attach(proxySocket, getHost(), _pContext, _pSession);
attachSocket(secureSocket);
if (_pContext->sessionCacheEnabled())
{
_pSession = secureSocket.currentSession();
}
}
else
{
SecureStreamSocket sss(socket());
if (sss.getPeerHostName().empty())
if (sss.getPeerHostName().empty())
{
sss.setPeerHostName(getHost());
sss.setPeerHostName(useProxy ? getProxyHost() : getHost());
}
if (_pContext->sessionCacheEnabled())
{
Expand All @@ -154,16 +180,6 @@ void HTTPSClientSession::connect(const SocketAddress& address)
_pSession = sss.currentSession();
}
}
else
{
StreamSocket proxySocket(proxyConnect());
SecureStreamSocket secureSocket = SecureStreamSocket::attach(proxySocket, getHost(), _pContext, _pSession);
attachSocket(secureSocket);
if (_pContext->sessionCacheEnabled())
{
_pSession = secureSocket.currentSession();
}
}
}


Expand All @@ -172,7 +188,7 @@ int HTTPSClientSession::read(char* buffer, std::streamsize length)
try
{
return HTTPSession::read(buffer, length);
}
}
catch(SSLConnectionUnexpectedlyClosedException&)
{
return 0;
Expand Down

0 comments on commit 9d23564

Please sign in to comment.