| Index: webrtc/p2p/base/p2ptransportchannel_unittest.cc
|
| diff --git a/webrtc/p2p/base/p2ptransportchannel_unittest.cc b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
|
| index 68c46d6a74da4c3553cbcfc0877535a611aa9c7d..20c43d3873440846058e45387050b2bcb228eec1 100644
|
| --- a/webrtc/p2p/base/p2ptransportchannel_unittest.cc
|
| +++ b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
|
| @@ -1884,3 +1884,181 @@ TEST_F(P2PTransportChannelPingTest, TestReceivingStateChange) {
|
| EXPECT_TRUE_WAIT(ch.receiving(), 1000);
|
| EXPECT_TRUE_WAIT(!ch.receiving(), 1000);
|
| }
|
| +
|
| +// When the p2p transport channel with a controlled role received a candidate,
|
| +// it should set up the best connection if the best connection was not nominated
|
| +// by the controlling side (via use_candidate attribute). If the best connection
|
| +// was nominated by the controlling side, the connection receiving a new
|
| +// candidate will not be set as the best connection.
|
| +TEST_F(P2PTransportChannelPingTest, TestPassiveAggressiveNomination) {
|
| + cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
|
| + cricket::P2PTransportChannel ch("receiving state change", 1, nullptr, &pa);
|
| + PrepareChannel(&ch);
|
| + ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
|
| + ch.Connect();
|
| + ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
|
| + cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
| + ASSERT_TRUE(conn1 != nullptr);
|
| + EXPECT_EQ(conn1, ch.best_connection());
|
| +
|
| + // When a higher priority candidate comes in, the new connection is chosen
|
| + // as the best connection.
|
| + ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 10));
|
| + cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
| + ASSERT_TRUE(conn1 != nullptr);
|
| + EXPECT_EQ(conn2, ch.best_connection());
|
| +
|
| + // If a stun request with use-candidate attribute arrives, the receiving
|
| + // connection will be set as the best connection, even though
|
| + // its priority is lower.
|
| + ch.OnCandidate(CreateCandidate("3.3.3.3", 3, 1));
|
| + cricket::Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3);
|
| + ASSERT_TRUE(conn3 != nullptr);
|
| + // Because it has a lower priority, the best connection is still conn2.
|
| + EXPECT_EQ(conn2, ch.best_connection());
|
| + conn3->ReceivedPingResponse(); // Become writable.
|
| + // But if it is nominated via use_candidate, it is chosen as the best
|
| + // connection.
|
| + conn3->set_nominated(true);
|
| + EXPECT_EQ(conn3, ch.best_connection());
|
| +
|
| + // Even if another higher priority candidate arrives,
|
| + // it will not be set as the best connection because the best connection
|
| + // is nominated by the controlling side.
|
| + ch.OnCandidate(CreateCandidate("4.4.4.4", 4, 100));
|
| + cricket::Connection* conn4 = WaitForConnectionTo(&ch, "4.4.4.4", 4);
|
| + ASSERT_TRUE(conn4 != nullptr);
|
| + EXPECT_EQ(conn3, ch.best_connection());
|
| + // But if it is nominated via use_candidate and writable, it will be set as
|
| + // the best connection.
|
| + conn4->set_nominated(true);
|
| + // Not switched yet because conn4 is not writable.
|
| + EXPECT_EQ(conn3, ch.best_connection());
|
| + // The best connection switches after conn4 becomes writable.
|
| + conn4->ReceivedPingResponse();
|
| + EXPECT_EQ(conn4, ch.best_connection());
|
| +}
|
| +
|
| +// For the controlled side, when a stun request with an unknown address is
|
| +// received, it will be selected as the best connection if the best connection
|
| +// is not nominated by the controlling side and vice versa. If the received
|
| +// stun request with an unknown address contains a use_candidate attribute, it
|
| +// will be treated as a nomination from the controlling side.
|
| +TEST_F(P2PTransportChannelPingTest, TestReceivingUnknownAddress) {
|
| + cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
|
| + cricket::P2PTransportChannel ch("receiving state change", 1, nullptr, &pa);
|
| + PrepareChannel(&ch);
|
| + ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
|
| + ch.Connect();
|
| + // A minimal STUN message with prflx priority.
|
| + cricket::IceMessage request;
|
| + request.SetType(cricket::STUN_BINDING_REQUEST);
|
| + request.AddAttribute(new cricket::StunByteStringAttribute(
|
| + cricket::STUN_ATTR_USERNAME, kIceUfrag[1]));
|
| + uint32 prflx_priority = cricket::ICE_TYPE_PREFERENCE_PRFLX << 24;
|
| + request.AddAttribute(new cricket::StunUInt32Attribute(
|
| + cricket::STUN_ATTR_PRIORITY, prflx_priority));
|
| + cricket::Port* port = GetPort(&ch);
|
| + port->SignalUnknownAddress(port, rtc::SocketAddress("1.1.1.1", 1),
|
| + cricket::PROTO_UDP, &request, kIceUfrag[1], false);
|
| + cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
| + ASSERT_TRUE(conn1 != nullptr);
|
| + EXPECT_EQ(conn1, ch.best_connection());
|
| + conn1->ReceivedPingResponse();
|
| + EXPECT_EQ(conn1, ch.best_connection());
|
| +
|
| + // Another connection is nominated via use_candidate.
|
| + ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 1));
|
| + cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
| + ASSERT_TRUE(conn2 != nullptr);
|
| + // Because it has a lower priority, the best connection is still conn1.
|
| + EXPECT_EQ(conn1, ch.best_connection());
|
| + // When it is nominated via use_candidate and writable, it is chosen as the
|
| + // best connection.
|
| + conn2->ReceivedPingResponse(); // Become writable.
|
| + conn2->set_nominated(true);
|
| + EXPECT_EQ(conn2, ch.best_connection());
|
| +
|
| + // Another request with unknown address, it will not be set as the best
|
| + // connection because the best connection was nominated by the controlling
|
| + // side.
|
| + port->SignalUnknownAddress(port, rtc::SocketAddress("3.3.3.3", 3),
|
| + cricket::PROTO_UDP, &request, kIceUfrag[1], false);
|
| + cricket::Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3);
|
| + ASSERT_TRUE(conn3 != nullptr);
|
| + conn3->ReceivedPingResponse(); // Become writable.
|
| + EXPECT_EQ(conn2, ch.best_connection());
|
| +
|
| + // However if the request contains use_candidate attribute, it will be
|
| + // selected as the best connection.
|
| + request.AddAttribute(
|
| + new cricket::StunByteStringAttribute(cricket::STUN_ATTR_USE_CANDIDATE));
|
| + port->SignalUnknownAddress(port, rtc::SocketAddress("4.4.4.4", 4),
|
| + cricket::PROTO_UDP, &request, kIceUfrag[1], false);
|
| + cricket::Connection* conn4 = WaitForConnectionTo(&ch, "4.4.4.4", 4);
|
| + ASSERT_TRUE(conn4 != nullptr);
|
| + // conn4 is not the best connection yet because it is not writable.
|
| + EXPECT_EQ(conn2, ch.best_connection());
|
| + conn4->ReceivedPingResponse(); // Become writable.
|
| + EXPECT_EQ(conn4, ch.best_connection());
|
| +}
|
| +
|
| +// For the controlled side, if a data packet is received on a connection,
|
| +// the sending path will mirror the receiving path if it is writable and
|
| +// the current best connection is not nominated by the controlling side.
|
| +// If the current best connection is already nominated by the controlling side,
|
| +// it will not switch the best connection (the sending path).
|
| +TEST_F(P2PTransportChannelPingTest, TestSendingPathMirrorReceivingPath) {
|
| + cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
|
| + cricket::P2PTransportChannel ch("receiving state change", 1, nullptr, &pa);
|
| + PrepareChannel(&ch);
|
| + ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
|
| + ch.Connect();
|
| + ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 10));
|
| + cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
| + ASSERT_TRUE(conn1 != nullptr);
|
| + EXPECT_EQ(conn1, ch.best_connection());
|
| +
|
| + // If a data packet is received on conn2, the best connection should
|
| + // switch to conn2 because the controlled side must mirror the media path
|
| + // chosen by the controlling side.
|
| + ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 1));
|
| + cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
| + ASSERT_TRUE(conn2 != nullptr);
|
| + conn2->ReceivedPing(); // Become readable.
|
| + // Do not switch because it is not writable.
|
| + conn2->OnReadPacket("ABC", 3, rtc::CreatePacketTime(0));
|
| + EXPECT_EQ(conn1, ch.best_connection());
|
| +
|
| + conn2->ReceivedPingResponse(); // Become writable.
|
| + // Switch because it is writable.
|
| + conn2->OnReadPacket("DEF", 3, rtc::CreatePacketTime(0));
|
| + EXPECT_EQ(conn2, ch.best_connection());
|
| +
|
| + // Now another STUN message with an unknown address and use_candidate will
|
| + // nominate the best connection.
|
| + cricket::IceMessage request;
|
| + request.SetType(cricket::STUN_BINDING_REQUEST);
|
| + request.AddAttribute(new cricket::StunByteStringAttribute(
|
| + cricket::STUN_ATTR_USERNAME, kIceUfrag[1]));
|
| + uint32 prflx_priority = cricket::ICE_TYPE_PREFERENCE_PRFLX << 24;
|
| + request.AddAttribute(new cricket::StunUInt32Attribute(
|
| + cricket::STUN_ATTR_PRIORITY, prflx_priority));
|
| + request.AddAttribute(
|
| + new cricket::StunByteStringAttribute(cricket::STUN_ATTR_USE_CANDIDATE));
|
| + cricket::Port* port = GetPort(&ch);
|
| + port->SignalUnknownAddress(port, rtc::SocketAddress("3.3.3.3", 3),
|
| + cricket::PROTO_UDP, &request, kIceUfrag[1], false);
|
| + cricket::Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3);
|
| + ASSERT_TRUE(conn3 != nullptr);
|
| + EXPECT_EQ(conn2, ch.best_connection()); // Not writable yet.
|
| + conn3->ReceivedPingResponse(); // Become writable.
|
| + EXPECT_EQ(conn3, ch.best_connection());
|
| +
|
| + // Now another data packet will not switch the best connection because the
|
| + // best connection was nominated by the controlling side.
|
| + conn2->ReceivedPing();
|
| + conn2->ReceivedPingResponse();
|
| + conn2->OnReadPacket("XYZ", 3, rtc::CreatePacketTime(0));
|
| + EXPECT_EQ(conn3, ch.best_connection());
|
| +}
|
|
|