[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    Re: [PATCH] DNS cache for Konqueror/KIO
From:       "Roland Harnau" <truthandprogress () googlemail ! com>
Date:       2008-06-25 18:03:05
Message-ID: 476f836a0806251103n192e1099gdc9827518ac1e826 () mail ! gmail ! com
[Download RAW message or body]

2008/6/23, Thiago Macieira <thiago@kde.org>:
> I didn't like the indention style. Please don't put function opening
> brackets on the same line as their declaration. You also have whitespace
> errors (lines ending in whitespace).

The location of the opening brackets is maybe too much of Java style
for KDE. Attached is somewhat improved patch adhering a bit more to
kdelibs coding style and  hopefully without superfluous whitespace.

> This is also a first step in fixing the long issue of not having a
> centralised slave manager. One that would allow us to say "only one FTP
> slave per host", for instance.

"Centralised" as session- or system-wide manager? Otherwise the
Scheduler  should take care of such things.


Roland

["kdelibs-DNS-cache.patch" (text/x-patch)]

diff --git a/kio/CMakeLists.txt b/kio/CMakeLists.txt
index 8984907..cf4acfe 100644
--- a/kio/CMakeLists.txt
+++ b/kio/CMakeLists.txt
@@ -116,6 +116,7 @@ set(kiocore_STAT_SRCS
   kio/slaveinterface.cpp
   kio/tcpslavebase.cpp
   kio/udsentry.cpp
+  kio/hostinfoagent.cpp    
 )
 
 qt4_add_dbus_adaptor(kiocore_STAT_SRCS kio/org.kde.kio.FileUndoManager.xml \
fileundomanager_p.h KIO::FileUndoManagerPrivate fileundomanager_adaptor \
                KIOFileUndoManagerAdaptor)
diff --git a/kio/kio/global.h b/kio/kio/global.h
index b1fcbc5..4e5d10b 100644
--- a/kio/kio/global.h
+++ b/kio/kio/global.h
@@ -177,7 +177,8 @@ namespace KIO
     CMD_READ = 'Z', // 90
     CMD_WRITE = 91,
     CMD_SEEK = 92,
-    CMD_CLOSE = 93
+    CMD_CLOSE = 93,
+    CMD_HOST_INFO = 94
     // Add new ones here once a release is done, to avoid breaking binary \
                compatibility.
     // Note that protocol-specific commands shouldn't be added here, but should use \
special.  };
diff --git a/kio/kio/hostinfoagent.cpp b/kio/kio/hostinfoagent.cpp
new file mode 100644
index 0000000..66bf233
--- /dev/null
+++ b/kio/kio/hostinfoagent.cpp
@@ -0,0 +1,181 @@
+/*
+Copyright 2008 Roland Harnau <tau@gmx.eu>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public 
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "hostinfoagent.h"
+
+#include <QtCore/QString>
+#include <QtCore/QHash>
+#include <QtCore/QCache>
+#include <QtCore/QTime>
+#include <QtCore/QList>
+#include <QtCore/QPair>
+#include <QtCore/QFutureWatcher>
+#include <QtCore/QtConcurrentRun>
+#include <QtNetwork/QHostInfo>
+
+#define TTL 300
+
+namespace KIO
+{
+    class HostInfoAgentPrivate : public QObject
+    {
+        Q_OBJECT
+    public:
+        HostInfoAgentPrivate(int cacheSize = 100);
+        virtual ~HostInfoAgentPrivate() {};
+        void lookupHost(const QString& hostName, QObject* receiver, const char* \
member); +    private Q_SLOTS:
+        void queryFinished(const QHostInfo&);
+    private:
+        class Result;
+        class Query;
+
+        QHash<QString, Query*> openQueries;
+        QCache<QString, QPair<QHostInfo, QTime> > dnsCache;
+    };
+
+    class HostInfoAgentPrivate::Result : public QObject
+    {
+        Q_OBJECT
+    Q_SIGNALS:
+        void result(QHostInfo);
+    friend class HostInfoAgentPrivate;
+    };
+
+    class HostInfoAgentPrivate::Query : public QObject 
+    {
+        Q_OBJECT
+    public:
+        Query(): m_watcher(), m_hostName()
+        {
+            connect(&m_watcher, SIGNAL(finished()), this, SLOT(relayFinished()));
+        }
+        void start(const QString& hostName)
+        {
+            m_hostName = hostName;
+            QFuture<QHostInfo> future = QtConcurrent::run(&QHostInfo::fromName, \
hostName); +            m_watcher.setFuture(future);
+        }
+        QString hostName() const
+        {
+            return m_hostName;
+        }
+    Q_SIGNALS:
+        void result(QHostInfo);
+    private Q_SLOTS:
+        void relayFinished()
+        {
+            Q_EMIT result(m_watcher.result());
+        }
+    private:
+        QFutureWatcher<QHostInfo> m_watcher;
+        QString m_hostName;
+    };
+}
+
+using namespace KIO;
+
+HostInfoAgent::HostInfoAgent(int cacheSize) : d(new HostInfoAgentPrivate(cacheSize)) \
{} +
+HostInfoAgent::~HostInfoAgent()
+{
+    delete(d);
+}
+
+HostInfoAgent* HostInfoAgent::instance()
+{
+    if (m_instance == 0) {
+        m_instance = new HostInfoAgent();
+    }
+    return m_instance; 
+}
+
+void HostInfoAgent::lookupHost(const QString& hostName, QObject* receiver, const \
char* member) +{
+    d->lookupHost(hostName, receiver, member);
+}
+
+HostInfoAgent* HostInfoAgent::m_instance;
+
+HostInfoAgentPrivate::HostInfoAgentPrivate(int cacheSize) : openQueries(), \
dnsCache(cacheSize) {} +
+void HostInfoAgentPrivate::lookupHost(const QString& hostName, QObject* receiver, \
const char* member) +{
+    if(dnsCache.contains(hostName)) {
+        QPair<QHostInfo, QTime> info (*dnsCache.object(hostName));
+
+        if (QTime::currentTime() <= info.second.addSecs(TTL)) {
+            Result result;
+            QObject::connect(&result, SIGNAL(result(QHostInfo)),receiver, member);
+            Q_EMIT result.result(info.first);
+            return;
+        }
+        dnsCache.remove(hostName);
+    }
+
+    if (openQueries.contains(hostName)) {
+        Query* query = openQueries.value(hostName);
+        connect(query, SIGNAL(result(QHostInfo)), receiver, member);
+        return;
+    }
+
+    Query* query = new Query();
+    openQueries.insert(hostName, query);
+    connect(query, SIGNAL(result(QHostInfo)), this, SLOT(queryFinished(QHostInfo)));
+    connect(query, SIGNAL(result(QHostInfo)), receiver, member);
+    query->start(hostName);
+}
+
+void HostInfoAgentPrivate::queryFinished(const QHostInfo& info)
+{
+    Query* query = static_cast<Query* >(sender());
+
+    openQueries.remove(query->hostName());
+    if (!info.addresses().isEmpty())
+        dnsCache.insert(query->hostName(), 
+            new QPair<QHostInfo, QTime>(info, QTime::currentTime()));
+
+    query->deleteLater();
+}
+
+QDataStream& operator<<(QDataStream& out, const QHostInfo& info)
+{
+    out << info.hostName() << info.addresses() << info.error() << \
info.errorString(); +    return out;
+}
+
+QDataStream& operator>>(QDataStream& in, QHostInfo& info)
+{
+    QString hostName;
+    QList<QHostAddress> addresses;
+    int error;
+    QString errorString;
+
+    in >> hostName >> addresses >> error >> errorString;
+
+    info.setHostName(hostName);
+    info.setAddresses(addresses);
+    info.setError(QHostInfo::HostInfoError(error));
+    info.setErrorString(errorString);
+
+    return in;
+}
+
+#include "hostinfoagent.moc"
diff --git a/kio/kio/hostinfoagent.h b/kio/kio/hostinfoagent.h
new file mode 100644
index 0000000..c1ce03b
--- /dev/null
+++ b/kio/kio/hostinfoagent.h
@@ -0,0 +1,49 @@
+/*
+Copyright 2008  Roland Harnau <tau@gmx.eu>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public 
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef HOSTINFO_H_
+#define HOSTINFO_H_
+
+#include <QtCore/QString>
+#include <QtCore/QDataStream>
+#include <QtNetwork/QHostInfo>
+
+namespace KIO
+{
+    class HostInfoAgentPrivate;
+    
+    class  HostInfoAgent
+    {
+    public:
+        static HostInfoAgent* instance();
+        void lookupHost(const QString& hostName, QObject* receiver, const char* \
member); +    private:
+        HostInfoAgent(int cacheSize = 100);
+        ~HostInfoAgent();
+        static HostInfoAgent* m_instance;
+
+        HostInfoAgentPrivate* d;
+    };
+}
+
+QDataStream& operator<<(QDataStream& out, const QHostInfo& info);
+QDataStream& operator>>(QDataStream& in, QHostInfo& info);
+
+#endif
diff --git a/kio/kio/slavebase.cpp b/kio/kio/slavebase.cpp
index 9485da1..8ce03a2 100644
--- a/kio/kio/slavebase.cpp
+++ b/kio/kio/slavebase.cpp
@@ -22,6 +22,7 @@
  **/
 
 #include "slavebase.h"
