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