summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp6
-rw-r--r--src/network/kernel/qhostinfo.cpp161
-rw-r--r--src/network/kernel/qhostinfo_p.h32
-rw-r--r--src/network/socket/qabstractsocket.cpp26
4 files changed, 211 insertions, 14 deletions
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index e92b2f3..806452c 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -681,7 +681,11 @@ void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
alreadyPipelinedRequests.clear();
- QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
+ // this function is called from _q_disconnected which is called because
+ // of ~QHttpNetworkConnectionPrivate
+ if (qobject_cast<QHttpNetworkConnection*>(connection))
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
void QHttpNetworkConnectionChannel::eatWhitespace()
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index 93f6d13..4800e9d 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -112,6 +112,13 @@ Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
To retrieve the name of the local host, use the static
QHostInfo::localHostName() function.
+ \note Since Qt 4.6.1 QHostInfo is using multiple threads for DNS lookup
+ instead of one dedicated DNS thread. This improves performance,
+ but also changes the order of signal emissions when using lookupHost()
+ compared to previous versions of Qt.
+ \note Since Qt 4.6.3 QHostInfo is using a small internal 60 second DNS cache
+ for performance improvements.
+
\sa QAbstractSocket, {http://www.rfc-editor.org/rfc/rfc3492.txt}{RFC 3492}
*/
@@ -181,9 +188,26 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver,
receiver, member, Qt::QueuedConnection);
result.data()->emitResultsReady(hostInfo);
#else
- QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id);
- QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
- theHostInfoLookupManager()->scheduleLookup(runnable);
+ QHostInfoLookupManager *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
+ QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id);
+ QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
+ manager->scheduleLookup(runnable);
+ }
#endif
return id;
@@ -418,14 +442,24 @@ void QHostInfoRunnable::run()
return;
}
- // check cache
- // FIXME
-
- // if not in cache: OS lookup
- QHostInfo hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
-
- // save to cache
- // FIXME
+ QHostInfo hostInfo;
+
+ // QHostInfo::lookupHost already checks the cache. However we need to check
+ // it here too because it might have been cache saved by another QHostInfoRunnable
+ // in the meanwhile while this QHostInfoRunnable was scheduled but not running
+ if (manager->cache.isEnabled()) {
+ // check the cache first
+ bool valid = false;
+ hostInfo = manager->cache.get(toBeLookedUp, &valid);
+ if (!valid) {
+ // not in cache, we need to do the lookup and store the result in the cache
+ hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
+ manager->cache.put(toBeLookedUp, hostInfo);
+ }
+ } else {
+ // cache is not enabled, just do the lookup and continue
+ hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
+ }
// check aborted again
if (manager->wasAborted(id)) {
@@ -451,6 +485,11 @@ QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), was
QHostInfoLookupManager::~QHostInfoLookupManager()
{
wasDeleted = true;
+
+ // don't qDeleteAll currentLookups, the QThreadPool has ownership
+ qDeleteAll(postponedLookups);
+ qDeleteAll(scheduledLookups);
+ qDeleteAll(finishedLookups);
}
void QHostInfoLookupManager::work()
@@ -570,6 +609,106 @@ void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r)
work();
}
+// This function returns immediatly 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)
+{
+ *valid = false;
+ *id = -1;
+
+ // check cache
+ QHostInfoLookupManager* manager = theHostInfoLookupManager();
+ if (manager && manager->cache.isEnabled()) {
+ QHostInfo info = manager->cache.get(name, valid);
+ if (*valid) {
+ return info;
+ }
+ }
+
+ // was not in cache, trigger lookup
+ *id = QHostInfo::lookupHost(name, receiver, member);
+
+ // return empty response, valid==false
+ return QHostInfo();
+}
+
+void qt_qhostinfo_clear_cache()
+{
+ QHostInfoLookupManager* manager = theHostInfoLookupManager();
+ if (manager) {
+ manager->cache.clear();
+ }
+}
+
+void Q_NETWORK_EXPORT qt_qhostinfo_enable_cache(bool e)
+{
+ QHostInfoLookupManager* manager = theHostInfoLookupManager();
+ if (manager) {
+ manager->cache.setEnabled(e);
+ }
+}
+
+// cache for 60 seconds
+// cache 64 items
+QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(64)
+{
+#ifdef QT_QHOSTINFO_CACHE_DISABLED_BY_DEFAULT
+ enabled = false;
+#endif
+}
+
+bool QHostInfoCache::isEnabled()
+{
+ return enabled;
+}
+
+// this function is currently only used for the auto tests
+// and not usable by public API
+void QHostInfoCache::setEnabled(bool e)
+{
+ enabled = e;
+}
+
+
+QHostInfo QHostInfoCache::get(const QString &name, bool *valid)
+{
+ QMutexLocker locker(&this->mutex);
+
+ *valid = false;
+ if (cache.contains(name)) {
+ QHostInfoCacheElement *element = cache.object(name);
+ if (element->age.elapsed() < max_age*1000)
+ *valid = true;
+ return element->info;
+
+ // FIXME idea:
+ // if too old but not expired, trigger a new lookup
+ // to freshen our cache
+ }
+
+ return QHostInfo();
+}
+
+void QHostInfoCache::put(const QString &name, const QHostInfo &info)
+{
+ // if the lookup failed, don't cache
+ if (info.error() != QHostInfo::NoError)
+ return;
+
+ QHostInfoCacheElement* element = new QHostInfoCacheElement();
+ element->info = info;
+ element->age = QTime();
+ element->age.start();
+
+ QMutexLocker locker(&this->mutex);
+ cache.insert(name, element); // cache will take ownership
+}
+
+void QHostInfoCache::clear()
+{
+ QMutexLocker locker(&this->mutex);
+ cache.clear();
+}
+
#endif // QT_NO_THREAD
QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h
index d6aa287..2b26b07 100644
--- a/src/network/kernel/qhostinfo_p.h
+++ b/src/network/kernel/qhostinfo_p.h
@@ -68,6 +68,8 @@
#include "QtCore/qrunnable.h"
#include "QtCore/qlist.h"
#include "QtCore/qqueue.h"
+#include <QTime>
+#include <QCache>
#endif
QT_BEGIN_NAMESPACE
@@ -111,6 +113,34 @@ public:
};
#ifndef QT_NO_THREAD
+// These functions are outside of the QHostInfo class and strictly internal.
+// Do NOT use them outside of QAbstractSocket.
+QHostInfo Q_NETWORK_EXPORT qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id);
+void Q_NETWORK_EXPORT qt_qhostinfo_clear_cache();
+void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e);
+
+class QHostInfoCache
+{
+public:
+ QHostInfoCache();
+ const int max_age; // seconds
+
+ QHostInfo get(const QString &name, bool *valid);
+ void put(const QString &name, const QHostInfo &info);
+ void clear();
+
+ bool isEnabled();
+ void setEnabled(bool e);
+private:
+ bool enabled;
+ struct QHostInfoCacheElement {
+ QHostInfo info;
+ QTime age;
+ };
+ QCache<QString,QHostInfoCacheElement> cache;
+ QMutex mutex;
+};
+
// the following classes are used for the (normal) case: We use multiple threads to lookup DNS
class QHostInfoRunnable : public QRunnable
@@ -141,6 +171,7 @@ public:
void lookupFinished(QHostInfoRunnable *r);
bool wasAborted(int id);
+ QHostInfoCache cache;
protected:
QList<QHostInfoRunnable*> currentLookups; // in progress
QList<QHostInfoRunnable*> postponedLookups; // postponed because in progress for same host
@@ -154,6 +185,7 @@ protected:
bool wasDeleted;
};
+
#endif
QT_END_NAMESPACE
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index 275c436..95721ee 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -172,6 +172,10 @@
This signal is emitted after connectToHost() has been called and
the host lookup has succeeded.
+ \note Since Qt 4.6.3 QAbstractSocket may emit hostFound()
+ directly from the connectToHost() call since a DNS result could have been
+ cached.
+
\sa connected()
*/
@@ -181,6 +185,10 @@
This signal is emitted after connectToHost() has been called and
a connection has been successfully established.
+ \note On some operating systems the connected() signal may
+ be directly emitted from the connectToHost() call for connections
+ to the localhost.
+
\sa connectToHost(), disconnected()
*/
@@ -354,6 +362,8 @@
#include "qabstractsocket.h"
#include "qabstractsocket_p.h"
+#include "private/qhostinfo_p.h"
+
#include <qabstracteventdispatcher.h>
#include <qdatetime.h>
#include <qhostaddress.h>
@@ -1369,8 +1379,20 @@ void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint
return;
#endif
} else {
- if (d->threadData->eventDispatcher)
- d->hostLookupId = QHostInfo::lookupHost(hostName, this, SLOT(_q_startConnecting(QHostInfo)));
+ if (d->threadData->eventDispatcher) {
+ // this internal API for QHostInfo either immediatly gives us the desired
+ // QHostInfo from cache or later calls the _q_startConnecting slot.
+ bool immediateResultValid = false;
+ QHostInfo hostInfo = qt_qhostinfo_lookup(hostName,
+ this,
+ SLOT(_q_startConnecting(QHostInfo)),
+ &immediateResultValid,
+ &d->hostLookupId);
+ if (immediateResultValid) {
+ d->hostLookupId = -1;
+ d->_q_startConnecting(hostInfo);
+ }
+ }
}
#if defined(QABSTRACTSOCKET_DEBUG)