+#include "hostinfoagent.h"
 
 #include <config.h>
 
@@ -1330,3 +1331,25 @@ void SlaveBase::send(int cmd, const QByteArray& arr )
 
 void SlaveBase::virtual_hook( int, void* )
 { /*BASE::virtual_hook( id, data );*/ }
+
+void SlaveBase::lookupHost(const QString& host)
+{
+    KIO_DATA << host;
+    send(MSG_HOST_INFO_REQ, data);
+}
+
+int SlaveBase::waitForHostInfo(QHostInfo& info)
+{
+    QByteArray data;
+    int result = waitForAnswer(CMD_HOST_INFO, 0, data);
+
+    if (result  == -1){
+        info.setError(QHostInfo::UnknownError);
+        info.setErrorString("Unknown Error.");
+        return result;
+    }
+
+    QDataStream stream(data);
+    stream >> info;
+    return result;
+}
diff --git a/kio/kio/slavebase.h b/kio/kio/slavebase.h
index 7d87e95..412389e 100644
--- a/kio/kio/slavebase.h
+++ b/kio/kio/slavebase.h
@@ -27,6 +27,7 @@
 #include <klocale.h>
 
 #include <QtCore/QByteArray>
+#include <QtNetwork/QHostInfo>
 
 class KConfigGroup;
 class KRemoteEncoding;
