blob: 790c452a865f648e09b33e73e8f49123ad5f79a1 [file] [log] [blame]
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/websocket/web_socket_message_container.h"
#include "base/basictypes.h"
namespace cobalt {
namespace websocket {
std::size_t CombineFramesChunks(WebSocketFrameContainer::const_iterator begin,
WebSocketFrameContainer::const_iterator end,
char *out_destination,
std::size_t buffer_length) {
DCHECK(out_destination);
std::size_t bytes_written = 0;
std::size_t bytes_available = buffer_length;
for (WebSocketFrameContainer::const_iterator iterator = begin;
iterator != end; ++iterator) {
const scoped_refptr<net::IOBufferWithSize> &data((*iterator)->data);
if (data) {
std::size_t frame_chunk_size = data->size();
if (bytes_available >= frame_chunk_size) {
SbMemoryCopy(out_destination, data->data(), frame_chunk_size);
out_destination += frame_chunk_size;
bytes_written += frame_chunk_size;
bytes_available -= frame_chunk_size;
}
}
}
DCHECK_LE(bytes_written, buffer_length);
return bytes_written;
}
bool WebSocketMessageContainer::Take(WebSocketFrameContainer *frame_container) {
DCHECK(frame_container);
DCHECK(!IsMessageComplete());
DCHECK(frame_container->IsFrameComplete());
if (!frame_container) {
return false;
}
if (frame_container->empty()) {
return true;
}
bool is_first_frame = frames_.empty();
bool is_continuation_frame = frame_container->IsContinuationFrame();
if (is_first_frame) {
if (is_continuation_frame) {
return false;
}
} else {
// All frames after the first one must be continuation frames.
if (!is_continuation_frame) {
return false;
}
}
frames_.push_back(WebSocketFrameContainer());
WebSocketFrameContainer &last_object(frames_.back());
last_object.swap(*frame_container);
DCHECK(last_object.IsFrameComplete());
payload_size_bytes_ += last_object.GetCurrentPayloadSizeBytes();
message_completed_ |= last_object.IsFinalFrame();
return true;
}
scoped_refptr<net::IOBufferWithSize>
WebSocketMessageContainer::GetMessageAsIOBuffer() const {
scoped_refptr<net::IOBufferWithSize> buf;
DCHECK_LE(kMaxMessagePayloadInBytes, static_cast<std::size_t>(kint32max));
DCHECK_LE(payload_size_bytes_, kMaxMessagePayloadInBytes);
DCHECK_GE(payload_size_bytes_, 0UL);
if ((payload_size_bytes_ > 0) &&
(payload_size_bytes_ <= kMaxMessagePayloadInBytes)) {
buf = make_scoped_refptr(
new net::IOBufferWithSize(static_cast<int>(payload_size_bytes_)));
std::size_t total_bytes_written = 0;
char *data_pointer = buf->data();
std::size_t size_remaining = buf->size();
for (WebSocketFrames::const_iterator iterator = frames_.begin();
iterator != frames_.end(); ++iterator) {
const WebSocketFrameContainer &frame_container(*iterator);
std::size_t bytes_written =
CombineFramesChunks(frame_container.begin(), frame_container.end(),
data_pointer, size_remaining);
DCHECK_LE(bytes_written, size_remaining);
size_remaining -= bytes_written;
data_pointer += bytes_written;
total_bytes_written += bytes_written;
}
DCHECK_EQ(total_bytes_written, payload_size_bytes_);
}
return buf;
}
} // namespace websocket
} // namespace cobalt