Direct Sockets API: add keepAlive option to openTCPSocket.
Allow users to set the keep-alive feature on socket opening and separate the idl definition for TCPSocketOptions from UDPSocketOptions while preserving a common base (SocketOptions). Updated doc: https://github.com/WICG/direct-sockets/pull/38 Bug: 1124249 Change-Id: I54f6c991ab7554f422fc37b21af03aeb250af438 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3348618 Reviewed-by: Greg Kerr <kerrnel@chromium.org> Reviewed-by: Matt Menke <mmenke@chromium.org> Reviewed-by: Eric Willigers <ericwilligers@chromium.org> Auto-Submit: Andrew Rayskiy <greengrape@google.com> Commit-Queue: Andrew Rayskiy <greengrape@google.com> Cr-Commit-Position: refs/heads/main@{#956511}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
eebb8833d0
commit
7d6de24e45
content/browser/direct_sockets
net/socket
services/network
third_party/blink
public
mojom
direct_sockets
renderer
bindings
modules
@ -62,6 +62,8 @@ struct RecordedCall {
|
||||
int32_t receive_buffer_size = 0;
|
||||
|
||||
bool no_delay = false;
|
||||
|
||||
network::mojom::TCPKeepAliveOptionsPtr keep_alive_options;
|
||||
};
|
||||
|
||||
constexpr char kPermissionDeniedHistogramName[] =
|
||||
@ -233,11 +235,13 @@ class MockNetworkContext : public network::TestNetworkContext {
|
||||
mojo::PendingRemote<network::mojom::SocketObserver> observer,
|
||||
CreateTCPConnectedSocketCallback callback) override {
|
||||
const net::IPEndPoint& peer_addr = remote_addr_list.front();
|
||||
Record(RecordedCall{DirectSocketsServiceImpl::ProtocolType::kTcp,
|
||||
peer_addr.address().ToString(), peer_addr.port(),
|
||||
tcp_connected_socket_options->send_buffer_size,
|
||||
tcp_connected_socket_options->receive_buffer_size,
|
||||
tcp_connected_socket_options->no_delay});
|
||||
Record(RecordedCall{
|
||||
DirectSocketsServiceImpl::ProtocolType::kTcp,
|
||||
peer_addr.address().ToString(), peer_addr.port(),
|
||||
tcp_connected_socket_options->send_buffer_size,
|
||||
tcp_connected_socket_options->receive_buffer_size,
|
||||
tcp_connected_socket_options->no_delay,
|
||||
std::move(tcp_connected_socket_options->keep_alive_options)});
|
||||
|
||||
mojo::ScopedDataPipeProducerHandle producer;
|
||||
mojo::ScopedDataPipeConsumerHandle consumer;
|
||||
@ -316,11 +320,13 @@ class MockUDPSocket : public network::mojom::UDPSocket {
|
||||
const net::Error result = (remote_addr.port() == 0)
|
||||
? net::ERR_INVALID_ARGUMENT
|
||||
: network_context_->result();
|
||||
network_context_->Record(RecordedCall{
|
||||
DirectSocketsServiceImpl::ProtocolType::kUdp,
|
||||
remote_addr.address().ToString(), remote_addr.port(),
|
||||
socket_options->send_buffer_size, socket_options->receive_buffer_size,
|
||||
/*no_delay=*/false});
|
||||
network_context_->Record(
|
||||
RecordedCall{DirectSocketsServiceImpl::ProtocolType::kUdp,
|
||||
remote_addr.address().ToString(),
|
||||
remote_addr.port(),
|
||||
socket_options->send_buffer_size,
|
||||
socket_options->receive_buffer_size,
|
||||
{}});
|
||||
|
||||
base::SequencedTaskRunnerHandle::Get()->PostTask(
|
||||
FROM_HERE, base::BindOnce(std::move(callback), result,
|
||||
@ -650,6 +656,7 @@ IN_PROC_BROWSER_TEST_F(DirectSocketsOpenBrowserTest, OpenTcp_OptionsOne) {
|
||||
EXPECT_EQ(3456, call.send_buffer_size);
|
||||
EXPECT_EQ(7890, call.receive_buffer_size);
|
||||
EXPECT_EQ(false, call.no_delay);
|
||||
EXPECT_FALSE(call.keep_alive_options);
|
||||
|
||||
// To sync histograms from renderer.
|
||||
FetchHistogramsFromChildProcesses();
|
||||
@ -674,7 +681,9 @@ IN_PROC_BROWSER_TEST_F(DirectSocketsOpenBrowserTest, OpenTcp_OptionsTwo) {
|
||||
remotePort: 789,
|
||||
sendBufferSize: 0,
|
||||
receiveBufferSize: 1234,
|
||||
noDelay: true
|
||||
noDelay: true,
|
||||
keepAlive: true,
|
||||
keepAliveDelay: 100_000
|
||||
})
|
||||
)";
|
||||
EXPECT_THAT(EvalJs(shell(), script).ExtractString(),
|
||||
@ -688,6 +697,44 @@ IN_PROC_BROWSER_TEST_F(DirectSocketsOpenBrowserTest, OpenTcp_OptionsTwo) {
|
||||
EXPECT_EQ(0, call.send_buffer_size);
|
||||
EXPECT_EQ(1234, call.receive_buffer_size);
|
||||
EXPECT_EQ(true, call.no_delay);
|
||||
EXPECT_TRUE(call.keep_alive_options);
|
||||
EXPECT_EQ(true, call.keep_alive_options->enable);
|
||||
EXPECT_EQ(100U, call.keep_alive_options->delay);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(DirectSocketsOpenBrowserTest, OpenTcp_OptionsThree) {
|
||||
EXPECT_TRUE(NavigateToURL(shell(), GetTestOpenPageURL()));
|
||||
|
||||
DirectSocketsServiceImpl::SetPermissionCallbackForTesting(
|
||||
base::BindRepeating(&UnconditionallyPermitConnection));
|
||||
|
||||
MockNetworkContext mock_network_context(net::OK);
|
||||
DirectSocketsServiceImpl::SetNetworkContextForTesting(&mock_network_context);
|
||||
|
||||
const std::string script =
|
||||
R"(
|
||||
openTcp({
|
||||
remoteAddress: 'fedc:ba98:7654:3210:fedc:ba98:7654:3210',
|
||||
remotePort: 789,
|
||||
sendBufferSize: 0,
|
||||
receiveBufferSize: 1234,
|
||||
noDelay: true,
|
||||
keepAlive: false
|
||||
})
|
||||
)";
|
||||
EXPECT_THAT(EvalJs(shell(), script).ExtractString(),
|
||||
StartsWith("openTcp succeeded"));
|
||||
|
||||
DCHECK_EQ(1U, mock_network_context.history().size());
|
||||
const RecordedCall& call = mock_network_context.history()[0];
|
||||
EXPECT_EQ(DirectSocketsServiceImpl::ProtocolType::kTcp, call.protocol_type);
|
||||
EXPECT_EQ("fedc:ba98:7654:3210:fedc:ba98:7654:3210", call.remote_address);
|
||||
EXPECT_EQ(789, call.remote_port);
|
||||
EXPECT_EQ(0, call.send_buffer_size);
|
||||
EXPECT_EQ(1234, call.receive_buffer_size);
|
||||
EXPECT_EQ(true, call.no_delay);
|
||||
EXPECT_TRUE(call.keep_alive_options);
|
||||
EXPECT_EQ(false, call.keep_alive_options->enable);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(DirectSocketsOpenBrowserTest, OpenUdp_Success_Hostname) {
|
||||
|
@ -300,6 +300,13 @@ class DirectSocketsServiceImpl::ResolveHostAndOpenSocket final
|
||||
std::min(options_->receive_buffer_size, kMaxBufferSize);
|
||||
}
|
||||
tcp_connected_socket_options->no_delay = options_->no_delay;
|
||||
if (options_->keep_alive_options) {
|
||||
// options_->keep_alive_options will be invalidated.
|
||||
tcp_connected_socket_options->keep_alive_options =
|
||||
std::move(options_->keep_alive_options);
|
||||
}
|
||||
// invalidate options_.
|
||||
options_.reset();
|
||||
|
||||
network_context->CreateTCPConnectedSocket(
|
||||
local_addr, *resolved_addresses,
|
||||
@ -614,4 +621,4 @@ void DirectSocketsServiceImpl::OnDialogProceedUdp(
|
||||
resolver->Start(network_context);
|
||||
}
|
||||
|
||||
} // namespace content
|
||||
} // namespace content
|
@ -254,13 +254,15 @@ class SocketDataProvider {
|
||||
bool no_delay() const { return no_delay_; }
|
||||
void set_no_delay(bool no_delay) { no_delay_ = no_delay; }
|
||||
|
||||
// Returns whether TCP keepalives were enabled or not. Returns 0 by default,
|
||||
// which may not match the default behavior on all platforms.
|
||||
bool keep_alive_enabled() const { return keep_alive_enabled_; }
|
||||
// Returns whether TCP keepalives were enabled or not. Returns kDefault by
|
||||
// default.
|
||||
enum class KeepAliveState { kEnabled, kDisabled, kDefault };
|
||||
KeepAliveState keep_alive_state() const { return keep_alive_state_; }
|
||||
// Last set TCP keepalive delay.
|
||||
int keep_alive_delay() const { return keep_alive_delay_; }
|
||||
void set_keep_alive(bool enable, int delay) {
|
||||
keep_alive_enabled_ = enable;
|
||||
keep_alive_state_ =
|
||||
enable ? KeepAliveState::kEnabled : KeepAliveState::kDisabled;
|
||||
keep_alive_delay_ = delay;
|
||||
}
|
||||
|
||||
@ -321,8 +323,8 @@ class SocketDataProvider {
|
||||
int send_buffer_size_ = -1;
|
||||
// This reflects the default state of TCPClientSockets.
|
||||
bool no_delay_ = true;
|
||||
// Default varies by platform. Just pretend it's disabled.
|
||||
bool keep_alive_enabled_ = false;
|
||||
|
||||
KeepAliveState keep_alive_state_ = KeepAliveState::kDefault;
|
||||
int keep_alive_delay_ = 0;
|
||||
|
||||
int set_receive_buffer_size_result_ = net::OK;
|
||||
|
@ -11,6 +11,14 @@ import "services/network/public/mojom/tls_socket.mojom";
|
||||
import "services/network/public/mojom/network_param.mojom";
|
||||
import "services/network/public/mojom/mutable_network_traffic_annotation_tag.mojom";
|
||||
|
||||
struct TCPKeepAliveOptions {
|
||||
// Enable/disable TCP Keep-Alive.
|
||||
bool enable = false;
|
||||
|
||||
// Keep-Alive delay in seconds.
|
||||
uint16 delay = 0;
|
||||
};
|
||||
|
||||
struct TCPConnectedSocketOptions {
|
||||
// Sets the OS send buffer size (in bytes) for the socket. This is the
|
||||
// SO_SNDBUF socket option. If 0, the default size is used. The value will
|
||||
@ -29,6 +37,11 @@ struct TCPConnectedSocketOptions {
|
||||
// false. See TCP_NODELAY in `man 7 tcp`. On Windows, the Nagle implementation
|
||||
// is governed by RFC 896.
|
||||
bool no_delay = true;
|
||||
|
||||
// This entry enables/disables the TCP Keep-Alive and sets SO_KEEPALIVE on the
|
||||
// socket to the specified delay in seconds. If not supplied, then default
|
||||
// socket settings will be applied, which may vary by platform.
|
||||
TCPKeepAliveOptions? keep_alive_options;
|
||||
};
|
||||
|
||||
// Represents a bound TCP socket. Once a call succeeds, cannot be reused.
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "net/log/net_log.h"
|
||||
#include "net/socket/client_socket_factory.h"
|
||||
#include "net/socket/client_socket_handle.h"
|
||||
#include "services/network/public/mojom/tcp_socket.mojom.h"
|
||||
#include "services/network/tls_client_socket.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
@ -31,9 +32,9 @@ int ClampTCPBufferSize(int requested_buffer_size) {
|
||||
// (TCPSocket::SetDefaultOptionsForClient()).
|
||||
int ConfigureSocket(
|
||||
net::TransportClientSocket* socket,
|
||||
const mojom::TCPConnectedSocketOptions& tcp_connected_socket_options) {
|
||||
const mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options) {
|
||||
int send_buffer_size =
|
||||
ClampTCPBufferSize(tcp_connected_socket_options.send_buffer_size);
|
||||
ClampTCPBufferSize(tcp_connected_socket_options->send_buffer_size);
|
||||
if (send_buffer_size > 0) {
|
||||
int result = socket->SetSendBufferSize(send_buffer_size);
|
||||
DCHECK_NE(net::ERR_IO_PENDING, result);
|
||||
@ -42,7 +43,7 @@ int ConfigureSocket(
|
||||
}
|
||||
|
||||
int receive_buffer_size =
|
||||
ClampTCPBufferSize(tcp_connected_socket_options.receive_buffer_size);
|
||||
ClampTCPBufferSize(tcp_connected_socket_options->receive_buffer_size);
|
||||
if (receive_buffer_size > 0) {
|
||||
int result = socket->SetReceiveBufferSize(receive_buffer_size);
|
||||
DCHECK_NE(net::ERR_IO_PENDING, result);
|
||||
@ -51,13 +52,24 @@ int ConfigureSocket(
|
||||
}
|
||||
|
||||
// No delay is set by default, so only update the setting if it's false.
|
||||
if (!tcp_connected_socket_options.no_delay) {
|
||||
if (!tcp_connected_socket_options->no_delay) {
|
||||
// Unlike the above calls, TcpSocket::SetNoDelay() returns a bool rather
|
||||
// than a network error code.
|
||||
if (!socket->SetNoDelay(false))
|
||||
return net::ERR_FAILED;
|
||||
}
|
||||
|
||||
const mojom::TCPKeepAliveOptionsPtr& keep_alive_options =
|
||||
tcp_connected_socket_options->keep_alive_options;
|
||||
if (keep_alive_options) {
|
||||
// TcpSocket::SetKeepAlive(...) returns a bool rather than a network error
|
||||
// code.
|
||||
if (!socket->SetKeepAlive(/*enable=*/keep_alive_options->enable,
|
||||
/*delay_secs=*/keep_alive_options->delay)) {
|
||||
return net::ERR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return net::OK;
|
||||
}
|
||||
|
||||
@ -142,8 +154,9 @@ void TCPConnectedSocket::ConnectWithSocket(
|
||||
connect_callback_ = std::move(callback);
|
||||
|
||||
if (tcp_connected_socket_options) {
|
||||
socket_->SetBeforeConnectCallback(base::BindRepeating(
|
||||
&ConfigureSocket, socket_.get(), *tcp_connected_socket_options));
|
||||
socket_->SetBeforeConnectCallback(
|
||||
base::BindRepeating(&ConfigureSocket, socket_.get(),
|
||||
base::Passed(&tcp_connected_socket_options)));
|
||||
}
|
||||
int result = socket_->Connect(base::BindOnce(
|
||||
&TCPConnectedSocket::OnConnectCompleted, base::Unretained(this)));
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "net/url_request/url_request_test_util.h"
|
||||
#include "services/network/mojo_socket_test_util.h"
|
||||
#include "services/network/public/mojom/network_service.mojom.h"
|
||||
#include "services/network/public/mojom/tcp_socket.mojom-forward.h"
|
||||
#include "services/network/public/mojom/tcp_socket.mojom.h"
|
||||
#include "services/network/public/mojom/tls_socket.mojom.h"
|
||||
#include "services/network/public/mojom/udp_socket.mojom.h"
|
||||
@ -1055,6 +1056,12 @@ TEST_P(TCPSocketWithMockSocketTest, InitialTCPConnectedSocketOptions) {
|
||||
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
|
||||
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
|
||||
|
||||
std::vector<mojom::TCPKeepAliveOptionsPtr> keep_alive_options_list;
|
||||
|
||||
keep_alive_options_list.emplace_back(nullptr);
|
||||
keep_alive_options_list.emplace_back(base::in_place, false, 0U);
|
||||
keep_alive_options_list.emplace_back(base::in_place, true, 100U);
|
||||
|
||||
for (int receive_buffer_size :
|
||||
{-1, 0, 1024, TCPConnectedSocket::kMaxBufferSize,
|
||||
TCPConnectedSocket::kMaxBufferSize + 1}) {
|
||||
@ -1062,44 +1069,64 @@ TEST_P(TCPSocketWithMockSocketTest, InitialTCPConnectedSocketOptions) {
|
||||
{-1, 0, 2048, TCPConnectedSocket::kMaxBufferSize,
|
||||
TCPConnectedSocket::kMaxBufferSize + 1}) {
|
||||
for (int no_delay : {false, true}) {
|
||||
mojo::Remote<mojom::TCPConnectedSocket> client_socket;
|
||||
net::StaticSocketDataProvider data_provider;
|
||||
data_provider.set_connect_data(
|
||||
net::MockConnect(GetParam(), net::OK, server_addr));
|
||||
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
|
||||
for (const auto& keep_alive_options : keep_alive_options_list) {
|
||||
mojo::Remote<mojom::TCPConnectedSocket> client_socket;
|
||||
net::StaticSocketDataProvider data_provider;
|
||||
data_provider.set_connect_data(
|
||||
net::MockConnect(GetParam(), net::OK, server_addr));
|
||||
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
|
||||
|
||||
mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options =
|
||||
mojom::TCPConnectedSocketOptions::New();
|
||||
tcp_connected_socket_options->receive_buffer_size = receive_buffer_size;
|
||||
tcp_connected_socket_options->send_buffer_size = send_buffer_size;
|
||||
tcp_connected_socket_options->no_delay = no_delay;
|
||||
EXPECT_EQ(net::OK,
|
||||
CreateTCPConnectedSocketSync(
|
||||
client_socket.BindNewPipeAndPassReceiver(),
|
||||
mojo::NullRemote() /*observer*/,
|
||||
absl::nullopt /*local_addr*/, server_addr,
|
||||
&client_socket_receive_handle, &client_socket_send_handle,
|
||||
std::move(tcp_connected_socket_options)));
|
||||
mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options =
|
||||
mojom::TCPConnectedSocketOptions::New();
|
||||
tcp_connected_socket_options->receive_buffer_size =
|
||||
receive_buffer_size;
|
||||
tcp_connected_socket_options->send_buffer_size = send_buffer_size;
|
||||
tcp_connected_socket_options->no_delay = no_delay;
|
||||
if (keep_alive_options) {
|
||||
tcp_connected_socket_options->keep_alive_options =
|
||||
keep_alive_options.Clone();
|
||||
}
|
||||
EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
|
||||
client_socket.BindNewPipeAndPassReceiver(),
|
||||
mojo::NullRemote() /*observer*/,
|
||||
absl::nullopt /*local_addr*/, server_addr,
|
||||
&client_socket_receive_handle,
|
||||
&client_socket_send_handle,
|
||||
std::move(tcp_connected_socket_options)));
|
||||
|
||||
if (receive_buffer_size <= 0) {
|
||||
EXPECT_EQ(-1, data_provider.receive_buffer_size());
|
||||
} else if (receive_buffer_size <= TCPConnectedSocket::kMaxBufferSize) {
|
||||
EXPECT_EQ(receive_buffer_size, data_provider.receive_buffer_size());
|
||||
} else {
|
||||
EXPECT_EQ(TCPConnectedSocket::kMaxBufferSize,
|
||||
data_provider.receive_buffer_size());
|
||||
if (receive_buffer_size <= 0) {
|
||||
EXPECT_EQ(-1, data_provider.receive_buffer_size());
|
||||
} else if (receive_buffer_size <=
|
||||
TCPConnectedSocket::kMaxBufferSize) {
|
||||
EXPECT_EQ(receive_buffer_size, data_provider.receive_buffer_size());
|
||||
} else {
|
||||
EXPECT_EQ(TCPConnectedSocket::kMaxBufferSize,
|
||||
data_provider.receive_buffer_size());
|
||||
}
|
||||
|
||||
if (send_buffer_size <= 0) {
|
||||
EXPECT_EQ(-1, data_provider.send_buffer_size());
|
||||
} else if (send_buffer_size <= TCPConnectedSocket::kMaxBufferSize) {
|
||||
EXPECT_EQ(send_buffer_size, data_provider.send_buffer_size());
|
||||
} else {
|
||||
EXPECT_EQ(TCPConnectedSocket::kMaxBufferSize,
|
||||
data_provider.send_buffer_size());
|
||||
}
|
||||
EXPECT_EQ(no_delay, data_provider.no_delay());
|
||||
if (!keep_alive_options) {
|
||||
EXPECT_EQ(data_provider.keep_alive_state(),
|
||||
net::SocketDataProvider::KeepAliveState::kDefault);
|
||||
} else {
|
||||
EXPECT_EQ(data_provider.keep_alive_state(),
|
||||
keep_alive_options->enable
|
||||
? net::SocketDataProvider::KeepAliveState::kEnabled
|
||||
: net::SocketDataProvider::KeepAliveState::kDisabled);
|
||||
if (keep_alive_options->enable) {
|
||||
EXPECT_EQ(keep_alive_options->delay,
|
||||
data_provider.keep_alive_delay());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (send_buffer_size <= 0) {
|
||||
EXPECT_EQ(-1, data_provider.send_buffer_size());
|
||||
} else if (send_buffer_size <= TCPConnectedSocket::kMaxBufferSize) {
|
||||
EXPECT_EQ(send_buffer_size, data_provider.send_buffer_size());
|
||||
} else {
|
||||
EXPECT_EQ(TCPConnectedSocket::kMaxBufferSize,
|
||||
data_provider.send_buffer_size());
|
||||
}
|
||||
|
||||
EXPECT_EQ(no_delay, data_provider.no_delay());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1114,10 +1141,11 @@ TEST_P(TCPSocketWithMockSocketTest, InitialTCPConnectedSocketOptionsFails) {
|
||||
SET_RECEIVE_BUFFER_SIZE,
|
||||
SET_SEND_BUFFER_SIZE,
|
||||
SET_NO_DELAY,
|
||||
SET_KEEP_ALIVE
|
||||
};
|
||||
for (const auto& failed_call :
|
||||
{FailedCall::SET_RECEIVE_BUFFER_SIZE, FailedCall::SET_SEND_BUFFER_SIZE,
|
||||
FailedCall::SET_NO_DELAY}) {
|
||||
FailedCall::SET_NO_DELAY, FailedCall::SET_KEEP_ALIVE}) {
|
||||
mojo::Remote<mojom::TCPConnectedSocket> client_socket;
|
||||
net::StaticSocketDataProvider data_provider;
|
||||
data_provider.set_connect_data(
|
||||
@ -1132,6 +1160,9 @@ TEST_P(TCPSocketWithMockSocketTest, InitialTCPConnectedSocketOptionsFails) {
|
||||
case FailedCall::SET_NO_DELAY:
|
||||
data_provider.set_set_no_delay_result(false);
|
||||
break;
|
||||
case FailedCall::SET_KEEP_ALIVE:
|
||||
data_provider.set_set_keep_alive_result(false);
|
||||
break;
|
||||
}
|
||||
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
|
||||
|
||||
@ -1140,6 +1171,9 @@ TEST_P(TCPSocketWithMockSocketTest, InitialTCPConnectedSocketOptionsFails) {
|
||||
tcp_connected_socket_options->receive_buffer_size = 1;
|
||||
tcp_connected_socket_options->send_buffer_size = 2;
|
||||
tcp_connected_socket_options->no_delay = false;
|
||||
tcp_connected_socket_options->keep_alive_options =
|
||||
mojom::TCPKeepAliveOptions::New(false, 0U);
|
||||
|
||||
EXPECT_EQ(net::ERR_FAILED,
|
||||
CreateTCPConnectedSocketSync(
|
||||
client_socket.BindNewPipeAndPassReceiver(),
|
||||
@ -1280,7 +1314,8 @@ TEST_F(TCPSocketWithMockSocketTest, SetNoDelayAndKeepAlive) {
|
||||
run_loop.Quit();
|
||||
}));
|
||||
run_loop.Run();
|
||||
EXPECT_TRUE(data_provider.keep_alive_enabled());
|
||||
EXPECT_EQ(data_provider.keep_alive_state(),
|
||||
net::SocketDataProvider::KeepAliveState::kEnabled);
|
||||
EXPECT_EQ(kKeepAliveDelay, data_provider.keep_alive_delay());
|
||||
}
|
||||
|
||||
@ -1292,7 +1327,8 @@ TEST_F(TCPSocketWithMockSocketTest, SetNoDelayAndKeepAlive) {
|
||||
run_loop.Quit();
|
||||
}));
|
||||
run_loop.Run();
|
||||
EXPECT_FALSE(data_provider.keep_alive_enabled());
|
||||
EXPECT_EQ(data_provider.keep_alive_state(),
|
||||
net::SocketDataProvider::KeepAliveState::kDisabled);
|
||||
}
|
||||
|
||||
{
|
||||
@ -1304,7 +1340,8 @@ TEST_F(TCPSocketWithMockSocketTest, SetNoDelayAndKeepAlive) {
|
||||
run_loop.Quit();
|
||||
}));
|
||||
run_loop.Run();
|
||||
EXPECT_TRUE(data_provider.keep_alive_enabled());
|
||||
EXPECT_EQ(data_provider.keep_alive_state(),
|
||||
net::SocketDataProvider::KeepAliveState::kEnabled);
|
||||
EXPECT_EQ(kKeepAliveDelay, data_provider.keep_alive_delay());
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ struct DirectSocketOptions {
|
||||
|
||||
// Only relevant for TCP:
|
||||
bool no_delay = false;
|
||||
|
||||
network.mojom.TCPKeepAliveOptions? keep_alive_options;
|
||||
};
|
||||
|
||||
// This wraps network.mojom.NetworkContext and handles extra work such as
|
||||
|
@ -2603,6 +2603,10 @@ if (target_os != "android") {
|
||||
generated_dictionary_sources_in_modules += [
|
||||
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_socket_options.cc",
|
||||
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_socket_options.h",
|
||||
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_tcp_socket_options.cc",
|
||||
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_tcp_socket_options.h",
|
||||
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_udp_socket_options.cc",
|
||||
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_udp_socket_options.h",
|
||||
]
|
||||
generated_interface_sources_in_modules += [
|
||||
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_tcp_socket.cc",
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "third_party/blink/renderer/modules/direct_sockets/navigator_socket.h"
|
||||
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/time/time.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "services/network/public/mojom/tcp_socket.mojom-blink.h"
|
||||
#include "services/network/public/mojom/udp_socket.mojom-blink.h"
|
||||
@ -14,6 +15,8 @@
|
||||
#include "third_party/blink/public/platform/task_type.h"
|
||||
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
|
||||
#include "third_party/blink/renderer/bindings/modules/v8/v8_socket_options.h"
|
||||
#include "third_party/blink/renderer/bindings/modules/v8/v8_tcp_socket_options.h"
|
||||
#include "third_party/blink/renderer/bindings/modules/v8/v8_udp_socket_options.h"
|
||||
#include "third_party/blink/renderer/core/dom/dom_exception.h"
|
||||
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
|
||||
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
|
||||
@ -55,7 +58,7 @@ NavigatorSocket& NavigatorSocket::From(ScriptState* script_state) {
|
||||
// static
|
||||
ScriptPromise NavigatorSocket::openTCPSocket(ScriptState* script_state,
|
||||
Navigator& navigator,
|
||||
const SocketOptions* options,
|
||||
const TCPSocketOptions* options,
|
||||
ExceptionState& exception_state) {
|
||||
return From(script_state)
|
||||
.openTCPSocket(script_state, options, exception_state);
|
||||
@ -64,7 +67,7 @@ ScriptPromise NavigatorSocket::openTCPSocket(ScriptState* script_state,
|
||||
// static
|
||||
ScriptPromise NavigatorSocket::openUDPSocket(ScriptState* script_state,
|
||||
Navigator& navigator,
|
||||
const SocketOptions* options,
|
||||
const UDPSocketOptions* options,
|
||||
ExceptionState& exception_state) {
|
||||
return From(script_state)
|
||||
.openUDPSocket(script_state, options, exception_state);
|
||||
@ -103,32 +106,45 @@ void NavigatorSocket::EnsureServiceConnected(LocalDOMWindow& window) {
|
||||
|
||||
// static
|
||||
mojom::blink::DirectSocketOptionsPtr NavigatorSocket::CreateSocketOptions(
|
||||
const SocketOptions& options) {
|
||||
const SocketOptions* options,
|
||||
NavigatorSocket::ProtocolType socket_type) {
|
||||
auto socket_options = mojom::blink::DirectSocketOptions::New();
|
||||
|
||||
if (options.hasLocalAddress())
|
||||
socket_options->local_hostname = options.localAddress();
|
||||
if (options.hasLocalPort())
|
||||
socket_options->local_port = options.localPort();
|
||||
if (options->hasLocalAddress())
|
||||
socket_options->local_hostname = options->localAddress();
|
||||
if (options->hasLocalPort())
|
||||
socket_options->local_port = options->localPort();
|
||||
|
||||
if (options.hasRemoteAddress())
|
||||
socket_options->remote_hostname = options.remoteAddress();
|
||||
if (options.hasRemotePort())
|
||||
socket_options->remote_port = options.remotePort();
|
||||
if (options->hasRemoteAddress())
|
||||
socket_options->remote_hostname = options->remoteAddress();
|
||||
if (options->hasRemotePort())
|
||||
socket_options->remote_port = options->remotePort();
|
||||
|
||||
if (options.hasSendBufferSize())
|
||||
socket_options->send_buffer_size = options.sendBufferSize();
|
||||
if (options.hasReceiveBufferSize())
|
||||
socket_options->receive_buffer_size = options.receiveBufferSize();
|
||||
if (options->hasSendBufferSize())
|
||||
socket_options->send_buffer_size = options->sendBufferSize();
|
||||
if (options->hasReceiveBufferSize())
|
||||
socket_options->receive_buffer_size = options->receiveBufferSize();
|
||||
|
||||
if (options.hasNoDelay())
|
||||
socket_options->no_delay = options.noDelay();
|
||||
if (socket_type == NavigatorSocket::ProtocolType::kTcp) {
|
||||
const TCPSocketOptions* tcp_options =
|
||||
static_cast<const TCPSocketOptions*>(options);
|
||||
if (tcp_options->hasNoDelay()) {
|
||||
socket_options->no_delay = tcp_options->noDelay();
|
||||
}
|
||||
if (tcp_options->hasKeepAlive()) {
|
||||
socket_options->keep_alive_options =
|
||||
network::mojom::blink::TCPKeepAliveOptions::New(
|
||||
/*enable=*/tcp_options->keepAlive(),
|
||||
/*delay=*/base::Milliseconds(tcp_options->getKeepAliveDelayOr(0))
|
||||
.InSeconds());
|
||||
}
|
||||
}
|
||||
|
||||
return socket_options;
|
||||
}
|
||||
|
||||
ScriptPromise NavigatorSocket::openTCPSocket(ScriptState* script_state,
|
||||
const SocketOptions* options,
|
||||
const TCPSocketOptions* options,
|
||||
ExceptionState& exception_state) {
|
||||
if (!OpenSocketPermitted(script_state, options, exception_state))
|
||||
return ScriptPromise();
|
||||
@ -139,7 +155,7 @@ ScriptPromise NavigatorSocket::openTCPSocket(ScriptState* script_state,
|
||||
ScriptPromise promise = resolver->Promise();
|
||||
|
||||
service_remote_->OpenTcpSocket(
|
||||
CreateSocketOptions(*options),
|
||||
CreateSocketOptions(options, NavigatorSocket::ProtocolType::kTcp),
|
||||
pending->GetTCPSocketReceiver(), pending->GetTCPSocketObserver(),
|
||||
WTF::Bind(&NavigatorSocket::OnTcpOpen, WrapPersistent(this),
|
||||
WrapPersistent(pending)));
|
||||
@ -147,7 +163,7 @@ ScriptPromise NavigatorSocket::openTCPSocket(ScriptState* script_state,
|
||||
}
|
||||
|
||||
ScriptPromise NavigatorSocket::openUDPSocket(ScriptState* script_state,
|
||||
const SocketOptions* options,
|
||||
const UDPSocketOptions* options,
|
||||
ExceptionState& exception_state) {
|
||||
if (!OpenSocketPermitted(script_state, options, exception_state))
|
||||
return ScriptPromise();
|
||||
@ -159,8 +175,8 @@ ScriptPromise NavigatorSocket::openUDPSocket(ScriptState* script_state,
|
||||
ScriptPromise promise = resolver->Promise();
|
||||
|
||||
service_remote_->OpenUdpSocket(
|
||||
CreateSocketOptions(*options), pending->GetUDPSocketReceiver(),
|
||||
pending->GetUDPSocketListener(),
|
||||
CreateSocketOptions(options, NavigatorSocket::ProtocolType::kUdp),
|
||||
pending->GetUDPSocketReceiver(), pending->GetUDPSocketListener(),
|
||||
WTF::Bind(&NavigatorSocket::OnUdpOpen, WrapPersistent(this),
|
||||
WrapPersistent(pending)));
|
||||
return promise;
|
||||
|
@ -25,6 +25,8 @@ class LocalDOMWindow;
|
||||
class Navigator;
|
||||
class ScriptState;
|
||||
class SocketOptions;
|
||||
class TCPSocketOptions;
|
||||
class UDPSocketOptions;
|
||||
|
||||
class MODULES_EXPORT NavigatorSocket final
|
||||
: public GarbageCollected<NavigatorSocket>,
|
||||
@ -33,6 +35,8 @@ class MODULES_EXPORT NavigatorSocket final
|
||||
public:
|
||||
static const char kSupplementName[];
|
||||
|
||||
enum class ProtocolType { kTcp, kUdp };
|
||||
|
||||
explicit NavigatorSocket(ExecutionContext*);
|
||||
~NavigatorSocket() override = default;
|
||||
|
||||
@ -46,12 +50,12 @@ class MODULES_EXPORT NavigatorSocket final
|
||||
// Navigator partial interface
|
||||
static ScriptPromise openTCPSocket(ScriptState*,
|
||||
Navigator&,
|
||||
const SocketOptions*,
|
||||
const TCPSocketOptions*,
|
||||
ExceptionState&);
|
||||
|
||||
static ScriptPromise openUDPSocket(ScriptState*,
|
||||
Navigator&,
|
||||
const SocketOptions*,
|
||||
const UDPSocketOptions*,
|
||||
ExceptionState&);
|
||||
|
||||
// ExecutionContextLifecycleStateObserver:
|
||||
@ -65,14 +69,15 @@ class MODULES_EXPORT NavigatorSocket final
|
||||
void EnsureServiceConnected(LocalDOMWindow&);
|
||||
|
||||
static mojom::blink::DirectSocketOptionsPtr CreateSocketOptions(
|
||||
const SocketOptions&);
|
||||
const SocketOptions*,
|
||||
NavigatorSocket::ProtocolType);
|
||||
|
||||
ScriptPromise openTCPSocket(ScriptState*,
|
||||
const SocketOptions*,
|
||||
const TCPSocketOptions*,
|
||||
ExceptionState&);
|
||||
|
||||
ScriptPromise openUDPSocket(ScriptState*,
|
||||
const SocketOptions*,
|
||||
const UDPSocketOptions*,
|
||||
ExceptionState&);
|
||||
|
||||
// Updates exception state whenever returning false.
|
||||
|
@ -11,8 +11,8 @@
|
||||
DirectSocketEnabled
|
||||
] partial interface Navigator {
|
||||
[SecureContext, RaisesException, CallWith=ScriptState, Measure]
|
||||
Promise<TCPSocket> openTCPSocket(optional SocketOptions options = {});
|
||||
Promise<TCPSocket> openTCPSocket(optional TCPSocketOptions options = {});
|
||||
|
||||
[SecureContext, RaisesException, CallWith=ScriptState, Measure]
|
||||
Promise<UDPSocket> openUDPSocket(optional SocketOptions options = {});
|
||||
Promise<UDPSocket> openUDPSocket(optional UDPSocketOptions options = {});
|
||||
};
|
||||
|
@ -13,8 +13,14 @@ dictionary SocketOptions {
|
||||
|
||||
unsigned long sendBufferSize;
|
||||
unsigned long receiveBufferSize;
|
||||
|
||||
// These are only relevant for TCP:
|
||||
boolean keepAlive;
|
||||
boolean noDelay;
|
||||
};
|
||||
|
||||
dictionary TCPSocketOptions : SocketOptions {
|
||||
boolean noDelay;
|
||||
boolean keepAlive;
|
||||
[Clamp] unsigned long keepAliveDelay;
|
||||
};
|
||||
|
||||
dictionary UDPSocketOptions : SocketOptions {
|
||||
|
||||
};
|
||||
|
Reference in New Issue
Block a user