/*! @file
@ id $ Id $
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
# ifndef PROXYFACE_HXX
# define PROXYFACE_HXX
# ifdef QT_NETWORK_LIB
# include <QtNetwork/QNetworkProxy>
# include <QtNetwork/QNetworkAccessManager>
# include <QtNetwork/QNetworkReply>
# include <QtCore/QTimer>
# include <QtCore/QDebug>
# include <map>
# endif
# include <list>
# include <string>
# include <sstream>
//! auto proxy configuration
namespace proxy {
//! exceptions
namespace exc {
//! unspecific error
class error : std : : exception {
public :
virtual const char * what ( ) const throw ( ) {
return " Auto Proxy Detection Error " ;
}
} ;
}
//! Proxy types
enum Type {
DIRECT , //< direct connection, no proxy
DEFAULT , //< default proxy
HTTP , //< HTTP proxy host
SOCKS //<SOCKS5 proxy
} ;
//! One proxy
struct Proxy {
Proxy ( ) : type ( DIRECT ) , port ( 0 ) { }
Proxy ( Type t , std : : string h , unsigned int p ) : type ( t ) , host ( h ) , port ( p ) { }
Proxy ( Type t , std : : string h , std : : string p ) : type ( t ) , host ( h ) , port ( 0 ) {
std : : stringstream ( p ) > > port ;
}
Type type ;
std : : string host ;
unsigned int port ;
} ;
//! List of proxies, type -> url and port
typedef std : : list < Proxy > List ;
//! Unified Interface for accessing proxy::Face
/*! Abstract interface, impementation for Unix and Windoze differs.
Instanciate proxy : : Face , which is a typedef to your
platform ' s implementation .
@ code
proxy : : Face pf ; // keep for program life time (because of caching)
proxy : : List pf . proxies ( " http://swisssign.com " ) ;
[ . . . ] // set proxy, read from http://swisssign.com
@ endcode
@ example test . cxx */
class Interface
# ifdef QT_NETWORK_LIB
: public QObject
# endif
{
# ifdef QT_NETWORK_LIB
Q_OBJECT ;
# endif
public :
//! Keep your instance as long as possible, because of caching.
Interface ( ) {
# ifdef QT_NETWORK_LIB
if ( ! connect ( & _timeout , SIGNAL ( timeout ( ) ) , SLOT ( timeout ( ) ) ) )
qFatal ( " connect failed " ) ;
# endif
}
virtual ~ Interface ( ) { }
//! Get list of proxies for a given URL.
virtual List proxies ( const std : : string & url ) = 0 ;
# ifdef QT_NETWORK_LIB
//! If Qt Network is available you may check the proxies found
/*! First checks for direct access to the target. If this is not
possible , starts scan for proxies .
Returns immediately and signals proxyFound ( ) if a valid
configuration has been found , or proxyError ( ) in case of
timeout . You should wait for either of the signals , before you
can call this method again .
@ param url the url to find a proxy for
@ param timeout [ ms ] time to give up search */
void proxy ( const std : : string & url , int timeout = 2000 ) {
qDebug ( ) < < " Search proxy for URL, direct and default "
< < " url= " < < url . data ( ) < < " timeout= " < < timeout ;
for ( Requests : : iterator it ( _requests . begin ( ) ) ; it ! = _requests . end ( ) ; + + it )
delete it - > second . first ;
_requests . clear ( ) ;
_direct = true ; // first try direct access
QNetworkProxy directProxy ( QNetworkProxy : : NoProxy ) ;
setupProxyCheck ( directProxy , url ) ;
QNetworkProxy defaultProxy ;
setupProxyCheck ( defaultProxy , url ) ;
_timeout . setSingleShot ( true ) ;
_timeout . setInterval ( timeout ) ;
_timeout . start ( ) ;
}
//! If Qt Network is available you may check the proxies found
/*! @copydoc proxy(const std::string&, int) */
void proxy ( const QUrl & url , int timeout = 2000 ) {
proxy ( url . toString ( ) . toStdString ( ) , timeout ) ;
}
//! If Qt Network is available you may check the proxies found
/*! @copydoc proxy(const std::string&, int) */
void proxy ( const QString & url , int timeout = 2000 ) {
proxy ( url . toStdString ( ) , timeout ) ;
}
static QString toString ( const QNetworkProxy & p ) {
switch ( p . type ( ) ) {
case QNetworkProxy : : NoProxy :
return QString ( " direct:// " ) ;
case QNetworkProxy : : DefaultProxy :
if ( QNetworkProxy : : applicationProxy ( ) . type ( )
! = QNetworkProxy : : DefaultProxy )
return QString ( " Default Proxy: %1 " )
. arg ( toString ( QNetworkProxy : : applicationProxy ( ) ) ) ;
else
return QString ( " Default Proxy: **ERROR** "
" application proxy is configured as "
" QNetworkProxy::DefaultProxy " ) ;
case QNetworkProxy : : Socks5Proxy :
return QString ( " socks://%1:%2 " ) . arg ( p . hostName ( ) ) . arg ( p . port ( ) ) ;
case QNetworkProxy : : HttpProxy :
return QString ( " http://%1:%2 " ) . arg ( p . hostName ( ) ) . arg ( p . port ( ) ) ;
case QNetworkProxy : : HttpCachingProxy :
return QString ( " http-cache://%1:%2 " ) . arg ( p . hostName ( ) ) . arg ( p . port ( ) ) ;
case QNetworkProxy : : FtpCachingProxy :
return QString ( " ftp-cache://%1:%2 " ) . arg ( p . hostName ( ) ) . arg ( p . port ( ) ) ;
}
return QString ( " **ERROR** illegal proxy type " ) ;
}
Q_SIGNALS :
//! Signals a valid proxy for the URL requested earlier.
void proxyFound ( const QUrl & , const QNetworkProxy & ) ;
//! Signals a timeout, and no valid proxy for the URL requested earlier.
void proxyError ( QNetworkReply : : NetworkError ) ;
private :
void setupProxyCheck ( const QNetworkProxy & prxy , const std : : string & url ) {
qDebug ( ) < < " Testing proxy for url: " < < toString ( prxy ) < < " url= " < < url . data ( ) ;
QNetworkAccessManager * manager ( new QNetworkAccessManager ) ;
manager - > setProxy ( prxy ) ;
if ( ! connect ( manager , SIGNAL ( finished ( QNetworkReply * ) ) ,
this , SLOT ( replyFinished ( QNetworkReply * ) ) ) )
qFatal ( " connect failed " ) ;
_requests . insert
( std : : make_pair ( manager - > head
( QNetworkRequest
( QUrl ( QString : : fromStdString ( url ) ) ) ) ,
std : : make_pair ( manager , prxy ) ) ) ;
}
private Q_SLOTS :
void timeout ( ) {
_timeout . stop ( ) ;
std : : string url ;
for ( Requests : : iterator it ( _requests . begin ( ) ) ;
it ! = _requests . end ( ) ; + + it ) {
url = it - > first - > url ( ) . toString ( ) . toStdString ( ) ;
delete it - > second . first ;
}
_requests . clear ( ) ;
qDebug ( ) < < " Proxy detection timed out " < < " url= " < < url . data ( ) ;
if ( _direct & & url . size ( ) ) {
qDebug ( ) < < " Direct or preconfigured proxy not available, "
" try autoproxy negotiation " ;
_direct = false ;
List l ( proxies ( url ) ) ;
for ( List : : const_iterator it ( l . begin ( ) ) ; it ! = l . end ( ) ; + + it ) {
QNetworkProxy prxy ( ( it - > type = = DEFAULT ? QNetworkProxy : : DefaultProxy
: ( it - > type = = HTTP ? QNetworkProxy : : HttpProxy
: ( it - > type = = SOCKS ? QNetworkProxy : : Socks5Proxy
: QNetworkProxy : : NoProxy ) ) ) ,
QString : : fromStdString ( it - > host ) , it - > port ) ;
setupProxyCheck ( prxy , url ) ;
}
_timeout . start ( ) ;
} else {
qDebug ( ) < < " No proxy at all, giving up - offline? " ;
proxyError ( QNetworkReply : : TimeoutError ) ;
}
}
void replyFinished ( QNetworkReply * reply ) {
_timeout . stop ( ) ;
QNetworkProxy prxy ( _requests [ reply ] . second ) ;
QUrl url ( reply - > url ( ) ) ;
for ( Requests : : iterator it ( _requests . begin ( ) ) ; it ! = _requests . end ( ) ; + + it )
delete it - > second . first ;
_requests . clear ( ) ;
qDebug ( ) < < " SUCCESS - Valid proxy found for url: "
< < " url= " < < url < < " proxy= " < < toString ( prxy ) ;
proxyFound ( url , prxy ) ;
}
private :
typedef std : : map
< QNetworkReply * , std : : pair < QNetworkAccessManager * , QNetworkProxy > >
Requests ;
Requests _requests ;
bool _direct ;
QTimer _timeout ;
# endif
} ;
}
# ifdef WIN32
// use windoze proprietary winhttp
# include <proxyface/windoze.hxx>
namespace proxy {
typedef Windoze Face ;
}
# else
// normal operating systems: use http://code.google.com/p/libproxy
# include <proxyface/unix.hxx>
namespace proxy {
typedef Unix Face ;
}
# endif
# endif