#include <QList>
HandshakeResponse::HandshakeResponse(const HandshakeRequest &request,
+ const QString &serverName,
+ bool isOriginAllowed,
const QList<WebSocketProtocol::Version> &supportedVersions,
const QList<QString> &supportedProtocols,
const QList<QString> &supportedExtensions) :
m_acceptedExtension(),
m_acceptedVersion(WebSocketProtocol::V_Unknow)
{
- m_response = getHandshakeResponse(request, supportedVersions, supportedProtocols, supportedExtensions);
+ m_response = getHandshakeResponse(request, serverName, isOriginAllowed, supportedVersions, supportedProtocols, supportedExtensions);
m_isValid = true;
}
}
QString HandshakeResponse::getHandshakeResponse(const HandshakeRequest &request,
+ const QString &serverName,
+ bool isOriginAllowed,
const QList<WebSocketProtocol::Version> &supportedVersions,
const QList<QString> &supportedProtocols,
const QList<QString> &supportedExtensions)
{
QStringList response;
m_canUpgrade = false;
- 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<WebSocketProtocol::Version> matchingVersions = request.getVersions().toSet().intersect(supportedVersions.toSet()).toList();
- qStableSort(matchingVersions.begin(), matchingVersions.end(), qGreater<WebSocketProtocol::Version>()); //sort in descending order
- if (matchingVersions.isEmpty())
+ if (!isOriginAllowed)
+ {
+ if (!m_canUpgrade)
{
- m_canUpgrade = false;
+ response << "HTTP/1.1 403 Access Forbidden";
}
- else
+ }
+ else
+ {
+ if (request.isValid())
{
- 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())
+ 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<WebSocketProtocol::Version> matchingVersions = request.getVersions().toSet().intersect(supportedVersions.toSet()).toList();
+ qStableSort(matchingVersions.begin(), matchingVersions.end(), qGreater<WebSocketProtocol::Version>()); //sort in descending order
+
+ if (matchingVersions.isEmpty())
{
- m_acceptedExtension = matchingExtensions.first();
- response << "Sec-WebSocket-Extensions: " + m_acceptedExtension;
+ m_canUpgrade = false;
}
- QString origin = request.getOrigin().trimmed();
- if (origin.isEmpty())
+ else
{
- origin = "*";
- }
- //TODO: header values should be configurable; i.e. Server, Allow-Credentials, Allow-Headers
- response << "Server: Imagine Delivery Server" <<
- "Access-Control-Allow-Credentials: true" <<
- "Access-Control-Allow-Headers: content-type" <<
- "Access-Control-Allow-Origin: " + origin <<
- "Date: " + QDateTime::currentDateTimeUtc().toString("ddd, dd MMM yyyy hh:mm:ss 'GMT'");
+ 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 = "*";
+ }
+ //TODO: header values should be configurable; i.e. Server, Allow-Credentials, Allow-Headers
+ 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 to be fixed; 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 = WebSocketProtocol::currentVersion();
- m_canUpgrade = true;
+ m_acceptedVersion = WebSocketProtocol::currentVersion();
+ m_canUpgrade = true;
+ }
}
- }
- else
- {
- m_canUpgrade = false;
- }
- if (!m_canUpgrade)
- {
- response << "HTTP/1.1 400 Bad Request";
- QStringList versions;
- Q_FOREACH(WebSocketProtocol::Version version, supportedVersions)
+ else
{
- versions << QString::number(static_cast<int>(version));
+ m_canUpgrade = false;
+ }
+ if (!m_canUpgrade)
+ {
+ response << "HTTP/1.1 400 Bad Request";
+ QStringList versions;
+ Q_FOREACH(WebSocketProtocol::Version version, supportedVersions)
+ {
+ versions << QString::number(static_cast<int>(version));
+ }
+ response << "Sec-WebSocket-Version: " + versions.join(", ");
}
- response << "Sec-WebSocket-Version: " + versions.join(", ");
}
response << "\r\n"; //append empty line at end of header
return response.join("\r\n");
\a parent is passed to the QObject constructor.
*/
-WebSocketServer::WebSocketServer(QObject *parent) :
+WebSocketServer::WebSocketServer(const QString &serverName, QObject *parent) :
QObject(parent),
m_pTcpServer(0),
+ m_serverName(serverName),
m_pendingConnections()
{
m_pTcpServer = new QTcpServer(this);
return supportedExtensions; //no extensions are currently supported
}
+//Checking on the origin does not make much sense when the server is accessed
+//via a non-browser client, as that client can set whatever origin header it likes
+//In case of a browser client, the server SHOULD check the validity of the origin
+//see http://tools.ietf.org/html/rfc6455#section-10
+bool WebSocketServer::isOriginAllowed(const QString &origin) const
+{
+ Q_UNUSED(origin)
+ return true;
+}
+
void WebSocketServer::onNewConnection()
{
QTcpSocket *pTcpSocket = m_pTcpServer->nextPendingConnection();
textStream >> request;
HandshakeResponse response(request,
+ m_serverName,
+ isOriginAllowed(request.getOrigin()),
supportedVersions(),
supportedProtocols(),
supportedExtensions());
}
else
{
- qDebug() << "WebSocketServer::dataReceived: Upgrading to WebSocket failed.";
+ qDebug() << "WebSocketServer::handshakeReceived: Upgrading to WebSocket failed.";
}
}
else
{
- qDebug() << "WebSocketServer::dataReceived: Cannot upgrade to websocket.";
+ qDebug() << "WebSocketServer::handshakeReceived: Cannot upgrade to websocket.";
}
}
else
{
- qDebug() << "WebSocketServer::dataReceived: Invalid response. This should not happen!!!";
+ qDebug() << "WebSocketServer::handshakeReceived: Invalid response. This should not happen!!!";
}
if (!success)
{
- qDebug() << "WebSocketServer::dataReceived: Closing socket because of invalid or unsupported request";
+ qDebug() << "WebSocketServer::handshakeReceived: Closing socket because of invalid or unsupported request";
pTcpSocket->close();
}
}
else
{
- qDebug() << "WebSocketServerImp::dataReceived: Sender socket is NULL. This should not happen!!!";
+ qDebug() << "WebSocketServerImp::handshakeReceived: Sender socket is NULL. This should not happen!!!";
}
}