1 #include "handshakeresponse_p.h"
2 #include "handshakerequest_p.h"
8 #include <QCryptographicHash>
17 HandshakeResponse::HandshakeResponse(const HandshakeRequest &request,
18 const QString &serverName,
20 const QList<QWebSocketProtocol::Version> &supportedVersions,
21 const QList<QString> &supportedProtocols,
22 const QList<QString> &supportedExtensions) :
27 m_acceptedExtension(),
28 m_acceptedVersion(QWebSocketProtocol::V_Unknow)
30 m_response = getHandshakeResponse(request, serverName, isOriginAllowed, supportedVersions, supportedProtocols, supportedExtensions);
37 HandshakeResponse::~HandshakeResponse()
44 bool HandshakeResponse::isValid() const
52 bool HandshakeResponse::canUpgrade() const
54 return m_isValid && m_canUpgrade;
60 QString HandshakeResponse::getAcceptedProtocol() const
62 return m_acceptedProtocol;
68 QString HandshakeResponse::calculateAcceptKey(const QString &key) const
70 QString tmpKey = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; //the UID comes from RFC6455
71 QByteArray hash = QCryptographicHash::hash(tmpKey.toLatin1(), QCryptographicHash::Sha1);
72 return QString(hash.toBase64());
78 QString HandshakeResponse::getHandshakeResponse(const HandshakeRequest &request,
79 const QString &serverName,
81 const QList<QWebSocketProtocol::Version> &supportedVersions,
82 const QList<QString> &supportedProtocols,
83 const QList<QString> &supportedExtensions)
92 response << "HTTP/1.1 403 Access Forbidden";
97 if (request.isValid())
99 QString acceptKey = calculateAcceptKey(request.getKey());
100 QList<QString> matchingProtocols = supportedProtocols.toSet().intersect(request.getProtocols().toSet()).toList();
101 QList<QString> matchingExtensions = supportedExtensions.toSet().intersect(request.getExtensions().toSet()).toList();
102 QList<QWebSocketProtocol::Version> matchingVersions = request.getVersions().toSet().intersect(supportedVersions.toSet()).toList();
103 qStableSort(matchingVersions.begin(), matchingVersions.end(), qGreater<QWebSocketProtocol::Version>()); //sort in descending order
105 if (matchingVersions.isEmpty())
107 m_canUpgrade = false;
111 response << "HTTP/1.1 101 Switching Protocols" <<
112 "Upgrade: websocket" <<
113 "Connection: Upgrade" <<
114 "Sec-WebSocket-Accept: " + acceptKey;
115 if (!matchingProtocols.isEmpty())
117 m_acceptedProtocol = matchingProtocols.first();
118 response << "Sec-WebSocket-Protocol: " + m_acceptedProtocol;
120 if (!matchingExtensions.isEmpty())
122 m_acceptedExtension = matchingExtensions.first();
123 response << "Sec-WebSocket-Extensions: " + m_acceptedExtension;
125 QString origin = request.getOrigin().trimmed();
126 if (origin.isEmpty())
130 //TODO: header values should be configurable; i.e. Server, Allow-Credentials, Allow-Headers
131 response << "Server: " + serverName <<
132 "Access-Control-Allow-Credentials: false" << //do not allow credentialed request (containing cookies)
133 "Access-Control-Allow-Methods: GET" << //only GET is allowed during handshaking
134 "Access-Control-Allow-Headers: content-type" << //this is OK to be fixed; only the content-type header is allowed, no other headers are accepted
135 "Access-Control-Allow-Origin: " + origin <<
136 "Date: " + QDateTime::currentDateTimeUtc().toString("ddd, dd MMM yyyy hh:mm:ss 'GMT'");
138 m_acceptedVersion = QWebSocketProtocol::currentVersion();
144 m_canUpgrade = false;
148 response << "HTTP/1.1 400 Bad Request";
149 QStringList versions;
150 Q_FOREACH(QWebSocketProtocol::Version version, supportedVersions)
152 versions << QString::number(static_cast<int>(version));
154 response << "Sec-WebSocket-Version: " + versions.join(", ");
157 response << "\r\n"; //append empty line at end of header
158 return response.join("\r\n");
164 QTextStream &HandshakeResponse::writeToStream(QTextStream &textStream) const
166 if (!m_response.isEmpty())
168 textStream << m_response.toLatin1().constData();
172 textStream.setStatus(QTextStream::WriteFailed);
180 QTextStream &operator <<(QTextStream &stream, const HandshakeResponse &response)
182 return response.writeToStream(stream);
188 QWebSocketProtocol::Version HandshakeResponse::getAcceptedVersion() const
190 return m_acceptedVersion;
196 QString HandshakeResponse::getAcceptedExtension() const
198 return m_acceptedExtension;