@@ -774,6 +775,16 @@ public:
      */
     void setKillFlag();
 
+    /** Internally used
+      * @internal
+    */
+    void lookupHost(const QString& host);
+
+    /** Internally used
+      * @internal
+    */
+    int waitForHostInfo(QHostInfo& info);
+
 protected:
     /**
      * Name of the protocol supported by this slave
diff --git a/kio/kio/slaveinterface.cpp b/kio/kio/slaveinterface.cpp
index 8e41ea7..54043ff 100644
--- a/kio/kio/slaveinterface.cpp
+++ b/kio/kio/slaveinterface.cpp
@@ -21,6 +21,7 @@
 
 #include "slavebase.h"
 #include "connection.h"
+#include "hostinfoagent.h"
 #include <errno.h>
 #include <assert.h>
 #include <kdebug.h>
@@ -336,7 +337,13 @@ bool SlaveInterface::dispatch( int _cmd, const QByteArray \
&rawdata )  emit needSubUrlData();
         break;
     }
-    default:
+    case MSG_HOST_INFO_REQ: {
+        QString hostName;
+        stream >> hostName;
+        HostInfoAgent::instance()->lookupHost(hostName, this, \
SLOT(setHostInfo(QHostInfo))); +        break;
+    }
+    default: 
         kWarning(7007) << "Slave sends unknown command (" << _cmd << "), dropping \
slave";  return false;
     }
@@ -496,4 +503,13 @@ int SlaveInterfacePrivate::messageBox(int type, const QString \
&text,  return result;
 }
 
+void SlaveInterface::setHostInfo(const QHostInfo& info)
+{
+    Q_D(SlaveInterface);
+    QByteArray data;
+    QDataStream stream(&data, QIODevice::WriteOnly);
+    stream << info;
+    d->connection->send(CMD_HOST_INFO, data);
+}
+
 #include "slaveinterface.moc"
diff --git a/kio/kio/slaveinterface.h b/kio/kio/slaveinterface.h
index d17c5c2..d3be6b4 100644
--- a/kio/kio/slaveinterface.h
+++ b/kio/kio/slaveinterface.h
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 
 #include <QtCore/QObject>
+#include <QtNetwork/QHostInfo>
 
 #include <kio/global.h>
 #include <kio/udsentry.h>
@@ -82,7 +83,8 @@ class SlaveInterfacePrivate;
    MSG_AUTH_KEY, // 115 // deprecated.
    MSG_DEL_AUTH_KEY, // deprecated.
    MSG_OPENED,
-   MSG_WRITTEN
+   MSG_WRITTEN,
+   MSG_HOST_INFO_REQ
    // add new ones here once a release is done, to avoid breaking binary \
compatibility  };
 
@@ -169,10 +171,11 @@ protected:
 
 protected Q_SLOTS:
     void calcSpeed();
-
 protected:
     SlaveInterfacePrivate* const d_ptr;
     Q_DECLARE_PRIVATE(SlaveInterface)
+private Q_SLOTS:
+    void setHostInfo(const QHostInfo& info);
 };
 
 }
diff --git a/kio/kio/tcpslavebase.cpp b/kio/kio/tcpslavebase.cpp
index e281cae..cd62a1f 100644
--- a/kio/kio/tcpslavebase.cpp
+++ b/kio/kio/tcpslavebase.cpp
@@ -2,7 +2,8 @@
  * Copyright (C) 2000 Alex Zepeda <zipzippy@sonic.net
  * Copyright (C) 2001-2003 George Staikos <staikos@kde.org>
  * Copyright (C) 2001 Dawit Alemayehu <adawit@kde.org>
- * Copyright (C) 2007,2008 Andreas Hartmetz <ahartmetz@gmail.com>
+ * Copyright (C) 2007,2008 Andreas Hartmetz <ahartmetz@gmail.com> 
+ * Copyright (C) 2008 Roland Harnau <tau@gmx.eu>
  *
  * This file is part of the KDE project
  *
@@ -279,25 +280,32 @@ bool TCPSlaveBase::connectToHost(const QString &/*protocol*/,
 
         //FIXME! KTcpSocket doesn't know or care about protocol ports! Fix it there, \
