6e26af48f4dc4242f73df23d1a60c0552638c658
[contrib/qtwebsockets.git] / tests / auto / handshakerequest / tst_handshakerequest.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL21$
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 ** In addition, as a special exception, Digia gives you certain additional
27 ** rights. These rights are described in the Digia Qt LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** $QT_END_LICENSE$
31 **
32 ****************************************************************************/
33 #include <QtTest/QtTest>
34 #include <QtTest/qtestcase.h>
35 #include <QtCore/QDebug>
36 #include <QtCore/QByteArray>
37 #include <QtCore/QtEndian>
38
39 #include "private/qwebsockethandshakerequest_p.h"
40 #include "private/qwebsocketprotocol_p.h"
41 #include "QtWebSockets/qwebsocketprotocol.h"
42
43 QT_USE_NAMESPACE
44
45 Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode)
46 Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode)
47
48 class tst_HandshakeRequest : public QObject
49 {
50     Q_OBJECT
51
52 public:
53     tst_HandshakeRequest();
54
55 private Q_SLOTS:
56     void initTestCase();
57     void cleanupTestCase();
58     void init();
59     void cleanup();
60
61     void tst_initialization();
62
63     void tst_invalidStream_data();
64     void tst_invalidStream();
65
66     void tst_multipleValuesInConnectionHeader();
67     void tst_multipleVersions();
68 };
69
70 tst_HandshakeRequest::tst_HandshakeRequest()
71 {}
72
73 void tst_HandshakeRequest::initTestCase()
74 {
75 }
76
77 void tst_HandshakeRequest::cleanupTestCase()
78 {}
79
80 void tst_HandshakeRequest::init()
81 {
82     qRegisterMetaType<QWebSocketProtocol::OpCode>("QWebSocketProtocol::OpCode");
83     qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode");
84 }
85
86 void tst_HandshakeRequest::cleanup()
87 {
88 }
89
90 void tst_HandshakeRequest::tst_initialization()
91 {
92     {
93         QWebSocketHandshakeRequest request(0, false);
94         QCOMPARE(request.port(), 0);
95         QVERIFY(!request.isSecure());
96         QVERIFY(!request.isValid());
97         QCOMPARE(request.extensions().length(), 0);
98         QCOMPARE(request.protocols().length(), 0);
99         QCOMPARE(request.headers().size(), 0);
100         QCOMPARE(request.key().length(), 0);
101         QCOMPARE(request.origin().length(), 0);
102         QCOMPARE(request.host().length(), 0);
103         QVERIFY(request.requestUrl().isEmpty());
104         QCOMPARE(request.resourceName().length(), 0);
105         QCOMPARE(request.versions().length(), 0);
106     }
107     {
108         QWebSocketHandshakeRequest request(80, true);
109         QCOMPARE(request.port(), 80);
110         QVERIFY(request.isSecure());
111         QVERIFY(!request.isValid());
112         QCOMPARE(request.extensions().length(), 0);
113         QCOMPARE(request.protocols().length(), 0);
114         QCOMPARE(request.headers().size(), 0);
115         QCOMPARE(request.key().length(), 0);
116         QCOMPARE(request.origin().length(), 0);
117         QCOMPARE(request.host().length(), 0);
118         QVERIFY(request.requestUrl().isEmpty());
119         QCOMPARE(request.resourceName().length(), 0);
120         QCOMPARE(request.versions().length(), 0);
121     }
122     {
123         QWebSocketHandshakeRequest request(80, true);
124         request.clear();
125         QCOMPARE(request.port(), 80);
126         QVERIFY(request.isSecure());
127         QVERIFY(!request.isValid());
128         QCOMPARE(request.extensions().length(), 0);
129         QCOMPARE(request.protocols().length(), 0);
130         QCOMPARE(request.headers().size(), 0);
131         QCOMPARE(request.key().length(), 0);
132         QCOMPARE(request.origin().length(), 0);
133         QCOMPARE(request.host().length(), 0);
134         QVERIFY(request.requestUrl().isEmpty());
135         QCOMPARE(request.resourceName().length(), 0);
136         QCOMPARE(request.versions().length(), 0);
137     }
138 }
139
140 void tst_HandshakeRequest::tst_invalidStream_data()
141 {
142     QTest::addColumn<QString>("dataStream");
143
144     QTest::newRow("garbage on 2 lines") << QStringLiteral("foofoofoo\r\nfoofoo\r\n\r\n");
145     QTest::newRow("garbage on 1 line") << QStringLiteral("foofoofoofoofoo");
146     QTest::newRow("Correctly formatted but invalid fields")
147             << QStringLiteral("VERB RESOURCE PROTOCOL");
148
149     //internally the fields are parsed and indexes are used to convert
150     //to a http version for instance
151     //this test checks if there doesn't occur an out-of-bounds exception
152     QTest::newRow("Correctly formatted but invalid short fields") << QStringLiteral("V R P");
153     QTest::newRow("Invalid \\0 character in header") << QStringLiteral("V R\0 P");
154     QTest::newRow("Invalid HTTP version in header") << QStringLiteral("V R HTTP/invalid");
155     QTest::newRow("Empty header field") << QStringLiteral("GET . HTTP/1.1\r\nHEADER: ");
156     QTest::newRow("All zeros") << QString::fromUtf8(QByteArray(10, char(0)));
157     QTest::newRow("Invalid hostname") << QStringLiteral("GET . HTTP/1.1\r\nHost: \xFF\xFF");
158     //doing extensive QStringLiteral concatenations here, because
159     //MSVC 2010 complains when using concatenation literal strings about
160     //concatenation of wide and narrow strings (error C2308)
161     QTest::newRow("Complete header - Invalid WebSocket version")
162             << QStringLiteral("GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: ") +
163                QStringLiteral("\xFF\xFF\r\n") +
164                QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
165                QStringLiteral("Upgrade: websocket\r\n") +
166                QStringLiteral("Connection: Upgrade\r\n\r\n");
167     QTest::newRow("Complete header - Invalid verb")
168             << QStringLiteral("XXX . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n") +
169                QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
170                QStringLiteral("Upgrade: websocket\r\n") +
171                QStringLiteral("Connection: Upgrade\r\n\r\n");
172     QTest::newRow("Complete header - Invalid HTTP version")
173             << QStringLiteral("GET . HTTP/a.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n") +
174                QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
175                QStringLiteral("Upgrade: websocket\r\n") +
176                QStringLiteral("Connection: Upgrade\r\n\r\n");
177     QTest::newRow("Complete header - Invalid connection")
178             << QStringLiteral("GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n") +
179                QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
180                QStringLiteral("Upgrade: websocket\r\n") +
181                QStringLiteral("Connection: xxxxxxx\r\n\r\n");
182     QTest::newRow("Complete header - Invalid upgrade")
183             << QStringLiteral("GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n") +
184                QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
185                QStringLiteral("Upgrade: wabsocket\r\n") +
186                QStringLiteral("Connection: Upgrade\r\n\r\n");
187     QTest::newRow("Complete header - Upgrade contains too many values")
188             << QStringLiteral("GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n") +
189                QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
190                QStringLiteral("Upgrade: websocket,ftp\r\n") +
191                QStringLiteral("Connection: Upgrade\r\n\r\n");
192 }
193
194 void tst_HandshakeRequest::tst_invalidStream()
195 {
196     QFETCH(QString, dataStream);
197
198     QByteArray data;
199     QTextStream textStream(&data);
200     QWebSocketHandshakeRequest request(80, true);
201
202     textStream << dataStream;
203     textStream.seek(0);
204     request.readHandshake(textStream);
205
206     QVERIFY(!request.isValid());
207     QCOMPARE(request.port(), 80);
208     QVERIFY(request.isSecure());
209     QCOMPARE(request.extensions().length(), 0);
210     QCOMPARE(request.protocols().length(), 0);
211     QCOMPARE(request.headers().size(), 0);
212     QCOMPARE(request.key().length(), 0);
213     QCOMPARE(request.origin().length(), 0);
214     QCOMPARE(request.host().length(), 0);
215     QVERIFY(request.requestUrl().isEmpty());
216     QCOMPARE(request.resourceName().length(), 0);
217     QCOMPARE(request.versions().length(), 0);
218 }
219
220 /*
221  * This is a regression test
222  * Checks for validity when more than one value is present in Connection
223  */
224 void tst_HandshakeRequest::tst_multipleValuesInConnectionHeader()
225 {
226     //doing extensive QStringLiteral concatenations here, because
227     //MSVC 2010 complains when using concatenation literal strings about
228     //concatenation of wide and narrow strings (error C2308)
229     QString header = QStringLiteral("GET /test HTTP/1.1\r\nHost: ") +
230                      QStringLiteral("foo.com\r\nSec-WebSocket-Version: 13\r\n") +
231                      QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
232                      QStringLiteral("Upgrade: websocket\r\n") +
233                      QStringLiteral("Connection: Upgrade,keepalive\r\n\r\n");
234     QByteArray data;
235     QTextStream textStream(&data);
236     QWebSocketHandshakeRequest request(80, false);
237
238     textStream << header;
239     textStream.seek(0);
240     request.readHandshake(textStream);
241
242     QVERIFY(request.isValid());
243     QCOMPARE(request.port(), 80);
244     QVERIFY(!request.isSecure());
245     QCOMPARE(request.extensions().length(), 0);
246     QCOMPARE(request.protocols().length(), 0);
247     QCOMPARE(request.headers().size(), 5);
248     QCOMPARE(request.key().length(), 9);
249     QCOMPARE(request.origin().length(), 0);
250     QCOMPARE(request.requestUrl(), QUrl("ws://foo.com/test"));
251     QCOMPARE(request.host(), QStringLiteral("foo.com"));
252     QCOMPARE(request.resourceName().length(), 5);
253     QCOMPARE(request.versions().length(), 1);
254     QCOMPARE(request.versions().at(0), QWebSocketProtocol::Version13);
255 }
256
257 void tst_HandshakeRequest::tst_multipleVersions()
258 {
259     QString header = QStringLiteral("GET /test HTTP/1.1\r\nHost: foo.com\r\n") +
260                      QStringLiteral("Sec-WebSocket-Version: 4, 5, 6, 7, 8, 13\r\n") +
261                      QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
262                      QStringLiteral("Upgrade: websocket\r\n") +
263                      QStringLiteral("Connection: Upgrade,keepalive\r\n\r\n");
264     QByteArray data;
265     QTextStream textStream(&data);
266     QWebSocketHandshakeRequest request(80, false);
267
268     textStream << header;
269     textStream.seek(0);
270     request.readHandshake(textStream);
271
272     QVERIFY(request.isValid());
273     QCOMPARE(request.port(), 80);
274     QVERIFY(!request.isSecure());
275     QCOMPARE(request.extensions().length(), 0);
276     QCOMPARE(request.protocols().length(), 0);
277     QCOMPARE(request.headers().size(), 5);
278     QVERIFY(request.headers().contains(QStringLiteral("Host")));
279     QVERIFY(request.headers().contains(QStringLiteral("Sec-WebSocket-Version")));
280     QVERIFY(request.headers().contains(QStringLiteral("Sec-WebSocket-Key")));
281     QVERIFY(request.headers().contains(QStringLiteral("Upgrade")));
282     QVERIFY(request.headers().contains(QStringLiteral("Connection")));
283     QCOMPARE(request.key(), QStringLiteral("AVDFBDDFF"));
284     QCOMPARE(request.origin().length(), 0);
285     QCOMPARE(request.requestUrl(), QUrl("ws://foo.com/test"));
286     QCOMPARE(request.host(), QStringLiteral("foo.com"));
287     QCOMPARE(request.resourceName().length(), 5);
288     QCOMPARE(request.versions().length(), 6);
289     //should be 13 since the list is ordered in decreasing order
290     QCOMPARE(request.versions().at(0), QWebSocketProtocol::Version13);
291 }
292
293 QTEST_MAIN(tst_HandshakeRequest)
294
295 #include "tst_handshakerequest.moc"