0

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:
Andrew Rayskiy
2022-01-07 15:06:25 +00:00
committed by Chromium LUCI CQ
parent eebb8833d0
commit 7d6de24e45
12 changed files with 248 additions and 96 deletions

@ -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 {
};