Dear Leo,

Thank you for your precious information.
It works fine now!
I applied your patch with some adjustement to fit the webkit's svn rev 124878 .
Here is in attachment.
I can see this patch is really old, do you plan to submit it to the webkit communauty ? I had a look to webkit's bugzilla website but i havent been able to find a bug related ...

Best regards.

Stéphane.
On 08/21/2012 02:24 PM, Leo Franchi wrote:
On Tuesday, August 21, 2012 12:16:09 PM Stephane Cerveau wrote:
Dear all,

I'm currently trying to use the webkit remote inspector using websocket.
I can see that an implementation is available in
Source/Webkit/qt/WebcoreSupport but i'm facing some problem to use it.

Here is my command line to launch the remote inspector
./QtTestBrowser -remote-inspector-port 9222 http://www.google.fr

When i'm launching another browser such as Chrome, i start with white
page with www.google.fr and when i'm clicking on it, i'm getting a
websocket error.

[snip]
And now the message received from the client is not handled correctly,
crashing in webSocketReadyRead on  Q_ASSERT(!m_data[0]);

So i'm wondering if anybody is using this functionnality or this is only
a beta code.
Any help would be appreciated :)
The websocket protocol version in the Qt inspector server is a bit old, I
believe it's using the Hixie76 protocol instead of the newer Hybi or RFC
drafts. If you want to connect to the inspector server with a modern Webkit
browser, you'll need to update the server, or you can use an older client.

To use an older client, try using the QtTestBrowser as your inspector client.

To use modern webkit versions, try applying this patch:

https://gitorious.org/~lfranchi/webkit/lfranchis-
qtwebkit/commit/93a72f2657c2a8bb6863d5d2ffe8ce6faedd4598

though you might also want one or two of the later commits here:

https://gitorious.org/~lfranchi/webkit/lfranchis-qtwebkit

Hope this helps!

cheers,
leo


diff --git a/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp b/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp
index 14f5dd7..3d000a9 100644
--- a/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp
+++ b/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp
@@ -36,49 +36,26 @@
 #include <qendian.h>
 #include <wtf/MD5.h>
 #include <wtf/text/CString.h>
