Title: [128731] trunk/Source/WebKit/qt
Revision
128731
Author
[email protected]
Date
2012-09-17 02:37:51 -0700 (Mon, 17 Sep 2012)

Log Message

[Qt] Inspector WebSocket backend protocol update
https://bugs.webkit.org/show_bug.cgi?id=77031

Also adds support for multi-frame messages and non-text messages.
Thanks to Jocelyn Turcotte for most of the WebSocket update code!

Patch by Leo Franchi <[email protected]> on 2012-09-17
Reviewed by Simon Hausmann.

* WebCoreSupport/InspectorServerQt.cpp:
(WebCore):
(WebCore::generateWebSocketChallengeResponse):
(WebCore::InspectorServerRequestHandlerQt::tcpReadyRead):
(WebCore::InspectorServerRequestHandlerQt::webSocketSend):
(WebCore::applyMask):
(WebCore::InspectorServerRequestHandlerQt::webSocketReadyRead):
* WebCoreSupport/InspectorServerQt.h:
(InspectorServerRequestHandlerQt):

Modified Paths

Diff

Modified: trunk/Source/WebKit/qt/ChangeLog (128730 => 128731)


--- trunk/Source/WebKit/qt/ChangeLog	2012-09-17 09:05:13 UTC (rev 128730)
+++ trunk/Source/WebKit/qt/ChangeLog	2012-09-17 09:37:51 UTC (rev 128731)
@@ -1,3 +1,23 @@
+2012-09-17  Leo Franchi  <[email protected]>
+
+        [Qt] Inspector WebSocket backend protocol update
+        https://bugs.webkit.org/show_bug.cgi?id=77031
+
+        Also adds support for multi-frame messages and non-text messages.
+        Thanks to Jocelyn Turcotte for most of the WebSocket update code!
+
+        Reviewed by Simon Hausmann.
+
+        * WebCoreSupport/InspectorServerQt.cpp:
+        (WebCore):
+        (WebCore::generateWebSocketChallengeResponse):
+        (WebCore::InspectorServerRequestHandlerQt::tcpReadyRead):
+        (WebCore::InspectorServerRequestHandlerQt::webSocketSend):
+        (WebCore::applyMask):
+        (WebCore::InspectorServerRequestHandlerQt::webSocketReadyRead):
+        * WebCoreSupport/InspectorServerQt.h:
+        (InspectorServerRequestHandlerQt):
+
 2012-09-15  Pierre Rossi  <[email protected]>
 
         [Qt] QDoc fixes for Qt 5 documentation

Modified: trunk/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp (128730 => 128731)


--- trunk/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp	2012-09-17 09:05:13 UTC (rev 128730)
+++ trunk/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp	2012-09-17 09:37:51 UTC (rev 128731)
@@ -34,53 +34,28 @@
 #include <QUrl>
 #include <QWidget>
 #include <qendian.h>
