1 /****************************************************************************
3 ** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtWebSockets module of the Qt Toolkit.
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.
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.
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.
32 ****************************************************************************/
34 #include "qwebsockethandshakerequest_p.h"
35 #include "qwebsocketprotocol.h"
36 #include "qwebsocketprotocol_p.h"
38 #include <QtCore/QString>
39 #include <QtCore/QMap>
40 #include <QtCore/QTextStream>
41 #include <QtCore/QUrl>
42 #include <QtCore/QList>
43 #include <QtCore/QStringList>
44 #include <functional> //for std::greater
49 \brief Constructs a new QWebSocketHandshakeRequest given \a port and \a isSecure
52 QWebSocketHandshakeRequest::QWebSocketHandshakeRequest(int port, bool isSecure) :
69 QWebSocketHandshakeRequest::~QWebSocketHandshakeRequest()
74 \brief Clears the request
77 void QWebSocketHandshakeRequest::clear()
92 int QWebSocketHandshakeRequest::port() const
94 return m_requestUrl.port(m_port);
100 bool QWebSocketHandshakeRequest::isSecure() const
108 bool QWebSocketHandshakeRequest::isValid() const
116 QMap<QString, QString> QWebSocketHandshakeRequest::headers() const
124 QList<QWebSocketProtocol::Version> QWebSocketHandshakeRequest::versions() const
132 QString QWebSocketHandshakeRequest::resourceName() const
134 return m_requestUrl.path();
140 QString QWebSocketHandshakeRequest::key() const
148 QString QWebSocketHandshakeRequest::host() const
150 return m_requestUrl.host();
156 QString QWebSocketHandshakeRequest::origin() const
164 QList<QString> QWebSocketHandshakeRequest::protocols() const
172 QList<QString> QWebSocketHandshakeRequest::extensions() const
180 QUrl QWebSocketHandshakeRequest::requestUrl() const
188 void QWebSocketHandshakeRequest::readHandshake(QTextStream &textStream)
192 if (Q_UNLIKELY(textStream.status() != QTextStream::Ok))
194 const QString requestLine = textStream.readLine();
195 const QStringList tokens = requestLine.split(' ', QString::SkipEmptyParts);
196 if (Q_UNLIKELY(tokens.length() < 3)) {
201 const QString verb(tokens.at(0));
202 const QString resourceName(tokens.at(1));
203 const QString httpProtocol(tokens.at(2));
204 bool conversionOk = false;
205 const float httpVersion = httpProtocol.midRef(5).toFloat(&conversionOk);
207 if (Q_UNLIKELY(!conversionOk)) {
212 QString headerLine = textStream.readLine();
214 while (!headerLine.isEmpty()) {
215 const QStringList headerField = headerLine.split(QStringLiteral(": "),
216 QString::SkipEmptyParts);
217 if (Q_UNLIKELY(headerField.length() < 2)) {
221 m_headers.insertMulti(headerField.at(0).toLower(), headerField.at(1));
222 headerLine = textStream.readLine();
225 const QString host = m_headers.value(QStringLiteral("host"), QString());
226 m_requestUrl = QUrl::fromEncoded(resourceName.toLatin1());
227 if (m_requestUrl.isRelative())
228 m_requestUrl.setHost(host);
229 if (m_requestUrl.scheme().isEmpty()) {
230 const QString scheme = isSecure() ? QStringLiteral("wss") : QStringLiteral("ws");
231 m_requestUrl.setScheme(scheme);
234 const QStringList versionLines = m_headers.values(QStringLiteral("sec-websocket-version"));
235 for (QStringList::const_iterator v = versionLines.begin(); v != versionLines.end(); ++v) {
236 const QStringList versions = (*v).split(QStringLiteral(","), QString::SkipEmptyParts);
237 for (QStringList::const_iterator i = versions.begin(); i != versions.end(); ++i) {
239 (void)(*i).toUInt(&ok);
244 const QWebSocketProtocol::Version ver =
245 QWebSocketProtocol::versionFromString((*i).trimmed());
249 //sort in descending order
250 std::sort(m_versions.begin(), m_versions.end(), std::greater<QWebSocketProtocol::Version>());
251 m_key = m_headers.value(QStringLiteral("sec-websocket-key"), QString());
252 //must contain "Upgrade", case-insensitive
253 const QString upgrade = m_headers.value(QStringLiteral("upgrade"), QString());
254 //must be equal to "websocket", case-insensitive
255 const QString connection = m_headers.value(QStringLiteral("connection"), QString());
256 const QStringList connectionLine = connection.split(QStringLiteral(","),
257 QString::SkipEmptyParts);
258 QStringList connectionValues;
259 for (QStringList::const_iterator c = connectionLine.begin(); c != connectionLine.end(); ++c)
260 connectionValues << (*c).trimmed();
263 m_origin = m_headers.value(QStringLiteral("sec-websocket-origin"), QString());
264 const QStringList protocolLines = m_headers.values(QStringLiteral("sec-websocket-protocol"));
265 for (QStringList::const_iterator pl = protocolLines.begin(); pl != protocolLines.end(); ++pl) {
266 QStringList protocols = (*pl).split(QStringLiteral(","), QString::SkipEmptyParts);
267 for (QStringList::const_iterator p = protocols.begin(); p != protocols.end(); ++p)
268 m_protocols << (*p).trimmed();
270 const QStringList extensionLines = m_headers.values(QStringLiteral("sec-websocket-extensions"));
271 for (QStringList::const_iterator el = extensionLines.begin();
272 el != extensionLines.end(); ++el) {
273 QStringList extensions = (*el).split(QStringLiteral(","), QString::SkipEmptyParts);
274 for (QStringList::const_iterator e = extensions.begin(); e != extensions.end(); ++e)
275 m_extensions << (*e).trimmed();
278 //TODO: authentication field
280 m_isValid = !(host.isEmpty() ||
281 resourceName.isEmpty() ||
282 m_versions.isEmpty() ||
284 (verb != QStringLiteral("GET")) ||
285 (!conversionOk || (httpVersion < 1.1f)) ||
286 (upgrade.toLower() != QStringLiteral("websocket")) ||
287 (!connectionValues.contains(QStringLiteral("upgrade"), Qt::CaseInsensitive)));
288 if (Q_UNLIKELY(!m_isValid))