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