Made parameters const references
[contrib/qtwebsockets.git] / src / handshakerequest_p.cpp
1 /*
2 QWebSockets implements the WebSocket protocol as defined in RFC 6455.
3 Copyright (C) 2013 Kurt Pattyn (pattyn.kurt@gmail.com)
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20 #include "handshakerequest_p.h"
21 #include <QString>
22 #include <QMap>
23 #include <QTextStream>
24 #include <QUrl>
25 #include <QList>
26 #include <QStringList>
27 #include "qwebsocketprotocol.h"
28
29 QT_BEGIN_NAMESPACE
30
31 /*!
32     \internal
33  */
34 HandshakeRequest::HandshakeRequest(int port, bool isSecure) :
35     m_port(port),
36     m_isSecure(isSecure),
37     m_isValid(false),
38     m_headers(),
39     m_versions(),
40     m_key(),
41     m_origin(),
42     m_protocols(),
43     m_extensions(),
44     m_requestUrl()
45 {
46 }
47
48 /*!
49     \internal
50  */
51 HandshakeRequest::~HandshakeRequest()
52 {
53 }
54
55 /*!
56     \internal
57  */
58 void HandshakeRequest::clear()
59 {
60     m_port = -1;
61     m_isSecure = false;
62     m_isValid = false;
63     m_headers.clear();
64     m_versions.clear();
65     m_key.clear();
66     m_origin.clear();
67     m_protocols.clear();
68     m_extensions.clear();
69     m_requestUrl.clear();
70 }
71
72 /*!
73     \internal
74  */
75 int HandshakeRequest::getPort() const
76 {
77     return m_requestUrl.port(m_port);
78 }
79
80 /*!
81     \internal
82  */
83 bool HandshakeRequest::isSecure() const
84 {
85     return m_isSecure;
86 }
87
88 /*!
89     \internal
90  */
91 bool HandshakeRequest::isValid() const
92 {
93     return m_isValid;
94 }
95
96 /*!
97     \internal
98  */
99 QMap<QString, QString> HandshakeRequest::getHeaders() const
100 {
101     return m_headers;
102 }
103
104 /*!
105     \internal
106  */
107 QList<QWebSocketProtocol::Version> HandshakeRequest::getVersions() const
108 {
109     return m_versions;
110 }
111
112 /*!
113     \internal
114  */
115 QString HandshakeRequest::getResourceName() const
116 {
117     return m_requestUrl.path();
118 }
119
120 /*!
121     \internal
122  */
123 QString HandshakeRequest::getKey() const
124 {
125     return m_key;
126 }
127
128 /*!
129     \internal
130  */
131 QString HandshakeRequest::getHost() const
132 {
133     return m_requestUrl.host();
134 }
135
136 /*!
137     \internal
138  */
139 QString HandshakeRequest::getOrigin() const
140 {
141     return m_origin;
142 }
143
144 /*!
145     \internal
146  */
147 QList<QString> HandshakeRequest::getProtocols() const
148 {
149     return m_protocols;
150 }
151
152 /*!
153     \internal
154  */
155 QList<QString> HandshakeRequest::getExtensions() const
156 {
157     return m_extensions;
158 }
159
160 /*!
161     \internal
162  */
163 QUrl HandshakeRequest::getRequestUrl() const
164 {
165     return m_requestUrl;
166 }
167
168 /*!
169     \internal
170  */
171 QTextStream &HandshakeRequest::readFromStream(QTextStream &textStream)
172 {
173     m_isValid = false;
174     clear();
175     if (textStream.status() == QTextStream::Ok)
176     {
177         QString requestLine = textStream.readLine();
178         QStringList tokens = requestLine.split(' ', QString::SkipEmptyParts);
179         QString verb = tokens[0];
180         QString resourceName = tokens[1];
181         QString httpProtocol = tokens[2];
182         bool conversionOk = false;
183         float httpVersion = httpProtocol.midRef(5).toFloat(&conversionOk);
184
185         QString headerLine = textStream.readLine();
186         m_headers.clear();
187         while (!headerLine.isEmpty())
188         {
189             QStringList headerField = headerLine.split(QString(": "), QString::SkipEmptyParts);
190             m_headers.insertMulti(headerField[0], headerField[1]);
191             headerLine = textStream.readLine();
192         }
193
194         QString host = m_headers.value("Host", "");
195         m_requestUrl = QUrl::fromEncoded(resourceName.toLatin1());
196         if (m_requestUrl.isRelative())
197         {
198             m_requestUrl.setHost(host);
199         }
200         if (m_requestUrl.scheme().isEmpty())
201         {
202             QString scheme =  isSecure() ? "wss://" : "ws://";
203             m_requestUrl.setScheme(scheme);
204         }
205
206         QStringList versionLines = m_headers.values("Sec-WebSocket-Version");
207         Q_FOREACH(QString versionLine, versionLines)
208         {
209             QStringList versions = versionLine.split(",", QString::SkipEmptyParts);
210             Q_FOREACH(QString version, versions)
211             {
212                 QWebSocketProtocol::Version ver = QWebSocketProtocol::versionFromString(version.trimmed());
213                 m_versions << ver;
214             }
215         }
216         qStableSort(m_versions.begin(), m_versions.end(), qGreater<QWebSocketProtocol::Version>());     //sort in descending order
217         m_key = m_headers.value("Sec-WebSocket-Key", "");
218         QString upgrade = m_headers.value("Upgrade", ""); //must be equal to "websocket", case-insensitive
219         QString connection = m_headers.value("Connection", ""); //must contain "Upgrade", case-insensitive
220         QStringList connectionLine = connection.split(",", QString::SkipEmptyParts);
221         QStringList connectionValues;
222         Q_FOREACH(QString connection, connectionLine)
223         {
224             connectionValues << connection.trimmed();
225         }
226
227         //optional headers
228         m_origin = m_headers.value("Sec-WebSocket-Origin", "");
229         QStringList protocolLines = m_headers.values("Sec-WebSocket-Protocol");
230         Q_FOREACH(QString protocolLine, protocolLines)
231         {
232             QStringList protocols = protocolLine.split(",", QString::SkipEmptyParts);
233             Q_FOREACH(QString protocol, protocols)
234             {
235                 m_protocols << protocol.trimmed();
236             }
237         }
238         QStringList extensionLines = m_headers.values("Sec-WebSocket-Extensions");
239         Q_FOREACH(QString extensionLine, extensionLines)
240         {
241             QStringList extensions = extensionLine.split(",", QString::SkipEmptyParts);
242             Q_FOREACH(QString extension, extensions)
243             {
244                 m_extensions << extension.trimmed();
245             }
246         }
247         //TODO: authentication field
248
249         m_isValid = !(host.isEmpty() ||
250                       resourceName.isEmpty() ||
251                       m_versions.isEmpty() ||
252                       m_key.isEmpty() ||
253                       (verb != "GET") ||
254                       (!conversionOk || (httpVersion < 1.1f)) ||
255                       (upgrade.toLower() != "websocket") ||
256                       (!connectionValues.contains("upgrade", Qt::CaseInsensitive)));
257     }
258     return textStream;
259 }
260
261 /*!
262     \internal
263  */
264 QTextStream &operator >>(QTextStream &stream, HandshakeRequest &request)
265 {
266     return request.readFromStream(stream);
267 }
268
269 QT_END_NAMESPACE