普通文本  |  524行  |  18 KB

// Copyright (c) 2012 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 "remoting/protocol/jingle_session.h"

#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/test_timeouts.h"
#include "base/time/time.h"
#include "jingle/glue/thread_wrapper.h"
#include "net/socket/socket.h"
#include "net/socket/stream_socket.h"
#include "net/url_request/url_request_context_getter.h"
#include "remoting/base/constants.h"
#include "remoting/jingle_glue/chromium_port_allocator.h"
#include "remoting/jingle_glue/fake_signal_strategy.h"
#include "remoting/jingle_glue/network_settings.h"
#include "remoting/protocol/authenticator.h"
#include "remoting/protocol/channel_authenticator.h"
#include "remoting/protocol/connection_tester.h"
#include "remoting/protocol/fake_authenticator.h"
#include "remoting/protocol/jingle_session_manager.h"
#include "remoting/protocol/libjingle_transport_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;
using testing::AtLeast;
using testing::AtMost;
using testing::DeleteArg;
using testing::DoAll;
using testing::InSequence;
using testing::Invoke;
using testing::InvokeWithoutArgs;
using testing::Return;
using testing::SaveArg;
using testing::SetArgumentPointee;
using testing::WithArg;

namespace remoting {
namespace protocol {

namespace {

const char kHostJid[] = "host1@gmail.com/123";
const char kClientJid[] = "host2@gmail.com/321";

// Send 100 messages 1024 bytes each. UDP messages are sent with 10ms delay
// between messages (about 1 second for 100 messages).
const int kMessageSize = 1024;
const int kMessages = 100;
const char kChannelName[] = "test_channel";

void QuitCurrentThread() {
  base::MessageLoop::current()->PostTask(FROM_HERE,
                                         base::MessageLoop::QuitClosure());
}

ACTION(QuitThread) {
  QuitCurrentThread();
}

ACTION_P(QuitThreadOnCounter, counter) {
  --(*counter);
  EXPECT_GE(*counter, 0);
  if (*counter == 0)
    QuitCurrentThread();
}

class MockSessionManagerListener : public SessionManager::Listener {
 public:
  MOCK_METHOD0(OnSessionManagerReady, void());
  MOCK_METHOD2(OnIncomingSession,
               void(Session*,
                    SessionManager::IncomingSessionResponse*));
};

class MockSessionEventHandler : public Session::EventHandler {
 public:
  MOCK_METHOD1(OnSessionStateChange, void(Session::State));
  MOCK_METHOD2(OnSessionRouteChange, void(const std::string& channel_name,
                                          const TransportRoute& route));
};

class MockStreamChannelCallback {
 public:
  MOCK_METHOD1(OnDone, void(net::StreamSocket* socket));
};

}  // namespace

class JingleSessionTest : public testing::Test {
 public:
  JingleSessionTest() {
    message_loop_.reset(new base::MessageLoopForIO());
    jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
  }

  // Helper method that handles OnIncomingSession().
  void SetHostSession(Session* session) {
    DCHECK(session);
    host_session_.reset(session);
    host_session_->SetEventHandler(&host_session_event_handler_);

    session->set_config(SessionConfig::ForTest());
  }

  void DeleteSession() {
    host_session_.reset();
  }

  void OnClientChannelCreated(scoped_ptr<net::StreamSocket> socket) {
    client_channel_callback_.OnDone(socket.get());
    client_socket_ = socket.Pass();
  }

  void OnHostChannelCreated(scoped_ptr<net::StreamSocket> socket) {
    host_channel_callback_.OnDone(socket.get());
    host_socket_ = socket.Pass();
  }

 protected:
  virtual void SetUp() {
  }

  virtual void TearDown() {
    CloseSessions();
    CloseSessionManager();
    base::RunLoop().RunUntilIdle();
  }

  void CloseSessions() {
    host_socket_.reset();
    host_session_.reset();
    client_socket_.reset();
    client_session_.reset();
  }

