Index: ipc/ipc_channel_reader_unittest.cc |
diff --git a/ipc/ipc_channel_reader_unittest.cc b/ipc/ipc_channel_reader_unittest.cc |
index fdf31446b005bb464cc3c0928512a3c630686b28..f49c275b0e3a1cb7ecd76505dab1067158d502a9 100644 |
--- a/ipc/ipc_channel_reader_unittest.cc |
+++ b/ipc/ipc_channel_reader_unittest.cc |
@@ -13,6 +13,14 @@ |
#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 && defined(OS_MACOSX) && !defined(OS_IOS) |
+#define MESSAGE_FINDNEXT_PARTIAL 0 |
+#else |
+#define MESSAGE_FINDNEXT_PARTIAL 1 |
+#endif |
+ |
namespace IPC { |
namespace internal { |
@@ -64,7 +72,14 @@ class MockChannelReader : public ChannelReader { |
: ChannelReader(nullptr), last_dispatched_message_(nullptr) {} |
ReadState ReadData(char* buffer, int buffer_len, int* bytes_read) override { |
- return READ_FAILED; |
+ 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; } |
@@ -92,9 +107,18 @@ class MockChannelReader : public ChannelReader { |
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 { |
@@ -103,6 +127,9 @@ class ExposedMessage: public Message { |
using Message::header; |
}; |
+// Payload that makes messages large |
+const size_t LargePayloadSize = Channel::kMaximumReadBufferSize * 3 / 2; |
+ |
} // namespace |
#if USE_ATTACHMENT_BROKER |
@@ -192,5 +219,155 @@ TEST(ChannelReaderTest, InvalidMessageSize) { |
#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 |