| // Copyright 2015 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" |
| |
| #include <limits> |
| #include <set> |
| |
| #include "base/run_loop.h" |
| #include "ipc/attachment_broker.h" |
| #include "ipc/brokerable_attachment.h" |
| #include "ipc/ipc_channel_reader.h" |
| #include "ipc/placeholder_brokerable_attachment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| // Whether IPC::Message::FindNext() can determine message size for |
| // partial messages. The condition is from FindNext() implementation. |
| #if USE_ATTACHMENT_BROKER |
| #define MESSAGE_FINDNEXT_PARTIAL 0 |
| #else |
| #define MESSAGE_FINDNEXT_PARTIAL 1 |
| #endif |
| |
| namespace IPC { |
| namespace internal { |
| |
| namespace { |
| |
| #if USE_ATTACHMENT_BROKER |
| |
| class MockAttachment : public BrokerableAttachment { |
| public: |
| MockAttachment() {} |
| MockAttachment(BrokerableAttachment::AttachmentId id) |
| : BrokerableAttachment(id) {} |
| |
| #if defined(OS_POSIX) |
| base::PlatformFile TakePlatformFile() override { |
| return base::PlatformFile(); |
| } |
| #endif // OS_POSIX |
| |
| BrokerableType GetBrokerableType() const override { return WIN_HANDLE; } |
| |
| private: |
| ~MockAttachment() override {} |
| }; |
| |
| class MockAttachmentBroker : public AttachmentBroker { |
| public: |
| typedef std::set<scoped_refptr<BrokerableAttachment>> AttachmentSet; |
| |
| bool SendAttachmentToProcess( |
| const scoped_refptr<BrokerableAttachment>& attachment, |
| base::ProcessId destination_process) override { |
| return false; |
| } |
| |
| bool OnMessageReceived(const Message& message) override { return false; } |
| |
| void AddAttachment(scoped_refptr<BrokerableAttachment> attachment) { |
| get_attachments()->push_back(attachment); |
| NotifyObservers(attachment->GetIdentifier()); |
| } |
| }; |
| |
| #endif // USE_ATTACHMENT_BROKER |
| |
| class MockChannelReader : public ChannelReader { |
| public: |
| MockChannelReader() |
| : ChannelReader(nullptr), last_dispatched_message_(nullptr) {} |
| |
| ReadState ReadData(char* buffer, int buffer_len, int* bytes_read) override { |
| if (data_.empty()) |
| return READ_PENDING; |
| |
| size_t read_len = std::min(static_cast<size_t>(buffer_len), data_.size()); |
| memcpy(buffer, data_.data(), read_len); |
| *bytes_read = static_cast<int>(read_len); |
| data_.erase(0, read_len); |
| return READ_SUCCEEDED; |
| } |
| |
| bool ShouldDispatchInputMessage(Message* msg) override { return true; } |
| |
| bool GetNonBrokeredAttachments(Message* msg) override { return true; } |
| |
| bool DidEmptyInputBuffers() override { return true; } |
| |
| void HandleInternalMessage(const Message& msg) override {} |
| |
| void DispatchMessage(Message* m) override { last_dispatched_message_ = m; } |
| |
| base::ProcessId GetSenderPID() override { return base::kNullProcessId; } |
| |
| bool IsAttachmentBrokerEndpoint() override { return false; } |
| |
| AttachmentBroker* GetAttachmentBroker() override { return broker_; } |
| |
| // This instance takes ownership of |m|. |
| void AddMessageForDispatch(Message* m) { |
| get_queued_messages()->push_back(m); |
| } |
| |
| Message* get_last_dispatched_message() { return last_dispatched_message_; } |
| |
| void set_broker(AttachmentBroker* broker) { broker_ = broker; } |
| |
| void AppendData(const void* data, size_t size) { |
| data_.append(static_cast<const char*>(data), size); |
| } |
| |
| void AppendMessageData(const Message& message) { |
| AppendData(message.data(), message.size()); |
| } |
| |
| private: |
| Message* last_dispatched_message_; |
| AttachmentBroker* broker_; |
| std::string data_; |
| }; |
| |
| class ExposedMessage: public Message { |
| public: |
| using Message::Header; |
| using Message::header; |
| }; |
| |
| // Payload that makes messages large |
| const size_t LargePayloadSize = Channel::kMaximumReadBufferSize * 3 / 2; |
| |
| } // namespace |
| |
| #if USE_ATTACHMENT_BROKER |
| |
| TEST(ChannelReaderTest, AttachmentAlreadyBrokered) { |
| MockAttachmentBroker broker; |
| MockChannelReader reader; |
| reader.set_broker(&broker); |
| scoped_refptr<MockAttachment> attachment(new MockAttachment); |
| broker.AddAttachment(attachment); |
| |
| Message* m = new Message; |
| PlaceholderBrokerableAttachment* needs_brokering_attachment = |
| new PlaceholderBrokerableAttachment(attachment->GetIdentifier()); |
| EXPECT_TRUE(m->WriteAttachment(needs_brokering_attachment)); |
| reader.AddMessageForDispatch(m); |
| EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, reader.DispatchMessages()); |
| EXPECT_EQ(m, reader.get_last_dispatched_message()); |
| } |
| |
| TEST(ChannelReaderTest, AttachmentNotYetBrokered) { |
| scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoopForIO()); |
| |
| MockAttachmentBroker broker; |
| MockChannelReader reader; |
| reader.set_broker(&broker); |
| scoped_refptr<MockAttachment> attachment(new MockAttachment); |
| |
| Message* m = new Message; |
| PlaceholderBrokerableAttachment* needs_brokering_attachment = |
| new PlaceholderBrokerableAttachment(attachment->GetIdentifier()); |
| EXPECT_TRUE(m->WriteAttachment(needs_brokering_attachment)); |
| reader.AddMessageForDispatch(m); |
| EXPECT_EQ(ChannelReader::DISPATCH_WAITING_ON_BROKER, |
| reader.DispatchMessages()); |
| EXPECT_EQ(nullptr, reader.get_last_dispatched_message()); |
| |
| broker.AddAttachment(attachment); |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| |
| EXPECT_EQ(m, reader.get_last_dispatched_message()); |
| } |
| |
| #endif // USE_ATTACHMENT_BROKER |
| |
| #if !USE_ATTACHMENT_BROKER |
| |
| // We can determine message size from its header (and hence resize the buffer) |
| // only when attachment broker is not used, see IPC::Message::FindNext(). |
| |
| TEST(ChannelReaderTest, ResizeOverflowBuffer) { |
| MockChannelReader reader; |
| |
| ExposedMessage::Header header = {}; |
| |
| header.payload_size = 128 * 1024; |
| EXPECT_LT(reader.input_overflow_buf_.capacity(), header.payload_size); |
| EXPECT_TRUE(reader.TranslateInputData( |
| reinterpret_cast<const char*>(&header), sizeof(header))); |
| |
| // Once message header is available we resize overflow buffer to |
| // fit the entire message. |
| EXPECT_GE(reader.input_overflow_buf_.capacity(), header.payload_size); |
| } |
| |
| TEST(ChannelReaderTest, InvalidMessageSize) { |
| MockChannelReader reader; |
| |
| ExposedMessage::Header header = {}; |
| |
| size_t capacity_before = reader.input_overflow_buf_.capacity(); |
| |
| // Message is slightly larger than maximum allowed size |
| header.payload_size = Channel::kMaximumMessageSize + 1; |
| EXPECT_FALSE(reader.TranslateInputData( |
| reinterpret_cast<const char*>(&header), sizeof(header))); |
| EXPECT_LE(reader.input_overflow_buf_.capacity(), capacity_before); |
| |
| // Payload size is negative, overflow is detected by Pickle::PeekNext() |
| header.payload_size = static_cast<uint32_t>(-1); |
| EXPECT_FALSE(reader.TranslateInputData( |
| reinterpret_cast<const char*>(&header), sizeof(header))); |
| EXPECT_LE(reader.input_overflow_buf_.capacity(), capacity_before); |
| |
| // Payload size is maximum int32 value |
| header.payload_size = std::numeric_limits<int32_t>::max(); |
| EXPECT_FALSE(reader.TranslateInputData( |
| reinterpret_cast<const char*>(&header), sizeof(header))); |
| EXPECT_LE(reader.input_overflow_buf_.capacity(), capacity_before); |
| } |
| |
| #endif // !USE_ATTACHMENT_BROKER |
| |
| TEST(ChannelReaderTest, TrimBuffer) { |
| // ChannelReader uses std::string as a buffer, and calls reserve() |
| // to trim it to kMaximumReadBufferSize. However, an implementation |
| // is free to actually reserve a larger amount. |
| size_t trimmed_buffer_size; |
| { |
| std::string buf; |
| buf.reserve(Channel::kMaximumReadBufferSize); |
| trimmed_buffer_size = buf.capacity(); |
| } |
| |
| // Buffer is trimmed after message is processed. |
| { |
| MockChannelReader reader; |
| |
| Message message; |
| message.WriteString(std::string(LargePayloadSize, 'X')); |
| |
| // Sanity check |
| EXPECT_TRUE(message.size() > trimmed_buffer_size); |
| |
| // Initially buffer is small |
| EXPECT_LE(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); |
| |
| // Write and process large message |
| reader.AppendMessageData(message); |
| EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, |
| reader.ProcessIncomingMessages()); |
| |
| // After processing large message buffer is trimmed |
| EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); |
| } |
| |
| // Buffer is trimmed only after entire message is processed. |
| { |
| MockChannelReader reader; |
| |
| ExposedMessage message; |
| message.WriteString(std::string(LargePayloadSize, 'X')); |
| |
| // Write and process message header |
| reader.AppendData(message.header(), sizeof(ExposedMessage::Header)); |
| EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, |
| reader.ProcessIncomingMessages()); |
| |
| #if MESSAGE_FINDNEXT_PARTIAL |
| // We determined message size for the message from its header, so |
| // we resized the buffer to fit. |
| EXPECT_GE(reader.input_overflow_buf_.capacity(), message.size()); |
| #else |
| // We couldn't determine message size, so we didn't resize the buffer. |
| #endif |
| |
| // Write and process payload |
| reader.AppendData(message.payload(), message.payload_size()); |
| EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, |
| reader.ProcessIncomingMessages()); |
| |
| // But once we process the message, we trim the buffer |
| EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); |
| } |
| |
| // Buffer is not trimmed if the next message is also large. |
| { |
| MockChannelReader reader; |
| |
| // Write large message |
| Message message1; |
| message1.WriteString(std::string(LargePayloadSize * 2, 'X')); |
| reader.AppendMessageData(message1); |
| |
| // Write header for the next large message |
| ExposedMessage message2; |
| message2.WriteString(std::string(LargePayloadSize, 'Y')); |
| reader.AppendData(message2.header(), sizeof(ExposedMessage::Header)); |
| |
| // Process messages |
| EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, |
| reader.ProcessIncomingMessages()); |
| |
| #if MESSAGE_FINDNEXT_PARTIAL |
| // We determined message size for the second (partial) message, so |
| // we resized the buffer to fit. |
| EXPECT_GE(reader.input_overflow_buf_.capacity(), message1.size()); |
| #else |
| // We couldn't determine message size for the second (partial) message, |
| // so we trimmed the buffer. |
| EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); |
| #endif |
| } |
| |
| // Buffer resized appropriately if next message is larger than the first. |
| // (Similar to the test above except for the order of messages.) |
| { |
| MockChannelReader reader; |
| |
| // Write large message |
| Message message1; |
| message1.WriteString(std::string(LargePayloadSize, 'Y')); |
| reader.AppendMessageData(message1); |
| |
| // Write header for the next even larger message |
| ExposedMessage message2; |
| message2.WriteString(std::string(LargePayloadSize * 2, 'X')); |
| reader.AppendData(message2.header(), sizeof(ExposedMessage::Header)); |
| |
| // Process messages |
| EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, |
| reader.ProcessIncomingMessages()); |
| |
| #if MESSAGE_FINDNEXT_PARTIAL |
| // We determined message size for the second (partial) message, and |
| // resized the buffer to fit it. |
| EXPECT_GE(reader.input_overflow_buf_.capacity(), message2.size()); |
| #else |
| // We couldn't determine message size for the second (partial) message, |
| // so we trimmed the buffer. |
| EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); |
| #endif |
| } |
| |
| // Buffer is not trimmed if we've just resized it to accommodate large |
| // incoming message. |
| { |
| MockChannelReader reader; |
| |
| // Write small message |
| Message message1; |
| message1.WriteString(std::string(11, 'X')); |
| reader.AppendMessageData(message1); |
| |
| // Write header for the next large message |
| ExposedMessage message2; |
| message2.WriteString(std::string(LargePayloadSize, 'Y')); |
| reader.AppendData(message2.header(), sizeof(ExposedMessage::Header)); |
| |
| EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, |
| reader.ProcessIncomingMessages()); |
| |
| #if MESSAGE_FINDNEXT_PARTIAL |
| // We determined message size for the second (partial) message, so |
| // we resized the buffer to fit. |
| EXPECT_GE(reader.input_overflow_buf_.capacity(), message2.size()); |
| #else |
| // We couldn't determine size for the second (partial) message, and |
| // first message was small, so we did nothing. |
| #endif |
| } |
| } |
| |
| } // namespace internal |
| } // namespace IPC |