// 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