// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <algorithm> #include <limits> #include "net/websockets/websocket_frame_handler.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" namespace net { WebSocketFrameHandler::WebSocketFrameHandler() : current_buffer_size_(0), original_current_buffer_size_(0) { } WebSocketFrameHandler::~WebSocketFrameHandler() { } void WebSocketFrameHandler::AppendData(const char* data, int length) { scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(length); memcpy(buffer->data(), data, length); pending_buffers_.push_back(buffer); } int WebSocketFrameHandler::UpdateCurrentBuffer(bool buffered) { if (current_buffer_) return 0; DCHECK(!current_buffer_size_); DCHECK(!original_current_buffer_size_); if (pending_buffers_.empty()) return 0; scoped_refptr<IOBufferWithSize> buffer = pending_buffers_.front(); int buffer_size = 0; if (buffered) { std::vector<FrameInfo> frame_info; buffer_size = ParseWebSocketFrame(buffer->data(), buffer->size(), &frame_info); if (buffer_size <= 0) return buffer_size; original_current_buffer_size_ = buffer_size; // TODO(ukai): filter(e.g. compress or decompress) frame messages. } else { original_current_buffer_size_ = buffer->size(); buffer_size = buffer->size(); } current_buffer_ = buffer; current_buffer_size_ = buffer_size; return buffer_size; } void WebSocketFrameHandler::ReleaseCurrentBuffer() { DCHECK(!pending_buffers_.empty()); scoped_refptr<IOBufferWithSize> front_buffer = pending_buffers_.front(); pending_buffers_.pop_front(); int remaining_size = front_buffer->size() - original_current_buffer_size_; if (remaining_size > 0) { scoped_refptr<IOBufferWithSize> next_buffer = NULL; int buffer_size = remaining_size; if (!pending_buffers_.empty()) { next_buffer = pending_buffers_.front(); buffer_size += next_buffer->size(); pending_buffers_.pop_front(); } // TODO(ukai): don't copy data. scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(buffer_size); memcpy(buffer->data(), front_buffer->data() + original_current_buffer_size_, remaining_size); if (next_buffer) memcpy(buffer->data() + remaining_size, next_buffer->data(), next_buffer->size()); pending_buffers_.push_front(buffer); } current_buffer_ = NULL; current_buffer_size_ = 0; original_current_buffer_size_ = 0; } /* static */ int WebSocketFrameHandler::ParseWebSocketFrame( const char* buffer, int size, std::vector<FrameInfo>* frame_info) { const char* end = buffer + size; const char* p = buffer; int buffer_size = 0; while (p < end) { FrameInfo frame; frame.frame_start = p; frame.message_length = -1; unsigned char frame_byte = static_cast<unsigned char>(*p++); if ((frame_byte & 0x80) == 0x80) { int length = 0; while (p < end) { // Note: might overflow later if numeric_limits<int>::max() is not // n*128-1. if (length > std::numeric_limits<int>::max() / 128) { // frame length overflow. return ERR_INSUFFICIENT_RESOURCES; } unsigned char c = static_cast<unsigned char>(*p); length = length * 128 + (c & 0x7f); ++p; if ((c & 0x80) != 0x80) break; } if (end - p >= length) { frame.message_start = p; frame.message_length = length; p += length; } else { break; } } else { frame.message_start = p; while (p < end && *p != '\xff') ++p; if (p < end && *p == '\xff') { frame.message_length = p - frame.message_start; ++p; } else { break; } } if (frame.message_length >= 0 && p <= end) { frame.frame_length = p - frame.frame_start; buffer_size += frame.frame_length; frame_info->push_back(frame); } } return buffer_size; } } // namespace net