+#include "wtf/SHA1.h"
+#include "wtf/text/Base64.h"
+
 
 namespace WebCore {
 
 /*!
     Computes the WebSocket handshake response given the two challenge numbers and key3.
  */
-static void generateWebSocketChallengeResponse(uint32_t number1, uint32_t number2, const unsigned char key3[8], unsigned char response[16])
-{
-    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);
-}
-
-/*!
-    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)
+static QByteArray generateWebSocketChallengeResponse(const QByteArray& key)
 {
-    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;
+    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());
 }
 
 static InspectorServerQt* s_inspectorServer;
@@ -192,7 +169,7 @@ void InspectorServerRequestHandlerQt::tcpReadyRead()
                 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")).toLower() == QLatin1String("websocket")))
                     isWebSocket = true;
 
                 m_data.clear();
@@ -211,23 +188,15 @@ void InspectorServerRequestHandlerQt::tcpReadyRead()
                 m_tcpConnection->disconnect(SIGNAL(readyRead()));
                 connect(m_tcpConnection, SIGNAL(readyRead()), SLOT(webSocketReadyRead()), Qt::QueuedConnection);
 
-                QByteArray key3 = m_tcpConnection->read(8);
-
-                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));
+                QByteArray key = header.value(QLatin1String("Sec-WebSocket-Key")).toLatin1();
+                QString accept = QString::fromLatin1(generateWebSocketChallengeResponse(key));
 
                 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->write(response);
                 m_tcpConnection->flush();
 
                 if ((words.size() == 4)
@@ -306,26 +275,55 @@ void InspectorServerRequestHandlerQt::tcpConnectionDisconnected()
     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;
+}
+
+#define BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d"
+#define BYTETOBINARY(byte)  \
+  (byte & 0x80 ? 1 : 0), \
+  (byte & 0x40 ? 1 : 0), \
+  (byte & 0x20 ? 1 : 0), \
+  (byte & 0x10 ? 1 : 0), \
+  (byte & 0x08 ? 1 : 0), \
+  (byte & 0x04 ? 1 : 0), \
+  (byte & 0x02 ? 1 : 0), \
+  (byte & 0x01 ? 1 : 0)
+
+
 void InspectorServerRequestHandlerQt::webSocketReadyRead()
 {
     Q_ASSERT(m_tcpConnection);
@@ -334,40 +332,65 @@ void InspectorServerRequestHandlerQt::webSocketReadyRead()
     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;
+        // Only support text for now.
+        // Q_ASSERT((m_data[0] & 0x0F) == 1);
+        //qDebug( "First byte: " BYTETOBINARYPATTERN "\n", BYTETOBINARY(m_data[0]));
+        //qDebug( "Second byte: " BYTETOBINARYPATTERN "\n", BYTETOBINARY(m_data[1]));
+
+        bool isMasked = m_data[1] & 0x80;
+        quint64 payloadLen = m_data[1] & 0x7F;
+        int pos = 2;
+
+        //qDebug() << "Payload is masked and length:" << isMasked << payloadLen;
+        //qDebug() << "data size is:" << m_data.size();
+        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;
+        QByteArray payload;
+        if (isMasked) {
+            QByteArray maskingKey = m_data.mid(pos, 4);
+            //qDebug() << "masking key size" << maskingKey.size() << "pos is:" << pos;
+            pos += 4;
+            payload = applyMask(m_data.mid(pos, payloadLen), maskingKey);
+       } else {
+            payload = m_data.mid(pos, payloadLen);
+        }
 
-        // After above checks, length will be >= 0.
-        size_t length = pos - 1;
-        if (length <= 0)
-            return;
+        // Handle fragmentation
+        if ((m_data[0] & 0x80) == 0) { // Non-last fragmented payload
+            //qDebug() << "HANDLING FRAGMENTED PAYLOAD!";
+            m_fragmentedPayload.append(payload);
 
-        QByteArray payload = m_data.mid(1, length);
+            m_data = m_data.mid(pos + payloadLen);
+            continue;
+        } else {
+            if ((m_data[0] & 0x0F) == 0) { // Last fragment
+                 //qDebug() << "HANDLING END OF FRAGMENT!";
+                 m_fragmentedPayload.append(payload);
+                 payload = m_fragmentedPayload;
+                m_fragmentedPayload.clear();
+            } else // Normal non-fragmented message
+            {
+                //qDebug() << "NORMAL FRAME";
+            }
+        }
 
-        // 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);
-        
 #if ENABLE(INSPECTOR)
         if (m_inspectorClient) {
           InspectorController* inspectorController = m_inspectorClient->m_inspectedWebPage->d->page->inspectorController();
           inspectorController->dispatchMessageFromFrontend(QString::fromUtf8(payload));
         }
 #endif
+        // 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.
+        //qDebug() << "Removing first N bytes from m_data:" << pos + payloadLen;
 
+        m_data = m_data.mid(pos + payloadLen);
     }
 }
 
diff --git a/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h b/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h
index 0638f86..633bdb8 100644
--- a/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h
+++ b/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h
@@ -72,7 +72,7 @@ public:
 
     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 slots:
@@ -89,6 +89,7 @@ private:
     int m_contentLength;
     bool m_endOfHeaders;
     QByteArray m_data;
+    QByteArray m_fragmentedPayload;
     InspectorClientQt* m_inspectorClient;
 
     void handleInspectorRequest(QStringList words);
_______________________________________________
webkit-qt mailing list
webkit-qt@lists.webkit.org
http://lists.webkit.org/mailman/listinfo/webkit-qt

Reply via email to