Index: content/browser/renderer_host/p2p_socket_host_udp_unittest.cc |
diff --git a/content/browser/renderer_host/p2p_socket_host_udp_unittest.cc b/content/browser/renderer_host/p2p_socket_host_udp_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b8e25ddbae8efa15a9050fc1345a4e7609455123 |
--- /dev/null |
+++ b/content/browser/renderer_host/p2p_socket_host_udp_unittest.cc |
@@ -0,0 +1,300 @@ |
+// Copyright (c) 2011 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 "build/build_config.h" |
+ |
+#if defined(OS_WIN) |
+#include <winsock2.h> // for htonl |
+#else |
+#include <arpa/inet.h> |
+#endif |
+ |
+#include <deque> |
+#include <vector> |
+ |
+#include "content/common/p2p_messages.h" |
+#include "net/base/io_buffer.h" |
+#include "net/base/ip_endpoint.h" |
+#include "net/base/net_errors.h" |
+#include "net/udp/datagram_server_socket.h" |
+#include "content/browser/renderer_host/p2p_socket_host_udp.h" |
jam
2011/04/08 23:38:57
nit: should move up
Sergey Ulanov
2011/04/09 00:52:36
Done.
|
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using ::testing::_; |
+using ::testing::DeleteArg; |
+using ::testing::DoAll; |
+using ::testing::Return; |
+ |
+namespace { |
+ |
+const char kTestLocalIpAddress[] = "123.44.22.4"; |
+const char kTestIpAddress1[] = "123.44.22.31"; |
+const int kTestPort1 = 234; |
+const char kTestIpAddress2[] = "133.11.22.33"; |
+const int kTestPort2 = 543; |
+ |
+const int kStunHeaderSize = 20; |
+const uint16 kStunBindingRequest = 0x0001; |
+const uint16 kStunBindingResponse = 0x0102; |
+const uint16 kStunBindingError = 0x0111; |
+const uint32 kStunMagicCookie = 0x2112A442; |
+ |
+class FakeDatagramServerSocket : public net::DatagramServerSocket { |
+ public: |
+ typedef std::pair<net::IPEndPoint, std::vector<char> > UDPPacket; |
+ |
+ // P2PSocketHostUdp destroyes a socket on errors so sent packets |
+ // need to be stored outside of this object. |
+ explicit FakeDatagramServerSocket(std::deque<UDPPacket>* sent_packets) |
+ : sent_packets_(sent_packets), recv_callback_(NULL) { |
+ } |
+ |
+ virtual void Close() OVERRIDE { |
+ } |
+ |
+ virtual int GetPeerAddress(net::IPEndPoint* address) const OVERRIDE { |
+ NOTREACHED(); |
+ return net::ERR_SOCKET_NOT_CONNECTED; |
+ } |
+ |
+ virtual int GetLocalAddress(net::IPEndPoint* address) const OVERRIDE { |
+ *address = address_; |
+ return 0; |
+ } |
+ |
+ virtual int Listen(const net::IPEndPoint& address) OVERRIDE { |
+ address_ = address; |
+ return 0; |
+ } |
+ |
+ virtual int RecvFrom(net::IOBuffer* buf, int buf_len, |
+ net::IPEndPoint* address, |
+ net::CompletionCallback* callback) OVERRIDE { |
+ CHECK(!recv_callback_); |
+ if (incoming_packets_.size() > 0) { |
+ scoped_refptr<net::IOBuffer> buffer(buf); |
+ int size = std::min( |
+ static_cast<int>(incoming_packets_.front().second.size()), buf_len); |
+ memcpy(buffer->data(), &*incoming_packets_.front().second.begin(), size); |
+ *address = incoming_packets_.front().first; |
+ incoming_packets_.pop_front(); |
+ return size; |
+ } else { |
+ recv_callback_ = callback; |
+ recv_buffer_ = buf; |
+ recv_size_ = buf_len; |
+ recv_address_ = address; |
+ return net::ERR_IO_PENDING; |
+ } |
+ } |
+ |
+ virtual int SendTo(net::IOBuffer* buf, int buf_len, |
+ const net::IPEndPoint& address, |
+ net::CompletionCallback* callback) OVERRIDE { |
+ scoped_refptr<net::IOBuffer> buffer(buf); |
+ std::vector<char> data_vector(buffer->data(), buffer->data() + buf_len); |
+ sent_packets_->push_back(UDPPacket(address, data_vector)); |
+ return buf_len; |
+ } |
+ |
+ void ReceivePacket(const net::IPEndPoint& address, std::vector<char> data) { |
+ if (recv_callback_) { |
+ int size = std::min(recv_size_, static_cast<int>(data.size())); |
+ memcpy(recv_buffer_->data(), &*data.begin(), size); |
+ *recv_address_ = address; |
+ net::CompletionCallback* cb = recv_callback_; |
+ recv_callback_ = NULL; |
+ recv_buffer_ = NULL; |
+ cb->Run(size); |
+ } else { |
+ incoming_packets_.push_back(UDPPacket(address, data)); |
+ } |
+ } |
+ |
+ private: |
+ net::IPEndPoint address_; |
+ std::deque<UDPPacket>* sent_packets_; |
+ std::deque<UDPPacket> incoming_packets_; |
+ |
+ scoped_refptr<net::IOBuffer> recv_buffer_; |
+ net::IPEndPoint* recv_address_; |
+ int recv_size_; |
+ net::CompletionCallback* recv_callback_; |
+}; |
+ |
+class MockIPCSender : public IPC::Message::Sender { |
+ public: |
+ MOCK_METHOD1(Send, bool(IPC::Message* msg)); |
+}; |
+ |
+MATCHER_P(MatchMessage, type, "") { |
+ return arg->type() == type; |
+} |
+ |
+} // namespace |
+ |
+class P2PSocketHostUdpTest : public testing::Test { |
+ protected: |
+ void SetUp() OVERRIDE { |
+ EXPECT_CALL(sender_, Send( |
+ MatchMessage(static_cast<uint32>(P2PMsg_OnSocketCreated::ID)))) |
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true))); |
+ |
+ socket_host_.reset(new P2PSocketHostUdp(&sender_, 0, 0)); |
+ socket_ = new FakeDatagramServerSocket(&sent_packets_); |
+ socket_host_->socket_.reset(socket_); |
+ |
+ net::IPAddressNumber local_ip; |
+ ASSERT_TRUE(net::ParseIPLiteralToNumber(kTestLocalIpAddress, &local_ip)); |
+ local_address_ = net::IPEndPoint(local_ip, kTestPort1); |
+ socket_host_->Init(local_address_); |
+ |
+ net::IPAddressNumber ip1; |
+ ASSERT_TRUE(net::ParseIPLiteralToNumber(kTestIpAddress1, &ip1)); |
+ dest1_ = net::IPEndPoint(ip1, kTestPort1); |
+ net::IPAddressNumber ip2; |
+ ASSERT_TRUE(net::ParseIPLiteralToNumber(kTestIpAddress2, &ip2)); |
+ dest2_ = net::IPEndPoint(ip2, kTestPort2); |
+ } |
+ |
+ void CreateRandomPacket(std::vector<char>* packet) { |
+ size_t size = kStunHeaderSize + rand() % 1000; |
+ packet->resize(size); |
+ for (size_t i = 0; i < size; i++) { |
+ (*packet)[i] = rand() % 256; |
+ } |
+ // Always set the first bit to ensure that generated packet is not |
+ // valid STUN packet. |
+ (*packet)[0] = (*packet)[0] | 0x80; |
+ } |
+ |
+ void CreateStunPacket(std::vector<char>* packet, uint16 type) { |
+ CreateRandomPacket(packet); |
+ *reinterpret_cast<uint16*>(&*packet->begin()) = htons(type); |
+ *reinterpret_cast<uint16*>(&*packet->begin() + 2) = |
+ htons(packet->size() - kStunHeaderSize); |
+ *reinterpret_cast<uint32*>(&*packet->begin() + 4) = htonl(kStunMagicCookie); |
+ } |
+ |
+ void CreateStunRequest(std::vector<char>* packet) { |
+ CreateStunPacket(packet, kStunBindingRequest); |
+ } |
+ |
+ void CreateStunResponse(std::vector<char>* packet) { |
+ CreateStunPacket(packet, kStunBindingResponse); |
+ } |
+ |
+ void CreateStunError(std::vector<char>* packet) { |
+ CreateStunPacket(packet, kStunBindingError); |
+ } |
+ |
+ std::deque<FakeDatagramServerSocket::UDPPacket> sent_packets_; |
+ FakeDatagramServerSocket* socket_; // Owned by |socket_host_|. |
+ scoped_ptr<P2PSocketHostUdp> socket_host_; |
+ MockIPCSender sender_; |
+ |
+ net::IPEndPoint local_address_; |
+ |
+ net::IPEndPoint dest1_; |
+ net::IPEndPoint dest2_; |
+}; |
+ |
+// Verify that we can send STUN messages before we receive anything |
+// from the other side. |
+TEST_F(P2PSocketHostUdpTest, SendStunNoAuth) { |
+ std::vector<char> packet1; |
+ CreateStunRequest(&packet1); |
+ socket_host_->Send(dest1_, packet1); |
+ |
+ std::vector<char> packet2; |
+ CreateStunResponse(&packet2); |
+ socket_host_->Send(dest1_, packet2); |
+ |
+ std::vector<char> packet3; |
+ CreateStunError(&packet3); |
+ socket_host_->Send(dest1_, packet3); |
+ |
+ ASSERT_EQ(sent_packets_.size(), 3U); |
+ ASSERT_EQ(sent_packets_[0].second, packet1); |
+ ASSERT_EQ(sent_packets_[1].second, packet2); |
+ ASSERT_EQ(sent_packets_[2].second, packet3); |
+} |
+ |
+// Verify that no data packets can be sent before STUN binding has |
+// finished. |
+TEST_F(P2PSocketHostUdpTest, SendDataNoAuth) { |
+ EXPECT_CALL(sender_, Send( |
+ MatchMessage(static_cast<uint32>(P2PMsg_OnError::ID)))) |
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true))); |
+ |
+ std::vector<char> packet; |
+ CreateRandomPacket(&packet); |
+ socket_host_->Send(dest1_, packet); |
+ |
+ ASSERT_EQ(sent_packets_.size(), 0U); |
+} |
+ |
+// Verify that we can send data after we've received STUN request |
+// from the other side. |
+TEST_F(P2PSocketHostUdpTest, SendAfterStunRequest) { |
+ EXPECT_CALL(sender_, Send( |
+ MatchMessage(static_cast<uint32>(P2PMsg_OnDataReceived::ID)))) |
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true))); |
+ |
+ // Receive packet from |dest1_|. |
+ std::vector<char> request_packet; |
+ CreateStunRequest(&request_packet); |
+ socket_->ReceivePacket(dest1_, request_packet); |
+ |
+ // Now we should be able to send any data to |dest1_|. |
+ std::vector<char> packet; |
+ CreateRandomPacket(&packet); |
+ socket_host_->Send(dest1_, packet); |
+ |
+ ASSERT_EQ(1U, sent_packets_.size()); |
+ ASSERT_EQ(dest1_, sent_packets_[0].first); |
+} |
+ |
+// Verify that we can send data after we've received STUN response |
+// from the other side. |
+TEST_F(P2PSocketHostUdpTest, SendAfterStunResponse) { |
+ EXPECT_CALL(sender_, Send( |
+ MatchMessage(static_cast<uint32>(P2PMsg_OnDataReceived::ID)))) |
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true))); |
+ |
+ // Receive packet from |dest1_|. |
+ std::vector<char> request_packet; |
+ CreateStunRequest(&request_packet); |
+ socket_->ReceivePacket(dest1_, request_packet); |
+ |
+ // Now we should be able to send any data to |dest1_|. |
+ std::vector<char> packet; |
+ CreateRandomPacket(&packet); |
+ socket_host_->Send(dest1_, packet); |
+ |
+ ASSERT_EQ(1U, sent_packets_.size()); |
+ ASSERT_EQ(dest1_, sent_packets_[0].first); |
+} |
+ |
+// Verify messages still cannot be sent to an unathorized host after |
+// successful binding with different host. |
+TEST_F(P2PSocketHostUdpTest, SendAfterStunResponseDifferentHost) { |
+ EXPECT_CALL(sender_, Send( |
+ MatchMessage(static_cast<uint32>(P2PMsg_OnDataReceived::ID)))) |
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true))); |
+ |
+ // Receive packet from |dest1_|. |
+ std::vector<char> request_packet; |
+ CreateStunRequest(&request_packet); |
+ socket_->ReceivePacket(dest1_, request_packet); |
+ |
+ // Should fail when trying to send the same packet to |dest2_|. |
+ std::vector<char> packet; |
+ CreateRandomPacket(&packet); |
+ EXPECT_CALL(sender_, Send( |
+ MatchMessage(static_cast<uint32>(P2PMsg_OnError::ID)))) |
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true))); |
+ socket_host_->Send(dest2_, packet); |
+} |