1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QTest>
31#include <QtTest/QTestEventLoop>
32
33#include <qcoreapplication.h>
34#include <qdebug.h>
35#include <qnetworkproxy.h>
36
37#include <QNetworkAccessManager>
38#include <QNetworkInterface>
39#include <QNetworkReply>
40#include <QNetworkRequest>
41#include <QList>
42#include <QSysInfo>
43#include <QThread>
44
45#include <private/qtnetworkglobal_p.h>
46
47class tst_QNetworkProxyFactory : public QObject {
48 Q_OBJECT
49
50public:
51 tst_QNetworkProxyFactory();
52
53 class QDebugProxyFactory : public QNetworkProxyFactory
54 {
55 public:
56 virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery())
57 {
58 returnedList = QNetworkProxyFactory::systemProxyForQuery(query);
59 requestCounter++;
60 return returnedList;
61 }
62 QList<QNetworkProxy> returnedList;
63
64 static int requestCounter;
65 };
66
67private slots:
68 void systemProxyForQueryCalledFromThread();
69 void systemProxyForQuery_data();
70 void systemProxyForQuery() const;
71 void systemProxyForQuery_local();
72 void genericSystemProxy();
73 void genericSystemProxy_data();
74
75private:
76 QString formatProxyName(const QNetworkProxy & proxy) const;
77 QDebugProxyFactory *factory;
78};
79
80int tst_QNetworkProxyFactory::QDebugProxyFactory::requestCounter = 0;
81
82tst_QNetworkProxyFactory::tst_QNetworkProxyFactory()
83{
84 factory = new QDebugProxyFactory;
85 QNetworkProxyFactory::setApplicationProxyFactory(factory);
86}
87
88QString tst_QNetworkProxyFactory::formatProxyName(const QNetworkProxy & proxy) const
89{
90 QString proxyName;
91 if (!proxy.user().isNull())
92 proxyName.append(s: QString("%1:%2@").arg(args: proxy.user(), args: proxy.password()));
93 proxyName.append(s: QString("%1:%2").arg(a: proxy.hostName()).arg(a: proxy.port()));
94 proxyName.append(s: QString(" (type=%1, capabilities=%2)").arg(a: proxy.type()).arg(a: proxy.capabilities()));
95
96 return proxyName;
97}
98
99void tst_QNetworkProxyFactory::systemProxyForQuery_data()
100{
101 QTest::addColumn<int>(name: "type");
102 QTest::addColumn<QUrl>(name: "url");
103 QTest::addColumn<QString>(name: "tag");
104 QTest::addColumn<QString>(name: "hostName");
105 QTest::addColumn<int>(name: "port");
106 QTest::addColumn<int>(name: "requiredCapabilities");
107
108 //URLs
109 QTest::newRow(dataTag: "http") << (int)QNetworkProxyQuery::UrlRequest << QUrl("http://qt-project.org") << QString() << QString() << 0 << 0;
110 //windows: "intranet" should be bypassed if "bypass proxy server for local addresses" is ticked
111 QTest::newRow(dataTag: "intranet") << (int)QNetworkProxyQuery::UrlRequest << QUrl("http://qt-test-server") << QString() << QString() << 0 << 0;
112 //windows: "intranet2" should be bypassed if "*.local" is in the exceptions list (advanced settings)
113 QTest::newRow(dataTag: "intranet2") << (int)QNetworkProxyQuery::UrlRequest << QUrl("http://qt-test-server.local") << QString() << QString() << 0 << 0;
114 QTest::newRow(dataTag: "https") << (int)QNetworkProxyQuery::UrlRequest << QUrl("https://qt-project.org") << QString() << QString() << 0 << (int)QNetworkProxy::TunnelingCapability;
115 QTest::newRow(dataTag: "ftp") << (int)QNetworkProxyQuery::UrlRequest << QUrl("ftp://qt-project.org") << QString() << QString() << 0 << 0;
116
117 //TCP
118 QTest::newRow(dataTag: "imap") << (int)QNetworkProxyQuery::TcpSocket << QUrl() << QString() << QString("qt-project.org") << 0 << (int)QNetworkProxy::TunnelingCapability;
119 QTest::newRow(dataTag: "autobind-server") << (int)QNetworkProxyQuery::TcpServer << QUrl() << QString() << QString() << 0 << (int)QNetworkProxy::ListeningCapability;
120 QTest::newRow(dataTag: "web-server") << (int)QNetworkProxyQuery::TcpServer << QUrl() << QString() << QString() << 80 << (int)QNetworkProxy::ListeningCapability;
121 //windows: these should be bypassed if "bypass proxy server for local addresses" is ticked
122 foreach (QHostAddress address, QNetworkInterface::allAddresses()) {
123 QTest::newRow(qPrintable(address.toString())) << (int)QNetworkProxyQuery::TcpSocket << QUrl() << QString() << address.toString() << 0 << 0;
124 }
125
126 //UDP
127 QTest::newRow(dataTag: "udp") << (int)QNetworkProxyQuery::UdpSocket << QUrl() << QString() << QString() << 0 << (int)QNetworkProxy::UdpTunnelingCapability;
128
129 //Protocol tags
130 QTest::newRow(dataTag: "http-tag") << (int)QNetworkProxyQuery::TcpSocket << QUrl() << QString("http") << QString("qt-project.org") << 80 << (int)QNetworkProxy::TunnelingCapability;
131 QTest::newRow(dataTag: "ftp-tag") << (int)QNetworkProxyQuery::TcpSocket << QUrl() << QString("ftp") << QString("qt-project.org") << 21 << (int)QNetworkProxy::TunnelingCapability;
132 QTest::newRow(dataTag: "https-tag") << (int)QNetworkProxyQuery::TcpSocket << QUrl() << QString("https") << QString("qt-project.org") << 443 << (int)QNetworkProxy::TunnelingCapability;
133#ifdef Q_OS_WIN
134 //in Qt 4.8, "socks" would get the socks proxy, but we don't want to enforce that for all platforms
135 QTest::newRow("socks-tag") << (int)QNetworkProxyQuery::TcpSocket << QUrl() << QString("socks") << QString("qt-project.org") << 21 << (int)(QNetworkProxy::TunnelingCapability | QNetworkProxy::ListeningCapability);
136#endif
137 //windows: ssh is not a tag provided by the os, but any tunneling proxy is acceptable
138 QTest::newRow(dataTag: "ssh-tag") << (int)QNetworkProxyQuery::TcpSocket << QUrl() << QString("ssh") << QString("qt-project.org") << 22 << (int)QNetworkProxy::TunnelingCapability;
139
140 //Server protocol tags (ftp/http proxies are no good, we need socks or nothing)
141 QTest::newRow(dataTag: "http-server-tag") << (int)QNetworkProxyQuery::TcpServer << QUrl() << QString("http") << QString() << 80 << (int)QNetworkProxy::ListeningCapability;
142 QTest::newRow(dataTag: "ftp-server-tag") << (int)QNetworkProxyQuery::TcpServer << QUrl() << QString("ftp") << QString() << 21 << (int)QNetworkProxy::ListeningCapability;
143 QTest::newRow(dataTag: "imap-server-tag") << (int)QNetworkProxyQuery::TcpServer << QUrl() << QString("imap") << QString() << 143 << (int)QNetworkProxy::ListeningCapability;
144
145 //UDP protocol tag
146 QTest::newRow(dataTag: "sip-udp-tag") << (int)QNetworkProxyQuery::UdpSocket << QUrl() << QString("sip") << QString("qt-project.org") << 5061 << (int)QNetworkProxy::UdpTunnelingCapability;
147}
148
149void tst_QNetworkProxyFactory::systemProxyForQuery() const
150{
151 QFETCH(int, type);
152 QFETCH(QUrl, url);
153 QFETCH(QString, tag);
154 QFETCH(QString, hostName);
155 QFETCH(int, port);
156 QFETCH(int, requiredCapabilities);
157
158 QNetworkProxyQuery query;
159
160 switch (type) {
161 case QNetworkProxyQuery::UrlRequest:
162 query = QNetworkProxyQuery(url);
163 break;
164 case QNetworkProxyQuery::TcpSocket:
165 case QNetworkProxyQuery::UdpSocket:
166 query = QNetworkProxyQuery(hostName, port, tag, QNetworkProxyQuery::QueryType(type));
167 break;
168 case QNetworkProxyQuery::TcpServer:
169 query = QNetworkProxyQuery(quint16(port), tag);
170 break;
171 }
172
173 QElapsedTimer sw;
174 sw.start();
175 QList<QNetworkProxy> systemProxyList = QNetworkProxyFactory::systemProxyForQuery(query);
176 qDebug() << sw.elapsed() << "ms";
177 QVERIFY(!systemProxyList.isEmpty());
178
179 // for manual comparison with system
180 qDebug() << systemProxyList;
181
182 foreach (const QNetworkProxy &proxy, systemProxyList) {
183 QVERIFY((requiredCapabilities == 0) || (proxy.capabilities() & requiredCapabilities));
184 }
185}
186
187void tst_QNetworkProxyFactory::systemProxyForQuery_local()
188{
189 QList<QNetworkProxy> list;
190 const QString proxyHost("myproxy.test.com");
191
192 // set an arbitrary proxy
193 QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, proxyHost, 80));
194 factory = 0;
195
196 // localhost
197 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QUrl("http://localhost/")));
198 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
199 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QString("localhost"), 80));
200 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
201
202 // 127.0.0.1
203 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QUrl("http://127.0.0.1/")));
204 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
205 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QString("127.0.0.1"), 80));
206 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
207
208 // [::1]
209 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QUrl("http://[::1]/")));
210 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
211 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QString("[::1]"), 80));
212 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
213
214 // an arbitrary host
215 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QUrl("http://another.host.com/")));
216 QVERIFY((!list.isEmpty()) && (list[0].hostName() == proxyHost));
217 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QString("another.host.com"), 80));
218 QVERIFY((!list.isEmpty()) && (list[0].hostName() == proxyHost));
219
220 // disable proxy
221 QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
222 factory = 0;
223
224 // localhost
225 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QUrl("http://localhost/")));
226 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
227 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QString("localhost"), 80));
228 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
229
230 // 127.0.0.1
231 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QUrl("http://127.0.0.1/")));
232 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
233 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QString("127.0.0.1"), 80));
234 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
235
236 // [::1]
237 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QUrl("http://[::1]/")));
238 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
239 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QString("[::1]"), 80));
240 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
241
242 // an arbitrary host
243 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QUrl("http://another.host.com/")));
244 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
245 list = QNetworkProxyFactory::proxyForQuery(query: QNetworkProxyQuery(QString("another.host.com"), 80));
246 QVERIFY(list.isEmpty() || (list[0].type() == QNetworkProxy::NoProxy));
247}
248
249Q_DECLARE_METATYPE(QNetworkProxy::ProxyType)
250
251void tst_QNetworkProxyFactory::genericSystemProxy()
252{
253 QFETCH(QByteArray, envVar);
254 QFETCH(QByteArray, url);
255 QFETCH(QNetworkProxy::ProxyType, proxyType);
256 QFETCH(QString, hostName);
257 QFETCH(int, port);
258
259// We can only use the generic system proxy where available:
260#if !defined(Q_OS_WIN) && !defined(Q_OS_MACOS) && !QT_CONFIG(libproxy)
261 qputenv(varName: envVar, value: url);
262 const QList<QNetworkProxy> systemProxy = QNetworkProxyFactory::systemProxyForQuery();
263 QCOMPARE(systemProxy.size(), 1);
264 QCOMPARE(systemProxy.first().type(), proxyType);
265 QCOMPARE(systemProxy.first().hostName(), hostName);
266 QCOMPARE(systemProxy.first().port(), static_cast<quint16>(port));
267 qunsetenv(varName: envVar);
268#else
269 Q_UNUSED(envVar)
270 Q_UNUSED(url)
271 Q_UNUSED(proxyType)
272 Q_UNUSED(hostName)
273 Q_UNUSED(port)
274 QSKIP("Generic system proxy not available on this platform.");
275#endif
276}
277
278void tst_QNetworkProxyFactory::genericSystemProxy_data()
279{
280 QTest::addColumn<QByteArray>(name: "envVar");
281 QTest::addColumn<QByteArray>(name: "url");
282 QTest::addColumn<QNetworkProxy::ProxyType>(name: "proxyType");
283 QTest::addColumn<QString>(name: "hostName");
284 QTest::addColumn<int>(name: "port");
285
286 QTest::newRow(dataTag: "no proxy") << QByteArray("http_proxy") << QByteArray() << QNetworkProxy::NoProxy
287 << QString() << 0;
288 QTest::newRow(dataTag: "socks5") << QByteArray("http_proxy") << QByteArray("socks5://127.0.0.1:4242")
289 << QNetworkProxy::Socks5Proxy << QString("127.0.0.1") << 4242;
290 QTest::newRow(dataTag: "http") << QByteArray("http_proxy") << QByteArray("http://example.com:666")
291 << QNetworkProxy::HttpProxy << QString("example.com") << 666;
292}
293
294class QSPFQThread : public QThread
295{
296protected:
297 virtual void run()
298 {
299 proxies = QNetworkProxyFactory::systemProxyForQuery(query);
300 }
301public:
302 QNetworkProxyQuery query;
303 QList<QNetworkProxy> proxies;
304};
305
306//regression test for QTBUG-18799
307void tst_QNetworkProxyFactory::systemProxyForQueryCalledFromThread()
308{
309 if (QSysInfo::productType() == QLatin1String("windows") && QSysInfo::productVersion() == QLatin1String("7sp1")) {
310 QSKIP("This test fails by the systemProxyForQuery() call hanging - QTQAINFRA-1200");
311 }
312 QUrl url(QLatin1String("http://qt-project.org"));
313 QNetworkProxyQuery query(url);
314 QSPFQThread thread;
315 thread.query = query;
316 connect(sender: &thread, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
317 thread.start();
318 QTestEventLoop::instance().enterLoop(secs: 5);
319 QVERIFY(thread.isFinished());
320 QCOMPARE(thread.proxies, QNetworkProxyFactory::systemProxyForQuery(query));
321}
322
323QTEST_MAIN(tst_QNetworkProxyFactory)
324#include "tst_qnetworkproxyfactory.moc"
325

source code of qtbase/tests/auto/network/kernel/qnetworkproxyfactory/tst_qnetworkproxyfactory.cpp