blob: ca1c83717489a88a79e1bdccb853a38535a4917c [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <utility>
#include "services/network/udp_socket.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "build/build_config.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "net/base/net_errors.h"
#include "net/socket/udp_socket.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/public/mojom/udp_socket.mojom.h"
#include "services/network/socket_factory.h"
#include "services/network/udp_socket_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
namespace {
const size_t kDatagramSize = 255;
class SocketWrapperTestImpl : public UDPSocket::SocketWrapper {
public:
SocketWrapperTestImpl() {}
~SocketWrapperTestImpl() override {}
int Connect(const net::IPEndPoint& remote_addr,
mojom::UDPSocketOptionsPtr options,
net::IPEndPoint* local_addr_out) override {
NOTREACHED();
return net::ERR_NOT_IMPLEMENTED;
}
int Bind(const net::IPEndPoint& local_addr,
mojom::UDPSocketOptionsPtr options,
net::IPEndPoint* local_addr_out) override {
NOTREACHED();
return net::ERR_NOT_IMPLEMENTED;
}
int SendTo(
net::IOBuffer* buf,
int buf_len,
const net::IPEndPoint& dest_addr,
net::CompletionOnceCallback callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) override {
NOTREACHED();
return net::ERR_NOT_IMPLEMENTED;
}
int SetBroadcast(bool broadcast) override {
NOTREACHED();
return net::ERR_NOT_IMPLEMENTED;
}
int SetSendBufferSize(int send_buffer_size) override {
NOTREACHED();
return net::ERR_NOT_IMPLEMENTED;
}
int SetReceiveBufferSize(int receive_buffer_size) override {
NOTREACHED();
return net::ERR_NOT_IMPLEMENTED;
}
int JoinGroup(const net::IPAddress& group_address) override {
NOTREACHED();
return net::ERR_NOT_IMPLEMENTED;
}
int LeaveGroup(const net::IPAddress& group_address) override {
NOTREACHED();
return net::ERR_NOT_IMPLEMENTED;
}
int Write(
net::IOBuffer* buf,
int buf_len,
net::CompletionOnceCallback callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) override {
NOTREACHED();
return net::ERR_NOT_IMPLEMENTED;
}
int RecvFrom(net::IOBuffer* buf,
int buf_len,
net::IPEndPoint* address,
net::CompletionOnceCallback callback) override {
NOTREACHED();
return net::ERR_NOT_IMPLEMENTED;
}
private:
DISALLOW_COPY_AND_ASSIGN(SocketWrapperTestImpl);
};
net::IPEndPoint GetLocalHostWithAnyPort() {
return net::IPEndPoint(net::IPAddress(127, 0, 0, 1), 0);
}
std::vector<uint8_t> CreateTestMessage(uint8_t initial, size_t size) {
std::vector<uint8_t> array(size);
for (size_t i = 0; i < size; ++i)
array[i] = static_cast<uint8_t>((i + initial) % 256);
return array;
}
// A Mock UDPSocket that always returns net::ERR_IO_PENDING for SendTo()s.
class HangingUDPSocket : public SocketWrapperTestImpl {
public:
HangingUDPSocket() {}
// SocketWrapperTestImpl implementation.
int Bind(const net::IPEndPoint& local_addr,
mojom::UDPSocketOptionsPtr options,
net::IPEndPoint* local_addr_out) override {
return net::OK;
}
int SendTo(
net::IOBuffer* buf,
int buf_len,
const net::IPEndPoint& address,
net::CompletionOnceCallback callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) override {
EXPECT_EQ(expected_data_,
std::vector<unsigned char>(buf->data(), buf->data() + buf_len));
if (should_complete_requests_)
return net::OK;
pending_io_buffers_.push_back(buf);
pending_io_buffer_lengths_.push_back(buf_len);
pending_send_requests_.push_back(std::move(callback));
return net::ERR_IO_PENDING;
}
void set_expected_data(std::vector<uint8_t> expected_data) {
expected_data_ = expected_data;
}
const std::vector<net::IOBuffer*>& pending_io_buffers() const {
return pending_io_buffers_;
}
const std::vector<int>& pending_io_buffer_lengths() const {
return pending_io_buffer_lengths_;
}
// Completes all pending requests.
void CompleteAllPendingRequests() {
should_complete_requests_ = true;
for (auto& request : pending_send_requests_) {
std::move(request).Run(net::OK);
}
pending_send_requests_.clear();
}
private:
std::vector<uint8_t> expected_data_;
bool should_complete_requests_ = false;
std::vector<net::IOBuffer*> pending_io_buffers_;
std::vector<int> pending_io_buffer_lengths_;
std::vector<net::CompletionOnceCallback> pending_send_requests_;
};
// A Mock UDPSocket that returns 0 byte read.
class ZeroByteReadUDPSocket : public SocketWrapperTestImpl {
public:
ZeroByteReadUDPSocket() {}
int Bind(const net::IPEndPoint& local_addr,
mojom::UDPSocketOptionsPtr options,
net::IPEndPoint* local_addr_out) override {
return net::OK;
}
int RecvFrom(net::IOBuffer* buf,
int buf_len,
net::IPEndPoint* address,
net::CompletionOnceCallback callback) override {
*address = GetLocalHostWithAnyPort();
return 0;
}
};
} // namespace
class UDPSocketTest : public testing::Test {
public:
UDPSocketTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::IO),
factory_(nullptr /*netlog*/, &url_request_context_) {}
~UDPSocketTest() override {}
void SetWrappedSocket(
UDPSocket* socket,
std::unique_ptr<UDPSocket::SocketWrapper> socket_wrapper) {
socket->wrapped_socket_ = std::move(socket_wrapper);
}
uint32_t GetRemainingRecvSlots(UDPSocket* socket) {
return socket->remaining_recv_slots_;
}
SocketFactory* factory() { return &factory_; }
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
net::TestURLRequestContext url_request_context_;
SocketFactory factory_;
DISALLOW_COPY_AND_ASSIGN(UDPSocketTest);
};
TEST_F(UDPSocketTest, Settings) {
mojom::UDPSocketPtr socket_ptr;
factory()->CreateUDPSocket(mojo::MakeRequest(&socket_ptr), nullptr);
net::IPEndPoint server_addr;
net::IPEndPoint any_port(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper helper(&socket_ptr);
net::IPEndPoint local_addr;
mojom::UDPSocketOptionsPtr options = mojom::UDPSocketOptions::New();
options->send_buffer_size = 1024;
options->receive_buffer_size = 2048;
ASSERT_EQ(net::OK,
helper.BindSync(any_port, std::move(options), &local_addr));
EXPECT_NE(local_addr.ToString(), any_port.ToString());
}
// Tests that Send() is used after Bind() is not supported. Send() should only
// be used after Connect().
TEST_F(UDPSocketTest, TestSendWithBind) {
mojom::UDPSocketPtr socket_ptr;
factory()->CreateUDPSocket(mojo::MakeRequest(&socket_ptr), nullptr);
net::IPEndPoint server_addr(GetLocalHostWithAnyPort());
// Bind() the socket.
test::UDPSocketTestHelper helper(&socket_ptr);
ASSERT_EQ(net::OK, helper.BindSync(server_addr, nullptr, &server_addr));
// Connect() has not been used, so Send() is not supported.
std::vector<uint8_t> test_msg{1};
int result = helper.SendSync(test_msg);
EXPECT_EQ(net::ERR_UNEXPECTED, result);
}
// Tests that when SendTo() is used after Connect() is not supported. SendTo()
// should only be used after Bind().
TEST_F(UDPSocketTest, TestSendToWithConnect) {
// Create a server socket to listen for incoming datagrams.
test::UDPSocketReceiverImpl receiver;
mojo::Binding<mojom::UDPSocketReceiver> receiver_binding(&receiver);
mojom::UDPSocketReceiverPtr receiver_interface_ptr;
receiver_binding.Bind(mojo::MakeRequest(&receiver_interface_ptr));
mojom::UDPSocketPtr server_socket;
factory()->CreateUDPSocket(mojo::MakeRequest(&server_socket),
std::move(receiver_interface_ptr));
net::IPEndPoint server_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper helper(&server_socket);
ASSERT_EQ(net::OK, helper.BindSync(server_addr, nullptr, &server_addr));
// Create a client socket to send datagrams.
mojom::UDPSocketPtr client_socket;
factory()->CreateUDPSocket(mojo::MakeRequest(&client_socket), nullptr);
net::IPEndPoint client_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper client_helper(&client_socket);
ASSERT_EQ(net::OK,
client_helper.ConnectSync(server_addr, nullptr, &client_addr));
std::vector<uint8_t> test_msg({1});
int result = client_helper.SendToSync(server_addr, test_msg);
EXPECT_EQ(net::ERR_UNEXPECTED, result);
}
// Tests that the sequence of calling Bind()/Connect() and setters is
// important.
TEST_F(UDPSocketTest, TestUnexpectedSequences) {
mojom::UDPSocketPtr socket_ptr;
factory()->CreateUDPSocket(mojo::MakeRequest(&socket_ptr), nullptr);
test::UDPSocketTestHelper helper(&socket_ptr);
net::IPEndPoint local_addr(GetLocalHostWithAnyPort());
// Now these Setters should not work before Bind().
EXPECT_EQ(net::ERR_UNEXPECTED, helper.SetBroadcastSync(true));
EXPECT_EQ(net::ERR_UNEXPECTED, helper.SetSendBufferSizeSync(4096));
EXPECT_EQ(net::ERR_UNEXPECTED, helper.SetReceiveBufferSizeSync(4096));
// Now Bind() the socket.
ASSERT_EQ(net::OK, helper.BindSync(local_addr, nullptr, &local_addr));
// Setting the buffer size should now succeed.
EXPECT_EQ(net::OK, helper.SetSendBufferSizeSync(4096));
EXPECT_EQ(net::OK, helper.SetReceiveBufferSizeSync(4096));
// Calling Connect() after Bind() should fail because they can't be both used.
ASSERT_EQ(net::ERR_SOCKET_IS_CONNECTED,
helper.ConnectSync(local_addr, nullptr, &local_addr));
// Now Close() the socket.
socket_ptr->Close();
// Re-Bind() is okay.
ASSERT_EQ(net::OK, helper.BindSync(local_addr, nullptr, &local_addr));
}
// Tests that if the underlying socket implementation's Send() returned
// ERR_IO_PENDING, udp_socket.cc doesn't free the send buffer.
TEST_F(UDPSocketTest, TestBufferValid) {
mojom::UDPSocketPtr socket_ptr;
UDPSocket impl(nullptr /*receiver*/, nullptr /*net_log*/);
mojo::Binding<mojom::UDPSocket> binding(&impl);
binding.Bind(mojo::MakeRequest(&socket_ptr));
net::IPEndPoint server_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper helper(&socket_ptr);
ASSERT_EQ(net::OK, helper.BindSync(server_addr, nullptr, &server_addr));
HangingUDPSocket* socket_raw_ptr = new HangingUDPSocket();
SetWrappedSocket(&impl, base::WrapUnique(socket_raw_ptr));
std::vector<uint8_t> test_msg(CreateTestMessage(0, kDatagramSize));
socket_raw_ptr->set_expected_data(test_msg);
base::RunLoop run_loop;
socket_ptr->SendTo(
GetLocalHostWithAnyPort(), test_msg,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
base::BindOnce(
[](base::RunLoop* run_loop, int result) {
EXPECT_EQ(net::OK, result);
run_loop->Quit();
},
base::Unretained(&run_loop)));
socket_ptr.FlushForTesting();
ASSERT_EQ(1u, socket_raw_ptr->pending_io_buffers().size());
ASSERT_EQ(1u, socket_raw_ptr->pending_io_buffer_lengths().size());
// Make sure the caller of HangingUDPSocket doesn't destroy the send buffer,
// and that buffer still contains the exact same data.
net::IOBuffer* buf = socket_raw_ptr->pending_io_buffers()[0];
int buf_len = socket_raw_ptr->pending_io_buffer_lengths()[0];
EXPECT_EQ(test_msg,
std::vector<unsigned char>(buf->data(), buf->data() + buf_len));
}
// Test that exercises the queuing of send requests and makes sure
// ERR_INSUFFICIENT_RESOURCES is returned appropriately.
TEST_F(UDPSocketTest, TestInsufficientResources) {
mojom::UDPSocketPtr socket_ptr;
UDPSocket impl(nullptr /*receiver*/, nullptr /*net_log*/);
mojo::Binding<mojom::UDPSocket> binding(&impl);
binding.Bind(mojo::MakeRequest(&socket_ptr));
const size_t kQueueSize = UDPSocket::kMaxPendingSendRequests;
net::IPEndPoint server_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper helper(&socket_ptr);
ASSERT_EQ(net::OK, helper.BindSync(server_addr, nullptr, &server_addr));
HangingUDPSocket* socket_raw_ptr = new HangingUDPSocket();
SetWrappedSocket(&impl, base::WrapUnique(socket_raw_ptr));
std::vector<uint8_t> test_msg(CreateTestMessage(0, kDatagramSize));
socket_raw_ptr->set_expected_data(test_msg);
// Send |kQueueSize| + 1 datagrams in a row, so the queue is filled up.
std::vector<std::unique_ptr<base::RunLoop>> run_loops;
for (size_t i = 0; i < kQueueSize + 1; ++i) {
run_loops.push_back(std::make_unique<base::RunLoop>());
socket_ptr->SendTo(
GetLocalHostWithAnyPort(), test_msg,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
base::BindOnce(
[](base::RunLoop* run_loop, int result) {
EXPECT_EQ(net::OK, result);
run_loop->Quit();
},
base::Unretained(run_loops[i].get())));
}
// SendTo() beyond the queue size should fail.
EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES,
helper.SendToSync(GetLocalHostWithAnyPort(), test_msg));
// Complete all pending requests. Queued SendTo() should hear back.
socket_raw_ptr->CompleteAllPendingRequests();
for (const auto& loop : run_loops) {
loop->Run();
}
}
TEST_F(UDPSocketTest, TestReceiveMoreOverflow) {
// Create a server socket to listen for incoming datagrams.
test::UDPSocketReceiverImpl receiver;
mojo::Binding<mojom::UDPSocketReceiver> receiver_binding(&receiver);
mojom::UDPSocketReceiverPtr receiver_interface_ptr;
receiver_binding.Bind(mojo::MakeRequest(&receiver_interface_ptr));
mojom::UDPSocketPtr server_socket;
UDPSocket impl(std::move(receiver_interface_ptr), nullptr /*net_log*/);
mojo::Binding<mojom::UDPSocket> binding(&impl);
binding.Bind(mojo::MakeRequest(&server_socket));
net::IPEndPoint server_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper helper(&server_socket);
ASSERT_EQ(net::OK, helper.BindSync(server_addr, nullptr, &server_addr));
server_socket->ReceiveMore(std::numeric_limits<uint32_t>::max());
server_socket.FlushForTesting();
EXPECT_EQ(std::numeric_limits<uint32_t>::max(), GetRemainingRecvSlots(&impl));
server_socket->ReceiveMore(1);
server_socket.FlushForTesting();
EXPECT_EQ(std::numeric_limits<uint32_t>::max(), GetRemainingRecvSlots(&impl));
}
TEST_F(UDPSocketTest, TestReadSend) {
// Create a server socket to listen for incoming datagrams.
test::UDPSocketReceiverImpl receiver;
mojo::Binding<mojom::UDPSocketReceiver> receiver_binding(&receiver);
mojom::UDPSocketReceiverPtr receiver_interface_ptr;
receiver_binding.Bind(mojo::MakeRequest(&receiver_interface_ptr));
mojom::UDPSocketPtr server_socket;
factory()->CreateUDPSocket(mojo::MakeRequest(&server_socket),
std::move(receiver_interface_ptr));
net::IPEndPoint server_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper helper(&server_socket);
ASSERT_EQ(net::OK, helper.BindSync(server_addr, nullptr, &server_addr));
// Create a client socket to send datagrams.
mojom::UDPSocketPtr client_socket;
factory()->CreateUDPSocket(mojo::MakeRequest(&client_socket), nullptr);
net::IPEndPoint client_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper client_helper(&client_socket);
ASSERT_EQ(net::OK,
client_helper.ConnectSync(server_addr, nullptr, &client_addr));
const size_t kDatagramCount = 6;
server_socket->ReceiveMore(kDatagramCount);
for (size_t i = 0; i < kDatagramCount; ++i) {
std::vector<uint8_t> test_msg(
CreateTestMessage(static_cast<uint8_t>(i), kDatagramSize));
int result = client_helper.SendSync(test_msg);
EXPECT_EQ(net::OK, result);
}
receiver.WaitForReceivedResults(kDatagramCount);
EXPECT_EQ(kDatagramCount, receiver.results().size());
int i = 0;
for (const auto& result : receiver.results()) {
EXPECT_EQ(net::OK, result.net_error);
EXPECT_EQ(result.src_addr, client_addr);
EXPECT_EQ(CreateTestMessage(static_cast<uint8_t>(i), kDatagramSize),
result.data.value());
i++;
}
// Tests that sending a message that is larger than the specified limit
// results in an early rejection.
std::vector<uint8_t> large_msg(64 * 1024, 1);
EXPECT_EQ(net::ERR_MSG_TOO_BIG, helper.SendToSync(client_addr, large_msg));
// Close and re-Connect client socket.
client_socket->Close();
client_socket.FlushForTesting();
// Make sure datagram can still be sent from the re-connected client socket.
ASSERT_EQ(net::OK,
client_helper.ConnectSync(server_addr, nullptr, &client_addr));
server_socket->ReceiveMore(1);
std::vector<uint8_t> msg(CreateTestMessage(0, kDatagramSize));
EXPECT_EQ(net::OK, client_helper.SendSync(msg));
receiver.WaitForReceivedResults(kDatagramCount + 1);
ASSERT_EQ(kDatagramCount + 1, receiver.results().size());
auto result = receiver.results()[kDatagramCount];
EXPECT_EQ(net::OK, result.net_error);
EXPECT_EQ(result.src_addr, client_addr);
EXPECT_EQ(msg, result.data.value());
}
TEST_F(UDPSocketTest, TestReadSendTo) {
// Create a server socket to send data.
mojom::UDPSocketPtr server_socket;
factory()->CreateUDPSocket(mojo::MakeRequest(&server_socket), nullptr);
net::IPEndPoint server_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper helper(&server_socket);
ASSERT_EQ(net::OK, helper.BindSync(server_addr, nullptr, &server_addr));
// Create a client socket to send datagrams.
test::UDPSocketReceiverImpl receiver;
mojo::Binding<mojom::UDPSocketReceiver> receiver_binding(&receiver);
mojom::UDPSocketReceiverPtr client_receiver_ptr;
receiver_binding.Bind(mojo::MakeRequest(&client_receiver_ptr));
mojom::UDPSocketPtr client_socket;
factory()->CreateUDPSocket(mojo::MakeRequest(&client_socket),
std::move(client_receiver_ptr));
net::IPEndPoint client_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper client_helper(&client_socket);
ASSERT_EQ(net::OK,
client_helper.ConnectSync(server_addr, nullptr, &client_addr));
const size_t kDatagramCount = 6;
client_socket->ReceiveMore(kDatagramCount);
for (size_t i = 0; i < kDatagramCount; ++i) {
std::vector<uint8_t> test_msg(
CreateTestMessage(static_cast<uint8_t>(i), kDatagramSize));
int result = helper.SendToSync(client_addr, test_msg);
EXPECT_EQ(net::OK, result);
}
receiver.WaitForReceivedResults(kDatagramCount);
EXPECT_EQ(kDatagramCount, receiver.results().size());
int i = 0;
for (const auto& result : receiver.results()) {
EXPECT_EQ(net::OK, result.net_error);
EXPECT_FALSE(result.src_addr);
EXPECT_EQ(CreateTestMessage(static_cast<uint8_t>(i), kDatagramSize),
result.data.value());
i++;
}
// Tests that sending a message that is larger than the specified limit
// results in an early rejection.
std::vector<uint8_t> large_msg(64 * 1024, 1);
EXPECT_EQ(net::ERR_MSG_TOO_BIG, helper.SendToSync(client_addr, large_msg));
// Make sure datagram can still be sent from the re-bound server socket.
server_socket->Close();
ASSERT_EQ(net::OK, helper.BindSync(server_addr, nullptr, &server_addr));
client_socket->ReceiveMore(1);
std::vector<uint8_t> msg(CreateTestMessage(0, kDatagramSize));
EXPECT_EQ(net::OK, helper.SendToSync(client_addr, msg));
receiver.WaitForReceivedResults(kDatagramCount + 1);
ASSERT_EQ(kDatagramCount + 1, receiver.results().size());
auto result = receiver.results()[kDatagramCount];
EXPECT_EQ(net::OK, result.net_error);
EXPECT_FALSE(result.src_addr);
EXPECT_EQ(msg, result.data.value());
}
TEST_F(UDPSocketTest, TestReceiveMoreWithBufferSize) {
// Create a server socket to listen for incoming datagrams.
test::UDPSocketReceiverImpl receiver;
mojo::Binding<mojom::UDPSocketReceiver> receiver_binding(&receiver);
mojom::UDPSocketReceiverPtr receiver_interface_ptr;
receiver_binding.Bind(mojo::MakeRequest(&receiver_interface_ptr));
mojom::UDPSocketPtr server_socket;
factory()->CreateUDPSocket(mojo::MakeRequest(&server_socket),
std::move(receiver_interface_ptr));
net::IPEndPoint server_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper helper(&server_socket);
ASSERT_EQ(net::OK, helper.BindSync(server_addr, nullptr, &server_addr));
// Create a client socket to send datagrams.
mojom::UDPSocketPtr client_socket;
factory()->CreateUDPSocket(mojo::MakeRequest(&client_socket), nullptr);
net::IPEndPoint client_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper client_helper(&client_socket);
ASSERT_EQ(net::OK,
client_helper.ConnectSync(server_addr, nullptr, &client_addr));
// Use a buffer size that is 1 byte smaller than the datagram size.
// This should result in net::ERR_MSG_TOO_BIG, because the transport can't
// complete the read with this small buffer size.
size_t buffer_size = kDatagramSize - 1;
server_socket->ReceiveMoreWithBufferSize(1, buffer_size);
std::vector<uint8_t> test_msg(CreateTestMessage(0, kDatagramSize));
ASSERT_EQ(net::OK, client_helper.SendSync(test_msg));
receiver.WaitForReceivedResults(1);
ASSERT_EQ(1u, receiver.results().size());
auto result = receiver.results()[0];
EXPECT_EQ(net::ERR_MSG_TOO_BIG, result.net_error);
EXPECT_FALSE(result.data);
// Now use a buffer size that is equal to datagram size. This should be okay.
server_socket->ReceiveMoreWithBufferSize(1, kDatagramSize);
ASSERT_EQ(net::OK, client_helper.SendSync(test_msg));
receiver.WaitForReceivedResults(2);
ASSERT_EQ(2u, receiver.results().size());
result = receiver.results()[1];
EXPECT_EQ(net::OK, result.net_error);
EXPECT_EQ(client_addr, result.src_addr.value());
EXPECT_EQ(test_msg, result.data.value());
}
// Make sure passing an invalid net::IPEndPoint will be detected by
// serialization/deserialization in mojo.
TEST_F(UDPSocketTest, TestSendToInvalidAddress) {
mojom::UDPSocketPtr server_socket;
factory()->CreateUDPSocket(mojo::MakeRequest(&server_socket), nullptr);
net::IPEndPoint server_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper helper(&server_socket);
ASSERT_EQ(net::OK, helper.BindSync(server_addr, nullptr, &server_addr));
std::vector<uint8_t> test_msg{1};
std::vector<uint8_t> invalid_ip_addr{127, 0, 0, 0, 1};
net::IPAddress ip_address(invalid_ip_addr.data(), invalid_ip_addr.size());
EXPECT_FALSE(ip_address.IsValid());
net::IPEndPoint invalid_addr(ip_address, 53);
server_socket->SendTo(
invalid_addr, test_msg,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
base::BindOnce([](int result) {}));
// Make sure that the pipe is broken upon processing |invalid_addr|.
base::RunLoop run_loop;
server_socket.set_connection_error_handler(
base::BindOnce([](base::RunLoop* run_loop) { run_loop->Quit(); },
base::Unretained(&run_loop)));
run_loop.Run();
}
// Tests that it is legal for UDPSocketReceiver::OnReceive() to be called with
// 0 byte payload.
TEST_F(UDPSocketTest, TestReadZeroByte) {
test::UDPSocketReceiverImpl receiver;
mojo::Binding<mojom::UDPSocketReceiver> receiver_binding(&receiver);
mojom::UDPSocketReceiverPtr receiver_interface_ptr;
receiver_binding.Bind(mojo::MakeRequest(&receiver_interface_ptr));
mojom::UDPSocketPtr socket_ptr;
UDPSocket impl(std::move(receiver_interface_ptr), nullptr /*net_log*/);
mojo::Binding<mojom::UDPSocket> binding(&impl);
binding.Bind(mojo::MakeRequest(&socket_ptr));
net::IPEndPoint server_addr(GetLocalHostWithAnyPort());
test::UDPSocketTestHelper helper(&socket_ptr);
ASSERT_EQ(net::OK, helper.BindSync(server_addr, nullptr, &server_addr));
SetWrappedSocket(&impl, std::make_unique<ZeroByteReadUDPSocket>());
socket_ptr->ReceiveMore(1);
receiver.WaitForReceivedResults(1);
ASSERT_EQ(1u, receiver.results().size());
auto result = receiver.results()[0];
EXPECT_EQ(net::OK, result.net_error);
EXPECT_TRUE(result.data);
EXPECT_EQ(std::vector<uint8_t>(), result.data.value());
}
#if defined(OS_ANDROID)
// Some Android devices do not support multicast socket.
// The ones supporting multicast need WifiManager.MulticastLock to enable it.
// https://developer.android.com/reference/android/net/wifi/WifiManager.MulticastLock.html
#define MAYBE_JoinMulticastGroup DISABLED_JoinMulticastGroup
#else
#define MAYBE_JoinMulticastGroup JoinMulticastGroup
#endif // defined(OS_ANDROID)
TEST_F(UDPSocketTest, MAYBE_JoinMulticastGroup) {
const char kGroup[] = "237.132.100.17";
net::IPAddress group_ip;
EXPECT_TRUE(group_ip.AssignFromIPLiteral(kGroup));
mojom::UDPSocketPtr socket_ptr;
mojom::UDPSocketReceiverPtr receiver_ptr;
test::UDPSocketReceiverImpl receiver;
mojo::Binding<mojom::UDPSocketReceiver> receiver_binding(&receiver);
receiver_binding.Bind(mojo::MakeRequest(&receiver_ptr));
factory()->CreateUDPSocket(mojo::MakeRequest(&socket_ptr),
std::move(receiver_ptr));
test::UDPSocketTestHelper helper(&socket_ptr);
mojom::UDPSocketOptionsPtr options = mojom::UDPSocketOptions::New();
#if defined(OS_FUCHSIA)
// Fuchsia currently doesn't support automatic interface selection for
// multicast, so interface index needs to be set explicitly.
// See https://fuchsia.atlassian.net/browse/NET-195 .
options->multicast_interface = 1;
#endif // defined(OS_FUCHSIA)
options->allow_address_sharing_for_multicast = true;
net::IPAddress bind_ip_address;
EXPECT_TRUE(bind_ip_address.AssignFromIPLiteral("0.0.0.0"));
net::IPEndPoint socket_address(bind_ip_address, 0);
ASSERT_EQ(net::OK, helper.BindSync(socket_address, std::move(options),
&socket_address));
int port = socket_address.port();
EXPECT_NE(0, port);
EXPECT_EQ(net::OK, helper.JoinGroupSync(group_ip));
// Joining group multiple times.
EXPECT_NE(net::OK, helper.JoinGroupSync(group_ip));
// Receive messages from itself.
std::vector<uint8_t> test_msg(CreateTestMessage(0, kDatagramSize));
net::IPEndPoint group_alias(group_ip, port);
EXPECT_EQ(net::OK, helper.SendToSync(group_alias, test_msg));
socket_ptr->ReceiveMore(1);
receiver.WaitForReceivedResults(1);
ASSERT_EQ(1u, receiver.results().size());
auto result = receiver.results()[0];
EXPECT_EQ(net::OK, result.net_error);
EXPECT_EQ(port, result.src_addr.value().port());
EXPECT_EQ(test_msg, result.data.value());
// Create a second socket to send a packet to multicast group.
mojom::UDPSocketPtr second_socket_ptr;
factory()->CreateUDPSocket(mojo::MakeRequest(&second_socket_ptr), nullptr);
test::UDPSocketTestHelper second_socket_helper(&second_socket_ptr);
net::IPEndPoint second_socket_address(bind_ip_address, 0);
ASSERT_EQ(net::OK,
second_socket_helper.BindSync(second_socket_address, nullptr,
&second_socket_address));
int second_port = second_socket_address.port();
ASSERT_EQ(net::OK, second_socket_helper.SendToSync(group_alias, test_msg));
// First socket should receive packet sent by the second socket.
socket_ptr->ReceiveMore(1);
receiver.WaitForReceivedResults(2);
ASSERT_EQ(2u, receiver.results().size());
result = receiver.results()[1];
EXPECT_EQ(net::OK, result.net_error);
EXPECT_EQ(second_port, result.src_addr.value().port());
EXPECT_EQ(test_msg, result.data.value());
// First socket leaves group multiple times.
EXPECT_EQ(net::OK, helper.LeaveGroupSync(group_ip));
EXPECT_NE(net::OK, helper.LeaveGroupSync(group_ip));
// No longer can receive messages from itself or from second socket.
EXPECT_EQ(net::OK, helper.SendToSync(group_alias, test_msg));
ASSERT_EQ(net::OK, second_socket_helper.SendToSync(group_alias, test_msg));
socket_ptr->ReceiveMore(1);
socket_ptr.FlushForTesting();
ASSERT_EQ(2u, receiver.results().size());
}
TEST_F(UDPSocketTest, ErrorHappensDuringSocketOptionsConfiguration) {
mojom::UDPSocketPtr server_socket_ptr;
factory()->CreateUDPSocket(mojo::MakeRequest(&server_socket_ptr), nullptr);
test::UDPSocketTestHelper server_helper(&server_socket_ptr);
net::IPEndPoint server_addr(GetLocalHostWithAnyPort());
ASSERT_EQ(net::OK,
server_helper.BindSync(server_addr, nullptr, &server_addr));
mojom::UDPSocketPtr socket_ptr;
factory()->CreateUDPSocket(mojo::MakeRequest(&socket_ptr), nullptr);
test::UDPSocketTestHelper helper(&socket_ptr);
// Invalid options.
mojom::UDPSocketOptionsPtr options = mojom::UDPSocketOptions::New();
options->multicast_time_to_live = 256;
net::IPEndPoint local_addr;
ASSERT_EQ(net::ERR_INVALID_ARGUMENT,
helper.ConnectSync(server_addr, std::move(options), &local_addr));
// It's legal to retry Connect() with valid options.
mojom::UDPSocketOptionsPtr valid_options = mojom::UDPSocketOptions::New();
valid_options->multicast_time_to_live = 255;
ASSERT_EQ(net::OK,
helper.ConnectSync(server_addr, std::move(options), &local_addr));
EXPECT_NE(0, local_addr.port());
}
} // namespace network