|  | // Copyright (c) 2012 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 "net/server/http_connection.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "net/server/web_socket.h" | 
|  | #include "net/socket/stream_socket.h" | 
|  | #include "starboard/memory.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | HttpConnection::ReadIOBuffer::ReadIOBuffer() | 
|  | : base_(base::MakeRefCounted<GrowableIOBuffer>()), | 
|  | max_buffer_size_(kDefaultMaxBufferSize) { | 
|  | SetCapacity(kInitialBufSize); | 
|  | } | 
|  |  | 
|  | HttpConnection::ReadIOBuffer::~ReadIOBuffer() { | 
|  | data_ = NULL;  // base_ owns data_. | 
|  | } | 
|  |  | 
|  | int HttpConnection::ReadIOBuffer::GetCapacity() const { | 
|  | return base_->capacity(); | 
|  | } | 
|  |  | 
|  | void HttpConnection::ReadIOBuffer::SetCapacity(int capacity) { | 
|  | DCHECK_LE(GetSize(), capacity); | 
|  | base_->SetCapacity(capacity); | 
|  | data_ = base_->data(); | 
|  | } | 
|  |  | 
|  | bool HttpConnection::ReadIOBuffer::IncreaseCapacity() { | 
|  | if (GetCapacity() >= max_buffer_size_) { | 
|  | LOG(ERROR) << "Too large read data is pending: capacity=" << GetCapacity() | 
|  | << ", max_buffer_size=" << max_buffer_size_ | 
|  | << ", read=" << GetSize(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int new_capacity = GetCapacity() * kCapacityIncreaseFactor; | 
|  | if (new_capacity > max_buffer_size_) | 
|  | new_capacity = max_buffer_size_; | 
|  | SetCapacity(new_capacity); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | char* HttpConnection::ReadIOBuffer::StartOfBuffer() const { | 
|  | return base_->StartOfBuffer(); | 
|  | } | 
|  |  | 
|  | int HttpConnection::ReadIOBuffer::GetSize() const { | 
|  | return base_->offset(); | 
|  | } | 
|  |  | 
|  | void HttpConnection::ReadIOBuffer::DidRead(int bytes) { | 
|  | DCHECK_GE(RemainingCapacity(), bytes); | 
|  | base_->set_offset(base_->offset() + bytes); | 
|  | data_ = base_->data(); | 
|  | } | 
|  |  | 
|  | int HttpConnection::ReadIOBuffer::RemainingCapacity() const { | 
|  | return base_->RemainingCapacity(); | 
|  | } | 
|  |  | 
|  | void HttpConnection::ReadIOBuffer::DidConsume(int bytes) { | 
|  | int previous_size = GetSize(); | 
|  | int unconsumed_size = previous_size - bytes; | 
|  | DCHECK_LE(0, unconsumed_size); | 
|  | if (unconsumed_size > 0) { | 
|  | // Move unconsumed data to the start of buffer. | 
|  | memmove(StartOfBuffer(), StartOfBuffer() + bytes, unconsumed_size); | 
|  | } | 
|  | base_->set_offset(unconsumed_size); | 
|  | data_ = base_->data(); | 
|  |  | 
|  | // If capacity is too big, reduce it. | 
|  | if (GetCapacity() > kMinimumBufSize && | 
|  | GetCapacity() > previous_size * kCapacityIncreaseFactor) { | 
|  | int new_capacity = GetCapacity() / kCapacityIncreaseFactor; | 
|  | if (new_capacity < kMinimumBufSize) | 
|  | new_capacity = kMinimumBufSize; | 
|  | // realloc() within GrowableIOBuffer::SetCapacity() could move data even | 
|  | // when size is reduced. If unconsumed_size == 0, i.e. no data exists in | 
|  | // the buffer, free internal buffer first to guarantee no data move. | 
|  | if (!unconsumed_size) | 
|  | base_->SetCapacity(0); | 
|  | SetCapacity(new_capacity); | 
|  | } | 
|  | } | 
|  |  | 
|  | HttpConnection::QueuedWriteIOBuffer::QueuedWriteIOBuffer() | 
|  | : total_size_(0), | 
|  | max_buffer_size_(kDefaultMaxBufferSize) { | 
|  | } | 
|  |  | 
|  | HttpConnection::QueuedWriteIOBuffer::~QueuedWriteIOBuffer() { | 
|  | data_ = NULL;  // pending_data_ owns data_. | 
|  | } | 
|  |  | 
|  | bool HttpConnection::QueuedWriteIOBuffer::IsEmpty() const { | 
|  | return pending_data_.empty(); | 
|  | } | 
|  |  | 
|  | bool HttpConnection::QueuedWriteIOBuffer::Append(const std::string& data) { | 
|  | if (data.empty()) | 
|  | return true; | 
|  |  | 
|  | if (total_size_ + static_cast<int>(data.size()) > max_buffer_size_) { | 
|  | LOG(ERROR) << "Too large write data is pending: size=" | 
|  | << total_size_ + data.size() | 
|  | << ", max_buffer_size=" << max_buffer_size_; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | pending_data_.push(std::make_unique<std::string>(data)); | 
|  | total_size_ += data.size(); | 
|  |  | 
|  | // If new data is the first pending data, updates data_. | 
|  | if (pending_data_.size() == 1) | 
|  | data_ = const_cast<char*>(pending_data_.front()->data()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void HttpConnection::QueuedWriteIOBuffer::DidConsume(int size) { | 
|  | DCHECK_GE(total_size_, size); | 
|  | DCHECK_GE(GetSizeToWrite(), size); | 
|  | if (size == 0) | 
|  | return; | 
|  |  | 
|  | if (size < GetSizeToWrite()) { | 
|  | data_ += size; | 
|  | } else {  // size == GetSizeToWrite(). Updates data_ to next pending data. | 
|  | pending_data_.pop(); | 
|  | data_ = IsEmpty() ? NULL : const_cast<char*>(pending_data_.front()->data()); | 
|  | } | 
|  | total_size_ -= size; | 
|  | } | 
|  |  | 
|  | int HttpConnection::QueuedWriteIOBuffer::GetSizeToWrite() const { | 
|  | if (IsEmpty()) { | 
|  | DCHECK_EQ(0, total_size_); | 
|  | return 0; | 
|  | } | 
|  | DCHECK_GE(data_, pending_data_.front()->data()); | 
|  | int consumed = static_cast<int>(data_ - pending_data_.front()->data()); | 
|  | DCHECK_GT(static_cast<int>(pending_data_.front()->size()), consumed); | 
|  | return pending_data_.front()->size() - consumed; | 
|  | } | 
|  |  | 
|  | HttpConnection::HttpConnection(int id, std::unique_ptr<StreamSocket> socket) | 
|  | : id_(id), | 
|  | socket_(std::move(socket)), | 
|  | read_buf_(base::MakeRefCounted<ReadIOBuffer>()), | 
|  | write_buf_(base::MakeRefCounted<QueuedWriteIOBuffer>()) {} | 
|  |  | 
|  | HttpConnection::~HttpConnection() = default; | 
|  |  | 
|  | void HttpConnection::SetWebSocket(std::unique_ptr<WebSocket> web_socket) { | 
|  | DCHECK(!web_socket_); | 
|  | web_socket_ = std::move(web_socket); | 
|  | } | 
|  |  | 
|  | }  // namespace net |