Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(384)

Side by Side Diff: net/socket/socket_bio_adapter.cc

Issue 2411033003: Drop buffers in idle SSLClientSockets (and SSLServerSockets). (Closed)
Patch Set: mmenke comment Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "net/socket/socket_bio_adapter.h"
6
7 #include <openssl/bio.h>
8 #include <string.h>
9
10 #include <algorithm>
11
12 #include "base/bind.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/threading/thread_task_runner_handle.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/socket/stream_socket.h"
19 #include "net/ssl/openssl_ssl_util.h"
20
21 namespace net {
22
23 SocketBIOAdapter::SocketBIOAdapter(StreamSocket* socket,
24 int read_buffer_capacity,
25 int write_buffer_capacity,
26 Delegate* delegate)
27 : socket_(socket),
28 read_buffer_capacity_(read_buffer_capacity),
29 read_offset_(0),
30 read_result_(0),
31 write_buffer_capacity_(write_buffer_capacity),
32 write_buffer_used_(0),
33 write_error_(OK),
34 delegate_(delegate),
35 weak_factory_(this) {
36 bio_.reset(BIO_new(&kBIOMethod));
37 bio_->ptr = this;
38 bio_->init = 1;
39
40 read_callback_ = base::Bind(&SocketBIOAdapter::OnSocketReadComplete,
41 weak_factory_.GetWeakPtr());
42 write_callback_ = base::Bind(&SocketBIOAdapter::OnSocketWriteComplete,
43 weak_factory_.GetWeakPtr());
44 }
45
46 SocketBIOAdapter::~SocketBIOAdapter() {
47 // BIOs are reference-counted and may outlive the adapter. Clear the pointer
48 // so future operations fail.
49 bio_->ptr = nullptr;
50 }
51
52 bool SocketBIOAdapter::HasPendingReadData() {
53 return read_result_ > 0;
54 }
55
56 int SocketBIOAdapter::BIORead(char* out, int len) {
57 if (len <= 0)
58 return len;
59
60 // If there is no result available synchronously, report any Write() errors
61 // that were observed. Otherwise the application may have encountered a socket
62 // error while writing that would otherwise not be reported until the
63 // application attempted to write again - which it may never do. See
64 // https://crbug.com/249848.
65 if (write_error_ != OK && write_error_ != ERR_IO_PENDING &&
66 (read_result_ == 0 || read_result_ == ERR_IO_PENDING)) {
67 OpenSSLPutNetError(FROM_HERE, write_error_);
68 return -1;
69 }
70
71 if (read_result_ == 0) {
72 // Instantiate the read buffer and read from the socket. Although only |len|
73 // bytes were requested, intentionally read to the full buffer size. The SSL
74 // layer reads the record header and body in separate reads to avoid
75 // overreading, but issuing one is more efficient. SSL sockets are not
76 // reused after shutdown for non-SSL traffic, so overreading is fine.
77 DCHECK(!read_buffer_);
78 DCHECK_EQ(0, read_offset_);
79 read_buffer_ = new IOBuffer(read_buffer_capacity_);
80 int result = socket_->Read(read_buffer_.get(), read_buffer_capacity_,
81 read_callback_);
82 if (result == ERR_IO_PENDING) {
83 read_result_ = ERR_IO_PENDING;
84 } else {
85 HandleSocketReadResult(result);
86 }
87 }
88
89 // There is a pending Read(). Inform the caller to retry when it completes.
90 if (read_result_ == ERR_IO_PENDING) {
91 BIO_set_retry_read(bio());
92 return -1;
93 }
94
95 // If the last Read() failed, report the error.
96 if (read_result_ < 0) {
97 OpenSSLPutNetError(FROM_HERE, read_result_);
98 return -1;
99 }
100
101 // Report the result of the last Read() if non-empty.
102 CHECK_LT(read_offset_, read_result_);
103 len = std::min(len, read_result_ - read_offset_);
104 memcpy(out, read_buffer_->data() + read_offset_, len);
105 read_offset_ += len;
106
107 // Release the buffer when empty.
108 if (read_offset_ == read_result_) {
109 read_buffer_ = nullptr;
110 read_offset_ = 0;
111 read_result_ = 0;
112 }
113
114 return len;
115 }
116
117 void SocketBIOAdapter::HandleSocketReadResult(int result) {
118 DCHECK_NE(ERR_IO_PENDING, result);
119
120 // If an EOF, canonicalize to ERR_CONNECTION_CLOSED here, so that higher
121 // levels don't report success.
122 if (result == 0)
123 result = ERR_CONNECTION_CLOSED;
124
125 read_result_ = result;
126
127 // The read buffer is no longer needed.
128 if (read_result_ <= 0)
129 read_buffer_ = nullptr;
130 }
131
132 void SocketBIOAdapter::OnSocketReadComplete(int result) {
133 DCHECK_EQ(ERR_IO_PENDING, read_result_);
134
135 HandleSocketReadResult(result);
136 delegate_->OnReadReady();
137 }
138
139 int SocketBIOAdapter::BIOWrite(const char* in, int len) {
140 if (len <= 0)
141 return len;
142
143 // If the write buffer is not empty, there must be a pending Write() to flush
144 // it.
145 DCHECK(write_buffer_used_ == 0 || write_error_ == ERR_IO_PENDING);
146
147 // If a previous Write() failed, report the error.
148 if (write_error_ != OK && write_error_ != ERR_IO_PENDING) {
149 OpenSSLPutNetError(FROM_HERE, write_error_);
150 return -1;
151 }
152
153 // Instantiate the write buffer if needed.
154 if (!write_buffer_) {
155 DCHECK_EQ(0, write_buffer_used_);
156 write_buffer_ = new GrowableIOBuffer;
157 write_buffer_->SetCapacity(write_buffer_capacity_);
158 }
159
160 // If the ring buffer is full, inform the caller to try again later.
161 if (write_buffer_used_ == write_buffer_->capacity()) {
162 BIO_set_retry_write(bio());
163 return -1;
164 }
165
166 len = std::min(len, write_buffer_->capacity() - write_buffer_used_);
167
168 int to_copy = len;
169 while (to_copy > 0) {
170 // Determine where to write.
171 int write_offset = write_buffer_->offset() + write_buffer_used_;
172 if (write_offset >= write_buffer_->capacity())
173 write_offset -= write_buffer_->capacity();
Ryan Sleevi 2016/10/18 01:30:17 This approach makes me *super* nervous from a secu
davidben 2016/10/18 20:49:11 Every way to write this involves different subtle
174 CHECK_LE(0, write_offset);
175 CHECK_LT(write_offset, write_buffer_->capacity());
176
177 // Determine how many bytes may be written.
178 int chunk = (write_offset < write_buffer_->offset())
179 ? write_buffer_->offset() - write_offset
180 : write_buffer_->capacity() - write_offset;
181 chunk = std::min(chunk, to_copy);
182
183 memcpy(write_buffer_->StartOfBuffer() + write_offset, in, chunk);
184 in += chunk;
185 to_copy -= chunk;
186 write_buffer_used_ += chunk;
187 }
188
189 // Schedule a socket Write() if necessary. (The ring buffer may previously
190 // have been empty.)
191 SocketWrite();
192
193 // If a read-interrupting write error was synchronously discovered,
194 // asynchronously notify OnReadReady. See https://crbug.com/249848. Avoid
195 // reentrancy by deferring it to a later event loop iteration.
196 if (write_error_ != OK && write_error_ != ERR_IO_PENDING &&
197 read_result_ == ERR_IO_PENDING) {
198 base::ThreadTaskRunnerHandle::Get()->PostTask(
199 FROM_HERE, base::Bind(&SocketBIOAdapter::CallOnReadReady,
200 weak_factory_.GetWeakPtr()));
201 }
202
203 return len;
204 }
205
206 void SocketBIOAdapter::SocketWrite() {
207 while (write_error_ == OK && write_buffer_used_ > 0) {
208 int write_size =
209 std::min(write_buffer_used_, write_buffer_->RemainingCapacity());
210 int result =
211 socket_->Write(write_buffer_.get(), write_size, write_callback_);
212 if (result == ERR_IO_PENDING) {
213 write_error_ = ERR_IO_PENDING;
214 return;
215 }
216
217 HandleSocketWriteResult(result);
218 }
219 }
220
221 void SocketBIOAdapter::HandleSocketWriteResult(int result) {
222 DCHECK_NE(ERR_IO_PENDING, result);
223
224 if (result < 0) {
225 write_error_ = result;
226
227 // The write buffer is no longer needed.
228 write_buffer_ = nullptr;
229 write_buffer_used_ = 0;
230 return;
231 }
232
233 // Advance the ring buffer.
234 write_buffer_->set_offset(write_buffer_->offset() + result);
235 write_buffer_used_ -= result;
236 if (write_buffer_->RemainingCapacity() == 0)
237 write_buffer_->set_offset(0);
238 write_error_ = OK;
239
240 // Release the write buffer if empty.
241 if (write_buffer_used_ == 0)
242 write_buffer_ = nullptr;
243 }
244
245 void SocketBIOAdapter::OnSocketWriteComplete(int result) {
246 DCHECK_EQ(ERR_IO_PENDING, write_error_);
247
248 bool was_full = write_buffer_used_ == write_buffer_->capacity();
249
250 HandleSocketWriteResult(result);
251 SocketWrite();
252
253 // If transitioning from being unable to accept data to being able to, signal
254 // OnWriteReady.
255 if (was_full) {
256 base::WeakPtr<SocketBIOAdapter> guard(weak_factory_.GetWeakPtr());
257 delegate_->OnWriteReady();
258 // OnWriteReady may delete the adapter.
259 if (!guard)
260 return;
261 }
262
263 // Write errors are fed back into BIO_read once the read buffer is empty. If
264 // BIO_read is currently blocked, signal early that a read result is ready.
265 if (result < 0 && read_result_ == ERR_IO_PENDING)
266 delegate_->OnReadReady();
267 }
268
269 void SocketBIOAdapter::CallOnReadReady() {
270 if (read_result_ == ERR_IO_PENDING)
271 delegate_->OnReadReady();
272 }
273
274 SocketBIOAdapter* SocketBIOAdapter::GetAdapter(BIO* bio) {
275 DCHECK_EQ(&kBIOMethod, bio->method);
276 SocketBIOAdapter* adapter = reinterpret_cast<SocketBIOAdapter*>(bio->ptr);
277 if (adapter)
278 DCHECK_EQ(bio, adapter->bio());
279 return adapter;
280 }
281
282 int SocketBIOAdapter::BIOWriteWrapper(BIO* bio, const char* in, int len) {
283 BIO_clear_retry_flags(bio);
284
285 SocketBIOAdapter* adapter = GetAdapter(bio);
286 if (!adapter) {
287 OpenSSLPutNetError(FROM_HERE, ERR_UNEXPECTED);
288 return -1;
289 }
290
291 return adapter->BIOWrite(in, len);
292 }
293
294 int SocketBIOAdapter::BIOReadWrapper(BIO* bio, char* out, int len) {
295 BIO_clear_retry_flags(bio);
296
297 SocketBIOAdapter* adapter = GetAdapter(bio);
298 if (!adapter) {
299 OpenSSLPutNetError(FROM_HERE, ERR_UNEXPECTED);
300 return -1;
301 }
302
303 return adapter->BIORead(out, len);
304 }
305
306 long SocketBIOAdapter::BIOCtrlWrapper(BIO* bio,
307 int cmd,
308 long larg,
309 void* parg) {
310 switch (cmd) {
311 case BIO_CTRL_FLUSH:
312 // The SSL stack requires BIOs handle BIO_flush.
313 return 1;
314 }
315
316 NOTIMPLEMENTED();
317 return 0;
318 }
319
320 const BIO_METHOD SocketBIOAdapter::kBIOMethod = {
321 0, // type (unused)
322 nullptr, // name (unused)
323 SocketBIOAdapter::BIOWriteWrapper,
324 SocketBIOAdapter::BIOReadWrapper,
325 nullptr, // puts
326 nullptr, // gets
327 SocketBIOAdapter::BIOCtrlWrapper,
328 nullptr, // create
329 nullptr, // destroy
330 nullptr, // callback_ctrl
331 };
332
333 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698