blob: 23c2dad8ce74dbe04c7369e7ffd331f52895abfe [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/openscreen_platform/tls_client_connection.h"
#include <algorithm>
#include <limits>
#include <sstream>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/numerics/safe_conversions.h"
namespace openscreen_platform {
using openscreen::Error;
TlsClientConnection::TlsClientConnection(
openscreen::IPEndpoint local_address,
openscreen::IPEndpoint remote_address,
mojo::ScopedDataPipeConsumerHandle receive_stream,
mojo::ScopedDataPipeProducerHandle send_stream,
mojo::Remote<network::mojom::TCPConnectedSocket> tcp_socket,
mojo::Remote<network::mojom::TLSClientSocket> tls_socket)
: local_address_(std::move(local_address)),
remote_address_(std::move(remote_address)),
receive_stream_(std::move(receive_stream)),
send_stream_(std::move(send_stream)),
tcp_socket_(std::move(tcp_socket)),
tls_socket_(std::move(tls_socket)),
receive_stream_watcher_(FROM_HERE,
mojo::SimpleWatcher::ArmingPolicy::MANUAL) {
if (receive_stream_.is_valid()) {
receive_stream_watcher_.Watch(
receive_stream_.get(),
MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
base::BindRepeating(&TlsClientConnection::ReceiveMore,
base::Unretained(this)));
receive_stream_watcher_.ArmOrNotify();
}
}
TlsClientConnection::~TlsClientConnection() = default;
void TlsClientConnection::SetClient(Client* client) {
client_ = client;
}
bool TlsClientConnection::Send(openscreen::ByteView data) {
if (!send_stream_.is_valid()) {
if (client_) {
client_->OnError(this, Error(Error::Code::kSocketClosedFailure,
"Send stream was closed."));
}
return false;
}
const MojoResult result = send_stream_->WriteAllData(data);
mojo::HandleSignalsState state = send_stream_->QuerySignalsState();
return ProcessMojoResult(result, state.peer_closed()
? Error::Code::kSocketClosedFailure
: Error::Code::kSocketSendFailure) ==
Error::Code::kNone;
}
openscreen::IPEndpoint TlsClientConnection::GetRemoteEndpoint() const {
return remote_address_;
}
void TlsClientConnection::ReceiveMore(MojoResult result,
const mojo::HandleSignalsState& state) {
if (!receive_stream_.is_valid()) {
if (client_) {
client_->OnError(this, Error(Error::Code::kSocketClosedFailure,
"Receive stream was closed."));
}
return;
}
if (result == MOJO_RESULT_OK) {
size_t num_bytes = 0;
result = receive_stream_->ReadData(MOJO_READ_DATA_FLAG_QUERY,
base::span<uint8_t>(), num_bytes);
if (result == MOJO_RESULT_OK) {
num_bytes = std::min(num_bytes, kMaxBytesPerRead);
std::vector<uint8_t> buffer(num_bytes);
result = receive_stream_->ReadData(MOJO_READ_DATA_FLAG_NONE, buffer,
num_bytes);
if (result == MOJO_RESULT_OK && client_) {
buffer.resize(num_bytes);
client_->OnRead(this, std::move(buffer));
}
}
}
const Error::Code interpretation = ProcessMojoResult(
result, state.peer_closed() ? Error::Code::kSocketClosedFailure
: Error::Code::kSocketReadFailure);
if (interpretation == Error::Code::kNone ||
interpretation == Error::Code::kAgain) {
receive_stream_watcher_.ArmOrNotify();
}
}
Error::Code TlsClientConnection::ProcessMojoResult(
MojoResult result,
Error::Code error_code_if_fatal) {
switch (result) {
case MOJO_RESULT_OK:
return Error::Code::kNone;
case MOJO_RESULT_UNAVAILABLE:
case MOJO_RESULT_OUT_OF_RANGE: // Cannot write all data (all-or-none mode).
case MOJO_RESULT_BUSY:
case MOJO_RESULT_SHOULD_WAIT:
// Transient (i.e., "try again") errors.
return Error::Code::kAgain;
default:
// Fatal errors.
if (client_) {
std::ostringstream error_message;
error_message << "MojoResult: " << result;
client_->OnError(this, Error(error_code_if_fatal, error_message.str()));
}
return error_code_if_fatal;
}
}
// static
constexpr size_t TlsClientConnection::kMaxBytesPerRead;
} // namespace openscreen_platform