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)
{
LOG(Network, "WebSocketChannel %p send arraybuffer %p", this, &binaryData);
ASSERT(!m_useHixie76Protocol);
- enqueueRawFrame(OpCodeBinary, static_cast<const char*>(binaryData.data()), binaryData.byteLength());
+ enqueueRawFrame(WebSocketFrame::OpCodeBinary, static_cast<const char*>(binaryData.data()), binaryData.byteLength());
return true;
}
{
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;
}
{
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;
}
buf.append(static_cast<char>(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)
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;
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;
payload[i] ^= maskingKey[i % maskingKeyWidthInBytes]; // Unmask the payload.
}
- frame.opCode = opCode;
+ frame.opCode = static_cast<WebSocketFrame::OpCode>(opCode);
frame.final = final;
frame.reserved1 = reserved1;
frame.reserved2 = reserved2;
frame.masked = masked;
frame.payload = p + maskingKeyLength;
frame.payloadLength = payloadLength;
- frame.frameEnd = p + maskingKeyLength + payloadLength;
+ frameEnd = p + maskingKeyLength + payloadLength;
return FrameOK;
}
{
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;
}
}
// 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
OwnPtr<Vector<char> > continuousFrameData = adoptPtr(new Vector<char>);
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());
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<Vector<char> > binaryData = adoptPtr(new Vector<char>(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<unsigned char>(frame.payload[0]);
unsigned char lowByte = static_cast<unsigned char>(frame.payload[1]);
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) {
}
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;
}
ASSERT(!m_useHixie76Protocol);
ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen);
OwnPtr<QueuedFrame> 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);
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);
#endif
}
-bool WebSocketChannel::sendFrame(OpCode opCode, const char* data, size_t dataLength)
+static void appendMaskedFramePayload(const WebSocketFrame& frame, Vector<char>& 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<char> 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<char>& 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<char> 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)
#include "SocketStreamHandleClient.h"
#include "ThreadableWebSocketChannel.h"
#include "Timer.h"
+#include "WebSocketFrame.h"
#include "WebSocketHandshake.h"
#include <wtf/Deque.h>
#include <wtf/Forward.h>
void startClosingHandshake(int code, const String& reason);
void closingTimerFired(Timer<WebSocketChannel>*);
- // 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();
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;
RefPtr<Blob> 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();
// 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)
// Private members only for hybi-10 protocol.
bool m_hasContinuousFrame;
- OpCode m_continuousFrameOpCode;
+ WebSocketFrame::OpCode m_continuousFrameOpCode;
Vector<char> m_continuousFrameData;
unsigned short m_closeEventCode;
String m_closeEventReason;