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 | #include <QtCore/QQueue> |
33 | #include <QtCore/QString> |
34 | #include <QtCore/QCoreApplication> |
35 | |
36 | #include <private/qhttpsocketengine_p.h> |
37 | #include <qhostinfo.h> |
38 | #include <qhostaddress.h> |
39 | #include <qtcpsocket.h> |
40 | #include <qdebug.h> |
41 | #include <qelapsedtimer.h> |
42 | #include <qtcpserver.h> |
43 | |
44 | #include "../../../network-settings.h" |
45 | |
46 | class tst_QHttpSocketEngine : public QObject |
47 | { |
48 | Q_OBJECT |
49 | |
50 | private slots: |
51 | void initTestCase(); |
52 | void init(); |
53 | void construction(); |
54 | void errorTest_data(); |
55 | void errorTest(); |
56 | void simpleConnectToIMAP(); |
57 | void simpleErrorsAndStates(); |
58 | |
59 | void tcpSocketBlockingTest(); |
60 | void tcpSocketNonBlockingTest(); |
61 | void downloadBigFile(); |
62 | // void tcpLoopbackPerformance(); |
63 | void passwordAuth(); |
64 | void ensureEofTriggersNotification(); |
65 | |
66 | protected slots: |
67 | void tcpSocketNonBlocking_hostFound(); |
68 | void tcpSocketNonBlocking_connected(); |
69 | void tcpSocketNonBlocking_closed(); |
70 | void tcpSocketNonBlocking_readyRead(); |
71 | void tcpSocketNonBlocking_bytesWritten(qint64); |
72 | void exitLoopSlot(); |
73 | void downloadBigFileSlot(); |
74 | |
75 | private: |
76 | QTcpSocket *tcpSocketNonBlocking_socket; |
77 | QStringList tcpSocketNonBlocking_data; |
78 | qint64 tcpSocketNonBlocking_totalWritten; |
79 | QTcpSocket *tmpSocket; |
80 | qint64 bytesAvailable; |
81 | }; |
82 | |
83 | class MiniHttpServer: public QTcpServer |
84 | { |
85 | Q_OBJECT |
86 | QTcpSocket *client; |
87 | QList<QByteArray> dataToTransmit; |
88 | |
89 | public: |
90 | QByteArray receivedData; |
91 | |
92 | MiniHttpServer(const QList<QByteArray> &data) : client(0), dataToTransmit(data) |
93 | { |
94 | listen(); |
95 | connect(sender: this, SIGNAL(newConnection()), receiver: this, SLOT(doAccept())); |
96 | } |
97 | |
98 | public slots: |
99 | void doAccept() |
100 | { |
101 | client = nextPendingConnection(); |
102 | connect(sender: client, SIGNAL(readyRead()), receiver: this, SLOT(sendData())); |
103 | } |
104 | |
105 | void sendData() |
106 | { |
107 | receivedData += client->readAll(); |
108 | int idx = client->property(name: "dataTransmitionIdx" ).toInt(); |
109 | if (receivedData.contains(c: "\r\n\r\n" ) || |
110 | receivedData.contains(c: "\n\n" )) { |
111 | if (idx < dataToTransmit.length()) |
112 | client->write(data: dataToTransmit.at(i: idx++)); |
113 | if (idx == dataToTransmit.length()) { |
114 | client->disconnectFromHost(); |
115 | disconnect(sender: client, signal: 0, receiver: this, member: 0); |
116 | client = 0; |
117 | } else { |
118 | client->setProperty(name: "dataTransmitionIdx" , value: idx); |
119 | } |
120 | } |
121 | } |
122 | }; |
123 | |
124 | void tst_QHttpSocketEngine::initTestCase() |
125 | { |
126 | #ifdef QT_TEST_SERVER |
127 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3128)); |
128 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1080)); |
129 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 80)); |
130 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::imapServerName(), 143)); |
131 | #else |
132 | if (!QtNetworkSettings::verifyTestNetworkSettings()) |
133 | QSKIP("No network test server available" ); |
134 | #endif |
135 | } |
136 | |
137 | void tst_QHttpSocketEngine::init() |
138 | { |
139 | tmpSocket = 0; |
140 | bytesAvailable = 0; |
141 | } |
142 | |
143 | //--------------------------------------------------------------------------- |
144 | void tst_QHttpSocketEngine::construction() |
145 | { |
146 | QHttpSocketEngine socketDevice; |
147 | |
148 | QVERIFY(!socketDevice.isValid()); |
149 | |
150 | // Initialize device |
151 | QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); |
152 | QVERIFY(socketDevice.isValid()); |
153 | QCOMPARE(socketDevice.protocol(), QAbstractSocket::IPv4Protocol); |
154 | QCOMPARE(socketDevice.socketType(), QAbstractSocket::TcpSocket); |
155 | QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
156 | // QVERIFY(socketDevice.socketDescriptor() != -1); |
157 | QCOMPARE(socketDevice.localAddress(), QHostAddress()); |
158 | QCOMPARE(socketDevice.localPort(), quint16(0)); |
159 | QCOMPARE(socketDevice.peerAddress(), QHostAddress()); |
160 | QCOMPARE(socketDevice.peerPort(), quint16(0)); |
161 | QCOMPARE(socketDevice.error(), QAbstractSocket::UnknownSocketError); |
162 | |
163 | //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::bytesAvailable() was called in QAbstractSocket::UnconnectedState"); |
164 | QCOMPARE(socketDevice.bytesAvailable(), 0); |
165 | |
166 | //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::hasPendingDatagrams() was called in QAbstractSocket::UnconnectedState"); |
167 | QVERIFY(!socketDevice.hasPendingDatagrams()); |
168 | } |
169 | |
170 | //--------------------------------------------------------------------------- |
171 | void tst_QHttpSocketEngine::errorTest_data() |
172 | { |
173 | QTest::addColumn<QString>(name: "hostname" ); |
174 | QTest::addColumn<int>(name: "port" ); |
175 | QTest::addColumn<QString>(name: "username" ); |
176 | QTest::addColumn<QString>(name: "response" ); |
177 | QTest::addColumn<int>(name: "expectedError" ); |
178 | |
179 | QQueue<QByteArray> responses; |
180 | QTest::newRow(dataTag: "proxy-host-not-found" ) << "this-host-does-not-exist." << 1080 << QString() |
181 | << QString() |
182 | << int(QAbstractSocket::ProxyNotFoundError); |
183 | QTest::newRow(dataTag: "proxy-connection-refused" ) << QtNetworkSettings::socksProxyServerName() << 2 << QString() |
184 | << QString() |
185 | << int(QAbstractSocket::ProxyConnectionRefusedError); |
186 | |
187 | QTest::newRow(dataTag: "garbage1" ) << QString() << 0 << QString() |
188 | << "This is not HTTP\r\n\r\n" |
189 | << int(QAbstractSocket::ProxyProtocolError); |
190 | |
191 | QTest::newRow(dataTag: "garbage2" ) << QString() << 0 << QString() |
192 | << "This is not HTTP" |
193 | << int(QAbstractSocket::ProxyProtocolError); |
194 | |
195 | QTest::newRow(dataTag: "garbage3" ) << QString() << 0 << QString() |
196 | << "" |
197 | << int(QAbstractSocket::ProxyConnectionClosedError); |
198 | |
199 | QTest::newRow(dataTag: "forbidden" ) << QString() << 0 << QString() |
200 | << "HTTP/1.0 403 Forbidden\r\n\r\n" |
201 | << int(QAbstractSocket::SocketAccessError); |
202 | |
203 | QTest::newRow(dataTag: "method-not-allowed" ) << QString() << 0 << QString() |
204 | << "HTTP/1.0 405 Method Not Allowed\r\n\r\n" |
205 | << int(QAbstractSocket::SocketAccessError); |
206 | |
207 | QTest::newRow(dataTag: "proxy-authentication-too-short" ) |
208 | << QString() << 0 << "foo" |
209 | << "HTTP/1.0 407 Proxy Authentication Required\r\n\r\n" |
210 | << int(QAbstractSocket::ProxyProtocolError); |
211 | |
212 | QTest::newRow(dataTag: "proxy-authentication-invalid-method" ) |
213 | << QString() << 0 << "foo" |
214 | << "HTTP/1.0 407 Proxy Authentication Required\r\n" |
215 | "Proxy-Authenticate: Frobnicator\r\n\r\n" |
216 | << int(QAbstractSocket::ProxyProtocolError); |
217 | |
218 | QTest::newRow(dataTag: "proxy-authentication-required" ) |
219 | << QString() << 0 << "foo" |
220 | << "HTTP/1.0 407 Proxy Authentication Required\r\n" |
221 | "Proxy-Connection: close\r\n" |
222 | "Proxy-Authenticate: Basic, realm=wonderland\r\n\r\n" |
223 | << int(QAbstractSocket::ProxyAuthenticationRequiredError); |
224 | |
225 | QTest::newRow(dataTag: "proxy-authentication-required2" ) |
226 | << QString() << 0 << "foo" |
227 | << "HTTP/1.0 407 Proxy Authentication Required\r\n" |
228 | "Proxy-Connection: keep-alive\r\n" |
229 | "Proxy-Authenticate: Basic, realm=wonderland\r\n\r\n" |
230 | "\1" |
231 | "HTTP/1.0 407 Proxy Authentication Required\r\n" |
232 | "Proxy-Authenticate: Basic, realm=wonderland\r\n\r\n" |
233 | << int(QAbstractSocket::ProxyAuthenticationRequiredError); |
234 | |
235 | QTest::newRow(dataTag: "proxy-authentication-required-noclose" ) |
236 | << QString() << 0 << "foo" |
237 | << "HTTP/1.0 407 Proxy Authentication Required\r\n" |
238 | "Proxy-Authenticate: Basic\r\n" |
239 | "\r\n" |
240 | << int(QAbstractSocket::ProxyAuthenticationRequiredError); |
241 | |
242 | QTest::newRow(dataTag: "connection-refused" ) << QString() << 0 << QString() |
243 | << "HTTP/1.0 503 Service Unavailable\r\n\r\n" |
244 | << int(QAbstractSocket::ConnectionRefusedError); |
245 | |
246 | QTest::newRow(dataTag: "host-not-found" ) << QString() << 0 << QString() |
247 | << "HTTP/1.0 404 Not Found\r\n\r\n" |
248 | << int(QAbstractSocket::HostNotFoundError); |
249 | |
250 | QTest::newRow(dataTag: "weird-http-reply" ) << QString() << 0 << QString() |
251 | << "HTTP/1.0 206 Partial Content\r\n\r\n" |
252 | << int(QAbstractSocket::ProxyProtocolError); |
253 | } |
254 | |
255 | void tst_QHttpSocketEngine::errorTest() |
256 | { |
257 | QFETCH(QString, hostname); |
258 | QFETCH(int, port); |
259 | QFETCH(QString, username); |
260 | QFETCH(QString, response); |
261 | QFETCH(int, expectedError); |
262 | |
263 | MiniHttpServer server(response.toLatin1().split(sep: '\1')); |
264 | |
265 | if (hostname.isEmpty()) { |
266 | hostname = "127.0.0.1" ; |
267 | port = server.serverPort(); |
268 | } |
269 | QTcpSocket socket; |
270 | socket.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, hostname, port, username, username)); |
271 | socket.connectToHost(hostName: "0.1.2.3" , port: 12345); |
272 | |
273 | connect(sender: &socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), |
274 | receiver: &QTestEventLoop::instance(), SLOT(exitLoop())); |
275 | QTestEventLoop::instance().enterLoop(secs: 30); |
276 | QVERIFY(!QTestEventLoop::instance().timeout()); |
277 | |
278 | QCOMPARE(int(socket.error()), expectedError); |
279 | } |
280 | |
281 | //--------------------------------------------------------------------------- |
282 | void tst_QHttpSocketEngine::simpleConnectToIMAP() |
283 | { |
284 | QHttpSocketEngine socketDevice; |
285 | |
286 | // Initialize device |
287 | QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); |
288 | QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
289 | |
290 | socketDevice.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3128)); |
291 | QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::imapServerIp(), 143)); |
292 | QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState); |
293 | QVERIFY(socketDevice.waitForWrite()); |
294 | QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectedState); |
295 | QCOMPARE(socketDevice.peerAddress(), QtNetworkSettings::imapServerIp()); |
296 | QVERIFY(!socketDevice.localAddress().isNull()); |
297 | QVERIFY(socketDevice.localPort() > 0); |
298 | |
299 | // Wait for the greeting |
300 | QVERIFY(socketDevice.waitForRead()); |
301 | |
302 | // Read the greeting |
303 | qint64 available = int(socketDevice.bytesAvailable()); |
304 | QVERIFY(available > 0); |
305 | QByteArray array; |
306 | array.resize(size: int(available)); |
307 | QVERIFY(socketDevice.read(array.data(), array.size()) == available); |
308 | |
309 | // Check that the greeting is what we expect it to be |
310 | QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); |
311 | |
312 | |
313 | // Write a logout message |
314 | QByteArray array2 = "XXXX LOGOUT\r\n" ; |
315 | QVERIFY(socketDevice.write(array2.data(), |
316 | array2.size()) == array2.size()); |
317 | |
318 | // Wait for the response |
319 | QVERIFY(socketDevice.waitForRead()); |
320 | |
321 | available = int(socketDevice.bytesAvailable()); |
322 | QVERIFY(available > 0); |
323 | array.resize(size: int(available)); |
324 | QVERIFY(socketDevice.read(array.data(), array.size()) == available); |
325 | |
326 | // Check that the greeting is what we expect it to be |
327 | QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n" ); |
328 | |
329 | // Wait for the response |
330 | QVERIFY(socketDevice.waitForRead()); |
331 | char c; |
332 | QCOMPARE(socketDevice.read(&c, sizeof(c)), qint64(-1)); |
333 | QCOMPARE(socketDevice.error(), QAbstractSocket::RemoteHostClosedError); |
334 | QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
335 | } |
336 | |
337 | //--------------------------------------------------------------------------- |
338 | void tst_QHttpSocketEngine::simpleErrorsAndStates() |
339 | { |
340 | { |
341 | QHttpSocketEngine socketDevice; |
342 | |
343 | // Initialize device |
344 | QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); |
345 | |
346 | socketDevice.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3128)); |
347 | |
348 | QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
349 | QVERIFY(!socketDevice.connectToHost(QHostAddress(QtNetworkSettings::socksProxyServerName()), 8088)); |
350 | QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState); |
351 | if (socketDevice.waitForWrite(msecs: 30000)) { |
352 | QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState || |
353 | socketDevice.state() == QAbstractSocket::UnconnectedState); |
354 | } else { |
355 | QCOMPARE(socketDevice.error(), QAbstractSocket::SocketTimeoutError); |
356 | } |
357 | } |
358 | |
359 | } |
360 | |
361 | /* |
362 | //--------------------------------------------------------------------------- |
363 | void tst_QHttpSocketEngine::tcpLoopbackPerformance() |
364 | { |
365 | QTcpServer server; |
366 | |
367 | // Bind to any port on all interfaces |
368 | QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); |
369 | QCOMPARE(server.state(), QAbstractSocket::BoundState); |
370 | quint16 port = server.localPort(); |
371 | |
372 | // Listen for incoming connections |
373 | QVERIFY(server.listen()); |
374 | QCOMPARE(server.state(), QAbstractSocket::ListeningState); |
375 | |
376 | // Initialize a Tcp socket |
377 | QHttpSocketEngine client; |
378 | QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); |
379 | |
380 | client.setProxy(QHostAddress("80.232.37.158"), 1081); |
381 | |
382 | // Connect to our server |
383 | if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { |
384 | QVERIFY(client.waitForWrite()); |
385 | QVERIFY(client.connectToHost(QHostAddress("127.0.0.1"), port)); |
386 | } |
387 | |
388 | // The server accepts the connectio |
389 | int socketDescriptor = server.accept(); |
390 | QVERIFY(socketDescriptor > 0); |
391 | |
392 | // A socket device is initialized on the server side, passing the |
393 | // socket descriptor from accept(). It's pre-connected. |
394 | QSocketLayer serverSocket; |
395 | QVERIFY(serverSocket.initialize(socketDescriptor)); |
396 | QCOMPARE(serverSocket.state(), QAbstractSocket::ConnectedState); |
397 | |
398 | const int messageSize = 1024 * 256; |
399 | QByteArray message1(messageSize, '@'); |
400 | QByteArray answer(messageSize, '@'); |
401 | |
402 | QElapsedTimer timer; |
403 | timer.start(); |
404 | qlonglong readBytes = 0; |
405 | while (timer.elapsed() < 30000) { |
406 | qlonglong written = serverSocket.write(message1.data(), message1.size()); |
407 | while (written > 0) { |
408 | client.waitForRead(); |
409 | if (client.bytesAvailable() > 0) { |
410 | qlonglong readNow = client.read(answer.data(), answer.size()); |
411 | written -= readNow; |
412 | readBytes += readNow; |
413 | } |
414 | } |
415 | } |
416 | |
417 | qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", |
418 | readBytes / (1024.0 * 1024.0), |
419 | timer.elapsed() / 1024.0, |
420 | (readBytes / (timer.elapsed() / 1000.0)) / (1024 * 1024)); |
421 | } |
422 | */ |
423 | |
424 | |
425 | |
426 | void tst_QHttpSocketEngine::tcpSocketBlockingTest() |
427 | { |
428 | QHttpSocketEngineHandler http; |
429 | |
430 | QTcpSocket socket; |
431 | |
432 | // Connect |
433 | socket.connectToHost(hostName: QtNetworkSettings::imapServerName(), port: 143); |
434 | QVERIFY(socket.waitForConnected()); |
435 | QCOMPARE(socket.state(), QTcpSocket::ConnectedState); |
436 | |
437 | // Read greeting |
438 | QVERIFY(socket.waitForReadyRead(30000)); |
439 | QString s = socket.readLine(); |
440 | QVERIFY2(QtNetworkSettings::compareReplyIMAP(s.toLatin1()), qPrintable(s)); |
441 | |
442 | // Write NOOP |
443 | QCOMPARE((int) socket.write("1 NOOP\r\n" , 8), 8); |
444 | |
445 | if (!socket.canReadLine()) |
446 | QVERIFY(socket.waitForReadyRead(30000)); |
447 | |
448 | // Read response |
449 | s = socket.readLine(); |
450 | QCOMPARE(s.toLatin1().constData(), "1 OK Completed\r\n" ); |
451 | |
452 | // Write LOGOUT |
453 | QCOMPARE((int) socket.write("2 LOGOUT\r\n" , 10), 10); |
454 | |
455 | if (!socket.canReadLine()) |
456 | QVERIFY(socket.waitForReadyRead(30000)); |
457 | |
458 | // Read two lines of respose |
459 | s = socket.readLine(); |
460 | QCOMPARE(s.toLatin1().constData(), "* BYE LOGOUT received\r\n" ); |
461 | |
462 | if (!socket.canReadLine()) |
463 | QVERIFY(socket.waitForReadyRead(30000)); |
464 | |
465 | s = socket.readLine(); |
466 | QCOMPARE(s.toLatin1().constData(), "2 OK Completed\r\n" ); |
467 | |
468 | // Close the socket |
469 | socket.close(); |
470 | |
471 | // Check that it's closed |
472 | QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); |
473 | } |
474 | |
475 | //---------------------------------------------------------------------------------- |
476 | |
477 | void tst_QHttpSocketEngine::tcpSocketNonBlockingTest() |
478 | { |
479 | QHttpSocketEngineHandler http; |
480 | |
481 | QTcpSocket socket; |
482 | connect(asender: &socket, SIGNAL(hostFound()), SLOT(tcpSocketNonBlocking_hostFound())); |
483 | connect(asender: &socket, SIGNAL(connected()), SLOT(tcpSocketNonBlocking_connected())); |
484 | connect(asender: &socket, SIGNAL(disconnected()), SLOT(tcpSocketNonBlocking_closed())); |
485 | connect(asender: &socket, SIGNAL(bytesWritten(qint64)), SLOT(tcpSocketNonBlocking_bytesWritten(qint64))); |
486 | connect(asender: &socket, SIGNAL(readyRead()), SLOT(tcpSocketNonBlocking_readyRead())); |
487 | tcpSocketNonBlocking_socket = &socket; |
488 | |
489 | // Connect |
490 | socket.connectToHost(hostName: QtNetworkSettings::imapServerName(), port: 143); |
491 | QVERIFY(socket.state() == QTcpSocket::HostLookupState || |
492 | socket.state() == QTcpSocket::ConnectingState); |
493 | |
494 | QTestEventLoop::instance().enterLoop(secs: 30); |
495 | if (QTestEventLoop::instance().timeout()) { |
496 | QFAIL("Timed out" ); |
497 | } |
498 | |
499 | if (socket.state() == QTcpSocket::ConnectingState) { |
500 | QTestEventLoop::instance().enterLoop(secs: 30); |
501 | if (QTestEventLoop::instance().timeout()) { |
502 | QFAIL("Timed out" ); |
503 | } |
504 | } |
505 | |
506 | QCOMPARE(socket.state(), QTcpSocket::ConnectedState); |
507 | |
508 | QTestEventLoop::instance().enterLoop(secs: 30); |
509 | if (QTestEventLoop::instance().timeout()) { |
510 | QFAIL("Timed out" ); |
511 | } |
512 | |
513 | // Read greeting |
514 | QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); |
515 | QByteArray data = tcpSocketNonBlocking_data.at(i: 0).toLatin1(); |
516 | QVERIFY2(QtNetworkSettings::compareReplyIMAP(data), data.constData()); |
517 | |
518 | |
519 | tcpSocketNonBlocking_data.clear(); |
520 | |
521 | tcpSocketNonBlocking_totalWritten = 0; |
522 | |
523 | // Write NOOP |
524 | QCOMPARE((int) socket.write("1 NOOP\r\n" , 8), 8); |
525 | |
526 | |
527 | QTestEventLoop::instance().enterLoop(secs: 30); |
528 | if (QTestEventLoop::instance().timeout()) { |
529 | QFAIL("Timed out" ); |
530 | } |
531 | |
532 | QCOMPARE(tcpSocketNonBlocking_totalWritten, 8); |
533 | |
534 | |
535 | QTestEventLoop::instance().enterLoop(secs: 30); |
536 | if (QTestEventLoop::instance().timeout()) { |
537 | QFAIL("Timed out" ); |
538 | } |
539 | |
540 | |
541 | // Read response |
542 | QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); |
543 | QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "1 OK Completed\r\n" ); |
544 | tcpSocketNonBlocking_data.clear(); |
545 | |
546 | |
547 | tcpSocketNonBlocking_totalWritten = 0; |
548 | |
549 | // Write LOGOUT |
550 | QCOMPARE((int) socket.write("2 LOGOUT\r\n" , 10), 10); |
551 | |
552 | QTestEventLoop::instance().enterLoop(secs: 30); |
553 | if (QTestEventLoop::instance().timeout()) { |
554 | QFAIL("Timed out" ); |
555 | } |
556 | |
557 | QCOMPARE(tcpSocketNonBlocking_totalWritten, 10); |
558 | |
559 | // Wait for greeting |
560 | QTestEventLoop::instance().enterLoop(secs: 30); |
561 | if (QTestEventLoop::instance().timeout()) { |
562 | QFAIL("Timed out" ); |
563 | } |
564 | |
565 | // Read two lines of respose |
566 | QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "* BYE LOGOUT received\r\n" ); |
567 | QCOMPARE(tcpSocketNonBlocking_data.at(1).toLatin1().constData(), "2 OK Completed\r\n" ); |
568 | tcpSocketNonBlocking_data.clear(); |
569 | |
570 | // Close the socket |
571 | socket.close(); |
572 | |
573 | // Check that it's closed |
574 | QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); |
575 | } |
576 | |
577 | void tst_QHttpSocketEngine::tcpSocketNonBlocking_hostFound() |
578 | { |
579 | QTestEventLoop::instance().exitLoop(); |
580 | } |
581 | |
582 | void tst_QHttpSocketEngine::tcpSocketNonBlocking_connected() |
583 | { |
584 | QTestEventLoop::instance().exitLoop(); |
585 | } |
586 | |
587 | void tst_QHttpSocketEngine::tcpSocketNonBlocking_readyRead() |
588 | { |
589 | while (tcpSocketNonBlocking_socket->canReadLine()) |
590 | tcpSocketNonBlocking_data.append(t: tcpSocketNonBlocking_socket->readLine()); |
591 | |
592 | QTestEventLoop::instance().exitLoop(); |
593 | } |
594 | |
595 | void tst_QHttpSocketEngine::tcpSocketNonBlocking_bytesWritten(qint64 written) |
596 | { |
597 | tcpSocketNonBlocking_totalWritten += written; |
598 | QTestEventLoop::instance().exitLoop(); |
599 | } |
600 | |
601 | void tst_QHttpSocketEngine::tcpSocketNonBlocking_closed() |
602 | { |
603 | } |
604 | |
605 | //---------------------------------------------------------------------------------- |
606 | |
607 | void tst_QHttpSocketEngine::downloadBigFile() |
608 | { |
609 | QHttpSocketEngineHandler http; |
610 | |
611 | if (tmpSocket) |
612 | delete tmpSocket; |
613 | tmpSocket = new QTcpSocket; |
614 | |
615 | connect(asender: tmpSocket, SIGNAL(connected()), SLOT(exitLoopSlot())); |
616 | connect(asender: tmpSocket, SIGNAL(readyRead()), SLOT(downloadBigFileSlot())); |
617 | |
618 | tmpSocket->connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 80); |
619 | |
620 | QTestEventLoop::instance().enterLoop(secs: 30); |
621 | if (QTestEventLoop::instance().timeout()) |
622 | QFAIL("Network operation timed out" ); |
623 | |
624 | QByteArray hostName = QtNetworkSettings::httpServerName().toLatin1(); |
625 | QCOMPARE(tmpSocket->state(), QAbstractSocket::ConnectedState); |
626 | QVERIFY(tmpSocket->write("GET /qtest/mediumfile HTTP/1.0\r\n" ) > 0); |
627 | QVERIFY(tmpSocket->write("Host: " ) > 0); |
628 | QVERIFY(tmpSocket->write(hostName.data()) > 0); |
629 | QVERIFY(tmpSocket->write("\r\n" ) > 0); |
630 | QVERIFY(tmpSocket->write("\r\n" ) > 0); |
631 | |
632 | bytesAvailable = 0; |
633 | |
634 | QElapsedTimer stopWatch; |
635 | stopWatch.start(); |
636 | |
637 | QTestEventLoop::instance().enterLoop(secs: 60); |
638 | if (QTestEventLoop::instance().timeout()) |
639 | QFAIL("Network operation timed out" ); |
640 | |
641 | QVERIFY(bytesAvailable >= 10000000); |
642 | |
643 | QCOMPARE(tmpSocket->state(), QAbstractSocket::ConnectedState); |
644 | |
645 | qDebug(msg: "\t\t%.1fMB/%.1fs: %.1fMB/s" , |
646 | bytesAvailable / (1024.0 * 1024.0), |
647 | stopWatch.elapsed() / 1024.0, |
648 | (bytesAvailable / (stopWatch.elapsed() / 1000.0)) / (1024 * 1024)); |
649 | |
650 | delete tmpSocket; |
651 | tmpSocket = 0; |
652 | } |
653 | |
654 | void tst_QHttpSocketEngine::exitLoopSlot() |
655 | { |
656 | QTestEventLoop::instance().exitLoop(); |
657 | } |
658 | |
659 | |
660 | void tst_QHttpSocketEngine::downloadBigFileSlot() |
661 | { |
662 | bytesAvailable += tmpSocket->readAll().size(); |
663 | if (bytesAvailable >= 10000000) |
664 | QTestEventLoop::instance().exitLoop(); |
665 | } |
666 | |
667 | void tst_QHttpSocketEngine::passwordAuth() |
668 | { |
669 | QHttpSocketEngine socketDevice; |
670 | |
671 | // Initialize device |
672 | QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); |
673 | QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
674 | |
675 | socketDevice.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3128, "qsockstest" , "password" )); |
676 | |
677 | QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::imapServerIp(), 143)); |
678 | QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState); |
679 | QVERIFY(socketDevice.waitForWrite()); |
680 | QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectedState); |
681 | QCOMPARE(socketDevice.peerAddress(), QtNetworkSettings::imapServerIp()); |
682 | |
683 | // Wait for the greeting |
684 | QVERIFY(socketDevice.waitForRead()); |
685 | |
686 | // Read the greeting |
687 | qint64 available = socketDevice.bytesAvailable(); |
688 | QVERIFY(available > 0); |
689 | QByteArray array; |
690 | array.resize(size: available); |
691 | QVERIFY(socketDevice.read(array.data(), array.size()) == available); |
692 | |
693 | // Check that the greeting is what we expect it to be |
694 | QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); |
695 | |
696 | |
697 | // Write a logout message |
698 | QByteArray array2 = "XXXX LOGOUT\r\n" ; |
699 | QVERIFY(socketDevice.write(array2.data(), |
700 | array2.size()) == array2.size()); |
701 | |
702 | // Wait for the response |
703 | QVERIFY(socketDevice.waitForRead()); |
704 | |
705 | available = socketDevice.bytesAvailable(); |
706 | QVERIFY(available > 0); |
707 | array.resize(size: available); |
708 | QVERIFY(socketDevice.read(array.data(), array.size()) == available); |
709 | |
710 | // Check that the greeting is what we expect it to be |
711 | QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n" ); |
712 | |
713 | // Wait for the response |
714 | QVERIFY(socketDevice.waitForRead()); |
715 | char c; |
716 | QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); |
717 | QCOMPARE(socketDevice.error(), QAbstractSocket::RemoteHostClosedError); |
718 | QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState); |
719 | } |
720 | |
721 | //---------------------------------------------------------------------------------- |
722 | |
723 | void tst_QHttpSocketEngine::ensureEofTriggersNotification() |
724 | { |
725 | QList<QByteArray> serverData; |
726 | // Set the handshake and server response data |
727 | serverData << "HTTP/1.0 200 Connection established\r\n\r\n" << "0" ; |
728 | MiniHttpServer server(serverData); |
729 | |
730 | QTcpSocket socket; |
731 | connect(asender: &socket, SIGNAL(connected()), SLOT(exitLoopSlot())); |
732 | socket.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, server.serverAddress().toString(), |
733 | server.serverPort())); |
734 | socket.connectToHost(hostName: "0.1.2.3" , port: 12345); |
735 | |
736 | QTestEventLoop::instance().enterLoop(secs: 5); |
737 | if (QTestEventLoop::instance().timeout()) |
738 | QFAIL("Connect timed out" ); |
739 | |
740 | QCOMPARE(socket.state(), QTcpSocket::ConnectedState); |
741 | // Disable read notification on server response |
742 | socket.setReadBufferSize(1); |
743 | socket.putChar(c: 0); |
744 | |
745 | // Wait for the response |
746 | connect(asender: &socket, SIGNAL(readyRead()), SLOT(exitLoopSlot())); |
747 | QTestEventLoop::instance().enterLoop(secs: 5); |
748 | if (QTestEventLoop::instance().timeout()) |
749 | QFAIL("Read timed out" ); |
750 | |
751 | QCOMPARE(socket.state(), QTcpSocket::ConnectedState); |
752 | QCOMPARE(socket.bytesAvailable(), 1); |
753 | // Trigger a read notification |
754 | socket.readAll(); |
755 | // Check for pending EOF at input |
756 | QCOMPARE(socket.bytesAvailable(), 0); |
757 | QCOMPARE(socket.state(), QTcpSocket::ConnectedState); |
758 | |
759 | // Try to read EOF |
760 | connect(asender: &socket, SIGNAL(disconnected()), SLOT(exitLoopSlot())); |
761 | QTestEventLoop::instance().enterLoop(secs: 5); |
762 | if (QTestEventLoop::instance().timeout()) |
763 | QFAIL("Disconnect timed out" ); |
764 | |
765 | // Check that it's closed |
766 | QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); |
767 | } |
768 | |
769 | QTEST_MAIN(tst_QHttpSocketEngine) |
770 | #include "tst_qhttpsocketengine.moc" |
771 | |