Added check on QT_NO_NETWORKPROXY to include proxy functionality only when required
[contrib/qtwebsockets.git] / src / handshakerequest_p.cpp
1 #include "handshakerequest_p.h"
2 #include <QString>
3 #include <QMap>
4 #include <QTextStream>
5 #include <QUrl>
6 #include <QList>
7 #include <QStringList>
8 #include "qwebsocketprotocol.h"
9
10 QT_BEGIN_NAMESPACE
11
12 /*!
13         \internal
14  */
15 HandshakeRequest::HandshakeRequest(int port, bool isSecure) :
16         m_port(port),
17         m_isSecure(isSecure),
18         m_isValid(false),
19         m_headers(),
20         m_versions(),
21         m_key(),
22         m_origin(),
23         m_protocols(),
24         m_extensions(),
25         m_requestUrl()
26 {
27 }
28
29 /*!
30         \internal
31  */
32 HandshakeRequest::~HandshakeRequest()
33 {
34 }
35
36 /*!
37         \internal
38  */
39 void HandshakeRequest::clear()
40 {
41         m_port = -1;
42         m_isSecure = false;
43         m_isValid = false;
44         m_headers.clear();
45         m_versions.clear();
46         m_key.clear();
47         m_origin.clear();
48         m_protocols.clear();
49         m_extensions.clear();
50         m_requestUrl.clear();
51 }
52
53 /*!
54         \internal
55  */
56 int HandshakeRequest::getPort() const
57 {
58         return m_requestUrl.port(m_port);
59 }
60
61 /*!
62         \internal
63  */
64 bool HandshakeRequest::isSecure() const
65 {
66         return m_isSecure;
67 }
68
69 /*!
70         \internal
71  */
72 bool HandshakeRequest::isValid() const
73 {
74         return m_isValid;
75 }
76
77 /*!
78         \internal
79  */
80 QMap<QString, QString> HandshakeRequest::getHeaders() const
81 {
82         return m_headers;
83 }
84
85 /*!
86         \internal
87  */
88 QList<QWebSocketProtocol::Version> HandshakeRequest::getVersions() const
89 {
90         return m_versions;
91 }
92
93 /*!
94         \internal
95  */
96 QString HandshakeRequest::getResourceName() const
97 {
98         return m_requestUrl.path();
99 }
100
101 /*!
102         \internal
103  */
104 QString HandshakeRequest::getKey() const
105 {
106         return m_key;
107 }
108
109 /*!
110         \internal
111  */
112 QString HandshakeRequest::getHost() const
113 {
114         return m_requestUrl.host();
115 }
116
117 /*!
118         \internal
119  */
120 QString HandshakeRequest::getOrigin() const
121 {
122         return m_origin;
123 }
124
125 /*!
126         \internal
127  */
128 QList<QString> HandshakeRequest::getProtocols() const
129 {
130         return m_protocols;
131 }
132
133 /*!
134         \internal
135  */
136 QList<QString> HandshakeRequest::getExtensions() const
137 {
138         return m_extensions;
139 }
140
141 /*!
142         \internal
143  */
144 QUrl HandshakeRequest::getRequestUrl() const
145 {
146         return m_requestUrl;
147 }
148
149 /*!
150         \internal
151  */
152 QTextStream &HandshakeRequest::readFromStream(QTextStream &textStream)
153 {
154         m_isValid = false;
155         clear();
156         if (textStream.status() == QTextStream::Ok)
157         {
158                 QString requestLine = textStream.readLine();
159                 QStringList tokens = requestLine.split(' ', QString::SkipEmptyParts);
160                 QString verb = tokens[0];
161                 QString resourceName = tokens[1];
162                 QString httpProtocol = tokens[2];
163                 bool conversionOk = false;
164                 float httpVersion = httpProtocol.midRef(5).toFloat(&conversionOk);
165
166                 QString headerLine = textStream.readLine();
167                 m_headers.clear();
168                 while (!headerLine.isEmpty())
169                 {
170                         QStringList headerField = headerLine.split(QString(": "), QString::SkipEmptyParts);
171                         m_headers.insertMulti(headerField[0], headerField[1]);
172                         headerLine = textStream.readLine();
173                 }
174
175                 QString host = m_headers.value("Host", "");
176                 m_requestUrl = QUrl::fromEncoded(resourceName.toLatin1());
177                 if (m_requestUrl.isRelative())
178                 {
179                         m_requestUrl.setHost(host);
180                 }
181                 if (m_requestUrl.scheme().isEmpty())
182                 {
183                         QString scheme =  isSecure() ? "wss://" : "ws://";
184                         m_requestUrl.setScheme(scheme);
185                 }
186
187                 QStringList versionLines = m_headers.values("Sec-WebSocket-Version");
188                 Q_FOREACH(QString versionLine, versionLines)
189                 {
190                         QStringList versions = versionLine.split(",", QString::SkipEmptyParts);
191                         Q_FOREACH(QString version, versions)
192                         {
193                                 QWebSocketProtocol::Version ver = QWebSocketProtocol::versionFromString(version.trimmed());
194                                 m_versions << ver;
195                         }
196                 }
197                 qStableSort(m_versions.begin(), m_versions.end(), qGreater<QWebSocketProtocol::Version>());     //sort in descending order
198                 m_key = m_headers.value("Sec-WebSocket-Key", "");
199                 QString upgrade = m_headers.value("Upgrade", ""); //must be equal to "websocket", case-insensitive
200                 QString connection = m_headers.value("Connection", ""); //must contain "Upgrade", case-insensitive
201                 QStringList connectionLine = connection.split(",", QString::SkipEmptyParts);
202                 QStringList connectionValues;
203                 Q_FOREACH(QString connection, connectionLine)
204                 {
205                         connectionValues << connection.trimmed();
206                 }
207
208                 //optional headers
209                 m_origin = m_headers.value("Sec-WebSocket-Origin", "");
210                 QStringList protocolLines = m_headers.values("Sec-WebSocket-Protocol");
211                 Q_FOREACH(QString protocolLine, protocolLines)
212                 {
213                         QStringList protocols = protocolLine.split(",", QString::SkipEmptyParts);
214                         Q_FOREACH(QString protocol, protocols)
215                         {
216                                 m_protocols << protocol.trimmed();
217                         }
218                 }
219                 QStringList extensionLines = m_headers.values("Sec-WebSocket-Extensions");
220                 Q_FOREACH(QString extensionLine, extensionLines)
221                 {
222                         QStringList extensions = extensionLine.split(",", QString::SkipEmptyParts);
223                         Q_FOREACH(QString extension, extensions)
224                         {
225                                 m_extensions << extension.trimmed();
226                         }
227                 }
228                 //TODO: authentication field
229
230                 m_isValid = !(host.isEmpty() ||
231                                           resourceName.isEmpty() ||
232                                           m_versions.isEmpty() ||
233                                           m_key.isEmpty() ||
234                                           (verb != "GET") ||
235                                           (!conversionOk || (httpVersion < 1.1f)) ||
236                                           (upgrade.toLower() != "websocket") ||
237                                           (!connectionValues.contains("upgrade", Qt::CaseInsensitive)));
238         }
239         return textStream;
240 }
241
242 /*!
243         \internal
244  */
245 QTextStream &operator >>(QTextStream &stream, HandshakeRequest &request)
246 {
247         return request.readFromStream(stream);
248 }
249
250 QT_END_NAMESPACE