Added serverName parameter to WebSocketServer constructor
authorKurt Pattyn <pattyn.kurt@gmail.com>
Thu, 22 Aug 2013 21:46:17 +0000 (23:46 +0200)
committerKurt Pattyn <pattyn.kurt@gmail.com>
Thu, 22 Aug 2013 21:46:17 +0000 (23:46 +0200)
Added isOriginAllowed() virtual method to WebSocketServer that can be overridden by subclasses
Added serverName and originAllowed parameters to handshake response constructor to avoid hardcoded values

source/handshakeresponse_p.cpp
source/handshakeresponse_p.h
source/websocketserver.cpp
source/websocketserver.h

index 93f21b9..fdee13d 100644 (file)
@@ -10,6 +10,8 @@
 #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) :
@@ -20,7 +22,7 @@ HandshakeResponse::HandshakeResponse(const HandshakeRequest &request,
        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;
 }
 
@@ -51,69 +53,83 @@ QString HandshakeResponse::calculateAcceptKey(const QString &key) const
 }
 
 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");
index c1adf43..38e60b5 100644 (file)
@@ -13,6 +13,8 @@ class HandshakeResponse:public QObject
        Q_OBJECT
 public:
        HandshakeResponse(const HandshakeRequest &request,
+                                         const QString &serverName,
+                                         bool isOriginAllowed,
                                          const QList<WebSocketProtocol::Version> &supportedVersions,
                                          const QList<QString> &supportedProtocols,
                                          const QList<QString> &supportedExtensions);
@@ -40,6 +42,8 @@ private:
 
        QString calculateAcceptKey(const QString &key) const;
        QString getHandshakeResponse(const HandshakeRequest &request,
+                                                                const QString &serverName,
+                                                                bool isOriginAllowed,
                                                                 const QList<WebSocketProtocol::Version> &supportedVersions,
                                                                 const QList<QString> &supportedProtocols,
                                                                 const QList<QString> &supportedExtensions);
index 0b42206..b9308e5 100644 (file)
 
        \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);
@@ -329,6 +330,16 @@ QList<QString> WebSocketServer::supportedExtensions() const
        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();
@@ -356,6 +367,8 @@ void WebSocketServer::handshakeReceived()
                textStream >> request;
 
                HandshakeResponse response(request,
+                                                                  m_serverName,
+                                                                  isOriginAllowed(request.getOrigin()),
                                                                   supportedVersions(),
                                                                   supportedProtocols(),
                                                                   supportedExtensions());
@@ -379,26 +392,26 @@ void WebSocketServer::handshakeReceived()
                                }
                                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!!!";
        }
 }
index 61e6108..0401050 100644 (file)
@@ -20,7 +20,7 @@ class WebSocketServer : public QObject
        Q_OBJECT
 
 public:
-       explicit WebSocketServer(QObject *parent = 0);
+       explicit WebSocketServer(const QString &serverName, QObject *parent = 0);
        virtual ~WebSocketServer();
 
        void close();
@@ -44,6 +44,9 @@ public:
        QList<QString> supportedProtocols() const;
        QList<QString> supportedExtensions() const;
 
+protected:
+       virtual bool isOriginAllowed(const QString &origin) const;
+
 Q_SIGNALS:
        void newConnection();
 
@@ -56,6 +59,7 @@ private:
        Q_DISABLE_COPY(WebSocketServer)
 
        QTcpServer *m_pTcpServer;
+       QString m_serverName;
        QQueue<WebSocket *> m_pendingConnections;
 
        void addPendingConnection(WebSocket *pWebSocket);