blob: 6b786a184b680e5460f4a07fbf1ea78b3250c5ef [file] [log] [blame]
// Copyright 2024 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/tcp_stream_attempt.h"
#include <memory>
#include <string_view>
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "net/base/address_list.h"
#include "net/base/net_errors.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/socket_performance_watcher.h"
#include "net/socket/socket_performance_watcher_factory.h"
#include "net/socket/transport_client_socket.h"
namespace net {
// static
std::string_view TcpStreamAttempt::StateToString(State state) {
switch (state) {
case State::kNone:
return "None";
case State::kConnecting:
return "Connecting";
}
}
TcpStreamAttempt::TcpStreamAttempt(const StreamAttemptParams* params,
IPEndPoint ip_endpoint,
perfetto::Track track,
const NetLogWithSource* net_log)
: StreamAttempt(params,
ip_endpoint,
track,
NetLogSourceType::TCP_STREAM_ATTEMPT,
NetLogEventType::TCP_STREAM_ATTEMPT_ALIVE,
net_log) {}
TcpStreamAttempt::~TcpStreamAttempt() {
MaybeRecordConnectEnd(ERR_ABORTED);
}
LoadState TcpStreamAttempt::GetLoadState() const {
switch (next_state_) {
case State::kNone:
return LOAD_STATE_IDLE;
case State::kConnecting:
return LOAD_STATE_CONNECTING;
}
}
base::Value::Dict TcpStreamAttempt::GetInfoAsValue() const {
base::Value::Dict dict;
dict.Set("next_state", StateToString(next_state_));
return dict;
}
int TcpStreamAttempt::StartInternal() {
next_state_ = State::kConnecting;
std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher;
if (params().socket_performance_watcher_factory) {
socket_performance_watcher =
params()
.socket_performance_watcher_factory->CreateSocketPerformanceWatcher(
SocketPerformanceWatcherFactory::PROTOCOL_TCP,
ip_endpoint().address());
}
std::unique_ptr<TransportClientSocket> stream_socket =
params().client_socket_factory->CreateTransportClientSocket(
AddressList(ip_endpoint()), std::move(socket_performance_watcher),
params().network_quality_estimator, net_log().net_log(),
net_log().source());
TransportClientSocket* socket_ptr = stream_socket.get();
SetStreamSocket(std::move(stream_socket));
mutable_connect_timing().connect_start = base::TimeTicks::Now();
CHECK(!timeout_timer_.IsRunning());
timeout_timer_.Start(
FROM_HERE, kTcpHandshakeTimeout,
base::BindOnce(&TcpStreamAttempt::OnTimeout, base::Unretained(this)));
TRACE_EVENT_BEGIN("net.stream", "TcpConnect", track());
net_log().AddEventReferencingSource(
NetLogEventType::TCP_STREAM_ATTEMPT_CONNECT,
socket_ptr->NetLog().source());
int rv = socket_ptr->Connect(
base::BindOnce(&TcpStreamAttempt::OnIOComplete, base::Unretained(this)));
if (rv != ERR_IO_PENDING) {
HandleCompletion(rv);
}
return rv;
}
base::Value::Dict TcpStreamAttempt::GetNetLogStartParams() {
base::Value::Dict dict;
dict.Set("ip_endpoint", ip_endpoint().ToString());
return dict;
}
void TcpStreamAttempt::HandleCompletion(int rv) {
MaybeRecordConnectEnd(rv);
next_state_ = State::kNone;
timeout_timer_.Stop();
mutable_connect_timing().connect_end = base::TimeTicks::Now();
}
void TcpStreamAttempt::OnIOComplete(int rv) {
CHECK_NE(rv, ERR_IO_PENDING);
HandleCompletion(rv);
NotifyOfCompletion(rv);
}
void TcpStreamAttempt::OnTimeout() {
SetStreamSocket(nullptr);
// TODO(bashi): The error code should be ERR_CONNECTION_TIMED_OUT but use
// ERR_TIMED_OUT for consistency with ConnectJobs.
OnIOComplete(ERR_TIMED_OUT);
}
void TcpStreamAttempt::MaybeRecordConnectEnd(int rv) {
if (!timeout_timer_.IsRunning()) {
return;
}
TRACE_EVENT_END("net.stream", track(), "result", rv);
}
} // namespace net