1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <algorithm>
6#include <limits>
7
8#include "net/websockets/websocket_frame_handler.h"
9
10#include "net/base/io_buffer.h"
11#include "net/base/net_errors.h"
12
13namespace net {
14
15WebSocketFrameHandler::WebSocketFrameHandler()
16    : current_buffer_size_(0),
17      original_current_buffer_size_(0) {
18}
19
20WebSocketFrameHandler::~WebSocketFrameHandler() {
21}
22
23void WebSocketFrameHandler::AppendData(const char* data, int length) {
24  scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(length);
25  memcpy(buffer->data(), data, length);
26  pending_buffers_.push_back(buffer);
27}
28
29int WebSocketFrameHandler::UpdateCurrentBuffer(bool buffered) {
30  if (current_buffer_)
31    return 0;
32  DCHECK(!current_buffer_size_);
33  DCHECK(!original_current_buffer_size_);
34
35  if (pending_buffers_.empty())
36    return 0;
37  scoped_refptr<IOBufferWithSize> buffer = pending_buffers_.front();
38
39  int buffer_size = 0;
40  if (buffered) {
41    std::vector<FrameInfo> frame_info;
42    buffer_size =
43        ParseWebSocketFrame(buffer->data(), buffer->size(), &frame_info);
44    if (buffer_size <= 0)
45      return buffer_size;
46
47    original_current_buffer_size_ = buffer_size;
48
49    // TODO(ukai): filter(e.g. compress or decompress) frame messages.
50  } else {
51    original_current_buffer_size_ = buffer->size();
52    buffer_size = buffer->size();
53  }
54
55  current_buffer_ = buffer;
56  current_buffer_size_ = buffer_size;
57  return buffer_size;
58}
59
60void WebSocketFrameHandler::ReleaseCurrentBuffer() {
61  DCHECK(!pending_buffers_.empty());
62  scoped_refptr<IOBufferWithSize> front_buffer = pending_buffers_.front();
63  pending_buffers_.pop_front();
64  int remaining_size = front_buffer->size() - original_current_buffer_size_;
65  if (remaining_size > 0) {
66    scoped_refptr<IOBufferWithSize> next_buffer = NULL;
67    int buffer_size = remaining_size;
68    if (!pending_buffers_.empty()) {
69      next_buffer = pending_buffers_.front();
70      buffer_size += next_buffer->size();
71      pending_buffers_.pop_front();
72    }
73    // TODO(ukai): don't copy data.
74    scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(buffer_size);
75    memcpy(buffer->data(), front_buffer->data() + original_current_buffer_size_,
76           remaining_size);
77    if (next_buffer)
78      memcpy(buffer->data() + remaining_size,
79             next_buffer->data(), next_buffer->size());
80    pending_buffers_.push_front(buffer);
81  }
82  current_buffer_ = NULL;
83  current_buffer_size_ = 0;
84  original_current_buffer_size_ = 0;
85}
86
87/* static */
88int WebSocketFrameHandler::ParseWebSocketFrame(
89    const char* buffer, int size, std::vector<FrameInfo>* frame_info) {
90  const char* end = buffer + size;
91  const char* p = buffer;
92  int buffer_size = 0;
93  while (p < end) {
94    FrameInfo frame;
95    frame.frame_start = p;
96    frame.message_length = -1;
97    unsigned char frame_byte = static_cast<unsigned char>(*p++);
98    if ((frame_byte & 0x80) == 0x80) {
99      int length = 0;
100      while (p < end) {
101        // Note: might overflow later if numeric_limits<int>::max() is not
102        // n*128-1.
103        if (length > std::numeric_limits<int>::max() / 128) {
104          // frame length overflow.
105          return ERR_INSUFFICIENT_RESOURCES;
106        }
107        unsigned char c = static_cast<unsigned char>(*p);
108        length = length * 128 + (c & 0x7f);
109        ++p;
110        if ((c & 0x80) != 0x80)
111          break;
112      }
113      if (end - p >= length) {
114        frame.message_start = p;
115        frame.message_length = length;
116        p += length;
117      } else {
118        break;
119      }
120    } else {
121      frame.message_start = p;
122      while (p < end && *p != '\xff')
123        ++p;
124      if (p < end && *p == '\xff') {
125        frame.message_length = p - frame.message_start;
126        ++p;
127      } else {
128        break;
129      }
130    }
131    if (frame.message_length >= 0 && p <= end) {
132      frame.frame_length = p - frame.frame_start;
133      buffer_size += frame.frame_length;
134      frame_info->push_back(frame);
135    }
136  }
137  return buffer_size;
138}
139
140}  // namespace net
141