summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Goetz <Markus.Goetz@nokia.com>2009-12-21 16:03:13 (GMT)
committerMarkus Goetz <Markus.Goetz@nokia.com>2010-03-02 10:15:48 (GMT)
commit5a028f794fe48dc7141acedbf0e896b488bb8cd5 (patch)
tree842a59a33a9e57693f7b537434fb9a04020bc675
parentb08f42e5cc0d94a3b54494f6fe0af90be858f1eb (diff)
downloadQt-5a028f794fe48dc7141acedbf0e896b488bb8cd5.zip
Qt-5a028f794fe48dc7141acedbf0e896b488bb8cd5.tar.gz
Qt-5a028f794fe48dc7141acedbf0e896b488bb8cd5.tar.bz2
Add DNS caching to QHostInfo
By default enabled, but it can be disabled via a compile flag. Reviewed-by: Thiago
-rw-r--r--src/network/kernel/qhostinfo.cpp129
-rw-r--r--src/network/kernel/qhostinfo_p.h32
-rw-r--r--tests/auto/qhostinfo/tst_qhostinfo.cpp55
3 files changed, 208 insertions, 8 deletions
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index b23f6db..a65ca50 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -181,9 +181,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 +435,12 @@ void QHostInfoRunnable::run()
return;
}
- // check cache
- // FIXME
-
// if not in cache: OS lookup
QHostInfo hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
// save to cache
- // FIXME
+ if (manager->cache.isEnabled())
+ manager->cache.put(toBeLookedUp, hostInfo);
// check aborted again
if (manager->wasAborted(id)) {
@@ -575,6 +590,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/tests/auto/qhostinfo/tst_qhostinfo.cpp b/tests/auto/qhostinfo/tst_qhostinfo.cpp
index d5411d0..cbadcf5 100644
--- a/tests/auto/qhostinfo/tst_qhostinfo.cpp
+++ b/tests/auto/qhostinfo/tst_qhostinfo.cpp
@@ -72,6 +72,7 @@
#endif
#include <qhostinfo.h>
+#include "private/qhostinfo_p.h"
#if !defined(QT_NO_GETADDRINFO)
# if !defined(Q_OS_WINCE)
@@ -108,10 +109,11 @@ public:
public slots:
void init();
void cleanup();
+ void initTestCase();
+
private slots:
void getSetCheck();
void staticInformation();
- void initTestCase();
void lookupIPv4_data();
void lookupIPv4();
void lookupIPv6_data();
@@ -128,6 +130,8 @@ private slots:
void multipleSameLookups();
void multipleDifferentLookups();
+ void cache();
+
protected slots:
void resultsReady(const QHostInfo &);
@@ -205,10 +209,21 @@ void tst_QHostInfo::initTestCase()
// We have IPv6 support
ipv6Available = true;
}
+
+
+ // run each testcase with and without test enabled
+ QTest::addColumn<bool>("cache");
+ QTest::newRow("WithCache") << false;
+ QTest::newRow("WithoutCache") << true;
}
void tst_QHostInfo::init()
{
+ // delete the cache so inidividual testcase results are independant from each other
+ qt_qhostinfo_clear_cache();
+
+ QFETCH_GLOBAL(bool, cache);
+ qt_qhostinfo_enable_cache(cache);
}
void tst_QHostInfo::cleanup()
@@ -458,6 +473,44 @@ void tst_QHostInfo::multipleDifferentLookups()
QTRY_VERIFY(lookupsDoneCounter == COUNT);
}
+void tst_QHostInfo::cache()
+{
+ QFETCH_GLOBAL(bool, cache);
+ if (!cache)
+ return; // test makes only sense when cache enabled
+
+ // reset slot counter
+ lookupsDoneCounter = 0;
+
+ // lookup once, wait in event loop, result should not come directly.
+ bool valid = true;
+ QHostInfo result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid);
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ QVERIFY(valid == false);
+ QVERIFY(result.addresses().isEmpty());
+
+ // loopkup second time, result should come directly
+ valid = false;
+ result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid);
+ QVERIFY(valid == true);
+ QVERIFY(!result.addresses().isEmpty());
+
+ // clear the cache
+ qt_qhostinfo_clear_cache();
+
+ // lookup third time, result should not come directly.
+ valid = true;
+ result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid);
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ QVERIFY(valid == false);
+ QVERIFY(result.addresses().isEmpty());
+
+ // the slot should have been called 2 times.
+ QVERIFY(lookupsDoneCounter == 2);
+}
+
void tst_QHostInfo::resultsReady(const QHostInfo &hi)
{
lookupDone = true;