diff --git a/Net/include/Poco/Net/HTTPClientSession.h b/Net/include/Poco/Net/HTTPClientSession.h index 31c5aa5cc1..9fec319a64 100644 --- a/Net/include/Poco/Net/HTTPClientSession.h +++ b/Net/include/Poco/Net/HTTPClientSession.h @@ -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 @@ -78,6 +79,8 @@ class Net_API HTTPClientSession: public HTTPSession { ProxyConfig(): port(HTTP_PORT), + protocol("http"), + tunnel(true), authMethod(PROXY_AUTH_HTTP_BASIC) { } @@ -86,6 +89,11 @@ class Net_API HTTPClientSession: public HTTPSession /// 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; @@ -138,8 +146,8 @@ 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. @@ -147,12 +155,24 @@ class Net_API HTTPClientSession: public HTTPSession 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. @@ -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; @@ -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; diff --git a/Net/src/HTTPClientSession.cpp b/Net/src/HTTPClientSession.cpp index d64c7aa22e..987d1fa010 100644 --- a/Net/src/HTTPClientSession.cpp +++ b/Net/src/HTTPClientSession.cpp @@ -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" @@ -48,6 +49,7 @@ HTTPClientSession::HTTPClientSession(): _responseReceived(false), _ntlmProxyAuthenticated(false) { + _proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator); } @@ -62,6 +64,7 @@ HTTPClientSession::HTTPClientSession(const StreamSocket& socket): _responseReceived(false), _ntlmProxyAuthenticated(false) { + _proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator); } @@ -76,6 +79,7 @@ HTTPClientSession::HTTPClientSession(const SocketAddress& address): _responseReceived(false), _ntlmProxyAuthenticated(false) { + _proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator); } @@ -90,6 +94,7 @@ HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port) _responseReceived(false), _ntlmProxyAuthenticated(false) { + _proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator); } @@ -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"); } @@ -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"); } @@ -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; @@ -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 proxySession (_proxySessionFactory.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()); - proxySession.proxyAuthenticateImpl(proxyRequest, _proxyConfig); - proxySession.setKeepAlive(true); - proxySession.sendRequest(proxyRequest); - proxySession.receiveResponse(proxyResponse); + proxyRequest.set(HTTPRequest::HOST, targetAddress); + proxySession->proxyAuthenticateImpl(proxyRequest, _proxyConfig); + 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(); } diff --git a/NetSSL_OpenSSL/src/HTTPSClientSession.cpp b/NetSSL_OpenSSL/src/HTTPSClientSession.cpp index 325f36a60a..62b6f6e3ed 100644 --- a/NetSSL_OpenSSL/src/HTTPSClientSession.cpp +++ b/NetSSL_OpenSSL/src/HTTPSClientSession.cpp @@ -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" @@ -36,6 +37,7 @@ HTTPSClientSession::HTTPSClientSession(): _pContext(SSLManager::instance().defaultClientContext()) { setPort(HTTPS_PORT); + _proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator); } @@ -44,6 +46,7 @@ HTTPSClientSession::HTTPSClientSession(const SecureStreamSocket& socket): _pContext(socket.context()) { setPort(HTTPS_PORT); + _proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator); } @@ -53,6 +56,7 @@ HTTPSClientSession::HTTPSClientSession(const SecureStreamSocket& socket, Session _pSession(pSession) { setPort(HTTPS_PORT); + _proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator); } @@ -62,6 +66,7 @@ HTTPSClientSession::HTTPSClientSession(const std::string& host, Poco::UInt16 por { setHost(host); setPort(port); + _proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator); } @@ -69,6 +74,7 @@ HTTPSClientSession::HTTPSClientSession(Context::Ptr pContext): HTTPClientSession(SecureStreamSocket(pContext)), _pContext(pContext) { + _proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator(pContext)); } @@ -77,6 +83,7 @@ HTTPSClientSession::HTTPSClientSession(Context::Ptr pContext, Session::Ptr pSess _pContext(pContext), _pSession(pSession) { + _proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator(pContext)); } @@ -86,6 +93,7 @@ HTTPSClientSession::HTTPSClientSession(const std::string& host, Poco::UInt16 por { setHost(host); setPort(port); + _proxySessionFactory.registerProtocol("https", new HTTPSSessionInstantiator(pContext)); } @@ -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"); } @@ -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; } @@ -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()) { @@ -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(); - } - } } @@ -172,7 +188,7 @@ int HTTPSClientSession::read(char* buffer, std::streamsize length) try { return HTTPSession::read(buffer, length); - } + } catch(SSLConnectionUnexpectedlyClosedException&) { return 0;