Renamed qwebsocket.pri to qwebsockets.pri
[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