From 5f52f08cb669cf8181ba7c927362da53b66ca623 Mon Sep 17 00:00:00 2001 From: "bashi@chromium.org" Date: Mon, 20 Feb 2012 14:54:49 +0000 Subject: [PATCH] [WebSocket] Move WebSocketChannel::FrameData into a separate header file https://bugs.webkit.org/show_bug.cgi?id=78682 Extract WebSocketChannel::FrameData as WebSocketFrame. This brings flexibility to add classes which want to do something for incoming/outgoing frames (e.g. compression/decompression). Reviewed by Kent Tamura. No new tests. No behavior change. * GNUmakefile.list.am: AddedWebSocketFrame.h * Target.pri: Ditto. * WebCore.gypi: Ditto. * WebCore.vcproj/WebCore.vcproj: Ditto. * WebCore.xcodeproj/project.pbxproj: Ditto. * websockets/WebSocketChannel.cpp: Modified to use WebSocketFrame instead of FrameData (WebCore): (WebCore::WebSocketChannel::send): (WebCore::WebSocketChannel::startClosingHandshake): (WebCore::WebSocketChannel::parseFrame): (WebCore::WebSocketChannel::processFrame): (WebCore::WebSocketChannel::enqueueTextFrame): (WebCore::WebSocketChannel::enqueueRawFrame): (WebCore::WebSocketChannel::enqueueBlobFrame): (WebCore::appendMaskedFramePayload): Added. (WebCore::makeFrameData): Added. (WebCore::WebSocketChannel::sendFrame): * websockets/WebSocketChannel.h: Removed FrameData. (WebSocketChannel): (QueuedFrame): * websockets/WebSocketFrame.h: Added. (WebCore): (WebSocketFrame): (WebCore::WebSocketFrame::isNonControlOpCode): (WebCore::WebSocketFrame::isControlOpCode): (WebCore::WebSocketFrame::isReservedOpCode): (WebCore::WebSocketFrame::WebSocketFrame): git-svn-id: http://svn.webkit.org/repository/webkit/trunk@108240 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- Source/WebCore/ChangeLog | 41 +++++++ Source/WebCore/GNUmakefile.list.am | 1 + Source/WebCore/Target.pri | 1 + Source/WebCore/WebCore.gypi | 1 + Source/WebCore/WebCore.vcproj/WebCore.vcproj | 4 + Source/WebCore/WebCore.xcodeproj/project.pbxproj | 4 + Source/WebCore/websockets/WebSocketChannel.cpp | 144 ++++++++++++----------- Source/WebCore/websockets/WebSocketChannel.h | 38 ++---- Source/WebCore/websockets/WebSocketFrame.h | 80 +++++++++++++ 9 files changed, 215 insertions(+), 99 deletions(-) create mode 100644 Source/WebCore/websockets/WebSocketFrame.h diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index cf33b92..d9207e1 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,3 +1,44 @@ +2012-02-20 Kenichi Ishibashi + + [WebSocket] Move WebSocketChannel::FrameData into a separate header file + https://bugs.webkit.org/show_bug.cgi?id=78682 + + Extract WebSocketChannel::FrameData as WebSocketFrame. This brings + flexibility to add classes which want to do something for + incoming/outgoing frames (e.g. compression/decompression). + + Reviewed by Kent Tamura. + + No new tests. No behavior change. + + * GNUmakefile.list.am: AddedWebSocketFrame.h + * Target.pri: Ditto. + * WebCore.gypi: Ditto. + * WebCore.vcproj/WebCore.vcproj: Ditto. + * WebCore.xcodeproj/project.pbxproj: Ditto. + * websockets/WebSocketChannel.cpp: Modified to use WebSocketFrame instead of FrameData + (WebCore): + (WebCore::WebSocketChannel::send): + (WebCore::WebSocketChannel::startClosingHandshake): + (WebCore::WebSocketChannel::parseFrame): + (WebCore::WebSocketChannel::processFrame): + (WebCore::WebSocketChannel::enqueueTextFrame): + (WebCore::WebSocketChannel::enqueueRawFrame): + (WebCore::WebSocketChannel::enqueueBlobFrame): + (WebCore::appendMaskedFramePayload): Added. + (WebCore::makeFrameData): Added. + (WebCore::WebSocketChannel::sendFrame): + * websockets/WebSocketChannel.h: Removed FrameData. + (WebSocketChannel): + (QueuedFrame): + * websockets/WebSocketFrame.h: Added. + (WebCore): + (WebSocketFrame): + (WebCore::WebSocketFrame::isNonControlOpCode): + (WebCore::WebSocketFrame::isControlOpCode): + (WebCore::WebSocketFrame::isReservedOpCode): + (WebCore::WebSocketFrame::WebSocketFrame): + 2012-02-20 Adam Roben 32-bit build fix diff --git a/Source/WebCore/GNUmakefile.list.am b/Source/WebCore/GNUmakefile.list.am index f20a408..2391875 100644 --- a/Source/WebCore/GNUmakefile.list.am +++ b/Source/WebCore/GNUmakefile.list.am @@ -4337,6 +4337,7 @@ webcore_sources += \ Source/WebCore/websockets/WebSocketExtensionDispatcher.cpp \ Source/WebCore/websockets/WebSocketExtensionDispatcher.h \ Source/WebCore/websockets/WebSocketExtensionProcessor.h \ + Source/WebCore/websockets/WebSocketFrame.h \ Source/WebCore/websockets/WebSocketHandshake.cpp \ Source/WebCore/websockets/WebSocketHandshake.h \ Source/WebCore/websockets/WebSocketHandshakeRequest.cpp \ diff --git a/Source/WebCore/Target.pri b/Source/WebCore/Target.pri index fbebcb5..c3190e4 100644 --- a/Source/WebCore/Target.pri +++ b/Source/WebCore/Target.pri @@ -3668,6 +3668,7 @@ contains(DEFINES, ENABLE_WEB_SOCKETS=1) { websockets/WebSocketChannelClient.h \ websockets/WebSocketExtensionDispatcher.h \ websockets/WebSocketExtensionProcessor.h \ + websockets/WebSocketFrame.h \ websockets/WebSocketHandshake.h \ websockets/WebSocketHandshakeRequest.h \ websockets/WebSocketHandshakeResponse.h \ diff --git a/Source/WebCore/WebCore.gypi b/Source/WebCore/WebCore.gypi index b337c09..6c2a6d8 100644 --- a/Source/WebCore/WebCore.gypi +++ b/Source/WebCore/WebCore.gypi @@ -4816,6 +4816,7 @@ 'websockets/WebSocketExtensionDispatcher.cpp', 'websockets/WebSocketExtensionDispatcher.h', 'websockets/WebSocketExtensionProcessor.h', + 'websockets/WebSocketFrame.h', 'websockets/WebSocketHandshake.cpp', 'websockets/WebSocketHandshake.h', 'websockets/WebSocketHandshakeRequest.cpp', diff --git a/Source/WebCore/WebCore.vcproj/WebCore.vcproj b/Source/WebCore/WebCore.vcproj/WebCore.vcproj index 7d65edb..62dcdcc 100755 --- a/Source/WebCore/WebCore.vcproj/WebCore.vcproj +++ b/Source/WebCore/WebCore.vcproj/WebCore.vcproj @@ -73458,6 +73458,10 @@ > + + diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj index 59e87f7..990f405 100644 --- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj +++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj @@ -1365,6 +1365,7 @@ 4A1E719614E101F900626F9D /* JSHTMLShadowElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A1E719414E101F900626F9D /* JSHTMLShadowElement.h */; }; 4A1E71A514E106AC00626F9D /* JSShadowRoot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A1E71A314E106AC00626F9D /* JSShadowRoot.cpp */; }; 4A1E71A614E106AC00626F9D /* JSShadowRoot.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A1E71A414E106AC00626F9D /* JSShadowRoot.h */; }; + 4A4A234614F1E1440046FBF1 /* WebSocketFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A4A234514F1E1440046FBF1 /* WebSocketFrame.h */; }; 4A6E9FC313C17D1D0046A7F8 /* FontFeatureValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A6E9FC113C17D1D0046A7F8 /* FontFeatureValue.cpp */; }; 4A6E9FC413C17D1D0046A7F8 /* FontFeatureValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A6E9FC213C17D1D0046A7F8 /* FontFeatureValue.h */; settings = {ATTRIBUTES = (Private, ); }; }; 4A6E9FC713C17D570046A7F8 /* FontFeatureSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A6E9FC513C17D570046A7F8 /* FontFeatureSettings.cpp */; }; @@ -8228,6 +8229,7 @@ 4A1E719414E101F900626F9D /* JSHTMLShadowElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSHTMLShadowElement.h; sourceTree = ""; }; 4A1E71A314E106AC00626F9D /* JSShadowRoot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSShadowRoot.cpp; sourceTree = ""; }; 4A1E71A414E106AC00626F9D /* JSShadowRoot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSShadowRoot.h; sourceTree = ""; }; + 4A4A234514F1E1440046FBF1 /* WebSocketFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocketFrame.h; sourceTree = ""; }; 4A6E9FC113C17D1D0046A7F8 /* FontFeatureValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FontFeatureValue.cpp; sourceTree = ""; }; 4A6E9FC213C17D1D0046A7F8 /* FontFeatureValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontFeatureValue.h; sourceTree = ""; }; 4A6E9FC513C17D570046A7F8 /* FontFeatureSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FontFeatureSettings.cpp; sourceTree = ""; }; @@ -14847,6 +14849,7 @@ 4A957F0314E241100049DBFB /* WebSocketExtensionDispatcher.cpp */, 4A957F0414E241100049DBFB /* WebSocketExtensionDispatcher.h */, 4ADE25F914E3BB4C004C2213 /* WebSocketExtensionProcessor.h */, + 4A4A234514F1E1440046FBF1 /* WebSocketFrame.h */, 51ABAE421043AB4A008C5260 /* WebSocketHandshake.cpp */, 51ABAE431043AB4A008C5260 /* WebSocketHandshake.h */, 7637C540112E7B74003D6CDC /* WebSocketHandshakeRequest.cpp */, @@ -24212,6 +24215,7 @@ 4AE02ABE14E8A9D200BC3BA7 /* WebSocketDeflater.h in Headers */, 4A957F0714E241300049DBFB /* WebSocketExtensionDispatcher.h in Headers */, 4ADE25FA14E3BB4C004C2213 /* WebSocketExtensionProcessor.h in Headers */, + 4A4A234614F1E1440046FBF1 /* WebSocketFrame.h in Headers */, 51ABAE451043AB4A008C5260 /* WebSocketHandshake.h in Headers */, 7637C543112E7B7E003D6CDC /* WebSocketHandshakeRequest.h in Headers */, 767F99C011A119560080C51D /* WebSocketHandshakeResponse.h in Headers */, diff --git a/Source/WebCore/websockets/WebSocketChannel.cpp b/Source/WebCore/websockets/WebSocketChannel.cpp index 8728e16..1c478e6 100644 --- a/Source/WebCore/websockets/WebSocketChannel.cpp +++ b/Source/WebCore/websockets/WebSocketChannel.cpp @@ -81,13 +81,6 @@ const size_t payloadLengthWithTwoByteExtendedLengthField = 126; const size_t payloadLengthWithEightByteExtendedLengthField = 127; const size_t maskingKeyWidthInBytes = 4; -const WebSocketChannel::OpCode WebSocketChannel::OpCodeContinuation = 0x0; -const WebSocketChannel::OpCode WebSocketChannel::OpCodeText = 0x1; -const WebSocketChannel::OpCode WebSocketChannel::OpCodeBinary = 0x2; -const WebSocketChannel::OpCode WebSocketChannel::OpCodeClose = 0x8; -const WebSocketChannel::OpCode WebSocketChannel::OpCodePing = 0x9; -const WebSocketChannel::OpCode WebSocketChannel::OpCodePong = 0xA; - WebSocketChannel::WebSocketChannel(Document* document, WebSocketChannelClient* client) : m_document(document) , m_client(client) @@ -183,7 +176,7 @@ bool WebSocketChannel::send(const ArrayBuffer& binaryData) { LOG(Network, "WebSocketChannel %p send arraybuffer %p", this, &binaryData); ASSERT(!m_useHixie76Protocol); - enqueueRawFrame(OpCodeBinary, static_cast(binaryData.data()), binaryData.byteLength()); + enqueueRawFrame(WebSocketFrame::OpCodeBinary, static_cast(binaryData.data()), binaryData.byteLength()); return true; } @@ -191,7 +184,7 @@ bool WebSocketChannel::send(const Blob& binaryData) { LOG(Network, "WebSocketChannel %p send blob %s", this, binaryData.url().string().utf8().data()); ASSERT(!m_useHixie76Protocol); - enqueueBlobFrame(OpCodeBinary, binaryData); + enqueueBlobFrame(WebSocketFrame::OpCodeBinary, binaryData); return true; } @@ -199,7 +192,7 @@ bool WebSocketChannel::send(const char* data, int length) { LOG(Network, "WebSocketChannel %p send binary %p (%dB)", this, data, length); ASSERT(!m_useHixie76Protocol); - enqueueRawFrame(OpCodeBinary, data, length); + enqueueRawFrame(WebSocketFrame::OpCodeBinary, data, length); return true; } @@ -527,7 +520,7 @@ void WebSocketChannel::startClosingHandshake(int code, const String& reason) buf.append(static_cast(lowByte)); buf.append(reason.utf8().data(), reason.utf8().length()); } - enqueueRawFrame(OpCodeClose, buf.data(), buf.size()); + enqueueRawFrame(WebSocketFrame::OpCodeClose, buf.data(), buf.size()); } m_closing = true; if (m_client) @@ -542,7 +535,7 @@ void WebSocketChannel::closingTimerFired(Timer* timer) m_handle->disconnect(); } -WebSocketChannel::ParseFrameResult WebSocketChannel::parseFrame(FrameData& frame) +WebSocketChannel::ParseFrameResult WebSocketChannel::parseFrame(WebSocketFrame& frame, const char*& frameEnd) { const char* p = m_buffer; const char* bufferEnd = m_buffer + m_bufferSize; @@ -557,7 +550,7 @@ WebSocketChannel::ParseFrameResult WebSocketChannel::parseFrame(FrameData& frame bool reserved1 = firstByte & reserved1Bit; bool reserved2 = firstByte & reserved2Bit; bool reserved3 = firstByte & reserved3Bit; - OpCode opCode = firstByte & opCodeMask; + unsigned char opCode = firstByte & opCodeMask; bool masked = secondByte & maskBit; uint64_t payloadLength64 = secondByte & payloadLengthMask; @@ -601,7 +594,7 @@ WebSocketChannel::ParseFrameResult WebSocketChannel::parseFrame(FrameData& frame payload[i] ^= maskingKey[i % maskingKeyWidthInBytes]; // Unmask the payload. } - frame.opCode = opCode; + frame.opCode = static_cast(opCode); frame.final = final; frame.reserved1 = reserved1; frame.reserved2 = reserved2; @@ -609,7 +602,7 @@ WebSocketChannel::ParseFrameResult WebSocketChannel::parseFrame(FrameData& frame frame.masked = masked; frame.payload = p + maskingKeyLength; frame.payloadLength = payloadLength; - frame.frameEnd = p + maskingKeyLength + payloadLength; + frameEnd = p + maskingKeyLength + payloadLength; return FrameOK; } @@ -617,12 +610,16 @@ bool WebSocketChannel::processFrame() { ASSERT(m_buffer); - FrameData frame; - if (parseFrame(frame) != FrameOK) + WebSocketFrame frame; + const char* frameEnd; + if (parseFrame(frame, frameEnd) != FrameOK) return false; + ASSERT(m_buffer < frameEnd); + ASSERT(frameEnd <= m_buffer + m_bufferSize); + // Validate the frame data. - if (isReservedOpCode(frame.opCode)) { + if (WebSocketFrame::isReservedOpCode(frame.opCode)) { fail("Unrecognized frame opcode: " + String::number(frame.opCode)); return false; } @@ -633,34 +630,34 @@ bool WebSocketChannel::processFrame() } // All control frames must not be fragmented. - if (isControlOpCode(frame.opCode) && !frame.final) { + if (WebSocketFrame::isControlOpCode(frame.opCode) && !frame.final) { fail("Received fragmented control frame: opcode = " + String::number(frame.opCode)); return false; } // All control frames must have a payload of 125 bytes or less, which means the frame must not contain // the "extended payload length" field. - if (isControlOpCode(frame.opCode) && frame.payloadLength > maxPayloadLengthWithoutExtendedLengthField) { + if (WebSocketFrame::isControlOpCode(frame.opCode) && frame.payloadLength > maxPayloadLengthWithoutExtendedLengthField) { fail("Received control frame having too long payload: " + String::number(frame.payloadLength) + " bytes"); return false; } // A new data frame is received before the previous continuous frame finishes. // Note that control frames are allowed to come in the middle of continuous frames. - if (m_hasContinuousFrame && frame.opCode != OpCodeContinuation && !isControlOpCode(frame.opCode)) { + if (m_hasContinuousFrame && frame.opCode != WebSocketFrame::OpCodeContinuation && !WebSocketFrame::isControlOpCode(frame.opCode)) { fail("Received new data frame but previous continuous frame is unfinished."); return false; } switch (frame.opCode) { - case OpCodeContinuation: + case WebSocketFrame::OpCodeContinuation: // An unexpected continuation frame is received without any leading frame. if (!m_hasContinuousFrame) { fail("Received unexpected continuation frame."); return false; } m_continuousFrameData.append(frame.payload, frame.payloadLength); - skipBuffer(frame.frameEnd - m_buffer); + skipBuffer(frameEnd - m_buffer); if (frame.final) { // onmessage handler may eventually call the other methods of this channel, // so we should pretend that we have finished to read this frame and @@ -670,7 +667,7 @@ bool WebSocketChannel::processFrame() OwnPtr > continuousFrameData = adoptPtr(new Vector); m_continuousFrameData.swap(*continuousFrameData); m_hasContinuousFrame = false; - if (m_continuousFrameOpCode == OpCodeText) { + if (m_continuousFrameOpCode == WebSocketFrame::OpCodeText) { String message; if (continuousFrameData->size()) message = String::fromUTF8(continuousFrameData->data(), continuousFrameData->size()); @@ -680,48 +677,48 @@ bool WebSocketChannel::processFrame() fail("Could not decode a text frame as UTF-8."); else m_client->didReceiveMessage(message); - } else if (m_continuousFrameOpCode == OpCodeBinary) + } else if (m_continuousFrameOpCode == WebSocketFrame::OpCodeBinary) m_client->didReceiveBinaryData(continuousFrameData.release()); } break; - case OpCodeText: + case WebSocketFrame::OpCodeText: if (frame.final) { String message; if (frame.payloadLength) message = String::fromUTF8(frame.payload, frame.payloadLength); else message = ""; - skipBuffer(frame.frameEnd - m_buffer); + skipBuffer(frameEnd - m_buffer); if (message.isNull()) fail("Could not decode a text frame as UTF-8."); else m_client->didReceiveMessage(message); } else { m_hasContinuousFrame = true; - m_continuousFrameOpCode = OpCodeText; + m_continuousFrameOpCode = WebSocketFrame::OpCodeText; ASSERT(m_continuousFrameData.isEmpty()); m_continuousFrameData.append(frame.payload, frame.payloadLength); - skipBuffer(frame.frameEnd - m_buffer); + skipBuffer(frameEnd - m_buffer); } break; - case OpCodeBinary: + case WebSocketFrame::OpCodeBinary: if (frame.final) { OwnPtr > binaryData = adoptPtr(new Vector(frame.payloadLength)); memcpy(binaryData->data(), frame.payload, frame.payloadLength); - skipBuffer(frame.frameEnd - m_buffer); + skipBuffer(frameEnd - m_buffer); m_client->didReceiveBinaryData(binaryData.release()); } else { m_hasContinuousFrame = true; - m_continuousFrameOpCode = OpCodeBinary; + m_continuousFrameOpCode = WebSocketFrame::OpCodeBinary; ASSERT(m_continuousFrameData.isEmpty()); m_continuousFrameData.append(frame.payload, frame.payloadLength); - skipBuffer(frame.frameEnd - m_buffer); + skipBuffer(frameEnd - m_buffer); } break; - case OpCodeClose: + case WebSocketFrame::OpCodeClose: if (frame.payloadLength >= 2) { unsigned char highByte = static_cast(frame.payload[0]); unsigned char lowByte = static_cast(frame.payload[1]); @@ -732,7 +729,7 @@ bool WebSocketChannel::processFrame() m_closeEventReason = String::fromUTF8(&frame.payload[2], frame.payloadLength - 2); else m_closeEventReason = ""; - skipBuffer(frame.frameEnd - m_buffer); + skipBuffer(frameEnd - m_buffer); m_receivedClosingHandshake = true; startClosingHandshake(m_closeEventCode, m_closeEventReason); if (m_closing) { @@ -741,20 +738,20 @@ bool WebSocketChannel::processFrame() } break; - case OpCodePing: - enqueueRawFrame(OpCodePong, frame.payload, frame.payloadLength); - skipBuffer(frame.frameEnd - m_buffer); + case WebSocketFrame::OpCodePing: + enqueueRawFrame(WebSocketFrame::OpCodePong, frame.payload, frame.payloadLength); + skipBuffer(frameEnd - m_buffer); break; - case OpCodePong: + case WebSocketFrame::OpCodePong: // A server may send a pong in response to our ping, or an unsolicited pong which is not associated with // any specific ping. Either way, there's nothing to do on receipt of pong. - skipBuffer(frame.frameEnd - m_buffer); + skipBuffer(frameEnd - m_buffer); break; default: ASSERT_NOT_REACHED(); - skipBuffer(frame.frameEnd - m_buffer); + skipBuffer(frameEnd - m_buffer); break; } @@ -855,14 +852,14 @@ void WebSocketChannel::enqueueTextFrame(const String& string) ASSERT(!m_useHixie76Protocol); ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); OwnPtr frame = adoptPtr(new QueuedFrame); - frame->opCode = OpCodeText; + frame->opCode = WebSocketFrame::OpCodeText; frame->frameType = QueuedFrameTypeString; frame->stringData = string; m_outgoingFrameQueue.append(frame.release()); processOutgoingFrameQueue(); } -void WebSocketChannel::enqueueRawFrame(OpCode opCode, const char* data, size_t dataLength) +void WebSocketChannel::enqueueRawFrame(WebSocketFrame::OpCode opCode, const char* data, size_t dataLength) { ASSERT(!m_useHixie76Protocol); ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); @@ -876,7 +873,7 @@ void WebSocketChannel::enqueueRawFrame(OpCode opCode, const char* data, size_t d processOutgoingFrameQueue(); } -void WebSocketChannel::enqueueBlobFrame(OpCode opCode, const Blob& blob) +void WebSocketChannel::enqueueBlobFrame(WebSocketFrame::OpCode opCode, const Blob& blob) { ASSERT(!m_useHixie76Protocol); ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); @@ -967,44 +964,55 @@ void WebSocketChannel::abortOutgoingFrameQueue() #endif } -bool WebSocketChannel::sendFrame(OpCode opCode, const char* data, size_t dataLength) +static void appendMaskedFramePayload(const WebSocketFrame& frame, Vector& frameData) { - ASSERT(m_handle); - ASSERT(!m_suspended); + size_t maskingKeyStart = frameData.size(); + frameData.grow(frameData.size() + maskingKeyWidthInBytes); // Add placeholder for masking key. Will be overwritten. + size_t payloadStart = frameData.size(); + frameData.append(frame.payload, frame.payloadLength); + + cryptographicallyRandomValues(frameData.data() + maskingKeyStart, maskingKeyWidthInBytes); + for (size_t i = 0; i < frame.payloadLength; ++i) + frameData[payloadStart + i] ^= frameData[maskingKeyStart + i % maskingKeyWidthInBytes]; +} - Vector frame; - ASSERT(!(opCode & ~opCodeMask)); // Checks whether "opCode" fits in the range of opCodes. - frame.append(finalBit | opCode); - if (dataLength <= maxPayloadLengthWithoutExtendedLengthField) - frame.append(maskBit | dataLength); - else if (dataLength <= 0xFFFF) { - frame.append(maskBit | payloadLengthWithTwoByteExtendedLengthField); - frame.append((dataLength & 0xFF00) >> 8); - frame.append(dataLength & 0xFF); +static void makeFrameData(const WebSocketFrame& frame, Vector& frameData) +{ + unsigned char firstByte = (frame.final ? finalBit : 0) | frame.opCode; + frameData.append(firstByte); + if (frame.payloadLength <= maxPayloadLengthWithoutExtendedLengthField) + frameData.append(maskBit | frame.payloadLength); + else if (frame.payloadLength <= 0xFFFF) { + frameData.append(maskBit | payloadLengthWithTwoByteExtendedLengthField); + frameData.append((frame.payloadLength & 0xFF00) >> 8); + frameData.append(frame.payloadLength & 0xFF); } else { - frame.append(maskBit | payloadLengthWithEightByteExtendedLengthField); + frameData.append(maskBit | payloadLengthWithEightByteExtendedLengthField); char extendedPayloadLength[8]; - size_t remaining = dataLength; + size_t remaining = frame.payloadLength; // Fill the length into extendedPayloadLength in the network byte order. for (int i = 0; i < 8; ++i) { extendedPayloadLength[7 - i] = remaining & 0xFF; remaining >>= 8; } ASSERT(!remaining); - frame.append(extendedPayloadLength, 8); + frameData.append(extendedPayloadLength, 8); } - // Mask the frame. - size_t maskingKeyStart = frame.size(); - frame.grow(frame.size() + maskingKeyWidthInBytes); // Add placeholder for masking key. Will be overwritten. - size_t payloadStart = frame.size(); - frame.append(data, dataLength); + appendMaskedFramePayload(frame, frameData); +} + +bool WebSocketChannel::sendFrame(WebSocketFrame::OpCode opCode, const char* data, size_t dataLength) +{ + ASSERT(m_handle); + ASSERT(!m_suspended); - cryptographicallyRandomValues(frame.data() + maskingKeyStart, maskingKeyWidthInBytes); - for (size_t i = 0; i < dataLength; ++i) - frame[payloadStart + i] ^= frame[maskingKeyStart + i % maskingKeyWidthInBytes]; + ASSERT(!(opCode & ~opCodeMask)); // Checks whether "opCode" fits in the range of opCodes. + WebSocketFrame frame(opCode, true, true, data, dataLength); + Vector frameData; + makeFrameData(frame, frameData); - return m_handle->send(frame.data(), frame.size()); + return m_handle->send(frameData.data(), frameData.size()); } bool WebSocketChannel::sendFrameHixie76(const char* data, size_t dataLength) diff --git a/Source/WebCore/websockets/WebSocketChannel.h b/Source/WebCore/websockets/WebSocketChannel.h index 73fc52d..4bb28c9 100644 --- a/Source/WebCore/websockets/WebSocketChannel.h +++ b/Source/WebCore/websockets/WebSocketChannel.h @@ -37,6 +37,7 @@ #include "SocketStreamHandleClient.h" #include "ThreadableWebSocketChannel.h" #include "Timer.h" +#include "WebSocketFrame.h" #include "WebSocketHandshake.h" #include #include @@ -128,38 +129,13 @@ private: void startClosingHandshake(int code, const String& reason); void closingTimerFired(Timer*); - // Hybi-10 opcodes. - typedef unsigned int OpCode; - static const OpCode OpCodeContinuation; - static const OpCode OpCodeText; - static const OpCode OpCodeBinary; - static const OpCode OpCodeClose; - static const OpCode OpCodePing; - static const OpCode OpCodePong; - - static bool isNonControlOpCode(OpCode opCode) { return opCode == OpCodeContinuation || opCode == OpCodeText || opCode == OpCodeBinary; } - static bool isControlOpCode(OpCode opCode) { return opCode == OpCodeClose || opCode == OpCodePing || opCode == OpCodePong; } - static bool isReservedOpCode(OpCode opCode) { return !isNonControlOpCode(opCode) && !isControlOpCode(opCode); } - enum ParseFrameResult { FrameOK, FrameIncomplete, FrameError }; - struct FrameData { - OpCode opCode; - bool final; - bool reserved1; - bool reserved2; - bool reserved3; - bool masked; - const char* payload; - size_t payloadLength; - const char* frameEnd; - }; - - ParseFrameResult parseFrame(FrameData&); // May modify part of m_buffer to unmask the frame. + ParseFrameResult parseFrame(WebSocketFrame&, const char*& frameEnd); // May modify part of m_buffer to unmask the frame. bool processFrame(); bool processFrameHixie76(); @@ -180,7 +156,7 @@ private: QueuedFrameTypeBlob }; struct QueuedFrame { - OpCode opCode; + WebSocketFrame::OpCode opCode; QueuedFrameType frameType; // Only one of the following items is used, according to the value of frameType. String stringData; @@ -188,8 +164,8 @@ private: RefPtr blobData; }; void enqueueTextFrame(const String&); - void enqueueRawFrame(OpCode, const char* data, size_t dataLength); - void enqueueBlobFrame(OpCode, const Blob&); + void enqueueRawFrame(WebSocketFrame::OpCode, const char* data, size_t dataLength); + void enqueueBlobFrame(WebSocketFrame::OpCode, const Blob&); void processOutgoingFrameQueue(); void abortOutgoingFrameQueue(); @@ -208,7 +184,7 @@ private: // 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 sendFrame(WebSocketFrame::OpCode, const char* data, size_t dataLength); bool sendFrameHixie76(const char* data, size_t dataLength); #if ENABLE(BLOB) @@ -242,7 +218,7 @@ private: // Private members only for hybi-10 protocol. bool m_hasContinuousFrame; - OpCode m_continuousFrameOpCode; + WebSocketFrame::OpCode m_continuousFrameOpCode; Vector m_continuousFrameData; unsigned short m_closeEventCode; String m_closeEventReason; diff --git a/Source/WebCore/websockets/WebSocketFrame.h b/Source/WebCore/websockets/WebSocketFrame.h new file mode 100644 index 0000000..19dd7ab --- /dev/null +++ b/Source/WebCore/websockets/WebSocketFrame.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebSocketFrame_h +#define WebSocketFrame_h + +#if ENABLE(WEB_SOCKETS) + +namespace WebCore { + +struct WebSocketFrame { + // RFC6455 opcodes. + enum OpCode { + OpCodeContinuation = 0x0, + OpCodeText = 0x1, + OpCodeBinary = 0x2, + OpCodeClose = 0x8, + OpCodePing = 0x9, + OpCodePong = 0xA, + OpCodeInvalid = 0x10 + }; + + static bool isNonControlOpCode(OpCode opCode) { return opCode == OpCodeContinuation || opCode == OpCodeText || opCode == OpCodeBinary; } + static bool isControlOpCode(OpCode opCode) { return opCode == OpCodeClose || opCode == OpCodePing || opCode == OpCodePong; } + static bool isReservedOpCode(OpCode opCode) { return !isNonControlOpCode(opCode) && !isControlOpCode(opCode); } + + WebSocketFrame(OpCode opCode = OpCodeInvalid, bool final = false, bool masked = false, const char* payload = 0, size_t payloadLength = 0) + : opCode(opCode) + , final(final) + , reserved1(false) + , reserved2(false) + , reserved3(false) + , masked(masked) + , payload(payload) + , payloadLength(payloadLength) + { + } + + OpCode opCode; + bool final; + bool reserved1; + bool reserved2; + bool reserved3; + bool masked; + const char* payload; + size_t payloadLength; +}; + +} // namespace WebCore + +#endif // ENABLE(WEB_SOCKETS) + +#endif // WebSocketFrame_h -- 2.7.4