Changeset 128731 in webkit


Ignore:
Timestamp:
Sep 17, 2012 2:37:51 AM (12 years ago)
Author:
commit-queue@webkit.org
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 <lfranchi@kde.org> 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):

Location:
trunk/Source/WebKit/qt
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/qt/ChangeLog

    r128688 r128731  
     12012-09-17  Leo Franchi  <lfranchi@kde.org>
     2
     3        [Qt] Inspector WebSocket backend protocol update
     4        https://bugs.webkit.org/show_bug.cgi?id=77031
     5
     6        Also adds support for multi-frame messages and non-text messages.
     7        Thanks to Jocelyn Turcotte for most of the WebSocket update code!
     8
     9        Reviewed by Simon Hausmann.
     10
     11        * WebCoreSupport/InspectorServerQt.cpp:
     12        (WebCore):
     13        (WebCore::generateWebSocketChallengeResponse):
     14        (WebCore::InspectorServerRequestHandlerQt::tcpReadyRead):
     15        (WebCore::InspectorServerRequestHandlerQt::webSocketSend):
     16        (WebCore::applyMask):
     17        (WebCore::InspectorServerRequestHandlerQt::webSocketReadyRead):
     18        * WebCoreSupport/InspectorServerQt.h:
     19        (InspectorServerRequestHandlerQt):
     20
    1212012-09-15  Pierre Rossi  <pierre.rossi@gmail.com>
    222
  • trunk/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp

    r119232 r128731  
    3535#include <QWidget>
    3636#include <qendian.h>
    37 #include <wtf/MD5.h>
     37#include <wtf/SHA1.h>
     38#include <wtf/text/Base64.h>
    3839#include <wtf/text/CString.h>
    3940
     
    4142
    4243/*!
    43     Computes the WebSocket handshake response given the two challenge numbers and key3.
     44    Computes the WebSocket handshake response given the input key
    4445 */
    45 static void generateWebSocketChallengeResponse(uint32_t number1, uint32_t number2, const unsigned char key3[8], unsigned char response[16])
    46 {
    47     uint8_t challenge[16];
    48     qToBigEndian<qint32>(number1, &challenge[0]);
    49     qToBigEndian<qint32>(number2, &challenge[4]);
    50     memcpy(&challenge[8], key3, 8);
    51     MD5 md5;
    52     md5.addBytes(challenge, sizeof(challenge));
    53     Vector<uint8_t, 16> digest;
    54     md5.checksum(digest);
    55     memcpy(response, digest.data(), 16);
    56 }
    57 
    58 /*!
    59     Parses and returns a WebSocket challenge number according to the
    60     method specified in the WebSocket protocol.
    61 
    62     The field contains numeric digits interspersed with spaces and
    63     non-numeric digits. The protocol ignores the characters that are
    64     neither digits nor spaces. The digits are concatenated and
    65     interpreted as a long int. The result is this number divided by
    66     the number of spaces.
    67  */
    68 static quint32 parseWebSocketChallengeNumber(QString field)
    69 {
    70     QString nString;
    71     int numSpaces = 0;
    72     for (int i = 0; i < field.size(); i++) {
    73         QChar c = field[i];
    74         if (c == QLatin1Char(' '))
    75             numSpaces++;
    76         else if ((c >= QLatin1Char('0')) && (c <= QLatin1Char('9')))
    77             nString.append(c);
    78     }
    79     quint32 num = nString.toULong();
    80     quint32 result = (numSpaces ? (num / numSpaces) : num);
    81     return result;
     46static QByteArray generateWebSocketChallengeResponse(const QByteArray& key)
     47{
     48    SHA1 sha1;
     49    Vector<uint8_t, 20> digest;
     50    Vector<char> encoded;
     51    QByteArray toHash("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
     52    toHash.prepend(key);
     53    sha1.addBytes((uint8_t*)toHash.data(), toHash.size());
     54    sha1.computeHash(digest);
     55    base64Encode((char*)digest.data(), digest.size(), encoded);
     56    return QByteArray(encoded.data(), encoded.size());
    8257}
    8358
     
    193168                m_contentType = header.contentType().toLatin1();
    194169                m_contentLength = header.contentLength();
    195                 if (header.hasKey(QLatin1String("Upgrade")) && (header.value(QLatin1String("Upgrade")) == QLatin1String("WebSocket")))
     170                if (header.hasKey(QLatin1String("Upgrade")) && (header.value(QLatin1String("Upgrade")) == QLatin1String("websocket")))
    196171                    isWebSocket = true;
    197172
     
    212187                connect(m_tcpConnection, SIGNAL(readyRead()), SLOT(webSocketReadyRead()), Qt::QueuedConnection);
    213188
    214                 QByteArray key3 = m_tcpConnection->read(8);
    215 
    216                 quint32 number1 = parseWebSocketChallengeNumber(header.value(QLatin1String("Sec-WebSocket-Key1")));
    217                 quint32 number2 = parseWebSocketChallengeNumber(header.value(QLatin1String("Sec-WebSocket-Key2")));
    218 
    219                 char responseData[16];
    220                 generateWebSocketChallengeResponse(number1, number2, (unsigned char*)key3.data(), (unsigned char*)responseData);
    221                 QByteArray response(responseData, sizeof(responseData));
     189                QByteArray key = header.value(QLatin1String("Sec-WebSocket-Key")).toLatin1();
     190                QString accept = QString::fromLatin1(generateWebSocketChallengeResponse(key));
    222191
    223192                WebKit::QHttpResponseHeader responseHeader(101, QLatin1String("WebSocket Protocol Handshake"), 1, 1);
    224193                responseHeader.setValue(QLatin1String("Upgrade"), header.value(QLatin1String("Upgrade")));
    225194                responseHeader.setValue(QLatin1String("Connection"), header.value(QLatin1String("Connection")));
    226                 responseHeader.setValue(QLatin1String("Sec-WebSocket-Origin"), header.value(QLatin1String("Origin")));
    227                 responseHeader.setValue(QLatin1String("Sec-WebSocket-Location"), (QLatin1String("ws://") + header.value(QLatin1String("Host")) + m_path));
    228                 responseHeader.setContentLength(response.size());
     195                responseHeader.setValue(QLatin1String("Sec-WebSocket-Accept"), accept);
    229196                m_tcpConnection->write(responseHeader.toString().toLatin1());
    230                 m_tcpConnection->write(response);
    231197                m_tcpConnection->flush();
    232198
     
    307273}
    308274
    309 int InspectorServerRequestHandlerQt::webSocketSend(QByteArray payload)
     275int InspectorServerRequestHandlerQt::webSocketSend(const QString& message)
     276{
     277    QByteArray payload = message.toUtf8();
     278    return webSocketSend(payload.data(), payload.size());
     279}
     280
     281int InspectorServerRequestHandlerQt::webSocketSend(const char* data, size_t length)
    310282{
    311283    Q_ASSERT(m_tcpConnection);
    312     m_tcpConnection->putChar(0x00);
    313     int nBytes = m_tcpConnection->write(payload);
    314     m_tcpConnection->putChar(0xFF);
     284    m_tcpConnection->putChar(0x81);
     285    if (length <= 125)
     286        m_tcpConnection->putChar(static_cast<uint8_t>(length));
     287    else if (length <= pow(2, 16)) {
     288        m_tcpConnection->putChar(126);
     289        quint16 length16 = qToBigEndian<quint16>(static_cast<quint16>(length));
     290        m_tcpConnection->write(reinterpret_cast<char*>(&length16), 2);
     291    } else {
     292        m_tcpConnection->putChar(127);
     293        quint64 length64 = qToBigEndian<quint64>(static_cast<quint64>(length));
     294        m_tcpConnection->write(reinterpret_cast<char*>(&length64), 8);
     295    }
     296    int nBytes = m_tcpConnection->write(data, length);
    315297    m_tcpConnection->flush();
    316298    return nBytes;
    317299}
    318300
    319 int InspectorServerRequestHandlerQt::webSocketSend(const char* data, size_t length)
    320 {
    321     Q_ASSERT(m_tcpConnection);
    322     m_tcpConnection->putChar(0x00);
    323     int nBytes = m_tcpConnection->write(data, length);
    324     m_tcpConnection->putChar(0xFF);
    325     m_tcpConnection->flush();
    326     return nBytes;
     301static QByteArray applyMask(const QByteArray& payload, const QByteArray& maskingKey)
     302{
     303    Q_ASSERT(maskingKey.size() == 4);
     304    QByteArray unmaskedPayload;
     305    for (int i = 0; i < payload.size(); ++i) {
     306        char unmaskedByte = payload[i] ^ maskingKey[i % 4];
     307        unmaskedPayload.append(unmaskedByte);
     308    }
     309    return unmaskedPayload;
    327310}
    328311
     
    334317    QByteArray content = m_tcpConnection->read(m_tcpConnection->bytesAvailable());
    335318    m_data.append(content);
    336     while (m_data.size() > 0) {
    337         // first byte in websocket frame should be 0
    338         Q_ASSERT(!m_data[0]);
    339 
    340         // Start of WebSocket frame is indicated by 0
    341         if (m_data[0]) {
    342             qCritical() <<  "webSocketReadyRead: unknown frame type" << m_data[0];
    343             m_data.clear();
    344             m_tcpConnection->close();
    345             return;
    346         }
    347 
    348         // End of WebSocket frame indicated by 0xff.
    349         int pos = m_data.indexOf(0xff, 1);
    350         if (pos < 1)
    351             return;
    352 
    353         // After above checks, length will be >= 0.
    354         size_t length = pos - 1;
    355         if (length <= 0)
    356             return;
    357 
    358         QByteArray payload = m_data.mid(1, length);
    359 
     319    while (m_data.size() > 0) {       
     320        const bool isMasked = m_data[1] & 0x80;
     321        quint64 payloadLen = m_data[1] & 0x7F;
     322        int pos = 2;
     323       
     324        if (payloadLen == 126) {
     325            payloadLen = qFromBigEndian<quint16>(*reinterpret_cast<quint16*>(m_data.mid(pos, 2).data()));
     326            pos = 4;
     327        } else if (payloadLen == 127) {
     328            payloadLen = qFromBigEndian<quint64>(*reinterpret_cast<quint64*>(m_data.mid(pos, 8).data()));
     329            pos = 8;
     330        }
     331       
     332        QByteArray payload;
     333        if (isMasked) {
     334            QByteArray maskingKey = m_data.mid(pos, 4);
     335            pos += 4;
     336            payload = applyMask(m_data.mid(pos, payloadLen), maskingKey);
     337        } else
     338            payload = m_data.mid(pos, payloadLen);
     339       
     340        // Handle fragmentation
     341        if (!(m_data[0] & 0x80)) { // Non-last fragmented payload
     342            m_fragmentedPayload.append(payload);                 
     343            m_data = m_data.mid(pos + payloadLen);
     344            continue;
     345        }
     346       
     347        if (!(m_data[0] & 0x0F)) { // Last fragment
     348            m_fragmentedPayload.append(payload);   
     349            payload = m_fragmentedPayload;
     350            m_fragmentedPayload.clear();
     351        }
     352       
    360353        // Remove this WebSocket message from m_data (payload, start-of-frame byte, end-of-frame byte).
    361354        // Truncate data before delivering message in case of re-entrancy.
    362         m_data = m_data.mid(length + 2);
     355        m_data = m_data.mid(pos + payloadLen);
    363356       
    364357#if ENABLE(INSPECTOR)
  • trunk/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h

    r125603 r128731  
    7373    InspectorServerRequestHandlerQt(QTcpSocket *tcpConnection, InspectorServerQt *server);
    7474    virtual ~InspectorServerRequestHandlerQt();
    75     virtual int webSocketSend(QByteArray payload);
     75    virtual int webSocketSend(const QString& message);
    7676    virtual int webSocketSend(const char *payload, size_t length);
    7777
     
    9090    bool m_endOfHeaders;
    9191    QByteArray m_data;
     92    QByteArray m_fragmentedPayload;
    9293    InspectorClientQt* m_inspectorClient;
    9394
Note: See TracChangeset for help on using the changeset viewer.