Title: [93773] trunk/Source/WebCore
Revision
93773
Author
[email protected]
Date
2011-08-25 04:31:46 -0700 (Thu, 25 Aug 2011)

Log Message

WebSocket: Queue messages to be sent
https://bugs.webkit.org/show_bug.cgi?id=66298

Reviewed by Kent Tamura.

Blobs must be read asynchronously and thus cannot be sent immediately. Therefore, we need to
create a queue of messages to be sent in order to handle pending requests.

No new tests. Strictly speaking, there is a small change in behavior but it is hard to
reproduce in tests. See description of WebSocketChannel::send below.

* websockets/WebSocketChannel.cpp:
(WebCore::WebSocketChannel::WebSocketChannel):
(WebCore::WebSocketChannel::send):
Now, this function always returns true if hybi-10 protocol is selected. The impact of this change
should be minimum, because the current WebSocket API defines the return type of WebSocket.send()
is void (see also bug 65850).
It's hard to make SocketStreamHandle::send() fail deliberately, therefore it's difficult to write
a test to detect this behavior change.
(WebCore::WebSocketChannel::didCloseSocketStream):
(WebCore::WebSocketChannel::startClosingHandshake):
(WebCore::WebSocketChannel::processFrame):
(WebCore::WebSocketChannel::enqueueTextFrame):
(WebCore::WebSocketChannel::enqueueRawFrame):
(WebCore::WebSocketChannel::processOutgoingFrameQueue):
(WebCore::WebSocketChannel::abortOutgoingFrameQueue):
* websockets/WebSocketChannel.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (93772 => 93773)


--- trunk/Source/WebCore/ChangeLog	2011-08-25 10:30:47 UTC (rev 93772)
+++ trunk/Source/WebCore/ChangeLog	2011-08-25 11:31:46 UTC (rev 93773)
@@ -1,3 +1,33 @@
+2011-08-25  Yuta Kitamura  <[email protected]>
+
+        WebSocket: Queue messages to be sent
+        https://bugs.webkit.org/show_bug.cgi?id=66298
+
+        Reviewed by Kent Tamura.
+
+        Blobs must be read asynchronously and thus cannot be sent immediately. Therefore, we need to
+        create a queue of messages to be sent in order to handle pending requests.
+
+        No new tests. Strictly speaking, there is a small change in behavior but it is hard to
+        reproduce in tests. See description of WebSocketChannel::send below.
+
+        * websockets/WebSocketChannel.cpp:
+        (WebCore::WebSocketChannel::WebSocketChannel):
+        (WebCore::WebSocketChannel::send):
+        Now, this function always returns true if hybi-10 protocol is selected. The impact of this change
+        should be minimum, because the current WebSocket API defines the return type of WebSocket.send()
+        is void (see also bug 65850).
+        It's hard to make SocketStreamHandle::send() fail deliberately, therefore it's difficult to write
+        a test to detect this behavior change.
+        (WebCore::WebSocketChannel::didCloseSocketStream):
+        (WebCore::WebSocketChannel::startClosingHandshake):
+        (WebCore::WebSocketChannel::processFrame):
+        (WebCore::WebSocketChannel::enqueueTextFrame):
+        (WebCore::WebSocketChannel::enqueueRawFrame):
+        (WebCore::WebSocketChannel::processOutgoingFrameQueue):
+        (WebCore::WebSocketChannel::abortOutgoingFrameQueue):
+        * websockets/WebSocketChannel.h:
+
 2011-08-24  Alexander Pavlov  <[email protected]>
 
         Web Inspector: Directional arrow on element info box looks terrible

Modified: trunk/Source/WebCore/websockets/WebSocketChannel.cpp (93772 => 93773)


--- trunk/Source/WebCore/websockets/WebSocketChannel.cpp	2011-08-25 10:30:47 UTC (rev 93772)
+++ trunk/Source/WebCore/websockets/WebSocketChannel.cpp	2011-08-25 11:31:46 UTC (rev 93773)
@@ -99,6 +99,7 @@
     , m_useHixie76Protocol(true)
     , m_hasContinuousFrame(false)
     , m_closeEventCode(CloseEventCodeAbnormalClosure)