  void CreateSessionManagers(int auth_round_trips, int messages_till_start,
                        FakeAuthenticator::Action auth_action) {
    host_signal_strategy_.reset(new FakeSignalStrategy(kHostJid));
    client_signal_strategy_.reset(new FakeSignalStrategy(kClientJid));
    FakeSignalStrategy::Connect(host_signal_strategy_.get(),
                                client_signal_strategy_.get());

    EXPECT_CALL(host_server_listener_, OnSessionManagerReady())
        .Times(1);

    NetworkSettings network_settings(NetworkSettings::NAT_TRAVERSAL_OUTGOING);

    scoped_ptr<TransportFactory> host_transport(new LibjingleTransportFactory(
        NULL,
        ChromiumPortAllocator::Create(NULL, network_settings)
            .PassAs<cricket::HttpPortAllocatorBase>(),
        network_settings));
    host_server_.reset(new JingleSessionManager(host_transport.Pass()));
    host_server_->Init(host_signal_strategy_.get(), &host_server_listener_);

    scoped_ptr<AuthenticatorFactory> factory(
        new FakeHostAuthenticatorFactory(auth_round_trips,
          messages_till_start, auth_action, true));
    host_server_->set_authenticator_factory(factory.Pass());

    EXPECT_CALL(client_server_listener_, OnSessionManagerReady())
        .Times(1);
    scoped_ptr<TransportFactory> client_transport(new LibjingleTransportFactory(
        NULL,
        ChromiumPortAllocator::Create(NULL, network_settings)
            .PassAs<cricket::HttpPortAllocatorBase>(),
        network_settings));
    client_server_.reset(
        new JingleSessionManager(client_transport.Pass()));
    client_server_->Init(client_signal_strategy_.get(),
                         &client_server_listener_);
  }

  void CreateSessionManagers(int auth_round_trips,
                             FakeAuthenticator::Action auth_action) {
    CreateSessionManagers(auth_round_trips, 0, auth_action);
  }

  void CloseSessionManager() {
    if (host_server_.get()) {
      host_server_->Close();
      host_server_.reset();
    }
    if (client_server_.get()) {
      client_server_->Close();
      client_server_.reset();
    }
    host_signal_strategy_.reset();
    client_signal_strategy_.reset();
  }

  void InitiateConnection(int auth_round_trips,
                          FakeAuthenticator::Action auth_action,
                          bool expect_fail) {
    EXPECT_CALL(host_server_listener_, OnIncomingSession(_, _))
        .WillOnce(DoAll(
            WithArg<0>(Invoke(this, &JingleSessionTest::SetHostSession)),
            SetArgumentPointee<1>(protocol::SessionManager::ACCEPT)));

    {
      InSequence dummy;

      EXPECT_CALL(host_session_event_handler_,
                  OnSessionStateChange(Session::CONNECTED))
          .Times(AtMost(1));
      EXPECT_CALL(host_session_event_handler_,
                  OnSessionStateChange(Session::AUTHENTICATING))
          .Times(AtMost(1));
      if (expect_fail) {
        EXPECT_CALL(host_session_event_handler_,
                    OnSessionStateChange(Session::FAILED))
            .Times(1);
      } else {
        EXPECT_CALL(host_session_event_handler_,
                    OnSessionStateChange(Session::AUTHENTICATED))
            .Times(1);
        // Expect that the connection will be closed eventually.
        EXPECT_CALL(host_session_event_handler_,
                    OnSessionStateChange(Session::CLOSED))
            .Times(AtMost(1));
      }
    }

    {
      InSequence dummy;

      EXPECT_CALL(client_session_event_handler_,
                  OnSessionStateChange(Session::CONNECTED))
          .Times(AtMost(1));
      EXPECT_CALL(client_session_event_handler_,
                  OnSessionStateChange(Session::AUTHENTICATING))
          .Times(AtMost(1));
      if (expect_fail) {
        EXPECT_CALL(client_session_event_handler_,
                    OnSessionStateChange(Session::FAILED))
            .Times(1);
      } else {
        EXPECT_CALL(client_session_event_handler_,
                    OnSessionStateChange(Session::AUTHENTICATED))
            .Times(1);
        // Expect that the connection will be closed eventually.
        EXPECT_CALL(client_session_event_handler_,
                    OnSessionStateChange(Session::CLOSED))
            .Times(AtMost(1));
      }
    }

    scoped_ptr<Authenticator> authenticator(new FakeAuthenticator(
        FakeAuthenticator::CLIENT, auth_round_trips, auth_action, true));

    client_session_ = client_server_->Connect(
        kHostJid, authenticator.Pass(),
        CandidateSessionConfig::CreateDefault());
    client_session_->SetEventHandler(&client_session_event_handler_);

    base::RunLoop().RunUntilIdle();
  }

