summaryrefslogtreecommitdiff
path: root/src/websockets/handshakeresponse_p.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/websockets/handshakeresponse_p.cpp')
-rw-r--r--src/websockets/handshakeresponse_p.cpp220
1 files changed, 220 insertions, 0 deletions
diff --git a/src/websockets/handshakeresponse_p.cpp b/src/websockets/handshakeresponse_p.cpp
new file mode 100644
index 0000000..c59b875
--- /dev/null
+++ b/src/websockets/handshakeresponse_p.cpp
@@ -0,0 +1,220 @@
+/*
+QWebSockets implements the WebSocket protocol as defined in RFC 6455.
+Copyright (C) 2013 Kurt Pattyn (pattyn.kurt@gmail.com)
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "handshakeresponse_p.h"
+#include "handshakerequest_p.h"
+#include <QString>
+#include <QTextStream>
+#include <QByteArray>
+#include <QStringList>
+#include <QDateTime>
+#include <QCryptographicHash>
+#include <QSet>
+#include <QList>
+#include <QStringBuilder> //for more efficient string concatenation
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ */
+HandshakeResponse::HandshakeResponse(const HandshakeRequest &request,
+ const QString &serverName,
+ bool isOriginAllowed,
+ const QList<QWebSocketProtocol::Version> &supportedVersions,
+ const QList<QString> &supportedProtocols,
+ const QList<QString> &supportedExtensions) :
+ m_isValid(false),
+ m_canUpgrade(false),
+ m_response(),
+ m_acceptedProtocol(),
+ m_acceptedExtension(),
+ m_acceptedVersion(QWebSocketProtocol::V_Unknow)
+{
+ m_response = getHandshakeResponse(request, serverName, isOriginAllowed, supportedVersions, supportedProtocols, supportedExtensions);
+ m_isValid = true;
+}
+
+/*!
+ \internal
+ */
+HandshakeResponse::~HandshakeResponse()
+{
+}
+
+/*!
+ \internal
+ */
+bool HandshakeResponse::isValid() const
+{
+ return m_isValid;
+}
+
+/*!
+ \internal
+ */
+bool HandshakeResponse::canUpgrade() const
+{
+ return m_isValid && m_canUpgrade;
+}
+
+/*!
+ \internal
+ */
+QString HandshakeResponse::getAcceptedProtocol() const
+{
+ return m_acceptedProtocol;
+}
+
+/*!
+ \internal
+ */
+QString HandshakeResponse::calculateAcceptKey(const QString &key) const
+{
+ QString tmpKey = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; //the UID comes from RFC6455
+ QByteArray hash = QCryptographicHash::hash(tmpKey.toLatin1(), QCryptographicHash::Sha1);
+ return QString(hash.toBase64());
+}
+
+/*!
+ \internal
+ */
+QString HandshakeResponse::getHandshakeResponse(const HandshakeRequest &request,
+ const QString &serverName,
+ bool isOriginAllowed,
+ const QList<QWebSocketProtocol::Version> &supportedVersions,
+ const QList<QString> &supportedProtocols,
+ const QList<QString> &supportedExtensions)
+{
+ QStringList response;
+ m_canUpgrade = false;
+
+ if (!isOriginAllowed)
+ {
+ if (!m_canUpgrade)
+ {
+ response << "HTTP/1.1 403 Access Forbidden";
+ }
+ }
+ else
+ {
+ if (request.isValid())
+ {
+ QString acceptKey = calculateAcceptKey(request.getKey());
+ QList<QString> matchingProtocols = supportedProtocols.toSet().intersect(request.getProtocols().toSet()).toList();
+ QList<QString> matchingExtensions = supportedExtensions.toSet().intersect(request.getExtensions().toSet()).toList();
+ QList<QWebSocketProtocol::Version> matchingVersions = request.getVersions().toSet().intersect(supportedVersions.toSet()).toList();
+ qStableSort(matchingVersions.begin(), matchingVersions.end(), qGreater<QWebSocketProtocol::Version>()); //sort in descending order
+
+ if (matchingVersions.isEmpty())
+ {
+ m_canUpgrade = false;
+ }
+ else
+ {
+ response << "HTTP/1.1 101 Switching Protocols" <<
+ "Upgrade: websocket" <<
+ "Connection: Upgrade" <<
+ "Sec-WebSocket-Accept: " % acceptKey;
+ if (!matchingProtocols.isEmpty())
+ {
+ m_acceptedProtocol = matchingProtocols.first();
+ response << "Sec-WebSocket-Protocol: " % m_acceptedProtocol;
+ }
+ if (!matchingExtensions.isEmpty())
+ {
+ m_acceptedExtension = matchingExtensions.first();
+ response << "Sec-WebSocket-Extensions: " % m_acceptedExtension;
+ }
+ QString origin = request.getOrigin().trimmed();
+ if (origin.isEmpty())
+ {
+ origin = "*";
+ }
+ response << "Server: " + serverName <<
+ "Access-Control-Allow-Credentials: false" << //do not allow credentialed request (containing cookies)
+ "Access-Control-Allow-Methods: GET" << //only GET is allowed during handshaking
+ "Access-Control-Allow-Headers: content-type" << //this is OK; only the content-type header is allowed, no other headers are accepted
+ "Access-Control-Allow-Origin: " % origin <<
+ "Date: " % QDateTime::currentDateTimeUtc().toString("ddd, dd MMM yyyy hh:mm:ss 'GMT'");
+
+ m_acceptedVersion = QWebSocketProtocol::currentVersion();
+ m_canUpgrade = true;
+ }
+ }
+ else
+ {
+ m_canUpgrade = false;
+ }
+ if (!m_canUpgrade)
+ {
+ response << "HTTP/1.1 400 Bad Request";
+ QStringList versions;
+ Q_FOREACH(QWebSocketProtocol::Version version, supportedVersions)
+ {
+ versions << QString::number(static_cast<int>(version));
+ }
+ response << "Sec-WebSocket-Version: " % versions.join(", ");
+ }
+ }
+ response << "\r\n"; //append empty line at end of header
+ return response.join("\r\n");
+}
+
+/*!
+ \internal
+ */
+QTextStream &HandshakeResponse::writeToStream(QTextStream &textStream) const
+{
+ if (!m_response.isEmpty())
+ {
+ textStream << m_response.toLatin1().constData();
+ }
+ else
+ {
+ textStream.setStatus(QTextStream::WriteFailed);
+ }
+ return textStream;
+}
+
+/*!
+ \internal
+ */
+QTextStream &operator <<(QTextStream &stream, const HandshakeResponse &response)
+{
+ return response.writeToStream(stream);
+}
+
+/*!
+ \internal
+ */
+QWebSocketProtocol::Version HandshakeResponse::getAcceptedVersion() const
+{
+ return m_acceptedVersion;
+}
+
+/*!
+ \internal
+ */
+QString HandshakeResponse::getAcceptedExtension() const
+{
+ return m_acceptedExtension;
+}
+
+QT_END_NAMESPACE