then use it here.  
-        kDebug(7029) << "before connectToHost: Socket error is"
-                    << d->socket.error() << ", Socket state is" << \
                d->socket.state();
-        d->socket.connectToHost(host, port);
-        kDebug(7029) << "after connectToHost: Socket error is"
-                    << d->socket.error() << ", Socket state is" << \
d->socket.state(); +        QHostAddress address;
+        QList<QHostAddress> addresses;
 
-        bool connectOk = d->socket.waitForConnected(d->timeout > -1 ? d->timeout * \
                1000 : -1);
-        kDebug(7029) << "after waitForConnected: Socket error is"
-                    << d->socket.error() << ", Socket state is" << d->socket.state()
-                    << ", waitForConnected returned " << connectOk;
+        if (address.setAddress(host)){
+            addresses.append(address);
+        } else {
+            QHostInfo info;
+            lookupHost(host);
+            waitForHostInfo(info);
+            if (info.error() != QHostInfo::NoError){
+                error(ERR_UNKNOWN_HOST, info.errorString());
+                return false;
+            }
+            addresses = info.addresses();
+        }
+
+        QListIterator<QHostAddress> it(addresses);
+        while (it.hasNext()){
+            d->socket.connectToHost(it.next(), port);
+            bool connectOk = d->socket.waitForConnected(d->timeout > -1 ? d->timeout \
* 1000 : -1); +            if (connectOk) break;
+        }
 
         if (d->socket.state() != KTcpSocket::ConnectedState) {
-            if (d->socket.error() == KTcpSocket::HostNotFoundError) {
-                error(ERR_UNKNOWN_HOST,
+            error(ERR_COULD_NOT_CONNECT,
                       host + QLatin1String(": ") + d->socket.errorString());
-            } else {
-                error(ERR_COULD_NOT_CONNECT,
-                      host + QLatin1String(": ") + d->socket.errorString());
-            }
             return false;
         }
 



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic