1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "net/server/http_connection.h"
11
12 #include <utility>
13
14 #include "base/logging.h"
15 #include "net/server/web_socket.h"
16 #include "net/socket/stream_socket.h"
17
18 namespace net {
19
ReadIOBuffer()20 HttpConnection::ReadIOBuffer::ReadIOBuffer()
21 : base_(base::MakeRefCounted<GrowableIOBuffer>()) {
22 SetCapacity(kInitialBufSize);
23 }
24
~ReadIOBuffer()25 HttpConnection::ReadIOBuffer::~ReadIOBuffer() {
26 data_ = nullptr; // Avoid dangling ptr when `base_` is destroyed.
27 }
28
GetCapacity() const29 int HttpConnection::ReadIOBuffer::GetCapacity() const {
30 return base_->capacity();
31 }
32
SetCapacity(int capacity)33 void HttpConnection::ReadIOBuffer::SetCapacity(int capacity) {
34 DCHECK_LE(GetSize(), capacity);
35 data_ = nullptr;
36 base_->SetCapacity(capacity);
37 data_ = base_->data();
38 }
39
IncreaseCapacity()40 bool HttpConnection::ReadIOBuffer::IncreaseCapacity() {
41 if (GetCapacity() >= max_buffer_size_) {
42 LOG(ERROR) << "Too large read data is pending: capacity=" << GetCapacity()
43 << ", max_buffer_size=" << max_buffer_size_
44 << ", read=" << GetSize();
45 return false;
46 }
47
48 int new_capacity = GetCapacity() * kCapacityIncreaseFactor;
49 if (new_capacity > max_buffer_size_)
50 new_capacity = max_buffer_size_;
51 SetCapacity(new_capacity);
52 return true;
53 }
54
StartOfBuffer() const55 char* HttpConnection::ReadIOBuffer::StartOfBuffer() const {
56 return base::as_writable_chars(base_->everything()).data();
57 }
58
GetSize() const59 int HttpConnection::ReadIOBuffer::GetSize() const {
60 return base_->offset();
61 }
62
DidRead(int bytes)63 void HttpConnection::ReadIOBuffer::DidRead(int bytes) {
64 DCHECK_GE(RemainingCapacity(), bytes);
65 base_->set_offset(base_->offset() + bytes);
66 data_ = base_->data();
67 }
68
RemainingCapacity() const69 int HttpConnection::ReadIOBuffer::RemainingCapacity() const {
70 return base_->RemainingCapacity();
71 }
72
DidConsume(int bytes)73 void HttpConnection::ReadIOBuffer::DidConsume(int bytes) {
74 int previous_size = GetSize();
75 int unconsumed_size = previous_size - bytes;
76 DCHECK_LE(0, unconsumed_size);
77 if (unconsumed_size > 0) {
78 // Move unconsumed data to the start of buffer.
79 memmove(StartOfBuffer(), StartOfBuffer() + bytes, unconsumed_size);
80 }
81 base_->set_offset(unconsumed_size);
82 data_ = base_->data();
83
84 // If capacity is too big, reduce it.
85 if (GetCapacity() > kMinimumBufSize &&
86 GetCapacity() > previous_size * kCapacityIncreaseFactor) {
87 int new_capacity = GetCapacity() / kCapacityIncreaseFactor;
88 if (new_capacity < kMinimumBufSize)
89 new_capacity = kMinimumBufSize;
90 // this avoids the pointer to dangle until `SetCapacity` gets called.
91 data_ = nullptr;
92 // realloc() within GrowableIOBuffer::SetCapacity() could move data even
93 // when size is reduced. If unconsumed_size == 0, i.e. no data exists in
94 // the buffer, free internal buffer first to guarantee no data move.
95 if (!unconsumed_size)
96 base_->SetCapacity(0);
97 SetCapacity(new_capacity);
98 }
99 }
100
101 HttpConnection::QueuedWriteIOBuffer::QueuedWriteIOBuffer() = default;
102
~QueuedWriteIOBuffer()103 HttpConnection::QueuedWriteIOBuffer::~QueuedWriteIOBuffer() {
104 data_ = nullptr; // pending_data_ owns data_.
105 }
106
IsEmpty() const107 bool HttpConnection::QueuedWriteIOBuffer::IsEmpty() const {
108 return pending_data_.empty();
109 }
110
Append(const std::string & data)111 bool HttpConnection::QueuedWriteIOBuffer::Append(const std::string& data) {
112 if (data.empty())
113 return true;
114
115 if (total_size_ + static_cast<int>(data.size()) > max_buffer_size_) {
116 LOG(ERROR) << "Too large write data is pending: size="
117 << total_size_ + data.size()
118 << ", max_buffer_size=" << max_buffer_size_;
119 return false;
120 }
121
122 pending_data_.push(std::make_unique<std::string>(data));
123 total_size_ += data.size();
124
125 // If new data is the first pending data, updates data_.
126 if (pending_data_.size() == 1)
127 data_ = const_cast<char*>(pending_data_.front()->data());
128 return true;
129 }
130
DidConsume(int size)131 void HttpConnection::QueuedWriteIOBuffer::DidConsume(int size) {
132 DCHECK_GE(total_size_, size);
133 DCHECK_GE(GetSizeToWrite(), size);
134 if (size == 0)
135 return;
136
137 if (size < GetSizeToWrite()) {
138 data_ += size;
139 } else { // size == GetSizeToWrite(). Updates data_ to next pending data.
140 data_ = nullptr;
141 pending_data_.pop();
142 data_ =
143 IsEmpty() ? nullptr : const_cast<char*>(pending_data_.front()->data());
144 }
145 total_size_ -= size;
146 }
147
GetSizeToWrite() const148 int HttpConnection::QueuedWriteIOBuffer::GetSizeToWrite() const {
149 if (IsEmpty()) {
150 DCHECK_EQ(0, total_size_);
151 return 0;
152 }
153 DCHECK_GE(data_, pending_data_.front()->data());
154 int consumed = static_cast<int>(data_ - pending_data_.front()->data());
155 DCHECK_GT(static_cast<int>(pending_data_.front()->size()), consumed);
156 return pending_data_.front()->size() - consumed;
157 }
158
HttpConnection(int id,std::unique_ptr<StreamSocket> socket)159 HttpConnection::HttpConnection(int id, std::unique_ptr<StreamSocket> socket)
160 : id_(id),
161 socket_(std::move(socket)),
162 read_buf_(base::MakeRefCounted<ReadIOBuffer>()),
163 write_buf_(base::MakeRefCounted<QueuedWriteIOBuffer>()) {}
164
165 HttpConnection::~HttpConnection() = default;
166
SetWebSocket(std::unique_ptr<WebSocket> web_socket)167 void HttpConnection::SetWebSocket(std::unique_ptr<WebSocket> web_socket) {
168 DCHECK(!web_socket_);
169 web_socket_ = std::move(web_socket);
170 }
171
172 } // namespace net
173