-#include <wtf/MD5.h>
+#include <wtf/SHA1.h>
+#include <wtf/text/Base64.h>
 #include <wtf/text/CString.h>
 
 namespace WebCore {
 
 /*!
-    Computes the WebSocket handshake response given the two challenge numbers and key3.
+    Computes the WebSocket handshake response given the input key
  */
-static void generateWebSocketChallengeResponse(uint32_t number1, uint32_t number2, const unsigned char key3[8], unsigned char response[16])
+static QByteArray generateWebSocketChallengeResponse(const QByteArray& key)
 {
-    uint8_t challenge[16];
-    qToBigEndian<qint32>(number1, &challenge[0]);
-    qToBigEndian<qint32>(number2, &challenge[4]);
-    memcpy(&challenge[8], key3, 8);
-    MD5 md5;
-    md5.addBytes(challenge, sizeof(challenge));
-    Vector<uint8_t, 16> digest;
-    md5.checksum(digest);
-    memcpy(response, digest.data(), 16);
+    SHA1 sha1;
+    Vector<uint8_t, 20> digest;
+    Vector<char> encoded;
+    QByteArray toHash("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+    toHash.prepend(key);
+    sha1.addBytes((uint8_t*)toHash.data(), toHash.size());
+    sha1.computeHash(digest);
+    base64Encode((char*)digest.data(), digest.size(), encoded);
+    return QByteArray(encoded.data(), encoded.size());
 }
 
-/*!
-    Parses and returns a WebSocket challenge number according to the
-    method specified in the WebSocket protocol.
-
-    The field contains numeric digits interspersed with spaces and
-    non-numeric digits. The protocol ignores the characters that are
-    neither digits nor spaces. The digits are concatenated and
-    interpreted as a long int. The result is this number divided by
-    the number of spaces.
- */
-static quint32 parseWebSocketChallengeNumber(QString field)
-{
-    QString nString;
-    int numSpaces = 0;
-    for (int i = 0; i < field.size(); i++) {
-        QChar c = field[i];
-        if (c == QLatin1Char(' '))
-            numSpaces++;
-        else if ((c >= QLatin1Char('0')) && (c <= QLatin1Char('9')))
-            nString.append(c);
-    }
-    quint32 num = nString.toULong();
-    quint32 result = (numSpaces ? (num / numSpaces) : num);
-    return result;
-}
-
 static InspectorServerQt* s_inspectorServer;
 
 InspectorServerQt* InspectorServerQt::server()
@@ -192,7 +167,7 @@
                 m_path = header.path();
                 m_contentType = header.contentType().toLatin1();
                 m_contentLength = header.contentLength();
-                if (header.hasKey(QLatin1String("Upgrade")) && (header.value(QLatin1String("Upgrade")) == QLatin1String("WebSocket")))
+                if (header.hasKey(QLatin1String("Upgrade")) && (header.value(QLatin1String("Upgrade")) == QLatin1String("websocket")))
                     isWebSocket = true;
 
                 m_data.clear();
@@ -211,23 +186,14 @@
                 m_tcpConnection->disconnect(SIGNAL(readyRead()));
                 connect(m_tcpConnection, SIGNAL(readyRead()), SLOT(webSocketReadyRead()), Qt::QueuedConnection);
 
-                QByteArray key3 = m_tcpConnection->read(8);
+                QByteArray key = header.value(QLatin1String("Sec-WebSocket-Key")).toLatin1();
+                QString accept = QString::fromLatin1(generateWebSocketChallengeResponse(key));
 
-                quint32 number1 = parseWebSocketChallengeNumber(header.value(QLatin1String("Sec-WebSocket-Key1")));
-                quint32 number2 = parseWebSocketChallengeNumber(header.value(QLatin1String("Sec-WebSocket-Key2")));
-
-                char responseData[16];
-                generateWebSocketChallengeResponse(number1, number2, (unsigned char*)key3.data(), (unsigned char*)responseData);
-                QByteArray response(responseData, sizeof(responseData));
-
                 WebKit::QHttpResponseHeader responseHeader(101, QLatin1String("WebSocket Protocol Handshake"), 1, 1);
                 responseHeader.setValue(QLatin1String("Upgrade"), header.value(QLatin1String("Upgrade")));
                 responseHeader.setValue(QLatin1String("Connection"), header.value(QLatin1String("Connection")));
-                responseHeader.setValue(QLatin1String("Sec-WebSocket-Origin"), header.value(QLatin1String("Origin")));
-                responseHeader.setValue(QLatin1String("Sec-WebSocket-Location"), (QLatin1String("ws://") + header.value(QLatin1String("Host")) + m_path));
-                responseHeader.setContentLength(response.size());
+                responseHeader.setValue(QLatin1String("Sec-WebSocket-Accept"), accept);
                 m_tcpConnection->write(responseHeader.toString().toLatin1());
-                m_tcpConnection->write(response);
                 m_tcpConnection->flush();
 
                 if ((words.size() == 4)
@@ -306,26 +272,43 @@
     m_tcpConnection = 0;
 }
 
-int InspectorServerRequestHandlerQt::webSocketSend(QByteArray payload)
+int InspectorServerRequestHandlerQt::webSocketSend(const QString& message)
 {
-    Q_ASSERT(m_tcpConnection);
-    m_tcpConnection->putChar(0x00);
-    int nBytes = m_tcpConnection->write(payload);
-    m_tcpConnection->putChar(0xFF);
-    m_tcpConnection->flush();
-    return nBytes;
+    QByteArray payload = message.toUtf8();
+    return webSocketSend(payload.data(), payload.size());
 }
 
 int InspectorServerRequestHandlerQt::webSocketSend(const char* data, size_t length)
 {
     Q_ASSERT(m_tcpConnection);
-    m_tcpConnection->putChar(0x00);
+    m_tcpConnection->putChar(0x81);
+    if (length <= 125)
+        m_tcpConnection->putChar(static_cast<uint8_t>(length));
+    else if (length <= pow(2, 16)) {
+        m_tcpConnection->putChar(126);
+        quint16 length16 = qToBigEndian<quint16>(static_cast<quint16>(length));
+        m_tcpConnection->write(reinterpret_cast<char*>(&length16), 2);
+    } else {
+        m_tcpConnection->putChar(127);
+        quint64 length64 = qToBigEndian<quint64>(static_cast<quint64>(length));
+        m_tcpConnection->write(reinterpret_cast<char*>(&length64), 8);
+    }
     int nBytes = m_tcpConnection->write(data, length);
-    m_tcpConnection->putChar(0xFF);
     m_tcpConnection->flush();
     return nBytes;
 }
 
+static QByteArray applyMask(const QByteArray& payload, const QByteArray& maskingKey)
+{
+    Q_ASSERT(maskingKey.size() == 4);
+    QByteArray unmaskedPayload;
+    for (int i = 0; i < payload.size(); ++i) {
+        char unmaskedByte = payload[i] ^ maskingKey[i % 4];
+        unmaskedPayload.append(unmaskedByte);
+    }
+    return unmaskedPayload;
+}
+
 void InspectorServerRequestHandlerQt::webSocketReadyRead()
 {
     Q_ASSERT(m_tcpConnection);
@@ -333,33 +316,43 @@
         return;
     QByteArray content = m_tcpConnection->read(m_tcpConnection->bytesAvailable());
     m_data.append(content);
-    while (m_data.size() > 0) {
-        // first byte in websocket frame should be 0
-        Q_ASSERT(!m_data[0]);
-
-        // Start of WebSocket frame is indicated by 0
-        if (m_data[0]) {
-            qCritical() <<  "webSocketReadyRead: unknown frame type" << m_data[0];
-            m_data.clear();
-            m_tcpConnection->close();
-            return;
+    while (m_data.size() > 0) {        
+        const bool isMasked = m_data[1] & 0x80;
+        quint64 payloadLen = m_data[1] & 0x7F;
+        int pos = 2;
+        
+        if (payloadLen == 126) {
+            payloadLen = qFromBigEndian<quint16>(*reinterpret_cast<quint16*>(m_data.mid(pos, 2).data()));
+            pos = 4;
+        } else if (payloadLen == 127) {
+            payloadLen = qFromBigEndian<quint64>(*reinterpret_cast<quint64*>(m_data.mid(pos, 8).data()));
+            pos = 8;
         }
-
-        // End of WebSocket frame indicated by 0xff.
-        int pos = m_data.indexOf(0xff, 1);
-        if (pos < 1)
-            return;
-
-        // After above checks, length will be >= 0.
-        size_t length = pos - 1;
-        if (length <= 0)
-            return;
-
-        QByteArray payload = m_data.mid(1, length);
-
+        
+        QByteArray payload;
+        if (isMasked) {
+            QByteArray maskingKey = m_data.mid(pos, 4);
+            pos += 4;
+            payload = applyMask(m_data.mid(pos, payloadLen), maskingKey);
+        } else
+            payload = m_data.mid(pos, payloadLen);
+        
+        // Handle fragmentation
+        if (!(m_data[0] & 0x80)) { // Non-last fragmented payload
+            m_fragmentedPayload.append(payload);                 
+            m_data = m_data.mid(pos + payloadLen);
+            continue;
+        }
+        
+        if (!(m_data[0] & 0x0F)) { // Last fragment
+            m_fragmentedPayload.append(payload);   
+            payload = m_fragmentedPayload;
+            m_fragmentedPayload.clear();
+        }
+        
         // Remove this WebSocket message from m_data (payload, start-of-frame byte, end-of-frame byte).
         // Truncate data before delivering message in case of re-entrancy.
-        m_data = m_data.mid(length + 2);
+        m_data = m_data.mid(pos + payloadLen);
         
 #if ENABLE(INSPECTOR)
         if (m_inspectorClient) {

Modified: trunk/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h (128730 => 128731)


--- trunk/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h	2012-09-17 09:05:13 UTC (rev 128730)
+++ trunk/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h	2012-09-17 09:37:51 UTC (rev 128731)
@@ -72,7 +72,7 @@
 
     InspectorServerRequestHandlerQt(QTcpSocket *tcpConnection, InspectorServerQt *server);
     virtual ~InspectorServerRequestHandlerQt();
-    virtual int webSocketSend(QByteArray payload);
+    virtual int webSocketSend(const QString& message);
     virtual int webSocketSend(const char *payload, size_t length);
 
 private Q_SLOTS:
@@ -89,6 +89,7 @@
     int m_contentLength;
     bool m_endOfHeaders;
     QByteArray m_data;
+    QByteArray m_fragmentedPayload;
     InspectorClientQt* m_inspectorClient;
 
     void handleInspectorRequest(QStringList words);
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to