blob: f898a293cfe350c4ae8c328b3beb672f0c3d2ff0 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_ucbhelper.hxx"
/**************************************************************************
TODO
**************************************************************************
*************************************************************************/
#include <utility>
#include <vector>
#include <list>
#include <osl/mutex.hxx>
#include <rtl/ref.hxx>
#include <osl/socket.hxx>
#include <rtl/ustrbuf.hxx>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/util/XChangesListener.hpp>
#include <com/sun/star/util/XChangesNotifier.hpp>
#include <cppuhelper/implbase1.hxx>
#include "ucbhelper/proxydecider.hxx"
using namespace com::sun::star;
using namespace ucbhelper;
#define CONFIG_ROOT_KEY "org.openoffice.Inet/Settings"
#define PROXY_TYPE_KEY "ooInetProxyType"
#define NO_PROXY_LIST_KEY "ooInetNoProxy"
#define HTTP_PROXY_NAME_KEY "ooInetHTTPProxyName"
#define HTTP_PROXY_PORT_KEY "ooInetHTTPProxyPort"
#define HTTPS_PROXY_NAME_KEY "ooInetHTTPSProxyName"
#define HTTPS_PROXY_PORT_KEY "ooInetHTTPSProxyPort"
#define FTP_PROXY_NAME_KEY "ooInetFTPProxyName"
#define FTP_PROXY_PORT_KEY "ooInetFTPProxyPort"
//=========================================================================
namespace ucbhelper
{
//=========================================================================
namespace proxydecider_impl
{
// A simple case ignoring wildcard matcher.
class WildCard
{
private:
rtl::OString m_aWildString;
public:
WildCard( const rtl::OUString& rWildCard )
: m_aWildString(
rtl::OUStringToOString(
rWildCard, RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase() ) {}
bool Matches( const rtl::OUString & rStr ) const;
};
//=========================================================================
typedef std::pair< WildCard, WildCard > NoProxyListEntry;
//=========================================================================
class HostnameCache
{
typedef std::pair< rtl::OUString, rtl::OUString > HostListEntry;
std::list< HostListEntry > m_aHostList;
sal_uInt32 m_nCapacity;
public:
explicit HostnameCache( sal_uInt32 nCapacity )
: m_nCapacity( nCapacity ) {}
bool get( const rtl::OUString & rKey, rtl::OUString & rValue ) const
{
std::list< HostListEntry >::const_iterator it
= m_aHostList.begin();
const std::list< HostListEntry >::const_iterator end
= m_aHostList.end();
while ( it != end )
{
if ( (*it).first == rKey )
{
rValue = (*it).second;
return true;
}
it++;
}
return false;
}
void put( const rtl::OUString & rKey, const rtl::OUString & rValue )
{
if ( m_aHostList.size() == m_nCapacity )
m_aHostList.resize( m_nCapacity / 2 );
m_aHostList.push_front( HostListEntry( rKey, rValue ) );
}
};
//=========================================================================
class InternetProxyDecider_Impl :
public cppu::WeakImplHelper1< util::XChangesListener >
{
mutable osl::Mutex m_aMutex;
InternetProxyServer m_aHttpProxy;
InternetProxyServer m_aHttpsProxy;
InternetProxyServer m_aFtpProxy;
const InternetProxyServer m_aEmptyProxy;
sal_Int32 m_nProxyType;
uno::Reference< util::XChangesNotifier > m_xNotifier;
std::vector< NoProxyListEntry > m_aNoProxyList;
mutable HostnameCache m_aHostnames;
private:
bool shouldUseProxy( const rtl::OUString & rHost,
sal_Int32 nPort,
bool bUseFullyQualified ) const;
public:
InternetProxyDecider_Impl(
const uno::Reference< lang::XMultiServiceFactory >& rxSMgr );
virtual ~InternetProxyDecider_Impl();
static rtl::Reference< InternetProxyDecider_Impl > createInstance(
const uno::Reference< lang::XMultiServiceFactory >& rxSMgr );
void dispose();
const InternetProxyServer & getProxy( const rtl::OUString & rProtocol,
const rtl::OUString & rHost,
sal_Int32 nPort ) const;
// XChangesListener
virtual void SAL_CALL changesOccurred( const util::ChangesEvent& Event )
throw( uno::RuntimeException );
// XEventListener ( base of XChangesLisetenr )
virtual void SAL_CALL disposing( const lang::EventObject& Source )
throw( uno::RuntimeException );
private:
void setNoProxyList( const rtl::OUString & rNoProxyList );
};
//=========================================================================
//=========================================================================
//
// WildCard Implementation.
//
//=========================================================================
//=========================================================================
bool WildCard::Matches( const rtl::OUString& rString ) const
{
rtl::OString aString
= rtl::OUStringToOString(
rString, RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase();
const char * pStr = aString.getStr();
const char * pWild = m_aWildString.getStr();
int pos = 0;
int flag = 0;
while ( *pWild || flag )
{
switch ( *pWild )
{
case '?':
if ( *pStr == '\0' )
return 0;
break;
default:
if ( ( *pWild == '\\' ) && ( ( *( pWild + 1 ) == '?' )
|| ( *( pWild + 1 ) == '*') ) )
pWild++;
if ( *pWild != *pStr )
if ( !pos )
return 0;
else
pWild += pos;
else
break;
// Note: fall-thru's are intended!
case '*':
while ( *pWild == '*' )
pWild++;
if ( *pWild == '\0' )
return 1;
flag = 1;
pos = 0;
if ( *pStr == '\0' )
return ( *pWild == '\0' );
while ( *pStr && *pStr != *pWild )
{
if ( *pWild == '?' ) {
pWild++;
while ( *pWild == '*' )
pWild++;
}
pStr++;
if ( *pStr == '\0' )
return ( *pWild == '\0' );
}
break;
}
if ( *pWild != '\0' )
pWild++;
if ( *pStr != '\0' )
pStr++;
else
flag = 0;
if ( flag )
pos--;
}
return ( *pStr == '\0' ) && ( *pWild == '\0' );
}
//=========================================================================
bool getConfigStringValue(
const uno::Reference< container::XNameAccess > & xNameAccess,
const char * key,
rtl::OUString & value )
{
try
{
if ( !( xNameAccess->getByName( rtl::OUString::createFromAscii( key ) )
>>= value ) )
{
OSL_ENSURE( sal_False,
"InternetProxyDecider - "
"Error getting config item value!" );
return false;
}
}
catch ( lang::WrappedTargetException const & )
{
return false;
}
catch ( container::NoSuchElementException const & )
{
return false;
}
return true;
}
//=========================================================================
bool getConfigInt32Value(
const uno::Reference< container::XNameAccess > & xNameAccess,
const char * key,
sal_Int32 & value )
{
try
{
uno::Any aValue = xNameAccess->getByName(
rtl::OUString::createFromAscii( key ) );
if ( aValue.hasValue() && !( aValue >>= value ) )
{
OSL_ENSURE( sal_False,
"InternetProxyDecider - "
"Error getting config item value!" );
return false;
}
}
catch ( lang::WrappedTargetException const & )
{
return false;
}
catch ( container::NoSuchElementException const & )
{
return false;
}
return true;
}
//=========================================================================
//=========================================================================
//
// InternetProxyDecider_Impl Implementation.
//
//=========================================================================
//=========================================================================
InternetProxyDecider_Impl::InternetProxyDecider_Impl(
const uno::Reference< lang::XMultiServiceFactory >& rxSMgr )
: m_nProxyType( 0 ),
m_aHostnames( 256 ) // cache size
{
try
{
//////////////////////////////////////////////////////////////
// Read proxy configuration from config db.
//////////////////////////////////////////////////////////////
uno::Reference< lang::XMultiServiceFactory > xConfigProv(
rxSMgr->createInstance(
rtl::OUString::createFromAscii(
"com.sun.star.configuration.ConfigurationProvider" ) ),
uno::UNO_QUERY );
uno::Sequence< uno::Any > aArguments( 1 );
aArguments[ 0 ] <<= rtl::OUString::createFromAscii( CONFIG_ROOT_KEY );
uno::Reference< uno::XInterface > xInterface(
xConfigProv->createInstanceWithArguments(
rtl::OUString::createFromAscii(
"com.sun.star.configuration.ConfigurationAccess" ),
aArguments ) );
OSL_ENSURE( xInterface.is(),
"InternetProxyDecider - No config access!" );
if ( xInterface.is() )
{
uno::Reference< container::XNameAccess > xNameAccess(
xInterface, uno::UNO_QUERY );
OSL_ENSURE( xNameAccess.is(),
"InternetProxyDecider - No name access!" );
if ( xNameAccess.is() )
{
// *** Proxy type ***
getConfigInt32Value(
xNameAccess, PROXY_TYPE_KEY, m_nProxyType );
// *** No proxy list ***
rtl::OUString aNoProxyList;
getConfigStringValue(
xNameAccess, NO_PROXY_LIST_KEY, aNoProxyList );
setNoProxyList( aNoProxyList );
// *** HTTP ***
getConfigStringValue(
xNameAccess, HTTP_PROXY_NAME_KEY, m_aHttpProxy.aName );
m_aHttpProxy.nPort = -1;
getConfigInt32Value(
xNameAccess, HTTP_PROXY_PORT_KEY, m_aHttpProxy.nPort );
if ( m_aHttpProxy.nPort == -1 )
m_aHttpProxy.nPort = 80; // standard HTTP port.
// *** HTTPS ***
getConfigStringValue(
xNameAccess, HTTPS_PROXY_NAME_KEY, m_aHttpsProxy.aName );
m_aHttpsProxy.nPort = -1;
getConfigInt32Value(
xNameAccess, HTTPS_PROXY_PORT_KEY, m_aHttpsProxy.nPort );
if ( m_aHttpsProxy.nPort == -1 )
m_aHttpsProxy.nPort = 443; // standard HTTPS port.
// *** FTP ***
getConfigStringValue(
xNameAccess, FTP_PROXY_NAME_KEY, m_aFtpProxy.aName );
m_aFtpProxy.nPort = -1;
getConfigInt32Value(
xNameAccess, FTP_PROXY_PORT_KEY, m_aFtpProxy.nPort );
}
// Register as listener for config changes.
m_xNotifier = uno::Reference< util::XChangesNotifier >(
xInterface, uno::UNO_QUERY );
OSL_ENSURE( m_xNotifier.is(),
"InternetProxyDecider - No notifier!" );
if ( m_xNotifier.is() )
m_xNotifier->addChangesListener( this );
}
}
catch ( uno::Exception const & )
{
// createInstance, createInstanceWithArguments
OSL_ENSURE( sal_False, "InternetProxyDecider - Exception!" );
}
}
//=========================================================================
// virtual
InternetProxyDecider_Impl::~InternetProxyDecider_Impl()
{
}
//=========================================================================
void InternetProxyDecider_Impl::dispose()
{
uno::Reference< util::XChangesNotifier > xNotifier;
if ( m_xNotifier.is() )
{
osl::Guard< osl::Mutex > aGuard( m_aMutex );
if ( m_xNotifier.is() )
{
xNotifier = m_xNotifier;
m_xNotifier.clear();
}
}
// Do this unguarded!
if ( xNotifier.is() )
xNotifier->removeChangesListener( this );
}
//=========================================================================
bool InternetProxyDecider_Impl::shouldUseProxy( const rtl::OUString & rHost,
sal_Int32 nPort,
bool bUseFullyQualified ) const
{
rtl::OUStringBuffer aBuffer;
if ( ( rHost.indexOf( ':' ) != -1 ) &&
( rHost[ 0 ] != sal_Unicode( '[' ) ) )
{
// host is given as numeric IPv6 address
aBuffer.appendAscii( "[" );
aBuffer.append( rHost );
aBuffer.appendAscii( "]" );
}
else
{
// host is given either as numeric IPv4 address or non-numeric hostname
aBuffer.append( rHost );
}
aBuffer.append( sal_Unicode( ':' ) );
aBuffer.append( rtl::OUString::valueOf( nPort ) );
const rtl::OUString aHostAndPort( aBuffer.makeStringAndClear() );
std::vector< NoProxyListEntry >::const_iterator it
= m_aNoProxyList.begin();
const std::vector< NoProxyListEntry >::const_iterator end
= m_aNoProxyList.end();
while ( it != end )
{
if ( bUseFullyQualified )
{
if ( (*it).second.Matches( aHostAndPort ) )
return false;
}
else
{
if ( (*it).first.Matches( aHostAndPort ) )
return false;
}
it++;
}
return true;
}
//=========================================================================
const InternetProxyServer & InternetProxyDecider_Impl::getProxy(
const rtl::OUString & rProtocol,
const rtl::OUString & rHost,
sal_Int32 nPort ) const
{
osl::Guard< osl::Mutex > aGuard( m_aMutex );
if ( m_nProxyType == 0 )
{
// Never use proxy.
return m_aEmptyProxy;
}
if ( rHost.getLength() && m_aNoProxyList.size() )
{
//////////////////////////////////////////////////////////////////
// First, try direct hostname match - #110515#
//////////////////////////////////////////////////////////////////
if ( !shouldUseProxy( rHost, nPort, false ) )
return m_aEmptyProxy;
//////////////////////////////////////////////////////////////////
// Second, try match against full qualified hostname - #104401#
//////////////////////////////////////////////////////////////////
rtl::OUString aHost;
if ( ( rHost[ 0 ] == sal_Unicode( '[' ) ) &&
( rHost.getLength() > 1 ) )
{
// host is given as numeric IPv6 address. name resolution
// functions need hostname without square brackets.
aHost = rHost.copy( 1, rHost.getLength() - 2 );
}
else
{
aHost = rHost;
}
rtl::OUString aFullyQualifiedHost;
if ( !m_aHostnames.get( aHost, aFullyQualifiedHost ) )
{
// This might be quite expensive (DNS lookup).
const osl::SocketAddr aAddr( aHost, nPort );
aFullyQualifiedHost = aAddr.getHostname().toAsciiLowerCase();
m_aHostnames.put( aHost, aFullyQualifiedHost );
}
// Error resolving name? -> fallback.
if ( !aFullyQualifiedHost.getLength() )
aFullyQualifiedHost = aHost;
if ( aFullyQualifiedHost != aHost )
{
if ( !shouldUseProxy( aFullyQualifiedHost, nPort, false ) )
return m_aEmptyProxy;
}
//////////////////////////////////////////////////////////////////
// Third, try match of fully qualified entries in no-proxy list
// against full qualified hostname
//
// Example:
// list: staroffice-doc -> full: xyz.germany.sun.com
// in: staroffice-doc.germany.sun.com -> full: xyz.germany.sun.com
//
//////////////////////////////////////////////////////////////////
if ( !shouldUseProxy( aFullyQualifiedHost, nPort, true ) )
return m_aEmptyProxy;
}
if ( rProtocol.toAsciiLowerCase()
.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "ftp" ) ) )
{
if ( m_aFtpProxy.aName.getLength() > 0 && m_aFtpProxy.nPort >= 0 )
return m_aFtpProxy;
}
else if ( rProtocol.toAsciiLowerCase()
.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "https" ) ) )
{
if ( m_aHttpsProxy.aName.getLength() )
return m_aHttpsProxy;
}
else if ( m_aHttpProxy.aName.getLength() )
{
// All other protocols use the HTTP proxy.
return m_aHttpProxy;
}
return m_aEmptyProxy;
}
//=========================================================================
// virtual
void SAL_CALL InternetProxyDecider_Impl::changesOccurred(
const util::ChangesEvent& Event )
throw( uno::RuntimeException )
{
osl::Guard< osl::Mutex > aGuard( m_aMutex );
sal_Int32 nCount = Event.Changes.getLength();
if ( nCount )
{
const util::ElementChange* pElementChanges
= Event.Changes.getConstArray();
for ( sal_Int32 n = 0; n < nCount; ++n )
{
const util::ElementChange& rElem = pElementChanges[ n ];
rtl::OUString aKey;
if ( ( rElem.Accessor >>= aKey ) && aKey.getLength() )
{
if ( aKey.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(
PROXY_TYPE_KEY ) ) )
{
if ( !( rElem.Element >>= m_nProxyType ) )
{
OSL_ENSURE( sal_False,
"InternetProxyDecider - changesOccurred - "
"Error getting config item value!" );
}
}
else if ( aKey.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(
NO_PROXY_LIST_KEY ) ) )
{
rtl::OUString aNoProxyList;
if ( !( rElem.Element >>= aNoProxyList ) )
{
OSL_ENSURE( sal_False,
"InternetProxyDecider - changesOccurred - "
"Error getting config item value!" );
}
setNoProxyList( aNoProxyList );
}
else if ( aKey.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(
HTTP_PROXY_NAME_KEY ) ) )
{
if ( !( rElem.Element >>= m_aHttpProxy.aName ) )
{
OSL_ENSURE( sal_False,
"InternetProxyDecider - changesOccurred - "
"Error getting config item value!" );
}
}
else if ( aKey.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(
HTTP_PROXY_PORT_KEY ) ) )
{
if ( !( rElem.Element >>= m_aHttpProxy.nPort ) )
{
OSL_ENSURE( sal_False,
"InternetProxyDecider - changesOccurred - "
"Error getting config item value!" );
}
if ( m_aHttpProxy.nPort == -1 )
m_aHttpProxy.nPort = 80; // standard HTTP port.
}
else if ( aKey.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(
HTTPS_PROXY_NAME_KEY ) ) )
{
if ( !( rElem.Element >>= m_aHttpsProxy.aName ) )
{
OSL_ENSURE( sal_False,
"InternetProxyDecider - changesOccurred - "
"Error getting config item value!" );
}
}
else if ( aKey.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(
HTTPS_PROXY_PORT_KEY ) ) )
{
if ( !( rElem.Element >>= m_aHttpsProxy.nPort ) )
{
OSL_ENSURE( sal_False,
"InternetProxyDecider - changesOccurred - "
"Error getting config item value!" );
}
if ( m_aHttpsProxy.nPort == -1 )
m_aHttpsProxy.nPort = 443; // standard HTTPS port.
}
else if ( aKey.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(
FTP_PROXY_NAME_KEY ) ) )
{
if ( !( rElem.Element >>= m_aFtpProxy.aName ) )
{
OSL_ENSURE( sal_False,
"InternetProxyDecider - changesOccurred - "
"Error getting config item value!" );
}
}
else if ( aKey.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(
FTP_PROXY_PORT_KEY ) ) )
{
if ( !( rElem.Element >>= m_aFtpProxy.nPort ) )
{
OSL_ENSURE( sal_False,
"InternetProxyDecider - changesOccurred - "
"Error getting config item value!" );
}
}
}
}
}
}
//=========================================================================
// virtual
void SAL_CALL InternetProxyDecider_Impl::disposing(const lang::EventObject&)
throw( uno::RuntimeException )
{
if ( m_xNotifier.is() )
{
osl::Guard< osl::Mutex > aGuard( m_aMutex );
if ( m_xNotifier.is() )
m_xNotifier.clear();
}
}
//=========================================================================
void InternetProxyDecider_Impl::setNoProxyList(
const rtl::OUString & rNoProxyList )
{
osl::Guard< osl::Mutex > aGuard( m_aMutex );
m_aNoProxyList.clear();
if ( rNoProxyList.getLength() )
{
// List of connection endpoints hostname[:port],
// separated by semicolon. Wilcards allowed.
sal_Int32 nPos = 0;
sal_Int32 nEnd = rNoProxyList.indexOf( ';' );
sal_Int32 nLen = rNoProxyList.getLength();
do
{
if ( nEnd == -1 )
nEnd = nLen;
rtl::OUString aToken = rNoProxyList.copy( nPos, nEnd - nPos );
if ( aToken.getLength() )
{
rtl::OUString aServer;
rtl::OUString aPort;
// numerical IPv6 address?
bool bIPv6Address = false;
sal_Int32 nClosedBracketPos = aToken.indexOf( ']' );
if ( nClosedBracketPos == -1 )
nClosedBracketPos = 0;
else
bIPv6Address = true;
sal_Int32 nColonPos = aToken.indexOf( ':', nClosedBracketPos );
if ( nColonPos == -1 )
{
// No port given, server pattern equals current token
aPort = rtl::OUString::createFromAscii( "*" );
if ( aToken.indexOf( '*' ) == -1 )
{
// pattern describes exactly one server
aServer = aToken;
}
aToken += rtl::OUString::createFromAscii( ":*" );
}
else
{
// Port given, extract server pattern
sal_Int32 nAsterixPos = aToken.indexOf( '*' );
aPort = aToken.copy( nColonPos + 1 );
if ( nAsterixPos < nColonPos )
{
// pattern describes exactly one server
aServer = aToken.copy( 0, nColonPos );
}
}
rtl::OUStringBuffer aFullyQualifiedHost;
if ( aServer.getLength() )
{
// Remember fully qualified server name if current list
// entry specifies exactly one non-fully qualified server
// name.
// remove square brackets from host name in case it's
// a numerical IPv6 address.
if ( bIPv6Address )
aServer = aServer.copy( 1, aServer.getLength() - 2 );
// This might be quite expensive (DNS lookup).
const osl::SocketAddr aAddr( aServer, 0 );
rtl::OUString aTmp = aAddr.getHostname().toAsciiLowerCase();
if ( aTmp != aServer.toAsciiLowerCase() )
{
if ( bIPv6Address )
{
aFullyQualifiedHost.appendAscii( "[" );
aFullyQualifiedHost.append( aTmp );
aFullyQualifiedHost.appendAscii( "]" );
}
else
{
aFullyQualifiedHost.append( aTmp );
}
aFullyQualifiedHost.appendAscii( ":" );
aFullyQualifiedHost.append( aPort );
}
}
m_aNoProxyList.push_back(
NoProxyListEntry( WildCard( aToken ),
WildCard(
aFullyQualifiedHost
.makeStringAndClear() ) ) );
}
if ( nEnd != nLen )
{
nPos = nEnd + 1;
nEnd = rNoProxyList.indexOf( ';', nPos );
}
}
while ( nEnd != nLen );
}
}
} // namespace proxydecider_impl
//=========================================================================
//=========================================================================
//
// InternetProxyDecider Implementation.
//
//=========================================================================
//=========================================================================
InternetProxyDecider::InternetProxyDecider(
const uno::Reference< lang::XMultiServiceFactory >& rxSMgr )
: m_pImpl( new proxydecider_impl::InternetProxyDecider_Impl( rxSMgr ) )
{
m_pImpl->acquire();
}
//=========================================================================
InternetProxyDecider::~InternetProxyDecider()
{
// Break circular reference between config listener and notifier.
m_pImpl->dispose();
// Let him go...
m_pImpl->release();
}
//=========================================================================
bool InternetProxyDecider::shouldUseProxy( const rtl::OUString & rProtocol,
const rtl::OUString & rHost,
sal_Int32 nPort ) const
{
const InternetProxyServer & rData = m_pImpl->getProxy( rProtocol,
rHost,
nPort );
return ( rData.aName.getLength() > 0 );
}
//=========================================================================
const InternetProxyServer & InternetProxyDecider::getProxy(
const rtl::OUString & rProtocol,
const rtl::OUString & rHost,
sal_Int32 nPort ) const
{
return m_pImpl->getProxy( rProtocol, rHost, nPort );
}
} // namespace ucbhelper