// Copyright 2013 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 "ppapi/tests/test_udp_socket.h" #include #include "ppapi/cpp/pass_ref.h" #include "ppapi/cpp/tcp_socket.h" #include "ppapi/cpp/udp_socket.h" #include "ppapi/cpp/var.h" #include "ppapi/tests/test_utils.h" #include "ppapi/tests/testing_instance.h" REGISTER_TEST_CASE(UDPSocket); namespace { const uint16_t kPortScanFrom = 1024; const uint16_t kPortScanTo = 4096; pp::NetAddress ReplacePort(const pp::InstanceHandle& instance, const pp::NetAddress& addr, uint16_t port) { switch (addr.GetFamily()) { case PP_NETADDRESS_FAMILY_IPV4: { PP_NetAddress_IPv4 ipv4_addr; if (!addr.DescribeAsIPv4Address(&ipv4_addr)) break; ipv4_addr.port = ConvertToNetEndian16(port); return pp::NetAddress(instance, ipv4_addr); } case PP_NETADDRESS_FAMILY_IPV6: { PP_NetAddress_IPv6 ipv6_addr; if (!addr.DescribeAsIPv6Address(&ipv6_addr)) break; ipv6_addr.port = ConvertToNetEndian16(port); return pp::NetAddress(instance, ipv6_addr); } default: { PP_NOTREACHED(); } } return pp::NetAddress(); } } // namespace TestUDPSocket::TestUDPSocket(TestingInstance* instance) : TestCase(instance), socket_interface_1_0_(NULL), socket_interface_1_1_(NULL) { } bool TestUDPSocket::Init() { bool tcp_socket_is_available = pp::TCPSocket::IsAvailable(); if (!tcp_socket_is_available) instance_->AppendError("PPB_TCPSocket interface not available"); bool udp_socket_is_available = pp::UDPSocket::IsAvailable(); if (!udp_socket_is_available) instance_->AppendError("PPB_UDPSocket interface not available"); bool net_address_is_available = pp::NetAddress::IsAvailable(); if (!net_address_is_available) instance_->AppendError("PPB_NetAddress interface not available"); std::string host; uint16_t port = 0; bool init_address = GetLocalHostPort(instance_->pp_instance(), &host, &port) && ResolveHost(instance_->pp_instance(), host, port, &address_); if (!init_address) instance_->AppendError("Can't init address"); socket_interface_1_0_ = static_cast( pp::Module::Get()->GetBrowserInterface(PPB_UDPSOCKET_INTERFACE_1_0)); if (!socket_interface_1_0_) instance_->AppendError("PPB_UDPSocket_1_0 interface not available"); socket_interface_1_1_ = static_cast( pp::Module::Get()->GetBrowserInterface(PPB_UDPSOCKET_INTERFACE_1_1)); if (!socket_interface_1_1_) instance_->AppendError("PPB_UDPSocket_1_1 interface not available"); return tcp_socket_is_available && udp_socket_is_available && net_address_is_available && init_address && CheckTestingInterface() && EnsureRunningOverHTTP() && socket_interface_1_0_ != NULL && socket_interface_1_1_ != NULL; } void TestUDPSocket::RunTests(const std::string& filter) { RUN_CALLBACK_TEST(TestUDPSocket, ReadWrite, filter); RUN_CALLBACK_TEST(TestUDPSocket, Broadcast, filter); RUN_CALLBACK_TEST(TestUDPSocket, SetOption_1_0, filter); RUN_CALLBACK_TEST(TestUDPSocket, SetOption_1_1, filter); RUN_CALLBACK_TEST(TestUDPSocket, SetOption, filter); RUN_CALLBACK_TEST(TestUDPSocket, ParallelSend, filter); RUN_CALLBACK_TEST(TestUDPSocket, Multicast, filter); } std::string TestUDPSocket::GetLocalAddress(pp::NetAddress* address) { pp::TCPSocket socket(instance_); TestCompletionCallback callback(instance_->pp_instance(), callback_type()); callback.WaitForResult(socket.Connect(address_, callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_OK, callback.result()); *address = socket.GetLocalAddress(); ASSERT_NE(0, address->pp_resource()); socket.Close(); PASS(); } std::string TestUDPSocket::SetBroadcastOptions(pp::UDPSocket* socket) { TestCompletionCallback callback_1(instance_->pp_instance(), callback_type()); callback_1.WaitForResult(socket->SetOption( PP_UDPSOCKET_OPTION_ADDRESS_REUSE, pp::Var(true), callback_1.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback_1); ASSERT_EQ(PP_OK, callback_1.result()); TestCompletionCallback callback_2(instance_->pp_instance(), callback_type()); callback_2.WaitForResult(socket->SetOption( PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(true), callback_2.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback_2); ASSERT_EQ(PP_OK, callback_2.result()); PASS(); } std::string TestUDPSocket::BindUDPSocket(pp::UDPSocket* socket, const pp::NetAddress& address) { TestCompletionCallback callback(instance_->pp_instance(), callback_type()); callback.WaitForResult(socket->Bind(address, callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_OK, callback.result()); PASS(); } std::string TestUDPSocket::LookupPortAndBindUDPSocket( pp::UDPSocket* socket, pp::NetAddress* address) { pp::NetAddress base_address; ASSERT_SUBTEST_SUCCESS(GetLocalAddress(&base_address)); bool is_free_port_found = false; std::string ret; for (uint16_t port = kPortScanFrom; port < kPortScanTo; ++port) { pp::NetAddress new_address = ReplacePort(instance_, base_address, port); ASSERT_NE(0, new_address.pp_resource()); ret = BindUDPSocket(socket, new_address); if (ret.empty()) { is_free_port_found = true; break; } } if (!is_free_port_found) return "Can't find available port (" + ret + ")"; *address = socket->GetBoundAddress(); ASSERT_NE(0, address->pp_resource()); PASS(); } std::string TestUDPSocket::ReadSocket(pp::UDPSocket* socket, pp::NetAddress* address, size_t size, std::string* message) { std::vector buffer(size); TestCompletionCallbackWithOutput callback( instance_->pp_instance(), callback_type()); callback.WaitForResult( socket->RecvFrom(&buffer[0], static_cast(size), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_FALSE(callback.result() < 0); ASSERT_EQ(size, static_cast(callback.result())); *address = callback.output(); message->assign(buffer.begin(), buffer.end()); PASS(); } std::string TestUDPSocket::PassMessage(pp::UDPSocket* target, pp::UDPSocket* source, const pp::NetAddress& target_address, const std::string& message, pp::NetAddress* recvfrom_address) { TestCompletionCallback callback(instance_->pp_instance(), callback_type()); int32_t rv = source->SendTo(message.c_str(), static_cast(message.size()), target_address, callback.GetCallback()); std::string str; ASSERT_SUBTEST_SUCCESS(ReadSocket(target, recvfrom_address, message.size(), &str)); callback.WaitForResult(rv); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_FALSE(callback.result() < 0); ASSERT_EQ(message.size(), static_cast(callback.result())); ASSERT_EQ(message, str); PASS(); } std::string TestUDPSocket::SetMulticastOptions(pp::UDPSocket* socket) { TestCompletionCallback callback(instance_->pp_instance(), callback_type()); callback.WaitForResult(socket->SetOption( PP_UDPSOCKET_OPTION_MULTICAST_LOOP, pp::Var(true), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_OK, callback.result()); callback.WaitForResult(socket->SetOption( PP_UDPSOCKET_OPTION_MULTICAST_TTL, pp::Var(1), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_OK, callback.result()); PASS(); } std::string TestUDPSocket::TestReadWrite() { pp::UDPSocket server_socket(instance_), client_socket(instance_); pp::NetAddress server_address, client_address; ASSERT_SUBTEST_SUCCESS(LookupPortAndBindUDPSocket(&server_socket, &server_address)); ASSERT_SUBTEST_SUCCESS(LookupPortAndBindUDPSocket(&client_socket, &client_address)); const std::string message = "Simple message that will be sent via UDP"; pp::NetAddress recvfrom_address; ASSERT_SUBTEST_SUCCESS(PassMessage(&server_socket, &client_socket, server_address, message, &recvfrom_address)); ASSERT_TRUE(EqualNetAddress(recvfrom_address, client_address)); server_socket.Close(); client_socket.Close(); if (server_socket.GetBoundAddress().pp_resource() != 0) return "PPB_UDPSocket::GetBoundAddress: expected failure"; PASS(); } std::string TestUDPSocket::TestBroadcast() { pp::UDPSocket server1(instance_), server2(instance_); ASSERT_SUBTEST_SUCCESS(SetBroadcastOptions(&server1)); ASSERT_SUBTEST_SUCCESS(SetBroadcastOptions(&server2)); PP_NetAddress_IPv4 any_ipv4_address = { 0, { 0, 0, 0, 0 } }; pp::NetAddress any_address(instance_, any_ipv4_address); ASSERT_SUBTEST_SUCCESS(BindUDPSocket(&server1, any_address)); // Fill port field of |server_address|. pp::NetAddress server_address = server1.GetBoundAddress(); ASSERT_NE(0, server_address.pp_resource()); ASSERT_SUBTEST_SUCCESS(BindUDPSocket(&server2, server_address)); PP_NetAddress_IPv4 server_ipv4_address; ASSERT_TRUE(server_address.DescribeAsIPv4Address(&server_ipv4_address)); PP_NetAddress_IPv4 broadcast_ipv4_address = { server_ipv4_address.port, { 0xff, 0xff, 0xff, 0xff } }; pp::NetAddress broadcast_address(instance_, broadcast_ipv4_address); std::string message; const std::string first_message = "first message"; const std::string second_message = "second_message"; pp::NetAddress recvfrom_address; ASSERT_SUBTEST_SUCCESS(PassMessage(&server1, &server2, broadcast_address, first_message, &recvfrom_address)); // |first_message| was also received by |server2|. ASSERT_SUBTEST_SUCCESS(ReadSocket(&server2, &recvfrom_address, first_message.size(), &message)); ASSERT_EQ(first_message, message); ASSERT_SUBTEST_SUCCESS(PassMessage(&server2, &server1, broadcast_address, second_message, &recvfrom_address)); // |second_message| was also received by |server1|. ASSERT_SUBTEST_SUCCESS(ReadSocket(&server1, &recvfrom_address, second_message.size(), &message)); ASSERT_EQ(second_message, message); server1.Close(); server2.Close(); PASS(); } int32_t TestUDPSocket::SetOptionValue(UDPSocketSetOption func, PP_Resource socket, PP_UDPSocket_Option option, const PP_Var& value) { PP_TimeTicks start_time(NowInTimeTicks()); TestCompletionCallback cb(instance_->pp_instance(), callback_type()); cb.WaitForResult(func(socket, option, value, cb.GetCallback().pp_completion_callback())); // Expanded from CHECK_CALLBACK_BEHAVIOR macro. if (cb.failed()) { std::string msg = MakeFailureMessage(__FILE__, __LINE__, cb.errors().c_str()); instance_->LogTest("SetOptionValue", msg, start_time); return PP_ERROR_FAILED; } return cb.result(); } std::string TestUDPSocket::TestSetOption_1_0() { PP_Resource socket = socket_interface_1_0_->Create(instance_->pp_instance()); ASSERT_NE(0, socket); // Multicast options are not supported in interface 1.0. ASSERT_EQ(PP_ERROR_BADARGUMENT, SetOptionValue(socket_interface_1_0_->SetOption, socket, PP_UDPSOCKET_OPTION_MULTICAST_LOOP, PP_MakeBool(PP_TRUE))); ASSERT_EQ(PP_ERROR_BADARGUMENT, SetOptionValue(socket_interface_1_0_->SetOption, socket, PP_UDPSOCKET_OPTION_MULTICAST_TTL, PP_MakeInt32(1))); socket_interface_1_0_->Close(socket); pp::Module::Get()->core()->ReleaseResource(socket); PASS(); } std::string TestUDPSocket::TestSetOption_1_1() { PP_Resource socket = socket_interface_1_1_->Create(instance_->pp_instance()); ASSERT_NE(0, socket); // Multicast options are not supported in interface 1.1. ASSERT_EQ(PP_ERROR_BADARGUMENT, SetOptionValue(socket_interface_1_1_->SetOption, socket, PP_UDPSOCKET_OPTION_MULTICAST_LOOP, PP_MakeBool(PP_TRUE))); ASSERT_EQ(PP_ERROR_BADARGUMENT, SetOptionValue(socket_interface_1_1_->SetOption, socket, PP_UDPSOCKET_OPTION_MULTICAST_TTL, PP_MakeInt32(1))); socket_interface_1_1_->Close(socket); pp::Module::Get()->core()->ReleaseResource(socket); PASS(); } std::string TestUDPSocket::TestSetOption() { pp::UDPSocket socket(instance_); ASSERT_SUBTEST_SUCCESS(SetBroadcastOptions(&socket)); ASSERT_SUBTEST_SUCCESS(SetMulticastOptions(&socket)); // Try to pass incorrect option value's type. TestCompletionCallback callback(instance_->pp_instance(), callback_type()); callback.WaitForResult(socket.SetOption( PP_UDPSOCKET_OPTION_ADDRESS_REUSE, pp::Var(1), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result()); // Invalid multicast TTL values (less than 0 and greater than 255). callback.WaitForResult(socket.SetOption( PP_UDPSOCKET_OPTION_MULTICAST_TTL, pp::Var(-1), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result()); callback.WaitForResult(socket.SetOption( PP_UDPSOCKET_OPTION_MULTICAST_TTL, pp::Var(256), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result()); callback.WaitForResult(socket.SetOption( PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(false), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_OK, callback.result()); callback.WaitForResult(socket.SetOption( PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE, pp::Var(4096), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_OK, callback.result()); callback.WaitForResult(socket.SetOption( PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE, pp::Var(512), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_OK, callback.result()); pp::NetAddress address; ASSERT_SUBTEST_SUCCESS(LookupPortAndBindUDPSocket(&socket, &address)); // ADDRESS_REUSE won't take effect after the socket is bound. callback.WaitForResult(socket.SetOption( PP_UDPSOCKET_OPTION_ADDRESS_REUSE, pp::Var(true), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_ERROR_FAILED, callback.result()); // BROADCAST, SEND_BUFFER_SIZE and RECV_BUFFER_SIZE can be set after the // socket is bound. callback.WaitForResult(socket.SetOption( PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(true), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_OK, callback.result()); callback.WaitForResult(socket.SetOption( PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE, pp::Var(2048), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_OK, callback.result()); callback.WaitForResult(socket.SetOption( PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE, pp::Var(1024), callback.GetCallback())); CHECK_CALLBACK_BEHAVIOR(callback); ASSERT_EQ(PP_OK, callback.result()); PASS(); } std::string TestUDPSocket::TestParallelSend() { // This test only makes sense when callbacks are optional. if (callback_type() != PP_OPTIONAL) PASS(); pp::UDPSocket server_socket(instance_), client_socket(instance_); pp::NetAddress server_address, client_address; ASSERT_SUBTEST_SUCCESS( LookupPortAndBindUDPSocket(&server_socket, &server_address)); ASSERT_SUBTEST_SUCCESS( LookupPortAndBindUDPSocket(&client_socket, &client_address)); const std::string message = "Simple message that will be sent via UDP"; pp::NetAddress recvfrom_address; const size_t kParallelSends = 10; std::vector sendto_callbacks(kParallelSends); std::vector sendto_results(kParallelSends); size_t pending = 0; for (size_t i = 0; i < kParallelSends; i++) { sendto_callbacks[i] = new TestCompletionCallback(instance_->pp_instance(), callback_type()); sendto_results[i] = client_socket.SendTo(message.c_str(), static_cast(message.size()), server_address, sendto_callbacks[i]->GetCallback()); if (sendto_results[i] == PP_ERROR_INPROGRESS) { // Run a pending send to completion to free a slot for the current send. ASSERT_GT(i, pending); sendto_callbacks[pending]->WaitForResult(sendto_results[pending]); CHECK_CALLBACK_BEHAVIOR(*sendto_callbacks[pending]); ASSERT_EQ(message.size(), static_cast(sendto_callbacks[pending]->result())); pending++; // Try to send the message again. sendto_results[i] = client_socket.SendTo(message.c_str(), static_cast(message.size()), server_address, sendto_callbacks[i]->GetCallback()); ASSERT_NE(PP_ERROR_INPROGRESS, sendto_results[i]); } } // Finish all pending sends. for (size_t i = pending; i < kParallelSends; i++) { sendto_callbacks[i]->WaitForResult(sendto_results[i]); CHECK_CALLBACK_BEHAVIOR(*sendto_callbacks[i]); ASSERT_EQ(message.size(), static_cast(sendto_callbacks[i]->result())); } for (size_t i = 0; i < kParallelSends; ++i) delete sendto_callbacks[i]; for (size_t i = 0; i < kParallelSends; i++) { std::string str; ASSERT_SUBTEST_SUCCESS( ReadSocket(&server_socket, &recvfrom_address, message.size(), &str)); ASSERT_EQ(message, str); } server_socket.Close(); client_socket.Close(); PASS(); } std::string TestUDPSocket::TestMulticast() { pp::UDPSocket server1(instance_), server2(instance_); ASSERT_SUBTEST_SUCCESS(SetMulticastOptions(&server1)); ASSERT_SUBTEST_SUCCESS(SetMulticastOptions(&server2)); server1.Close(); server2.Close(); PASS(); }