summaryrefslogtreecommitdiff
path: root/net/socket/socket_bio_adapter.cc
diff options
context:
space:
mode:
authorCronet Mainline Eng <cronet-mainline-eng+copybara@google.com>2023-03-20 09:24:50 -0800
committerPatrick Rohr <prohr@google.com>2023-03-20 10:25:51 -0700
commit14c9064f78517fd0e9366547030c0493aa075b47 (patch)
tree6e03046ec4055bb9881ff0341716266b5d53782b /net/socket/socket_bio_adapter.cc
parentd1add53d6e90815f363c91d433735556ce79b0d2 (diff)
downloadcronet-14c9064f78517fd0e9366547030c0493aa075b47.tar.gz
Import Cronet version 108.0.5359.128
Project import generated by Copybara. FolderOrigin-RevId: /tmp/copybara-origin/src Test: none Change-Id: I98ebcd5784650764c7cd70ab175dd4e1cc790dff
Diffstat (limited to 'net/socket/socket_bio_adapter.cc')
-rw-r--r--net/socket/socket_bio_adapter.cc399
1 files changed, 399 insertions, 0 deletions
diff --git a/net/socket/socket_bio_adapter.cc b/net/socket/socket_bio_adapter.cc
new file mode 100644
index 000000000..014be2d2d
--- /dev/null
+++ b/net/socket/socket_bio_adapter.cc
@@ -0,0 +1,399 @@
+// Copyright 2016 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/socket/socket_bio_adapter.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/check_op.h"
+#include "base/location.h"
+#include "base/notreached.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/socket/socket.h"
+#include "net/socket/stream_socket.h"
+#include "net/ssl/openssl_ssl_util.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "third_party/boringssl/src/include/openssl/bio.h"
+
+namespace {
+
+const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+ net::DefineNetworkTrafficAnnotation("socket_bio_adapter", R"(
+ semantics {
+ sender: "Socket BIO Adapter"
+ description:
+ "SocketBIOAdapter is used only internal to //net code as an internal "
+ "detail to implement a TLS connection for a Socket class, and is not "
+ "being called directly outside of this abstraction."
+ trigger:
+ "Establishing a TLS connection to a remote endpoint. There are many "
+ "different ways in which a TLS connection may be triggered, such as "
+ "loading an HTTPS URL."
+ data:
+ "All data sent or received over a TLS connection. This traffic may "
+ "either be the handshake or application data. During the handshake, "
+ "the target host name, user's IP, data related to previous "
+ "handshake, client certificates, and channel ID, may be sent. When "
+ "the connection is used to load an HTTPS URL, the application data "
+ "includes cookies, request headers, and the response body."
+ destination: OTHER
+ destination_other:
+ "Any destination the implementing socket is connected to."
+ }
+ policy {
+ cookies_allowed: NO
+ setting: "This feature cannot be disabled."
+ policy_exception_justification: "Essential for navigation."
+ })");
+
+} // namespace
+
+namespace net {
+
+SocketBIOAdapter::SocketBIOAdapter(StreamSocket* socket,
+ int read_buffer_capacity,
+ int write_buffer_capacity,
+ Delegate* delegate)
+ : socket_(socket),
+ read_buffer_capacity_(read_buffer_capacity),
+ write_buffer_capacity_(write_buffer_capacity),
+ delegate_(delegate) {
+ bio_.reset(BIO_new(&kBIOMethod));
+ bio_->ptr = this;
+ bio_->init = 1;
+
+ read_callback_ = base::BindRepeating(&SocketBIOAdapter::OnSocketReadComplete,
+ weak_factory_.GetWeakPtr());
+ write_callback_ = base::BindRepeating(
+ &SocketBIOAdapter::OnSocketWriteComplete, weak_factory_.GetWeakPtr());
+}
+
+SocketBIOAdapter::~SocketBIOAdapter() {
+ // BIOs are reference-counted and may outlive the adapter. Clear the pointer
+ // so future operations fail.
+ bio_->ptr = nullptr;
+}
+
+bool SocketBIOAdapter::HasPendingReadData() {
+ return read_result_ > 0;
+}
+
+size_t SocketBIOAdapter::GetAllocationSize() const {
+ size_t buffer_size = 0;
+ if (read_buffer_)
+ buffer_size += read_buffer_capacity_;
+
+ if (write_buffer_)
+ buffer_size += write_buffer_capacity_;
+ return buffer_size;
+}
+
+int SocketBIOAdapter::BIORead(char* out, int len) {
+ if (len <= 0)
+ return len;
+
+ // If there is no result available synchronously, report any Write() errors
+ // that were observed. Otherwise the application may have encountered a socket
+ // error while writing that would otherwise not be reported until the
+ // application attempted to write again - which it may never do. See
+ // https://crbug.com/249848.
+ if (write_error_ != OK && write_error_ != ERR_IO_PENDING &&
+ (read_result_ == 0 || read_result_ == ERR_IO_PENDING)) {
+ OpenSSLPutNetError(FROM_HERE, write_error_);
+ return -1;
+ }
+
+ if (read_result_ == 0) {
+ // Instantiate the read buffer and read from the socket. Although only |len|
+ // bytes were requested, intentionally read to the full buffer size. The SSL
+ // layer reads the record header and body in separate reads to avoid
+ // overreading, but issuing one is more efficient. SSL sockets are not
+ // reused after shutdown for non-SSL traffic, so overreading is fine.
+ DCHECK(!read_buffer_);
+ DCHECK_EQ(0, read_offset_);
+ read_buffer_ = base::MakeRefCounted<IOBuffer>(read_buffer_capacity_);
+ int result = socket_->ReadIfReady(
+ read_buffer_.get(), read_buffer_capacity_,
+ base::BindOnce(&SocketBIOAdapter::OnSocketReadIfReadyComplete,
+ weak_factory_.GetWeakPtr()));
+ if (result == ERR_IO_PENDING)
+ read_buffer_ = nullptr;
+ if (result == ERR_READ_IF_READY_NOT_IMPLEMENTED) {
+ result = socket_->Read(read_buffer_.get(), read_buffer_capacity_,
+ read_callback_);
+ }
+ if (result == ERR_IO_PENDING) {
+ read_result_ = ERR_IO_PENDING;
+ } else {
+ HandleSocketReadResult(result);
+ }
+ }
+
+ // There is a pending Read(). Inform the caller to retry when it completes.
+ if (read_result_ == ERR_IO_PENDING) {
+ BIO_set_retry_read(bio());
+ return -1;
+ }
+
+ // If the last Read() failed, report the error.
+ if (read_result_ < 0) {
+ OpenSSLPutNetError(FROM_HERE, read_result_);
+ return -1;
+ }
+
+ // Report the result of the last Read() if non-empty.
+ CHECK_LT(read_offset_, read_result_);
+ len = std::min(len, read_result_ - read_offset_);
+ memcpy(out, read_buffer_->data() + read_offset_, len);
+ read_offset_ += len;
+
+ // Release the buffer when empty.
+ if (read_offset_ == read_result_) {
+ read_buffer_ = nullptr;
+ read_offset_ = 0;
+ read_result_ = 0;
+ }
+
+ return len;
+}
+
+void SocketBIOAdapter::HandleSocketReadResult(int result) {
+ DCHECK_NE(ERR_IO_PENDING, result);
+
+ // If an EOF, canonicalize to ERR_CONNECTION_CLOSED here, so that higher
+ // levels don't report success.
+ if (result == 0)
+ result = ERR_CONNECTION_CLOSED;
+
+ read_result_ = result;
+
+ // The read buffer is no longer needed.
+ if (read_result_ <= 0)
+ read_buffer_ = nullptr;
+}
+
+void SocketBIOAdapter::OnSocketReadComplete(int result) {
+ DCHECK_EQ(ERR_IO_PENDING, read_result_);
+
+ HandleSocketReadResult(result);
+ delegate_->OnReadReady();
+}
+
+void SocketBIOAdapter::OnSocketReadIfReadyComplete(int result) {
+ DCHECK_EQ(ERR_IO_PENDING, read_result_);
+ DCHECK_GE(OK, result);
+
+ // Do not use HandleSocketReadResult() because result == OK doesn't mean EOF.
+ read_result_ = result;
+
+ delegate_->OnReadReady();
+}
+
+int SocketBIOAdapter::BIOWrite(const char* in, int len) {
+ if (len <= 0)
+ return len;
+
+ // If the write buffer is not empty, there must be a pending Write() to flush
+ // it.
+ DCHECK(write_buffer_used_ == 0 || write_error_ == ERR_IO_PENDING);
+
+ // If a previous Write() failed, report the error.
+ if (write_error_ != OK && write_error_ != ERR_IO_PENDING) {
+ OpenSSLPutNetError(FROM_HERE, write_error_);
+ return -1;
+ }
+
+ // Instantiate the write buffer if needed.
+ if (!write_buffer_) {
+ DCHECK_EQ(0, write_buffer_used_);
+ write_buffer_ = base::MakeRefCounted<GrowableIOBuffer>();
+ write_buffer_->SetCapacity(write_buffer_capacity_);
+ }
+
+ // If the ring buffer is full, inform the caller to try again later.
+ if (write_buffer_used_ == write_buffer_->capacity()) {
+ BIO_set_retry_write(bio());
+ return -1;
+ }
+
+ int bytes_copied = 0;
+
+ // If there is space after the offset, fill it.
+ if (write_buffer_used_ < write_buffer_->RemainingCapacity()) {
+ int chunk =
+ std::min(write_buffer_->RemainingCapacity() - write_buffer_used_, len);
+ memcpy(write_buffer_->data() + write_buffer_used_, in, chunk);
+ in += chunk;
+ len -= chunk;
+ bytes_copied += chunk;
+ write_buffer_used_ += chunk;
+ }
+
+ // If there is still space for remaining data, try to wrap around.
+ if (len > 0 && write_buffer_used_ < write_buffer_->capacity()) {
+ // If there were any room after the offset, the previous branch would have
+ // filled it.
+ CHECK_LE(write_buffer_->RemainingCapacity(), write_buffer_used_);
+ int write_offset = write_buffer_used_ - write_buffer_->RemainingCapacity();
+ int chunk = std::min(len, write_buffer_->capacity() - write_buffer_used_);
+ memcpy(write_buffer_->StartOfBuffer() + write_offset, in, chunk);
+ in += chunk;
+ len -= chunk;
+ bytes_copied += chunk;
+ write_buffer_used_ += chunk;
+ }
+
+ // Either the buffer is now full or there is no more input.
+ DCHECK(len == 0 || write_buffer_used_ == write_buffer_->capacity());
+
+ // Schedule a socket Write() if necessary. (The ring buffer may previously
+ // have been empty.)
+ SocketWrite();
+
+ // If a read-interrupting write error was synchronously discovered,
+ // asynchronously notify OnReadReady. See https://crbug.com/249848. Avoid
+ // reentrancy by deferring it to a later event loop iteration.
+ if (write_error_ != OK && write_error_ != ERR_IO_PENDING &&
+ read_result_ == ERR_IO_PENDING) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&SocketBIOAdapter::CallOnReadReady,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ return bytes_copied;
+}
+
+void SocketBIOAdapter::SocketWrite() {
+ while (write_error_ == OK && write_buffer_used_ > 0) {
+ int write_size =
+ std::min(write_buffer_used_, write_buffer_->RemainingCapacity());
+ int result = socket_->Write(write_buffer_.get(), write_size,
+ write_callback_, kTrafficAnnotation);
+ if (result == ERR_IO_PENDING) {
+ write_error_ = ERR_IO_PENDING;
+ return;
+ }
+
+ HandleSocketWriteResult(result);
+ }
+}
+
+void SocketBIOAdapter::HandleSocketWriteResult(int result) {
+ DCHECK_NE(ERR_IO_PENDING, result);
+
+ if (result < 0) {
+ write_error_ = result;
+
+ // The write buffer is no longer needed.
+ write_buffer_ = nullptr;
+ write_buffer_used_ = 0;
+ return;
+ }
+
+ // Advance the ring buffer.
+ write_buffer_->set_offset(write_buffer_->offset() + result);
+ write_buffer_used_ -= result;
+ if (write_buffer_->RemainingCapacity() == 0)
+ write_buffer_->set_offset(0);
+ write_error_ = OK;
+
+ // Release the write buffer if empty.
+ if (write_buffer_used_ == 0)
+ write_buffer_ = nullptr;
+}
+
+void SocketBIOAdapter::OnSocketWriteComplete(int result) {
+ DCHECK_EQ(ERR_IO_PENDING, write_error_);
+
+ bool was_full = write_buffer_used_ == write_buffer_->capacity();
+
+ HandleSocketWriteResult(result);
+ SocketWrite();
+
+ // If transitioning from being unable to accept data to being able to, signal
+ // OnWriteReady.
+ if (was_full) {
+ base::WeakPtr<SocketBIOAdapter> guard(weak_factory_.GetWeakPtr());
+ delegate_->OnWriteReady();
+ // OnWriteReady may delete the adapter.
+ if (!guard)
+ return;
+ }
+
+ // Write errors are fed back into BIO_read once the read buffer is empty. If
+ // BIO_read is currently blocked, signal early that a read result is ready.
+ if (result < 0 && read_result_ == ERR_IO_PENDING)
+ delegate_->OnReadReady();
+}
+
+void SocketBIOAdapter::CallOnReadReady() {
+ if (read_result_ == ERR_IO_PENDING)
+ delegate_->OnReadReady();
+}
+
+SocketBIOAdapter* SocketBIOAdapter::GetAdapter(BIO* bio) {
+ DCHECK_EQ(&kBIOMethod, bio->method);
+ SocketBIOAdapter* adapter = reinterpret_cast<SocketBIOAdapter*>(bio->ptr);
+ if (adapter)
+ DCHECK_EQ(bio, adapter->bio());
+ return adapter;
+}
+
+int SocketBIOAdapter::BIOWriteWrapper(BIO* bio, const char* in, int len) {
+ BIO_clear_retry_flags(bio);
+
+ SocketBIOAdapter* adapter = GetAdapter(bio);
+ if (!adapter) {
+ OpenSSLPutNetError(FROM_HERE, ERR_UNEXPECTED);
+ return -1;
+ }
+
+ return adapter->BIOWrite(in, len);
+}
+
+int SocketBIOAdapter::BIOReadWrapper(BIO* bio, char* out, int len) {
+ BIO_clear_retry_flags(bio);
+
+ SocketBIOAdapter* adapter = GetAdapter(bio);
+ if (!adapter) {
+ OpenSSLPutNetError(FROM_HERE, ERR_UNEXPECTED);
+ return -1;
+ }
+
+ return adapter->BIORead(out, len);
+}
+
+long SocketBIOAdapter::BIOCtrlWrapper(BIO* bio,
+ int cmd,
+ long larg,
+ void* parg) {
+ switch (cmd) {
+ case BIO_CTRL_FLUSH:
+ // The SSL stack requires BIOs handle BIO_flush.
+ return 1;
+ }
+
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+const BIO_METHOD SocketBIOAdapter::kBIOMethod = {
+ 0, // type (unused)
+ nullptr, // name (unused)
+ SocketBIOAdapter::BIOWriteWrapper,
+ SocketBIOAdapter::BIOReadWrapper,
+ nullptr, // puts
+ nullptr, // gets
+ SocketBIOAdapter::BIOCtrlWrapper,
+ nullptr, // create
+ nullptr, // destroy
+ nullptr, // callback_ctrl
+};
+
+} // namespace net