2 QWebSockets implements the WebSocket protocol as defined in RFC 6455.
3 Copyright (C) 2013 Kurt Pattyn (pattyn.kurt@gmail.com)
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.
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.
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
20 #include "handshakeresponse_p.h"
21 #include "handshakerequest_p.h"
23 #include <QTextStream>
25 #include <QStringList>
27 #include <QCryptographicHash>
30 #include <QStringBuilder> //for more efficient string concatenation
37 HandshakeResponse::HandshakeResponse(const HandshakeRequest &request,
38 const QString &serverName,
40 const QList<QWebSocketProtocol::Version> &supportedVersions,
41 const QList<QString> &supportedProtocols,
42 const QList<QString> &supportedExtensions) :
47 m_acceptedExtension(),
48 m_acceptedVersion(QWebSocketProtocol::V_Unknow)
50 m_response = getHandshakeResponse(request, serverName, isOriginAllowed, supportedVersions, supportedProtocols, supportedExtensions);
57 HandshakeResponse::~HandshakeResponse()
64 bool HandshakeResponse::isValid() const
72 bool HandshakeResponse::canUpgrade() const
74 return m_isValid && m_canUpgrade;
80 QString HandshakeResponse::getAcceptedProtocol() const
82 return m_acceptedProtocol;
88 QString HandshakeResponse::calculateAcceptKey(const QString &key) const
90 QString tmpKey = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; //the UID comes from RFC6455
91 QByteArray hash = QCryptographicHash::hash(tmpKey.toLatin1(), QCryptographicHash::Sha1);
92 return QString(hash.toBase64());
98 QString HandshakeResponse::getHandshakeResponse(const HandshakeRequest &request,
99 const QString &serverName,
100 bool isOriginAllowed,
101 const QList<QWebSocketProtocol::Version> &supportedVersions,
102 const QList<QString> &supportedProtocols,
103 const QList<QString> &supportedExtensions)
105 QStringList response;
106 m_canUpgrade = false;
108 if (!isOriginAllowed)
112 response << "HTTP/1.1 403 Access Forbidden";
117 if (request.isValid())
119 QString acceptKey = calculateAcceptKey(request.getKey());
120 QList<QString> matchingProtocols = supportedProtocols.toSet().intersect(request.getProtocols().toSet()).toList();
121 QList<QString> matchingExtensions = supportedExtensions.toSet().intersect(request.getExtensions().toSet()).toList();
122 QList<QWebSocketProtocol::Version> matchingVersions = request.getVersions().toSet().intersect(supportedVersions.toSet()).toList();
123 qStableSort(matchingVersions.begin(), matchingVersions.end(), qGreater<QWebSocketProtocol::Version>()); //sort in descending order
125 if (matchingVersions.isEmpty())
127 m_canUpgrade = false;
131 response << "HTTP/1.1 101 Switching Protocols" <<
132 "Upgrade: websocket" <<
133 "Connection: Upgrade" <<
134 "Sec-WebSocket-Accept: " % acceptKey;
135 if (!matchingProtocols.isEmpty())
137 m_acceptedProtocol = matchingProtocols.first();
138 response << "Sec-WebSocket-Protocol: " % m_acceptedProtocol;
140 if (!matchingExtensions.isEmpty())
142 m_acceptedExtension = matchingExtensions.first();
143 response << "Sec-WebSocket-Extensions: " % m_acceptedExtension;
145 QString origin = request.getOrigin().trimmed();
146 if (origin.isEmpty())
150 //TODO: header values should be configurable; i.e. Server, Allow-Credentials, Allow-Headers
151 response << "Server: " + serverName <<
152 "Access-Control-Allow-Credentials: false" << //do not allow credentialed request (containing cookies)
153 "Access-Control-Allow-Methods: GET" << //only GET is allowed during handshaking
154 "Access-Control-Allow-Headers: content-type" << //this is OK to be fixed; only the content-type header is allowed, no other headers are accepted
155 "Access-Control-Allow-Origin: " % origin <<
156 "Date: " % QDateTime::currentDateTimeUtc().toString("ddd, dd MMM yyyy hh:mm:ss 'GMT'");
158 m_acceptedVersion = QWebSocketProtocol::currentVersion();
164 m_canUpgrade = false;
168 response << "HTTP/1.1 400 Bad Request";
169 QStringList versions;
170 Q_FOREACH(QWebSocketProtocol::Version version, supportedVersions)
172 versions << QString::number(static_cast<int>(version));
174 response << "Sec-WebSocket-Version: " % versions.join(", ");
177 response << "\r\n"; //append empty line at end of header
178 return response.join("\r\n");
184 QTextStream &HandshakeResponse::writeToStream(QTextStream &textStream) const
186 if (!m_response.isEmpty())
188 textStream << m_response.toLatin1().constData();
192 textStream.setStatus(QTextStream::WriteFailed);
200 QTextStream &operator <<(QTextStream &stream, const HandshakeResponse &response)
202 return response.writeToStream(stream);
208 QWebSocketProtocol::Version HandshakeResponse::getAcceptedVersion() const
210 return m_acceptedVersion;
216 QString HandshakeResponse::getAcceptedExtension() const
218 return m_acceptedExtension;