  void CreateChannel() {
    client_session_->GetTransportChannelFactory()->CreateStreamChannel(
        kChannelName, base::Bind(&JingleSessionTest::OnClientChannelCreated,
                                 base::Unretained(this)));
    host_session_->GetTransportChannelFactory()->CreateStreamChannel(
        kChannelName, base::Bind(&JingleSessionTest::OnHostChannelCreated,
                                 base::Unretained(this)));

    int counter = 2;
    ExpectRouteChange(kChannelName);
    EXPECT_CALL(client_channel_callback_, OnDone(_))
        .WillOnce(QuitThreadOnCounter(&counter));
    EXPECT_CALL(host_channel_callback_, OnDone(_))
        .WillOnce(QuitThreadOnCounter(&counter));
    message_loop_->Run();

    EXPECT_TRUE(client_socket_.get());
    EXPECT_TRUE(host_socket_.get());
  }

  void ExpectRouteChange(const std::string& channel_name) {
    EXPECT_CALL(host_session_event_handler_,
                OnSessionRouteChange(channel_name, _))
        .Times(AtLeast(1));
    EXPECT_CALL(client_session_event_handler_,
                OnSessionRouteChange(channel_name, _))
        .Times(AtLeast(1));
  }

  scoped_ptr<base::MessageLoopForIO> message_loop_;

  scoped_ptr<FakeSignalStrategy> host_signal_strategy_;
  scoped_ptr<FakeSignalStrategy> client_signal_strategy_;

  scoped_ptr<JingleSessionManager> host_server_;
  MockSessionManagerListener host_server_listener_;
  scoped_ptr<JingleSessionManager> client_server_;
  MockSessionManagerListener client_server_listener_;

  scoped_ptr<Session> host_session_;
  MockSessionEventHandler host_session_event_handler_;
  scoped_ptr<Session> client_session_;
  MockSessionEventHandler client_session_event_handler_;

  MockStreamChannelCallback client_channel_callback_;
  MockStreamChannelCallback host_channel_callback_;