+    , m_outgoingFrameQueueStatus(OutgoingFrameQueueOpen)
 {
     ASSERT(m_context->isDocument());
     Document* document = static_cast<Document*>(m_context);
@@ -146,10 +147,18 @@
 bool WebSocketChannel::send(const String& message)
 {
     LOG(Network, "WebSocketChannel %p send %s", this, message.utf8().data());
-    CString utf8 = message.utf8();
-    if (m_useHixie76Protocol)
+    if (m_useHixie76Protocol) {
+        CString utf8 = message.utf8();
         return sendFrameHixie76(utf8.data(), utf8.length());
-    return sendFrame(OpCodeText, utf8.data(), utf8.length());
+    }
+    enqueueTextFrame(message);
+    // According to WebSocket API specification, WebSocket.send() should return void instead
+    // of boolean. However, our implementation still returns boolean due to compatibility
+    // concern (see bug 65850).
+    // m_channel->send() may happen later, thus it's not always possible to know whether
+    // the message has been sent to the socket successfully. In this case, we have no choice
+    // but to return true.
+    return true;
 }
 
 unsigned long WebSocketChannel::bufferedAmount() const
@@ -238,6 +247,8 @@
     m_closed = true;
     if (m_closingTimer.isActive())
         m_closingTimer.stop();
+    if (!m_useHixie76Protocol && m_outgoingFrameQueueStatus != OutgoingFrameQueueClosed)
+        abortOutgoingFrameQueue();
     if (m_handle) {
         m_unhandledBufferedAmount = m_handle->bufferedAmount();
         if (m_suspended)
@@ -417,19 +428,17 @@
     if (m_closing)
         return;
     ASSERT(m_handle);
-    bool sentSuccessfully;
     if (m_useHixie76Protocol) {
         Vector<char> buf;
         buf.append('\xff');
         buf.append('\0');
-        sentSuccessfully = m_handle->send(buf.data(), buf.size());
+        if (!m_handle->send(buf.data(), buf.size())) {
+            m_handle->disconnect();
+            return;
+        }
     } else
-        sentSuccessfully = sendFrame(OpCodeClose, "", 0); // FIXME: Send status code and reason message.
+        enqueueRawFrame(OpCodeClose, "", 0); // FIXME: Send status code and reason message.
 
-    if (!sentSuccessfully) {
-        m_handle->disconnect();
-        return;
-    }
     m_closing = true;
     if (m_client)
         m_client->didStartClosingHandshake();
@@ -637,17 +646,16 @@
         skipBuffer(frame.frameEnd - m_buffer);
         m_receivedClosingHandshake = true;
         startClosingHandshake();
-        if (m_closing)
-            m_handle->close(); // Close after sending a close frame.
+        if (m_closing) {
+            m_outgoingFrameQueueStatus = OutgoingFrameQueueClosing;
+            processOutgoingFrameQueue();
+        }
         break;
 
-    case OpCodePing: {
-        bool result = sendFrame(OpCodePong, frame.payload, frame.payloadLength);
+    case OpCodePing:
+        enqueueRawFrame(OpCodePong, frame.payload, frame.payloadLength);
         skipBuffer(frame.frameEnd - m_buffer);
-        if (!result)
-            fail("Failed to send a pong frame.");
         break;
-    }
 
     case OpCodePong:
         // A server may send a pong in response to our ping, or an unsolicited pong which is not associated with
@@ -748,6 +756,73 @@
     return false;
 }
 
+void WebSocketChannel::enqueueTextFrame(const String& string)
+{
+    ASSERT(!m_useHixie76Protocol);
+    ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen);
+    OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame);
+    frame->opCode = OpCodeText;
+    frame->frameType = QueuedFrameTypeString;
+    frame->stringData = string;
+    m_outgoingFrameQueue.append(frame.release());
+    processOutgoingFrameQueue();
+}
+
+void WebSocketChannel::enqueueRawFrame(OpCode opCode, const char* data, size_t dataLength)
+{
+    ASSERT(!m_useHixie76Protocol);
+    ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen);
+    OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame);
+    frame->opCode = opCode;
+    frame->frameType = QueuedFrameTypeVector;
+    frame->vectorData.resize(dataLength);
+    if (dataLength)
+        memcpy(frame->vectorData.data(), data, dataLength);
+    m_outgoingFrameQueue.append(frame.release());
+    processOutgoingFrameQueue();
+}
+
+void WebSocketChannel::processOutgoingFrameQueue()
+{
+    ASSERT(!m_useHixie76Protocol);
+    if (m_outgoingFrameQueueStatus == OutgoingFrameQueueClosed)
+        return;
+
+    while (!m_outgoingFrameQueue.isEmpty()) {
+        OwnPtr<QueuedFrame> frame = m_outgoingFrameQueue.takeFirst();
+        switch (frame->frameType) {
+        case QueuedFrameTypeString: {
+            CString utf8 = frame->stringData.utf8();
+            if (!sendFrame(frame->opCode, utf8.data(), utf8.length()))
+                fail("Failed to send WebSocket frame.");
+            break;
+        }
+
+        case QueuedFrameTypeVector:
+            if (!sendFrame(frame->opCode, frame->vectorData.data(), frame->vectorData.size()))
+                fail("Failed to send WebSocket frame.");
+            break;
+
+        default:
+            ASSERT_NOT_REACHED();
+            break;
+        }
+    }
+
+    ASSERT(m_outgoingFrameQueue.isEmpty());
+    if (m_outgoingFrameQueueStatus == OutgoingFrameQueueClosing) {
+        m_outgoingFrameQueueStatus = OutgoingFrameQueueClosed;
+        m_handle->close();
+    }
+}
+
+void WebSocketChannel::abortOutgoingFrameQueue()
+{
+    ASSERT(!m_useHixie76Protocol);
+    m_outgoingFrameQueue.clear();
+    m_outgoingFrameQueueStatus = OutgoingFrameQueueClosed;
+}
+
 bool WebSocketChannel::sendFrame(OpCode opCode, const char* data, size_t dataLength)
 {
     ASSERT(m_handle);

Modified: trunk/Source/WebCore/websockets/WebSocketChannel.h (93772 => 93773)


--- trunk/Source/WebCore/websockets/WebSocketChannel.h	2011-08-25 10:30:47 UTC (rev 93772)
+++ trunk/Source/WebCore/websockets/WebSocketChannel.h	2011-08-25 11:31:46 UTC (rev 93773)
@@ -37,6 +37,7 @@
 #include "ThreadableWebSocketChannel.h"
 #include "Timer.h"
 #include "WebSocketHandshake.h"
+#include <wtf/Deque.h>
 #include <wtf/Forward.h>
 #include <wtf/RefCounted.h>
 #include <wtf/Vector.h>
@@ -138,6 +139,50 @@
         bool processFrame();
         bool processFrameHixie76();
 
+        // It is allowed to send a Blob as a binary frame if hybi-10 protocol is in use. Sending a Blob
+        // can be delayed because it must be read asynchronously. Other types of data (String or
+        // ArrayBuffer) may also be blocked by preceding sending request of a Blob.
+        //
+        // To address this situation, messages to be sent need to be stored in a queue. Whenever a new
+        // data frame is going to be sent, it first must go to the queue. Items in the queue are processed
+        // in the order they were put into the queue. Sending request of a Blob blocks further processing
+        // until the Blob is completely read and sent to the socket stream.
+        //
+        // When hixie-76 protocol is chosen, the queue is not used and messages are sent directly.
+        enum QueuedFrameType {
+            QueuedFrameTypeString,
+            QueuedFrameTypeVector
+            // FIXME: Add QueuedFrameTypeBlob.
+        };
+        struct QueuedFrame {
+            OpCode opCode;
+            QueuedFrameType frameType;
+            // Only one of the following items is used, according to the value of frameType.
+            String stringData;
+            Vector<char> vectorData;
+            // FIXME: Add blobData.
+        };
+        void enqueueTextFrame(const String&);
+        void enqueueRawFrame(OpCode, const char* data, size_t dataLength);
+        // FIXME: Add enqueueBlobFrame().
+
+        void processOutgoingFrameQueue();
+        void abortOutgoingFrameQueue();
+
+        enum OutgoingFrameQueueStatus {
+            // It is allowed to put a new item into the queue.
+            OutgoingFrameQueueOpen,
+            // Close frame has already been put into the queue but may not have been sent yet;
+            // m_handle->close() will be called as soon as the queue is cleared. It is not
+            // allowed to put a new item into the queue.
+            OutgoingFrameQueueClosing,
+            // Close frame has been sent or the queue was aborted. It is not allowed to put
+            // a new item to the queue.
+            OutgoingFrameQueueClosed
+        };
+
+        // If you are going to send a hybi-10 frame, you need to use the outgoing frame queue
+        // instead of call sendFrame() directly.
         bool sendFrame(OpCode, const char* data, size_t dataLength);
         bool sendFrameHixie76(const char* data, size_t dataLength);
 
@@ -167,6 +212,9 @@
         Vector<char> m_continuousFrameData;
         unsigned short m_closeEventCode;
         String m_closeEventReason;
+
+        Deque<OwnPtr<QueuedFrame> > m_outgoingFrameQueue;
+        OutgoingFrameQueueStatus m_outgoingFrameQueueStatus;
     };
 
 } // namespace WebCore
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to