diff options
author | Aaron Tunney <ext-aaron.2.tunney@nokia.com> | 2011-03-09 14:26:43 (GMT) |
---|---|---|
committer | Aaron Tunney <ext-aaron.2.tunney@nokia.com> | 2011-03-17 14:26:45 (GMT) |
commit | 6f7076d6fe80f656d8d2f7f1b005dc138b05bef3 (patch) | |
tree | 6fd42f51a9c05a267c675b739b9979b63c9238ce /src/network/kernel | |
parent | 8f239f0b0cb7faa580ba66197f56fb3d8109aaf7 (diff) | |
download | Qt-6f7076d6fe80f656d8d2f7f1b005dc138b05bef3.zip Qt-6f7076d6fe80f656d8d2f7f1b005dc138b05bef3.tar.gz Qt-6f7076d6fe80f656d8d2f7f1b005dc138b05bef3.tar.bz2 |
Implementation of async DNS lookup.
Reviewed-by: Shane Kearns
Diffstat (limited to 'src/network/kernel')
-rw-r--r-- | src/network/kernel/qhostinfo.cpp | 53 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo_p.h | 115 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo_symbian.cpp | 323 |
3 files changed, 473 insertions, 18 deletions
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index 5ec6041..7a15a05 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -56,10 +56,14 @@ QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager) - //#define QHOSTINFO_DEBUG +#ifndef Q_OS_SYMBIAN +Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager) +#else +Q_GLOBAL_STATIC(QSymbianHostInfoLookupManger, theHostInfoLookupManager) +#endif + /*! \class QHostInfo \brief The QHostInfo class provides static functions for host name lookups. @@ -152,6 +156,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, qDebug("QHostInfo::lookupHost(\"%s\", %p, %s)", name.toLatin1().constData(), receiver, member ? member + 1 : 0); #endif + if (!QAbstractEventDispatcher::instance(QThread::currentThread())) { qWarning("QHostInfo::lookupHost() called with no event dispatcher"); return -1; @@ -172,7 +177,9 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, return id; } +#ifndef Q_OS_SYMBIAN QHostInfoLookupManager *manager = theHostInfoLookupManager(); + if (manager) { // the application is still alive if (manager->cache.isEnabled()) { @@ -187,11 +194,38 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, return id; } } + // cache is not enabled or it was not in the cache, do normal lookup QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id); QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); manager->scheduleLookup(runnable); } +#else + QSymbianHostInfoLookupManger *manager = theHostInfoLookupManager(); + + if (manager) { + // the application is still alive + if (manager->cache.isEnabled()) { + // check cache first + bool valid = false; + QHostInfo info = manager->cache.get(name, &valid); + if (valid) { + info.setLookupId(id); + QHostInfoResult result; + QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); + result.emitResultsReady(info); + return id; + } + } + + // cache is not enabled or it was not in the cache, do normal lookup + QSymbianHostResolver *symbianResolver = 0; + QT_TRAP_THROWING(symbianResolver = new QSymbianHostResolver(name, id)); + QObject::connect(&symbianResolver->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); + manager->scheduleLookup(symbianResolver); + } +#endif + return id; } @@ -225,7 +259,7 @@ QHostInfo QHostInfo::fromName(const QString &name) #endif QHostInfo hostInfo = QHostInfoAgent::fromName(name); - QHostInfoLookupManager *manager = theHostInfoLookupManager(); + QAbstractHostInfoLookupManger* manager = theHostInfoLookupManager(); manager->cache.put(name, hostInfo); return hostInfo; } @@ -406,6 +440,7 @@ void QHostInfo::setErrorString(const QString &str) \sa hostName() */ +#ifndef Q_OS_SYMBIAN QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i) { setAutoDelete(true); @@ -632,6 +667,7 @@ void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r) finishedLookups.append(r); work(); } +#endif // This function returns immediately when we had a result in the cache, else it will later emit a signal QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id) @@ -640,7 +676,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *id = -1; // check cache - QHostInfoLookupManager* manager = theHostInfoLookupManager(); + QAbstractHostInfoLookupManger* manager = theHostInfoLookupManager(); if (manager && manager->cache.isEnabled()) { QHostInfo info = manager->cache.get(name, valid); if (*valid) { @@ -657,7 +693,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char void qt_qhostinfo_clear_cache() { - QHostInfoLookupManager* manager = theHostInfoLookupManager(); + QAbstractHostInfoLookupManger* manager = theHostInfoLookupManager(); if (manager) { manager->clear(); } @@ -665,7 +701,7 @@ void qt_qhostinfo_clear_cache() void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e) { - QHostInfoLookupManager* manager = theHostInfoLookupManager(); + QAbstractHostInfoLookupManger* manager = theHostInfoLookupManager(); if (manager) { manager->cache.setEnabled(e); } @@ -733,4 +769,9 @@ void QHostInfoCache::clear() cache.clear(); } +QAbstractHostInfoLookupManger* QAbstractHostInfoLookupManger::globalInstance() +{ + return theHostInfoLookupManager(); +} + QT_END_NAMESPACE diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h index b568ec2..ec7a63e 100644 --- a/src/network/kernel/qhostinfo_p.h +++ b/src/network/kernel/qhostinfo_p.h @@ -69,9 +69,18 @@ #include <QElapsedTimer> #include <QCache> +#include <QNetworkSession> + +#ifdef Q_OS_SYMBIAN +// Symbian Headers +#include <es_sock.h> +#include <in_sock.h> +#endif + QT_BEGIN_NAMESPACE + class QHostInfoResult : public QObject { Q_OBJECT @@ -91,6 +100,12 @@ class QHostInfoAgent : public QObject Q_OBJECT public: static QHostInfo fromName(const QString &hostName); + static QHostInfo fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession); + +#ifdef Q_OS_SYMBIAN + static int lookupHost(const QString &name, QObject *receiver, const char *member); + static void abortHostLookup(int lookupId); +#endif }; class QHostInfoPrivate @@ -151,7 +166,25 @@ public: QHostInfoResult resultEmitter; }; -class QHostInfoLookupManager : public QObject + +class QAbstractHostInfoLookupManger : public QObject +{ + Q_OBJECT + +public: + ~QAbstractHostInfoLookupManger() {} + virtual void clear() = 0; + + QHostInfoCache cache; + +protected: + QAbstractHostInfoLookupManger() {} + static QAbstractHostInfoLookupManger* globalInstance(); + +}; + +#ifndef Q_OS_SYMBIAN +class QHostInfoLookupManager : public QAbstractInfoLookupManager { Q_OBJECT public: @@ -169,8 +202,6 @@ public: void lookupFinished(QHostInfoRunnable *r); bool wasAborted(int id); - QHostInfoCache cache; - friend class QHostInfoRunnable; protected: QList<QHostInfoRunnable*> currentLookups; // in progress @@ -189,6 +220,84 @@ private slots: void waitForThreadPoolDone() { threadPool.waitForDone(); } }; +#else + +class QSymbianHostResolver : public CActive +{ +public: + QSymbianHostResolver(const QString &hostName, int id); + ~QSymbianHostResolver(); + + QHostInfo requestHostLookup(); + int id(); + + QHostInfoResult resultEmitter; + +private: + void DoCancel(); + void RunL(); + void run(); + TInt RunError(TInt aError); + + void processNameResults(); + void processAddressResults(); + +private: + int iId; + + const QString iHostName; + + RSocketServ& iSocketServ; + RHostResolver iHostResolver; + + TRequestStatus iStatus; + + TNameEntry iNameResult; + QHostAddress iAddress; + + QHostInfo iResults; + + enum { + EIdle, + EGetByName, + EGetByAddress + } iState; +}; + +class QSymbianHostInfoLookupManger : public QAbstractHostInfoLookupManger +{ + Q_OBJECT +public: + QSymbianHostInfoLookupManger(); + ~QSymbianHostInfoLookupManger(); + + static QSymbianHostInfoLookupManger* globalInstance(); + + int id(); + void clear(); + + // called from QHostInfo + void scheduleLookup(QSymbianHostResolver *r); + void abortLookup(int id); + + // called from QSymbianHostResolver + void lookupFinished(QSymbianHostResolver *r); + +private: + void runNextLookup(); + + static const int KMaxConcurrentLookups = 5; + + RPointerArray<QSymbianHostResolver> iCurrentLookups; + RPointerArray<QSymbianHostResolver> iScheduledLookups; + RPointerArray<QSymbianHostResolver> iFinishedLookups; + + QMutex mutex; +}; +#endif + + + QT_END_NAMESPACE #endif // QHOSTINFO_P_H diff --git a/src/network/kernel/qhostinfo_symbian.cpp b/src/network/kernel/qhostinfo_symbian.cpp index 9599274..2b582bd 100644 --- a/src/network/kernel/qhostinfo_symbian.cpp +++ b/src/network/kernel/qhostinfo_symbian.cpp @@ -41,10 +41,6 @@ //#define QHOSTINFO_DEBUG -// Symbian Headers -#include <es_sock.h> -#include <in_sock.h> - // Qt Headers #include <QByteArray> #include <QUrl> @@ -54,11 +50,13 @@ #include "qhostinfo_p.h" #include <private/qcore_symbian_p.h> +#include <private/qsystemerror_p.h> QT_BEGIN_NAMESPACE -QHostInfo QHostInfoAgent::fromName(const QString &hostName) + +QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession) { QHostInfo results; @@ -66,12 +64,20 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) RSocketServ socketServ(qt_symbianGetSocketServer()); RHostResolver hostResolver; + + // TODO - check if networkSession is null + // RConnection connection = magicalApi(networkSession); + // int err = connection.Open(blah, blah); + // if (err) { + // do something; + // } + // Will return both IPv4 and IPv6 // TODO: Pass RHostResolver.Open() the global RConnection int err = hostResolver.Open(socketServ, KAfInet, KProtocolInetUdp); if (err) { results.setError(QHostInfo::UnknownError); - results.setErrorString(tr("Symbian error code: %1").arg(err)); + results.setErrorString(QSystemError(err,QSystemError::NativeError).toString()); return results; } @@ -99,7 +105,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) results.setErrorString(tr("Host not found")); } else { results.setError(QHostInfo::UnknownError); - results.setErrorString(tr("Symbian error code: %1").arg(err)); + results.setErrorString(QSystemError(err,QSystemError::NativeError).toString()); } return results; @@ -134,7 +140,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) results.setErrorString(tr("Host not found")); } else { results.setError(QHostInfo::UnknownError); - results.setErrorString(tr("Symbian error code: %1").arg(err)); + results.setErrorString(QSystemError(err,QSystemError::NativeError).toString()); } return results; @@ -156,7 +162,8 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) hostAdd = nameResult().iAddr; hostAdd.Output(ipAddr); - if (ipAddr.Length() > 0) { + // Ensure that record is valid (not an alias and with length greater than 0) + if (!(nameResult().iFlags & TNameRecord::EAlias) && (ipAddr.Length() > 0)) { if (nameResult().iAddr.Family() == KAfInet) { // IPv4 - prepend hostAddresses.prepend(QHostAddress(qt_TDesC2QString(ipAddr))); @@ -173,6 +180,13 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) return results; } +QHostInfo QHostInfoAgent::fromName(const QString &hostName) +{ + // null shared pointer + QSharedPointer<QNetworkSession> networkSession; + return fromName(hostName, networkSession); +} + QString QHostInfo::localHostName() { // Connect to ESOCK @@ -201,4 +215,295 @@ QString QHostInfo::localDomainName() return QString(); } + +QSymbianHostResolver::QSymbianHostResolver(const QString &hostName, int identifier) + : CActive(CActive::EPriorityStandard), iId(identifier), iHostName(hostName), + iSocketServ(qt_symbianGetSocketServer()) +{ + CActiveScheduler::Add(this); +} + +QSymbianHostResolver::~QSymbianHostResolver() +{ + Cancel(); + iHostResolver.Close(); +} + +// Async equivalent to QHostInfoAgent::fromName() +QHostInfo QSymbianHostResolver::requestHostLookup() +{ + +#if defined(QHOSTINFO_DEBUG) + qDebug("QHostInfoAgent::fromName(%s) looking up...", + hostName.toLatin1().constData()); +#endif + + int err = iHostResolver.Open(iSocketServ, KAfInet, KProtocolInetUdp); + if (err) { + // What are we doing with iResults?? + iResults.setError(QHostInfo::UnknownError); + iResults.setErrorString(QObject::tr("Symbian error code: %1").arg(err)); + + iHostResolver.Close(); + return iResults; + } + + if (iAddress.setAddress(iHostName)) { + // Reverse lookup + + TInetAddr IpAdd; + IpAdd.Input(qt_QString2TPtrC(iHostName)); + + // Asynchronous request. + iHostResolver.GetByAddress(IpAdd, iNameResult, iStatus); // <---- ASYNC + iState = EGetByAddress; + + } else { + + // IDN support + QByteArray aceHostname = QUrl::toAce(iHostName); + iResults.setHostName(iHostName); + if (aceHostname.isEmpty()) { + iResults.setError(QHostInfo::HostNotFound); + iResults.setErrorString(iHostName.isEmpty() ? + QCoreApplication::translate("QHostInfoAgent", "No host name given") : + QCoreApplication::translate("QHostInfoAgent", "Invalid hostname")); + + iHostResolver.Close(); + return iResults; + } + + // Asynchronous request. + iHostResolver.GetByName(qt_QString2TPtrC(QString::fromLatin1(aceHostname)), iNameResult, iStatus); + iState = EGetByName; + } + + SetActive(); + + return iResults; +} + +void QSymbianHostResolver::DoCancel() +{ + QSymbianHostInfoLookupManger *manager = QSymbianHostInfoLookupManger::globalInstance(); + manager->lookupFinished(this); + iHostResolver.Cancel(); +} + +void QSymbianHostResolver::RunL() +{ + QT_TRYCATCH_LEAVING(run()); +} + +void QSymbianHostResolver::run() +{ + if (iState == EGetByName) + processNameResults(); + else if (iState == EGetByAddress) + processAddressResults(); + + iState = EIdle; + + QSymbianHostInfoLookupManger *manager = QSymbianHostInfoLookupManger::globalInstance(); + manager->lookupFinished(this); + + resultEmitter.emitResultsReady(iResults); + + delete this; +} + +TInt QSymbianHostResolver::RunError(TInt aError) +{ + QT_TRY { + iState = EIdle; + + QSymbianHostInfoLookupManger *manager = QSymbianHostInfoLookupManger::globalInstance(); + manager->lookupFinished(this); + + iResults.setError(QHostInfo::UnknownError); + iResults.setErrorString(QSystemError(aError,QSystemError::NativeError).toString()); + + resultEmitter.emitResultsReady(iResults); + } + QT_CATCH(...) {} + + delete this; + + return KErrNone; +} + +void QSymbianHostResolver::processNameResults() +{ + TInt err = iStatus.Int(); + if (err < 0) { + // TODO - Could there be other errors? Symbian docs don't say. + if (err = KErrNotFound) { + iResults.setError(QHostInfo::HostNotFound); + iResults.setErrorString(QObject::tr("Host not found")); + } else { + iResults.setError(QHostInfo::UnknownError); + iResults.setErrorString(QSystemError(err,QSystemError::NativeError).toString()); + } + + iHostResolver.Close(); + return; + } + + QList<QHostAddress> hostAddresses; + + TInetAddr hostAdd = iNameResult().iAddr; + // 39 is the maximum length of an IPv6 address. + TBuf<39> ipAddr; + + // Fill ipAddr with the IP address from hostAdd + hostAdd.Output(ipAddr); + if (ipAddr.Length() > 0) + hostAddresses.append(QHostAddress(qt_TDesC2QString(ipAddr))); + + // Check if there's more than one IP address linkd to this name + while (iHostResolver.Next(iNameResult) == KErrNone) { + hostAdd = iNameResult().iAddr; + hostAdd.Output(ipAddr); + + // Ensure that record is valid (not an alias and with length greater than 0) + if (!(iNameResult().iFlags & TNameRecord::EAlias) && (ipAddr.Length() > 0)) { + if (iNameResult().iAddr.Family() == KAfInet) { + // IPv4 - prepend + hostAddresses.prepend(QHostAddress(qt_TDesC2QString(ipAddr))); + } else { + // IPv6 - append + hostAddresses.append(QHostAddress(qt_TDesC2QString(ipAddr))); + } + } + } + + iResults.setAddresses(hostAddresses); +} + +void QSymbianHostResolver::processAddressResults() +{ + TInt err = iStatus.Int(); + + if (err < 0) { + // TODO - Could there be other errors? Symbian docs don't say. + if (err = KErrNotFound) { + iResults.setError(QHostInfo::HostNotFound); + iResults.setErrorString(QObject::tr("Host not found")); + } else { + iResults.setError(QHostInfo::UnknownError); + iResults.setErrorString(QSystemError(err,QSystemError::NativeError).toString()); + } + + return; + } + + iResults.setHostName(qt_TDesC2QString(iNameResult().iName)); + iResults.setAddresses(QList<QHostAddress>() << iAddress); +} + + +int QSymbianHostResolver::id() +{ + return iId; +} + +QSymbianHostInfoLookupManger::QSymbianHostInfoLookupManger() +{ +} + +QSymbianHostInfoLookupManger::~QSymbianHostInfoLookupManger() +{ + iCurrentLookups.Close(); + iScheduledLookups.Close(); +} + +void QSymbianHostInfoLookupManger::clear() +{ + QMutexLocker locker(&mutex); + iCurrentLookups.ResetAndDestroy(); + iScheduledLookups.ResetAndDestroy(); +} + +void QSymbianHostInfoLookupManger::lookupFinished(QSymbianHostResolver *r) +{ + QMutexLocker locker(&mutex); + + // remove finished lookup from array and destroy + TInt count = iCurrentLookups.Count(); + for (TInt i = 0; i < count; i++) { + if (iCurrentLookups[i]->id() == r->id()) { + iCurrentLookups.Remove(i); + break; + } + } + + runNextLookup(); +} + +void QSymbianHostInfoLookupManger::runNextLookup() +{ + // check to see if there are any scheduled lookups + if (iScheduledLookups.Count() > 0) { + // if so, move one to the current lookups and run it + // FIFO + QSymbianHostResolver* hostResolver = iScheduledLookups[0]; + iCurrentLookups.Append(hostResolver); + iScheduledLookups.Remove(0); + hostResolver->requestHostLookup(); + } +} + +// called from QHostInfo +void QSymbianHostInfoLookupManger::scheduleLookup(QSymbianHostResolver* r) +{ + QMutexLocker locker(&mutex); + + // Check to see if we have space on the current lookups pool. + if (iCurrentLookups.Count() >= KMaxConcurrentLookups) { + // If no, schedule for later. + iScheduledLookups.Append(r); + return; + } else { + // If yes, add it to the current lookups. + iCurrentLookups.Append(r); + + // ... and trigger the async call. + r->requestHostLookup(); + } +} + +void QSymbianHostInfoLookupManger::abortLookup(int id) +{ + QMutexLocker locker(&mutex); + + int i = 0; + // Find the aborted lookup by ID. + // First in the current lookups. + for (i = 0; i < iCurrentLookups.Count(); i++) { + if (id = iCurrentLookups[i]->id()) { + QSymbianHostResolver* r = iCurrentLookups[i]; + iCurrentLookups.Remove(i); + r->Cancel(); + runNextLookup(); + return; + } + } + // Then in the scheduled lookups. + for (i = 0; i < iScheduledLookups.Count(); i++) { + if (id = iScheduledLookups[i]->id()) { + QSymbianHostResolver* r = iScheduledLookups[i]; + iScheduledLookups.Remove(i); + delete r; + return; + } + } +} + + +QSymbianHostInfoLookupManger* QSymbianHostInfoLookupManger::globalInstance() +{ + return static_cast<QSymbianHostInfoLookupManger*> + (QAbstractHostInfoLookupManger::globalInstance()); +} + QT_END_NAMESPACE |