  scoped_ptr<net::StreamSocket> client_socket_;
  scoped_ptr<net::StreamSocket> host_socket_;
};


// Verify that we can create and destroy session managers without a
// connection.
TEST_F(JingleSessionTest, CreateAndDestoy) {
  CreateSessionManagers(1, FakeAuthenticator::ACCEPT);
}

// Verify that an incoming session can be rejected, and that the
// status of the connection is set to FAILED in this case.
TEST_F(JingleSessionTest, RejectConnection) {
  CreateSessionManagers(1, FakeAuthenticator::ACCEPT);

  // Reject incoming session.
  EXPECT_CALL(host_server_listener_, OnIncomingSession(_, _))
      .WillOnce(SetArgumentPointee<1>(protocol::SessionManager::DECLINE));

  {
    InSequence dummy;
    EXPECT_CALL(client_session_event_handler_,
                OnSessionStateChange(Session::FAILED))
        .Times(1);
  }

  scoped_ptr<Authenticator> authenticator(new FakeAuthenticator(
      FakeAuthenticator::CLIENT, 1, FakeAuthenticator::ACCEPT, true));
  client_session_ = client_server_->Connect(
      kHostJid, authenticator.Pass(), CandidateSessionConfig::CreateDefault());
  client_session_->SetEventHandler(&client_session_event_handler_);

  base::RunLoop().RunUntilIdle();
}

// Verify that we can connect two endpoints with single-step authentication.
TEST_F(JingleSessionTest, Connect) {
  CreateSessionManagers(1, FakeAuthenticator::ACCEPT);
  InitiateConnection(1, FakeAuthenticator::ACCEPT, false);

  // Verify that the client specified correct initiator value.
  ASSERT_GT(host_signal_strategy_->received_messages().size(), 0U);
  const buzz::XmlElement* initiate_xml =
      host_signal_strategy_->received_messages().front();
  const buzz::XmlElement* jingle_element =
      initiate_xml->FirstNamed(buzz::QName(kJingleNamespace, "jingle"));
  ASSERT_TRUE(jingle_element);
  ASSERT_EQ(kClientJid,
            jingle_element->Attr(buzz::QName(std::string(), "initiator")));
}

// Verify that we can connect two endpoints with multi-step authentication.
TEST_F(JingleSessionTest, ConnectWithMultistep) {
  CreateSessionManagers(3, FakeAuthenticator::ACCEPT);
  InitiateConnection(3, FakeAuthenticator::ACCEPT, false);
}

// Verify that connection is terminated when single-step auth fails.
TEST_F(JingleSessionTest, ConnectWithBadAuth) {
  CreateSessionManagers(1, FakeAuthenticator::REJECT);
  InitiateConnection(1, FakeAuthenticator::ACCEPT, true);
}

// Verify that connection is terminated when multi-step auth fails.
TEST_F(JingleSessionTest, ConnectWithBadMultistepAuth) {
  CreateSessionManagers(3, FakeAuthenticator::REJECT);
  InitiateConnection(3, FakeAuthenticator::ACCEPT, true);
}

// Verify that data can be sent over stream channel.
TEST_F(JingleSessionTest, TestStreamChannel) {
  CreateSessionManagers(1, FakeAuthenticator::ACCEPT);
  ASSERT_NO_FATAL_FAILURE(
      InitiateConnection(1, FakeAuthenticator::ACCEPT, false));

  ASSERT_NO_FATAL_FAILURE(CreateChannel());

  StreamConnectionTester tester(host_socket_.get(), client_socket_.get(),
                                kMessageSize, kMessages);
  tester.Start();
  message_loop_->Run();
  tester.CheckResults();
}

TEST_F(JingleSessionTest, DeleteSessionOnIncomingConnection) {
  CreateSessionManagers(3, FakeAuthenticator::ACCEPT);

  EXPECT_CALL(host_server_listener_, OnIncomingSession(_, _))
      .WillOnce(DoAll(
          WithArg<0>(Invoke(this, &JingleSessionTest::SetHostSession)),
          SetArgumentPointee<1>(protocol::SessionManager::ACCEPT)));

  EXPECT_CALL(host_session_event_handler_,
      OnSessionStateChange(Session::CONNECTED))
      .Times(AtMost(1));

  EXPECT_CALL(host_session_event_handler_,
      OnSessionStateChange(Session::AUTHENTICATING))
      .WillOnce(InvokeWithoutArgs(this, &JingleSessionTest::DeleteSession));

  scoped_ptr<Authenticator> authenticator(new FakeAuthenticator(
      FakeAuthenticator::CLIENT, 3, FakeAuthenticator::ACCEPT, true));

  client_session_ = client_server_->Connect(
      kHostJid, authenticator.Pass(),
      CandidateSessionConfig::CreateDefault());

  base::RunLoop().RunUntilIdle();
}

TEST_F(JingleSessionTest, DeleteSessionOnAuth) {
  // Same as the previous test, but set messages_till_started to 2 in
  // CreateSessionManagers so that the session will goes into the
  // AUTHENTICATING state after two message exchanges.
  CreateSessionManagers(3, 2, FakeAuthenticator::ACCEPT);

  EXPECT_CALL(host_server_listener_, OnIncomingSession(_, _))
      .WillOnce(DoAll(
          WithArg<0>(Invoke(this, &JingleSessionTest::SetHostSession)),
          SetArgumentPointee<1>(protocol::SessionManager::ACCEPT)));

  EXPECT_CALL(host_session_event_handler_,
      OnSessionStateChange(Session::CONNECTED))
      .Times(AtMost(1));

  EXPECT_CALL(host_session_event_handler_,
      OnSessionStateChange(Session::AUTHENTICATING))
      .WillOnce(InvokeWithoutArgs(this, &JingleSessionTest::DeleteSession));

  scoped_ptr<Authenticator> authenticator(new FakeAuthenticator(
      FakeAuthenticator::CLIENT, 3, FakeAuthenticator::ACCEPT, true));

  client_session_ = client_server_->Connect(
      kHostJid, authenticator.Pass(),
      CandidateSessionConfig::CreateDefault());
  base::RunLoop().RunUntilIdle();
}

// Verify that data can be sent over a multiplexed channel.
TEST_F(JingleSessionTest, TestMuxStreamChannel) {
  CreateSessionManagers(1, FakeAuthenticator::ACCEPT);
  ASSERT_NO_FATAL_FAILURE(
      InitiateConnection(1, FakeAuthenticator::ACCEPT, false));

  client_session_->GetMultiplexedChannelFactory()->CreateStreamChannel(
      kChannelName, base::Bind(&JingleSessionTest::OnClientChannelCreated,
                               base::Unretained(this)));
  host_session_->GetMultiplexedChannelFactory()->CreateStreamChannel(
      kChannelName, base::Bind(&JingleSessionTest::OnHostChannelCreated,
                               base::Unretained(this)));

  int counter = 2;
  ExpectRouteChange("mux");
  EXPECT_CALL(client_channel_callback_, OnDone(_))
      .WillOnce(QuitThreadOnCounter(&counter));
  EXPECT_CALL(host_channel_callback_, OnDone(_))
      .WillOnce(QuitThreadOnCounter(&counter));
  message_loop_->Run();

  EXPECT_TRUE(client_socket_.get());
  EXPECT_TRUE(host_socket_.get());

  StreamConnectionTester tester(host_socket_.get(), client_socket_.get(),
                                kMessageSize, kMessages);
  tester.Start();
  message_loop_->Run();
  tester.CheckResults();
}

// Verify that we can connect channels with multistep auth.
TEST_F(JingleSessionTest, TestMultistepAuthStreamChannel) {
  CreateSessionManagers(3, FakeAuthenticator::ACCEPT);
  ASSERT_NO_FATAL_FAILURE(
      InitiateConnection(3, FakeAuthenticator::ACCEPT, false));

  ASSERT_NO_FATAL_FAILURE(CreateChannel());

  StreamConnectionTester tester(host_socket_.get(), client_socket_.get(),
                                kMessageSize, kMessages);
  tester.Start();
  message_loop_->Run();
  tester.CheckResults();
}

// Verify that we shutdown properly when channel authentication fails.
TEST_F(JingleSessionTest, TestFailedChannelAuth) {
  CreateSessionManagers(1, FakeAuthenticator::REJECT_CHANNEL);
  ASSERT_NO_FATAL_FAILURE(
      InitiateConnection(1, FakeAuthenticator::ACCEPT, false));

  client_session_->GetTransportChannelFactory()->CreateStreamChannel(
      kChannelName, base::Bind(&JingleSessionTest::OnClientChannelCreated,
                               base::Unretained(this)));
  host_session_->GetTransportChannelFactory()->CreateStreamChannel(
      kChannelName, base::Bind(&JingleSessionTest::OnHostChannelCreated,
                               base::Unretained(this)));

  // Terminate the message loop when we get rejection notification
  // from the host.
  EXPECT_CALL(host_channel_callback_, OnDone(NULL))
      .WillOnce(QuitThread());
  EXPECT_CALL(client_channel_callback_, OnDone(_))
      .Times(AtMost(1));
  ExpectRouteChange(kChannelName);

  message_loop_->Run();

  EXPECT_TRUE(!host_socket_.get());
}

}  // namespace protocol
}  // namespace remoting