blob: 40187153724a656b0d05c8c46e15d2118fa2338d [file] [log] [blame]
// Copyright 2014 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 "ipc/mojo/ipc_channel_mojo.h"
#include "base/base_paths.h"
#include "base/files/file.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/pickle.h"
#include "base/run_loop.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_test_base.h"
#include "ipc/ipc_test_channel_listener.h"
#include "ipc/mojo/ipc_channel_mojo_host.h"
#include "ipc/mojo/ipc_mojo_handle_attachment.h"
#include "ipc/mojo/ipc_mojo_message_helper.h"
#include "ipc/mojo/ipc_mojo_param_traits.h"
#include "ipc/mojo/scoped_ipc_support.h"
#if defined(OS_POSIX)
#include "base/file_descriptor_posix.h"
#include "ipc/ipc_platform_file_attachment_posix.h"
#endif
namespace {
class ListenerThatExpectsOK : public IPC::Listener {
public:
ListenerThatExpectsOK()
: received_ok_(false) {}
~ListenerThatExpectsOK() override {}
bool OnMessageReceived(const IPC::Message& message) override {
PickleIterator iter(message);
std::string should_be_ok;
EXPECT_TRUE(iter.ReadString(&should_be_ok));
EXPECT_EQ(should_be_ok, "OK");
received_ok_ = true;
base::MessageLoop::current()->Quit();
return true;
}
void OnChannelError() override {
// The connection should be healthy while the listener is waiting
// message. An error can occur after that because the peer
// process dies.
DCHECK(received_ok_);
}
static void SendOK(IPC::Sender* sender) {
IPC::Message* message = new IPC::Message(
0, 2, IPC::Message::PRIORITY_NORMAL);
message->WriteString(std::string("OK"));
ASSERT_TRUE(sender->Send(message));
}
private:
bool received_ok_;
};
class ChannelClient {
public:
explicit ChannelClient(IPC::Listener* listener, const char* name) {
channel_ = IPC::ChannelMojo::Create(NULL, main_message_loop_.task_runner(),
IPCTestBase::GetChannelName(name),
IPC::Channel::MODE_CLIENT, listener);
}
void Connect() {
CHECK(channel_->Connect());
}
void Close() {
channel_->Close();
base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
}
IPC::ChannelMojo* channel() const { return channel_.get(); }
private:
base::MessageLoopForIO main_message_loop_;
scoped_ptr<IPC::ChannelMojo> channel_;
};
class IPCChannelMojoTestBase : public IPCTestBase {
public:
void InitWithMojo(const std::string& test_client_name) {
Init(test_client_name);
}
void TearDown() override {
// Make sure Mojo IPC support is properly shutdown on the I/O loop before
// TearDown continues.
base::RunLoop run_loop;
task_runner()->PostTask(FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
IPCTestBase::TearDown();
}
};
class IPCChannelMojoTest : public IPCChannelMojoTestBase {
protected:
scoped_ptr<IPC::ChannelFactory> CreateChannelFactory(
const IPC::ChannelHandle& handle,
base::SequencedTaskRunner* runner) override {
host_.reset(new IPC::ChannelMojoHost(task_runner()));
return IPC::ChannelMojo::CreateServerFactory(host_->channel_delegate(),
task_runner(), handle);
}
bool DidStartClient() override {
bool ok = IPCTestBase::DidStartClient();
DCHECK(ok);
host_->OnClientLaunched(client_process().Handle());
return ok;
}
private:
scoped_ptr<IPC::ChannelMojoHost> host_;
};
class TestChannelListenerWithExtraExpectations
: public IPC::TestChannelListener {
public:
TestChannelListenerWithExtraExpectations()
: is_connected_called_(false) {
}
void OnChannelConnected(int32 peer_pid) override {
IPC::TestChannelListener::OnChannelConnected(peer_pid);
EXPECT_TRUE(base::kNullProcessId != peer_pid);
is_connected_called_ = true;
}
bool is_connected_called() const { return is_connected_called_; }
private:
bool is_connected_called_;
};
TEST_F(IPCChannelMojoTest, ConnectedFromClient) {
InitWithMojo("IPCChannelMojoTestClient");
// Set up IPC channel and start client.
TestChannelListenerWithExtraExpectations listener;
CreateChannel(&listener);
listener.Init(sender());
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
IPC::TestChannelListener::SendOneMessage(
sender(), "hello from parent");
base::MessageLoop::current()->Run();
EXPECT_TRUE(base::kNullProcessId != this->channel()->GetPeerPID());
this->channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
EXPECT_TRUE(listener.is_connected_called());
EXPECT_TRUE(listener.HasSentAll());
DestroyChannel();
}
// A long running process that connects to us
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestClient) {
TestChannelListenerWithExtraExpectations listener;
ChannelClient client(&listener, "IPCChannelMojoTestClient");
client.Connect();
listener.Init(client.channel());
IPC::TestChannelListener::SendOneMessage(
client.channel(), "hello from child");
base::MessageLoop::current()->Run();
EXPECT_TRUE(listener.is_connected_called());
EXPECT_TRUE(listener.HasSentAll());
client.Close();
return 0;
}
class ListenerExpectingErrors : public IPC::Listener {
public:
ListenerExpectingErrors()
: has_error_(false) {
}
void OnChannelConnected(int32 peer_pid) override {
base::MessageLoop::current()->Quit();
}
bool OnMessageReceived(const IPC::Message& message) override { return true; }
void OnChannelError() override {
has_error_ = true;
base::MessageLoop::current()->Quit();
}
bool has_error() const { return has_error_; }
private:
bool has_error_;
};
class IPCChannelMojoErrorTest : public IPCChannelMojoTestBase {
protected:
scoped_ptr<IPC::ChannelFactory> CreateChannelFactory(
const IPC::ChannelHandle& handle,
base::SequencedTaskRunner* runner) override {
host_.reset(new IPC::ChannelMojoHost(task_runner()));
return IPC::ChannelMojo::CreateServerFactory(host_->channel_delegate(),
task_runner(), handle);
}
bool DidStartClient() override {
bool ok = IPCTestBase::DidStartClient();
DCHECK(ok);
host_->OnClientLaunched(client_process().Handle());
return ok;
}
private:
scoped_ptr<IPC::ChannelMojoHost> host_;
};
class ListenerThatQuits : public IPC::Listener {
public:
ListenerThatQuits() {
}
bool OnMessageReceived(const IPC::Message& message) override {
return true;
}
void OnChannelConnected(int32 peer_pid) override {
base::MessageLoop::current()->Quit();
}
};
// A long running process that connects to us.
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoErraticTestClient) {
ListenerThatQuits listener;
ChannelClient client(&listener, "IPCChannelMojoErraticTestClient");
client.Connect();
base::MessageLoop::current()->Run();
client.Close();
return 0;
}
TEST_F(IPCChannelMojoErrorTest, SendFailWithPendingMessages) {
InitWithMojo("IPCChannelMojoErraticTestClient");
// Set up IPC channel and start client.
ListenerExpectingErrors listener;
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
// This matches a value in mojo/edk/system/constants.h
const int kMaxMessageNumBytes = 4 * 1024 * 1024;
std::string overly_large_data(kMaxMessageNumBytes, '*');
// This messages are queued as pending.
for (size_t i = 0; i < 10; ++i) {
IPC::TestChannelListener::SendOneMessage(
sender(), overly_large_data.c_str());
}
ASSERT_TRUE(StartClient());
base::MessageLoop::current()->Run();
this->channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
EXPECT_TRUE(listener.has_error());
DestroyChannel();
}
struct TestingMessagePipe {
TestingMessagePipe() {
EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateMessagePipe(nullptr, &self, &peer));
}
mojo::ScopedMessagePipeHandle self;
mojo::ScopedMessagePipeHandle peer;
};
class HandleSendingHelper {
public:
static std::string GetSendingFileContent() { return "Hello"; }
static void WritePipe(IPC::Message* message, TestingMessagePipe* pipe) {
std::string content = HandleSendingHelper::GetSendingFileContent();
EXPECT_EQ(MOJO_RESULT_OK,
mojo::WriteMessageRaw(pipe->self.get(), &content[0],
static_cast<uint32_t>(content.size()),
nullptr, 0, 0));
EXPECT_TRUE(
IPC::MojoMessageHelper::WriteMessagePipeTo(message, pipe->peer.Pass()));
}
static void WritePipeThenSend(IPC::Sender* sender, TestingMessagePipe* pipe) {
IPC::Message* message =
new IPC::Message(0, 2, IPC::Message::PRIORITY_NORMAL);
WritePipe(message, pipe);
ASSERT_TRUE(sender->Send(message));
}
static void ReadReceivedPipe(const IPC::Message& message,
PickleIterator* iter) {
mojo::ScopedMessagePipeHandle pipe;
EXPECT_TRUE(
IPC::MojoMessageHelper::ReadMessagePipeFrom(&message, iter, &pipe));
std::string content(GetSendingFileContent().size(), ' ');
uint32_t num_bytes = static_cast<uint32_t>(content.size());
EXPECT_EQ(MOJO_RESULT_OK,
mojo::ReadMessageRaw(pipe.get(), &content[0], &num_bytes, nullptr,
nullptr, 0));
EXPECT_EQ(content, GetSendingFileContent());
}
#if defined(OS_POSIX)
static base::FilePath GetSendingFilePath() {
base::FilePath path;
bool ok = PathService::Get(base::DIR_CACHE, &path);
EXPECT_TRUE(ok);
return path.Append("ListenerThatExpectsFile.txt");
}
static void WriteFile(IPC::Message* message, base::File& file) {
std::string content = GetSendingFileContent();
file.WriteAtCurrentPos(content.data(), content.size());
file.Flush();
message->WriteAttachment(new IPC::internal::PlatformFileAttachment(
base::ScopedFD(file.TakePlatformFile())));
}
static void WriteFileThenSend(IPC::Sender* sender, base::File& file) {
IPC::Message* message =
new IPC::Message(0, 2, IPC::Message::PRIORITY_NORMAL);
WriteFile(message, file);
ASSERT_TRUE(sender->Send(message));
}
static void WriteFileAndPipeThenSend(IPC::Sender* sender,
base::File& file,
TestingMessagePipe* pipe) {
IPC::Message* message =
new IPC::Message(0, 2, IPC::Message::PRIORITY_NORMAL);
WriteFile(message, file);
WritePipe(message, pipe);
ASSERT_TRUE(sender->Send(message));
}
static void ReadReceivedFile(const IPC::Message& message,
PickleIterator* iter) {
base::ScopedFD fd;
scoped_refptr<IPC::MessageAttachment> attachment;
EXPECT_TRUE(message.ReadAttachment(iter, &attachment));
base::File file(attachment->TakePlatformFile());
std::string content(GetSendingFileContent().size(), ' ');
file.Read(0, &content[0], content.size());
EXPECT_EQ(content, GetSendingFileContent());
}
#endif
};
class ListenerThatExpectsMessagePipe : public IPC::Listener {
public:
ListenerThatExpectsMessagePipe() : sender_(NULL) {}
~ListenerThatExpectsMessagePipe() override {}
bool OnMessageReceived(const IPC::Message& message) override {
PickleIterator iter(message);
HandleSendingHelper::ReadReceivedPipe(message, &iter);
base::MessageLoop::current()->Quit();
ListenerThatExpectsOK::SendOK(sender_);
return true;
}
void OnChannelError() override { NOTREACHED(); }
void set_sender(IPC::Sender* sender) { sender_ = sender; }
private:
IPC::Sender* sender_;
};
TEST_F(IPCChannelMojoTest, SendMessagePipe) {
InitWithMojo("IPCChannelMojoTestSendMessagePipeClient");
ListenerThatExpectsOK listener;
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
TestingMessagePipe pipe;
HandleSendingHelper::WritePipeThenSend(channel(), &pipe);
base::MessageLoop::current()->Run();
this->channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
DestroyChannel();
}
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestSendMessagePipeClient) {
ListenerThatExpectsMessagePipe listener;
ChannelClient client(&listener, "IPCChannelMojoTestSendMessagePipeClient");
client.Connect();
listener.set_sender(client.channel());
base::MessageLoop::current()->Run();
client.Close();
return 0;
}
void ReadOK(mojo::MessagePipeHandle pipe) {
std::string should_be_ok("xx");
uint32_t num_bytes = static_cast<uint32_t>(should_be_ok.size());
CHECK_EQ(MOJO_RESULT_OK,
mojo::ReadMessageRaw(pipe, &should_be_ok[0], &num_bytes, nullptr,
nullptr, 0));
EXPECT_EQ(should_be_ok, std::string("OK"));
}
void WriteOK(mojo::MessagePipeHandle pipe) {
std::string ok("OK");
CHECK_EQ(MOJO_RESULT_OK,
mojo::WriteMessageRaw(pipe, &ok[0], static_cast<uint32_t>(ok.size()),
nullptr, 0, 0));
}
class ListenerThatExpectsMessagePipeUsingParamTrait : public IPC::Listener {
public:
explicit ListenerThatExpectsMessagePipeUsingParamTrait(bool receiving_valid)
: sender_(NULL), receiving_valid_(receiving_valid) {}
~ListenerThatExpectsMessagePipeUsingParamTrait() override {}
bool OnMessageReceived(const IPC::Message& message) override {
PickleIterator iter(message);
mojo::MessagePipeHandle handle;
EXPECT_TRUE(IPC::ParamTraits<mojo::MessagePipeHandle>::Read(&message, &iter,
&handle));
EXPECT_EQ(handle.is_valid(), receiving_valid_);
if (receiving_valid_) {
ReadOK(handle);
MojoClose(handle.value());
}
base::MessageLoop::current()->Quit();
ListenerThatExpectsOK::SendOK(sender_);
return true;
}
void OnChannelError() override { NOTREACHED(); }
void set_sender(IPC::Sender* sender) { sender_ = sender; }
private:
IPC::Sender* sender_;
bool receiving_valid_;
};
void ParamTraitMessagePipeClient(bool receiving_valid_handle,
const char* channel_name) {
ListenerThatExpectsMessagePipeUsingParamTrait listener(
receiving_valid_handle);
ChannelClient client(&listener, channel_name);
client.Connect();
listener.set_sender(client.channel());
base::MessageLoop::current()->Run();
client.Close();
}
TEST_F(IPCChannelMojoTest, ParamTraitValidMessagePipe) {
InitWithMojo("ParamTraitValidMessagePipeClient");
ListenerThatExpectsOK listener;
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
TestingMessagePipe pipe;
scoped_ptr<IPC::Message> message(new IPC::Message());
IPC::ParamTraits<mojo::MessagePipeHandle>::Write(message.get(),
pipe.peer.release());
WriteOK(pipe.self.get());
this->channel()->Send(message.release());
base::MessageLoop::current()->Run();
this->channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
DestroyChannel();
}
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(ParamTraitValidMessagePipeClient) {
ParamTraitMessagePipeClient(true, "ParamTraitValidMessagePipeClient");
return 0;
}
TEST_F(IPCChannelMojoTest, ParamTraitInvalidMessagePipe) {
InitWithMojo("ParamTraitInvalidMessagePipeClient");
ListenerThatExpectsOK listener;
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
mojo::MessagePipeHandle invalid_handle;
scoped_ptr<IPC::Message> message(new IPC::Message());
IPC::ParamTraits<mojo::MessagePipeHandle>::Write(message.get(),
invalid_handle);
this->channel()->Send(message.release());
base::MessageLoop::current()->Run();
this->channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
DestroyChannel();
}
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(ParamTraitInvalidMessagePipeClient) {
ParamTraitMessagePipeClient(false, "ParamTraitInvalidMessagePipeClient");
return 0;
}
#if defined(OS_WIN)
class IPCChannelMojoDeadHandleTest : public IPCChannelMojoTestBase {
protected:
scoped_ptr<IPC::ChannelFactory> CreateChannelFactory(
const IPC::ChannelHandle& handle,
base::SequencedTaskRunner* runner) override {
host_.reset(new IPC::ChannelMojoHost(task_runner()));
return IPC::ChannelMojo::CreateServerFactory(host_->channel_delegate(),
task_runner(), handle);
}
bool DidStartClient() override {
IPCTestBase::DidStartClient();
const base::ProcessHandle client = client_process().Handle();
// Forces GetFileHandleForProcess() fail. It happens occasionally
// in production, so we should exercise it somehow.
// TODO(morrita): figure out how to safely test this. See crbug.com/464109.
// ::CloseHandle(client);
host_->OnClientLaunched(client);
return true;
}
private:
scoped_ptr<IPC::ChannelMojoHost> host_;
};
TEST_F(IPCChannelMojoDeadHandleTest, InvalidClientHandle) {
// Any client type is fine as it is going to be killed anyway.
InitWithMojo("IPCChannelMojoTestDoNothingClient");
// Set up IPC channel and start client.
ListenerExpectingErrors listener;
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
base::MessageLoop::current()->Run();
this->channel()->Close();
// TODO(morrita): We need CloseHandle() call in DidStartClient(),
// which has been disabled since crrev.com/843113003, to
// make this fail. See crbug.com/464109.
// EXPECT_FALSE(WaitForClientShutdown());
WaitForClientShutdown();
EXPECT_TRUE(listener.has_error());
DestroyChannel();
}
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestDoNothingClient) {
ListenerThatQuits listener;
ChannelClient client(&listener, "IPCChannelMojoTestDoNothingClient");
client.Connect();
// Quits without running the message loop as this client won't
// receive any messages from the server.
return 0;
}
#endif
#if defined(OS_POSIX)
class ListenerThatExpectsFile : public IPC::Listener {
public:
ListenerThatExpectsFile()
: sender_(NULL) {}
~ListenerThatExpectsFile() override {}
bool OnMessageReceived(const IPC::Message& message) override {
PickleIterator iter(message);
HandleSendingHelper::ReadReceivedFile(message, &iter);
base::MessageLoop::current()->Quit();
ListenerThatExpectsOK::SendOK(sender_);
return true;
}
void OnChannelError() override {
NOTREACHED();
}
void set_sender(IPC::Sender* sender) { sender_ = sender; }
private:
IPC::Sender* sender_;
};
TEST_F(IPCChannelMojoTest, SendPlatformHandle) {
InitWithMojo("IPCChannelMojoTestSendPlatformHandleClient");
ListenerThatExpectsOK listener;
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
base::File file(HandleSendingHelper::GetSendingFilePath(),
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
base::File::FLAG_READ);
HandleSendingHelper::WriteFileThenSend(channel(), file);
base::MessageLoop::current()->Run();
this->channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
DestroyChannel();
}
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestSendPlatformHandleClient) {
ListenerThatExpectsFile listener;
ChannelClient client(
&listener, "IPCChannelMojoTestSendPlatformHandleClient");
client.Connect();
listener.set_sender(client.channel());
base::MessageLoop::current()->Run();
client.Close();
return 0;
}
class ListenerThatExpectsFileAndPipe : public IPC::Listener {
public:
ListenerThatExpectsFileAndPipe() : sender_(NULL) {}
~ListenerThatExpectsFileAndPipe() override {}
bool OnMessageReceived(const IPC::Message& message) override {
PickleIterator iter(message);
HandleSendingHelper::ReadReceivedFile(message, &iter);
HandleSendingHelper::ReadReceivedPipe(message, &iter);
base::MessageLoop::current()->Quit();
ListenerThatExpectsOK::SendOK(sender_);
return true;
}
void OnChannelError() override { NOTREACHED(); }
void set_sender(IPC::Sender* sender) { sender_ = sender; }
private:
IPC::Sender* sender_;
};
TEST_F(IPCChannelMojoTest, SendPlatformHandleAndPipe) {
InitWithMojo("IPCChannelMojoTestSendPlatformHandleAndPipeClient");
ListenerThatExpectsOK listener;
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
base::File file(HandleSendingHelper::GetSendingFilePath(),
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
base::File::FLAG_READ);
TestingMessagePipe pipe;
HandleSendingHelper::WriteFileAndPipeThenSend(channel(), file, &pipe);
base::MessageLoop::current()->Run();
this->channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
DestroyChannel();
}
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(
IPCChannelMojoTestSendPlatformHandleAndPipeClient) {
ListenerThatExpectsFileAndPipe listener;
ChannelClient client(&listener,
"IPCChannelMojoTestSendPlatformHandleAndPipeClient");
client.Connect();
listener.set_sender(client.channel());
base::MessageLoop::current()->Run();
client.Close();
return 0;
}
#endif
#if defined(OS_LINUX)
const base::ProcessId kMagicChildId = 54321;
class ListenerThatVerifiesPeerPid : public IPC::Listener {
public:
void OnChannelConnected(int32 peer_pid) override {
EXPECT_EQ(peer_pid, kMagicChildId);
base::MessageLoop::current()->Quit();
}
bool OnMessageReceived(const IPC::Message& message) override {
NOTREACHED();
return true;
}
};
TEST_F(IPCChannelMojoTest, VerifyGlobalPid) {
InitWithMojo("IPCChannelMojoTestVerifyGlobalPidClient");
ListenerThatVerifiesPeerPid listener;
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
base::MessageLoop::current()->Run();
channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
DestroyChannel();
}
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestVerifyGlobalPidClient) {
IPC::Channel::SetGlobalPid(kMagicChildId);
ListenerThatQuits listener;
ChannelClient client(&listener,
"IPCChannelMojoTestVerifyGlobalPidClient");
client.Connect();
base::MessageLoop::current()->Run();
client.Close();
return 0;
}
#endif // OS_LINUX
} // namespace