diff --git a/Net/include/Poco/Net/HTTPClientSession.h b/Net/include/Poco/Net/HTTPClientSession.h index 2f2e21c599..dc3272ed17 100644 --- a/Net/include/Poco/Net/HTTPClientSession.h +++ b/Net/include/Poco/Net/HTTPClientSession.h @@ -20,6 +20,7 @@ #include "Poco/Net/Net.h" #include "Poco/Net/HTTPSession.h" +#include "Poco/Net/HTTPSessionFactory.h" #include "Poco/Net/SocketAddress.h" #include "Poco/SharedPtr.h" #include @@ -66,7 +67,7 @@ class Net_API HTTPClientSession: public HTTPSession /// HTTP proxy server configuration. { ProxyConfig(): - port(HTTP_PORT) + port(HTTP_PORT), protocol("http"), tunnel(true) { } @@ -74,6 +75,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; @@ -123,8 +129,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. @@ -132,12 +138,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. @@ -306,6 +324,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; @@ -355,6 +375,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 6918417a0e..93b0eeccf7 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" @@ -47,6 +48,7 @@ HTTPClientSession::HTTPClientSession(): _expectResponseBody(false), _responseReceived(false) { + _proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator); } @@ -60,6 +62,7 @@ HTTPClientSession::HTTPClientSession(const StreamSocket& socket): _expectResponseBody(false), _responseReceived(false) { + _proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator); } @@ -73,6 +76,7 @@ HTTPClientSession::HTTPClientSession(const SocketAddress& address): _expectResponseBody(false), _responseReceived(false) { + _proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator); } @@ -86,6 +90,7 @@ HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port) _expectResponseBody(false), _responseReceived(false) { + _proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator); } @@ -99,11 +104,13 @@ HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port, _expectResponseBody(false), _responseReceived(false) { + _proxySessionFactory.registerProtocol("http", new HTTPSessionInstantiator); } HTTPClientSession::~HTTPClientSession() { + _proxySessionFactory.unregisterProtocol("http"); } @@ -125,14 +132,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"); } @@ -154,6 +166,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; @@ -430,23 +463,28 @@ void HTTPClientSession::proxyAuthenticateImpl(HTTPRequest& request) 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("Proxy-Connection", "keep-alive"); - proxyRequest.set("Host", getHost()); + proxyRequest.set("Host", targetAddress); proxyAuthenticateImpl(proxyRequest); - 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(); } 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;