1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qhostinfo.h"
43 #include "qhostinfo_p.h"
44
45 #include "QtCore/qscopedpointer.h"
46 #include <qabstracteventdispatcher.h>
47 #include <qcoreapplication.h>
48 #include <qmetaobject.h>
49 #include <qstringlist.h>
50 #include <qthread.h>
51 #include <qurl.h>
52 #include <private/qnetworksession_p.h>
53
54 #ifdef Q_OS_UNIX
55 # include <unistd.h>
56 #endif
57
58 QT_BEGIN_NAMESPACE
59
60 //#define QHOSTINFO_DEBUG
61
62 #ifndef Q_OS_SYMBIAN
63 Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
64 #else
65 Q_GLOBAL_STATIC(QSymbianHostInfoLookupManager, theHostInfoLookupManager)
66 #endif
67
68 /*!
69 \class QHostInfo
70 \brief The QHostInfo class provides static functions for host name lookups.
71
72 \reentrant
73 \inmodule QtNetwork
74 \ingroup network
75
76 QHostInfo uses the lookup mechanisms provided by the operating
77 system to find the IP address(es) associated with a host name,
78 or the host name associated with an IP address.
79 The class provides two static convenience functions: one that
80 works asynchronously and emits a signal once the host is found,
81 and one that blocks and returns a QHostInfo object.
82
83 To look up a host's IP addresses asynchronously, call lookupHost(),
84 which takes the host name or IP address, a receiver object, and a slot
85 signature as arguments and returns an ID. You can abort the
86 lookup by calling abortHostLookup() with the lookup ID.
87
88 Example:
89
90 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 0
91
92
93 The slot is invoked when the results are ready. The results are
94 stored in a QHostInfo object. Call
95 addresses() to get the list of IP addresses for the host, and
96 hostName() to get the host name that was looked up.
97
98 If the lookup failed, error() returns the type of error that
99 occurred. errorString() gives a human-readable description of the
100 lookup error.
101
102 If you want a blocking lookup, use the QHostInfo::fromName() function:
103
104 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 1
105
106 QHostInfo supports Internationalized Domain Names (IDNs) through the
107 IDNA and Punycode standards.
108
109 To retrieve the name of the local host, use the static
110 QHostInfo::localHostName() function.
111
112 \note Since Qt 4.6.1 QHostInfo is using multiple threads for DNS lookup
113 instead of one dedicated DNS thread. This improves performance,
114 but also changes the order of signal emissions when using lookupHost()
115 compared to previous versions of Qt.
116 \note Since Qt 4.6.3 QHostInfo is using a small internal 60 second DNS cache
117 for performance improvements.
118
119 \sa QAbstractSocket, {http://www.rfc-editor.org/rfc/rfc3492.txt}{RFC 3492}
120 */
121
122 static QBasicAtomicInt theIdCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
123
124 /*!
125 Looks up the IP address(es) associated with host name \a name, and
126 returns an ID for the lookup. When the result of the lookup is
127 ready, the slot or signal \a member in \a receiver is called with
128 a QHostInfo argument. The QHostInfo object can then be inspected
129 to get the results of the lookup.
130
131 The lookup is performed by a single function call, for example:
132
133 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 2
134
135 The implementation of the slot prints basic information about the
136 addresses returned by the lookup, or reports an error if it failed:
137
138 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 3
139
140 If you pass a literal IP address to \a name instead of a host name,
141 QHostInfo will search for the domain name for the IP (i.e., QHostInfo will
142 perform a \e reverse lookup). On success, the resulting QHostInfo will
143 contain both the resolved domain name and IP addresses for the host
144 name. Example:
145
146 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 4
147
148 \note There is no guarantee on the order the signals will be emitted
149 if you start multiple requests with lookupHost().
150
151 \sa abortHostLookup(), addresses(), error(), fromName()
152 */
lookupHost(const QString & name,QObject * receiver,const char * member)153 int QHostInfo::lookupHost(const QString &name, QObject *receiver,
154 const char *member)
155 {
156 #if defined QHOSTINFO_DEBUG
157 qDebug("QHostInfo::lookupHost(\"%s\", %p, %s)",
158 name.toLatin1().constData(), receiver, member ? member + 1 : 0);
159 #endif
160
161 if (!QAbstractEventDispatcher::instance(QThread::currentThread())) {
162 qWarning("QHostInfo::lookupHost() called with no event dispatcher");
163 return -1;
164 }
165
166 qRegisterMetaType<QHostInfo>("QHostInfo");
167
168 int id = theIdCounter.fetchAndAddRelaxed(1); // generate unique ID
169
170 if (name.isEmpty()) {
171 if (!receiver)
172 return -1;
173
174 QHostInfo hostInfo(id);
175 hostInfo.setError(QHostInfo::HostNotFound);
176 hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given"));
177 QScopedPointer<QHostInfoResult> result(new QHostInfoResult);
178 QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)),
179 receiver, member, Qt::QueuedConnection);
180 result.data()->emitResultsReady(hostInfo);
181 return id;
182 }
183
184 #ifndef Q_OS_SYMBIAN
185 QHostInfoLookupManager *manager = theHostInfoLookupManager();
186
187 if (manager) {
188 // the application is still alive
189 if (manager->cache.isEnabled()) {
190 // check cache first
191 bool valid = false;
192 QHostInfo info = manager->cache.get(name, &valid);
193 if (valid) {
194 if (!receiver)
195 return -1;
196
197 info.setLookupId(id);
198 QHostInfoResult result;
199 QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
200 result.emitResultsReady(info);
201 return id;
202 }
203 }
204
205 // cache is not enabled or it was not in the cache, do normal lookup
206 QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id);
207 if (receiver)
208 QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
209 manager->scheduleLookup(runnable);
210 }
211 #else
212 QSymbianHostInfoLookupManager *manager = theHostInfoLookupManager();
213
214 if (manager) {
215 // the application is still alive
216 if (manager->cache.isEnabled()) {
217 // check cache first
218 bool valid = false;
219 QHostInfo info = manager->cache.get(name, &valid);
220 if (valid) {
221 info.setLookupId(id);
222 QHostInfoResult result;
223 QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
224 result.emitResultsReady(info);
225 return id;
226 }
227 }
228
229 // cache is not enabled or it was not in the cache, do normal lookup
230 #ifndef QT_NO_BEARERMANAGEMENT
231 QSharedPointer<QNetworkSession> networkSession;
232 QVariant v(receiver->property("_q_networksession"));
233 if (v.isValid())
234 networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(v);
235 #endif
236
237 QSymbianHostResolver *symbianResolver = 0;
238 QT_TRAP_THROWING(symbianResolver = new QSymbianHostResolver(name, id, networkSession));
239 QObject::connect(&symbianResolver->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
240 manager->scheduleLookup(symbianResolver);
241 }
242 #endif
243
244 return id;
245 }
246
247 /*!
248 Aborts the host lookup with the ID \a id, as returned by lookupHost().
249
250 \sa lookupHost(), lookupId()
251 */
abortHostLookup(int id)252 void QHostInfo::abortHostLookup(int id)
253 {
254 theHostInfoLookupManager()->abortLookup(id);
255 }
256
257 /*!
258 Looks up the IP address(es) for the given host \a name. The
259 function blocks during the lookup which means that execution of
260 the program is suspended until the results of the lookup are
261 ready. Returns the result of the lookup in a QHostInfo object.
262
263 If you pass a literal IP address to \a name instead of a host name,
264 QHostInfo will search for the domain name for the IP (i.e., QHostInfo will
265 perform a \e reverse lookup). On success, the returned QHostInfo will
266 contain both the resolved domain name and IP addresses for the host name.
267
268 \sa lookupHost()
269 */
fromName(const QString & name)270 QHostInfo QHostInfo::fromName(const QString &name)
271 {
272 #if defined QHOSTINFO_DEBUG
273 qDebug("QHostInfo::fromName(\"%s\")",name.toLatin1().constData());
274 #endif
275
276 QHostInfo hostInfo = QHostInfoAgent::fromName(name);
277 QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
278 manager->cache.put(name, hostInfo);
279 return hostInfo;
280 }
281
282 #ifndef QT_NO_BEARERMANAGEMENT
fromName(const QString & name,QSharedPointer<QNetworkSession> session)283 QHostInfo QHostInfoPrivate::fromName(const QString &name, QSharedPointer<QNetworkSession> session)
284 {
285 #if defined QHOSTINFO_DEBUG
286 qDebug("QHostInfoPrivate::fromName(\"%s\") with session %p",name.toLatin1().constData(), session.data());
287 #endif
288
289 QHostInfo hostInfo = QHostInfoAgent::fromName(name, session);
290 QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
291 manager->cache.put(name, hostInfo);
292 return hostInfo;
293 }
294 #endif
295
296 #ifndef Q_OS_SYMBIAN
297 #ifndef QT_NO_BEARERMANAGEMENT
298 // This function has a special implementation for symbian right now in qhostinfo_symbian.cpp but not on other OS.
fromName(const QString & hostName,QSharedPointer<QNetworkSession>)299 QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetworkSession>)
300 {
301 return QHostInfoAgent::fromName(hostName);
302 }
303 #endif
304 #endif
305
306
307 /*!
308 \enum QHostInfo::HostInfoError
309
310 This enum describes the various errors that can occur when trying
311 to resolve a host name.
312
313 \value NoError The lookup was successful.
314 \value HostNotFound No IP addresses were found for the host.
315 \value UnknownError An unknown error occurred.
316
317 \sa error(), setError()
318 */
319
320 /*!
321 Constructs an empty host info object with lookup ID \a id.
322
323 \sa lookupId()
324 */
QHostInfo(int id)325 QHostInfo::QHostInfo(int id)
326 : d(new QHostInfoPrivate)
327 {
328 d->lookupId = id;
329 }
330
331 /*!
332 Constructs a copy of \a other.
333 */
QHostInfo(const QHostInfo & other)334 QHostInfo::QHostInfo(const QHostInfo &other)
335 : d(new QHostInfoPrivate(*other.d.data()))
336 {
337 }
338
339 /*!
340 Assigns the data of the \a other object to this host info object,
341 and returns a reference to it.
342 */
operator =(const QHostInfo & other)343 QHostInfo &QHostInfo::operator=(const QHostInfo &other)
344 {
345 *d.data() = *other.d.data();
346 return *this;
347 }
348
349 /*!
350 Destroys the host info object.
351 */
~QHostInfo()352 QHostInfo::~QHostInfo()
353 {
354 }
355
356 /*!
357 Returns the list of IP addresses associated with hostName(). This
358 list may be empty.
359
360 Example:
361
362 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 5
363
364 \sa hostName(), error()
365 */
addresses() const366 QList<QHostAddress> QHostInfo::addresses() const
367 {
368 return d->addrs;
369 }
370
371 /*!
372 Sets the list of addresses in this QHostInfo to \a addresses.
373
374 \sa addresses()
375 */
setAddresses(const QList<QHostAddress> & addresses)376 void QHostInfo::setAddresses(const QList<QHostAddress> &addresses)
377 {
378 d->addrs = addresses;
379 }
380
381 /*!
382 Returns the name of the host whose IP addresses were looked up.
383
384 \sa localHostName()
385 */
hostName() const386 QString QHostInfo::hostName() const
387 {
388 return d->hostName;
389 }
390
391 /*!
392 Sets the host name of this QHostInfo to \a hostName.
393
394 \sa hostName()
395 */
setHostName(const QString & hostName)396 void QHostInfo::setHostName(const QString &hostName)
397 {
398 d->hostName = hostName;
399 }
400
401 /*!
402 Returns the type of error that occurred if the host name lookup
403 failed; otherwise returns NoError.
404
405 \sa setError(), errorString()
406 */
error() const407 QHostInfo::HostInfoError QHostInfo::error() const
408 {
409 return d->err;
410 }
411
412 /*!
413 Sets the error type of this QHostInfo to \a error.
414
415 \sa error(), errorString()
416 */
setError(HostInfoError error)417 void QHostInfo::setError(HostInfoError error)
418 {
419 d->err = error;
420 }
421
422 /*!
423 Returns the ID of this lookup.
424
425 \sa setLookupId(), abortHostLookup(), hostName()
426 */
lookupId() const427 int QHostInfo::lookupId() const
428 {
429 return d->lookupId;
430 }
431
432 /*!
433 Sets the ID of this lookup to \a id.
434
435 \sa lookupId(), lookupHost()
436 */
setLookupId(int id)437 void QHostInfo::setLookupId(int id)
438 {
439 d->lookupId = id;
440 }
441
442 /*!
443 If the lookup failed, this function returns a human readable
444 description of the error; otherwise "Unknown error" is returned.
445
446 \sa setErrorString(), error()
447 */
errorString() const448 QString QHostInfo::errorString() const
449 {
450 return d->errorStr;
451 }
452
453 /*!
454 Sets the human readable description of the error that occurred to \a str
455 if the lookup failed.
456
457 \sa errorString(), setError()
458 */
setErrorString(const QString & str)459 void QHostInfo::setErrorString(const QString &str)
460 {
461 d->errorStr = str;
462 }
463
464 /*!
465 \fn QString QHostInfo::localHostName()
466
467 Returns the host name of this machine.
468
469 \sa hostName()
470 */
471
472 /*!
473 \fn QString QHostInfo::localDomainName()
474
475 Returns the DNS domain of this machine.
476
477 Note: DNS domains are not related to domain names found in
478 Windows networks.
479
480 \sa hostName()
481 */
482
483 #ifndef Q_OS_SYMBIAN
QHostInfoRunnable(QString hn,int i)484 QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i)
485 {
486 setAutoDelete(true);
487 }
488
489 // the QHostInfoLookupManager will at some point call this via a QThreadPool
run()490 void QHostInfoRunnable::run()
491 {
492 QHostInfoLookupManager *manager = theHostInfoLookupManager();
493 // check aborted
494 if (manager->wasAborted(id)) {
495 manager->lookupFinished(this);
496 return;
497 }
498
499 QHostInfo hostInfo;
500
501 // QHostInfo::lookupHost already checks the cache. However we need to check
502 // it here too because it might have been cache saved by another QHostInfoRunnable
503 // in the meanwhile while this QHostInfoRunnable was scheduled but not running
504 if (manager->cache.isEnabled()) {
505 // check the cache first
506 bool valid = false;
507 hostInfo = manager->cache.get(toBeLookedUp, &valid);
508 if (!valid) {
509 // not in cache, we need to do the lookup and store the result in the cache
510 hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
511 manager->cache.put(toBeLookedUp, hostInfo);
512 }
513 } else {
514 // cache is not enabled, just do the lookup and continue
515 hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
516 }
517
518 // check aborted again
519 if (manager->wasAborted(id)) {
520 manager->lookupFinished(this);
521 return;
522 }
523
524 // signal emission
525 hostInfo.setLookupId(id);
526 resultEmitter.emitResultsReady(hostInfo);
527
528 // now also iterate through the postponed ones
529 {
530 QMutexLocker locker(&manager->mutex);
531 QMutableListIterator<QHostInfoRunnable*> iterator(manager->postponedLookups);
532 while (iterator.hasNext()) {
533 QHostInfoRunnable* postponed = iterator.next();
534 if (toBeLookedUp == postponed->toBeLookedUp) {
535 // we can now emit
536 iterator.remove();
537 hostInfo.setLookupId(postponed->id);
538 postponed->resultEmitter.emitResultsReady(hostInfo);
539 delete postponed;
540 }
541 }
542 }
543
544 manager->lookupFinished(this);
545
546 // thread goes back to QThreadPool
547 }
548
QHostInfoLookupManager()549 QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false)
550 {
551 moveToThread(QCoreApplicationPrivate::mainThread());
552 connect(QCoreApplication::instance(), SIGNAL(destroyed()), SLOT(waitForThreadPoolDone()), Qt::DirectConnection);
553 threadPool.setMaxThreadCount(5); // do 5 DNS lookups in parallel
554 }
555
~QHostInfoLookupManager()556 QHostInfoLookupManager::~QHostInfoLookupManager()
557 {
558 wasDeleted = true;
559
560 // don't qDeleteAll currentLookups, the QThreadPool has ownership
561 clear();
562 }
563
clear()564 void QHostInfoLookupManager::clear()
565 {
566 {
567 QMutexLocker locker(&mutex);
568 qDeleteAll(postponedLookups);
569 qDeleteAll(scheduledLookups);
570 qDeleteAll(finishedLookups);
571 postponedLookups.clear();
572 scheduledLookups.clear();
573 finishedLookups.clear();
574 }
575
576 threadPool.waitForDone();
577 cache.clear();
578 }
579
work()580 void QHostInfoLookupManager::work()
581 {
582 if (wasDeleted)
583 return;
584
585 // goals of this function:
586 // - launch new lookups via the thread pool
587 // - make sure only one lookup per host/IP is in progress
588
589 QMutexLocker locker(&mutex);
590
591 if (!finishedLookups.isEmpty()) {
592 // remove ID from aborted if it is in there
593 for (int i = 0; i < finishedLookups.length(); i++) {
594 abortedLookups.removeAll(finishedLookups.at(i)->id);
595 }
596
597 finishedLookups.clear();
598 }
599
600 if (!postponedLookups.isEmpty()) {
601 // try to start the postponed ones
602
603 QMutableListIterator<QHostInfoRunnable*> iterator(postponedLookups);
604 while (iterator.hasNext()) {
605 QHostInfoRunnable* postponed = iterator.next();
606
607 // check if none of the postponed hostnames is currently running
608 bool alreadyRunning = false;
609 for (int i = 0; i < currentLookups.length(); i++) {
610 if (currentLookups.at(i)->toBeLookedUp == postponed->toBeLookedUp) {
611 alreadyRunning = true;
612 break;
613 }
614 }
615 if (!alreadyRunning) {
616 iterator.remove();
617 scheduledLookups.prepend(postponed); // prepend! we want to finish it ASAP
618 }
619 }
620 }
621
622 if (!scheduledLookups.isEmpty()) {
623 // try to start the new ones
624 QMutableListIterator<QHostInfoRunnable*> iterator(scheduledLookups);
625 while (iterator.hasNext()) {
626 QHostInfoRunnable *scheduled = iterator.next();
627
628 // check if a lookup for this host is already running, then postpone
629 for (int i = 0; i < currentLookups.size(); i++) {
630 if (currentLookups.at(i)->toBeLookedUp == scheduled->toBeLookedUp) {
631 iterator.remove();
632 postponedLookups.append(scheduled);
633 scheduled = 0;
634 break;
635 }
636 }
637
638 if (scheduled && currentLookups.size() < threadPool.maxThreadCount()) {
639 // runnable now running in new thread, track this in currentLookups
640 threadPool.start(scheduled);
641 iterator.remove();
642 currentLookups.append(scheduled);
643 } else {
644 // was postponed, continue iterating
645 continue;
646 }
647 };
648 }
649 }
650
651 // called by QHostInfo
scheduleLookup(QHostInfoRunnable * r)652 void QHostInfoLookupManager::scheduleLookup(QHostInfoRunnable *r)
653 {
654 if (wasDeleted)
655 return;
656
657 QMutexLocker locker(&this->mutex);
658 scheduledLookups.enqueue(r);
659 work();
660 }
661
662 // called by QHostInfo
abortLookup(int id)663 void QHostInfoLookupManager::abortLookup(int id)
664 {
665 if (wasDeleted)
666 return;
667
668 QMutexLocker locker(&this->mutex);
669
670 // is postponed? delete and return
671 for (int i = 0; i < postponedLookups.length(); i++) {
672 if (postponedLookups.at(i)->id == id) {
673 delete postponedLookups.takeAt(i);
674 return;
675 }
676 }
677
678 // is scheduled? delete and return
679 for (int i = 0; i < scheduledLookups.length(); i++) {
680 if (scheduledLookups.at(i)->id == id) {
681 delete scheduledLookups.takeAt(i);
682 return;
683 }
684 }
685
686 if (!abortedLookups.contains(id))
687 abortedLookups.append(id);
688 }
689
690 // called from QHostInfoRunnable
wasAborted(int id)691 bool QHostInfoLookupManager::wasAborted(int id)
692 {
693 if (wasDeleted)
694 return true;
695
696 QMutexLocker locker(&this->mutex);
697 return abortedLookups.contains(id);
698 }
699
700 // called from QHostInfoRunnable
lookupFinished(QHostInfoRunnable * r)701 void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r)
702 {
703 if (wasDeleted)
704 return;
705
706 QMutexLocker locker(&this->mutex);
707 currentLookups.removeOne(r);
708 finishedLookups.append(r);
709 work();
710 }
711 #endif
712
713 // This function returns immediately when we had a result in the cache, else it will later emit a signal
qt_qhostinfo_lookup(const QString & name,QObject * receiver,const char * member,bool * valid,int * id)714 QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id)
715 {
716 *valid = false;
717 *id = -1;
718
719 // check cache
720 QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
721 if (manager && manager->cache.isEnabled()) {
722 QHostInfo info = manager->cache.get(name, valid);
723 if (*valid) {
724 return info;
725 }
726 }
727
728 // was not in cache, trigger lookup
729 *id = QHostInfo::lookupHost(name, receiver, member);
730
731 // return empty response, valid==false
732 return QHostInfo();
733 }
734
qt_qhostinfo_clear_cache()735 void qt_qhostinfo_clear_cache()
736 {
737 QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
738 if (manager) {
739 manager->clear();
740 }
741 }
742
qt_qhostinfo_enable_cache(bool e)743 void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
744 {
745 QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
746 if (manager) {
747 manager->cache.setEnabled(e);
748 }
749 }
750
751 // cache for 60 seconds
752 // cache 128 items
QHostInfoCache()753 QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(128)
754 {
755 #ifdef QT_QHOSTINFO_CACHE_DISABLED_BY_DEFAULT
756 enabled = false;
757 #endif
758 }
759
isEnabled()760 bool QHostInfoCache::isEnabled()
761 {
762 return enabled;
763 }
764
765 // this function is currently only used for the auto tests
766 // and not usable by public API
setEnabled(bool e)767 void QHostInfoCache::setEnabled(bool e)
768 {
769 enabled = e;
770 }
771
772
get(const QString & name,bool * valid)773 QHostInfo QHostInfoCache::get(const QString &name, bool *valid)
774 {
775 QMutexLocker locker(&this->mutex);
776
777 *valid = false;
778 if (cache.contains(name)) {
779 QHostInfoCacheElement *element = cache.object(name);
780 if (element->age.elapsed() < max_age*1000)
781 *valid = true;
782 return element->info;
783
784 // FIXME idea:
785 // if too old but not expired, trigger a new lookup
786 // to freshen our cache
787 }
788
789 return QHostInfo();
790 }
791
put(const QString & name,const QHostInfo & info)792 void QHostInfoCache::put(const QString &name, const QHostInfo &info)
793 {
794 // if the lookup failed, don't cache
795 if (info.error() != QHostInfo::NoError)
796 return;
797
798 QHostInfoCacheElement* element = new QHostInfoCacheElement();
799 element->info = info;
800 element->age = QElapsedTimer();
801 element->age.start();
802
803 QMutexLocker locker(&this->mutex);
804 cache.insert(name, element); // cache will take ownership
805 }
806
clear()807 void QHostInfoCache::clear()
808 {
809 QMutexLocker locker(&this->mutex);
810 cache.clear();
811 }
812
globalInstance()813 QAbstractHostInfoLookupManager* QAbstractHostInfoLookupManager::globalInstance()
814 {
815 return theHostInfoLookupManager();
816 }
817
818 QT_END_NAMESPACE
819