[WebSocket] Update renderer to use mojo writable datapipe
This commit updates the renderer to use mojo datapipe to transfer
WebSocket mesasge to the network service, which later sends out the
message with appropriate framing. It adds new ProducePendingData() and
ProduceData() functions to write pending messages to the datapipe. It
also adds mojo SimpleWatcher and OnWritable callback function in case
when the data pipe is unavailable at the moment.
This also updates the unittests to stop testing the quota system,
and start testing the new datapipe transfer.
This commit is a follow-up CL from the following CL:
https://chromium-review.googlesource.com/c/chromium/src/+/2071189
Design Doc:
https://docs.google.com/document/d/1YWj1z9r8wxemGdod6S2tkchudhp6PvNaH3qSO0oucfY/
This is a rebase of
https://chromium-review.googlesource.com/c/chromium/src/+/2089564,
originally by Keita Suzuki.
Bug: 1056030
Change-Id: Ic57abc92b95dad0765c55945439ced5adaf227b9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2206556
Reviewed-by: Yutaka Hirano <[email protected]>
Commit-Queue: Adam Rice <[email protected]>
Cr-Commit-Position: refs/heads/master@{#771697}
diff --git a/net/websockets/websocket_channel.cc b/net/websockets/websocket_channel.cc
index dea22fc..66074138 100644
--- a/net/websockets/websocket_channel.cc
+++ b/net/websockets/websocket_channel.cc
@@ -44,7 +44,6 @@
using base::StreamingUtf8Validator;
-const int kDefaultSendQuotaLowWaterMark = 1 << 16;
const int kDefaultSendQuotaHighWaterMark = 1 << 17;
const size_t kWebSocketCloseCodeLength = 2;
// Timeout for waiting for the server to acknowledge a closing handshake.
@@ -286,7 +285,6 @@
URLRequestContext* url_request_context)
: event_interface_(std::move(event_interface)),
url_request_context_(url_request_context),
- send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark),
send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark),
current_send_quota_(0),
closing_handshake_timeout_(
@@ -354,12 +352,6 @@
}
DCHECK_EQ(state_, CONNECTED);
- if (buffer_size > base::checked_cast<size_t>(current_send_quota_)) {
- // TODO(ricea): Kill renderer.
- FailChannel("Send quota exceeded", kWebSocketErrorGoingAway, "");
- return CHANNEL_DELETED;
- // |this| has been deleted.
- }
DCHECK(WebSocketFrameHeader::IsKnownDataOpCode(op_code))
<< "Got SendFrame with bogus op_code " << op_code << " fin=" << fin
@@ -381,7 +373,6 @@
sending_text_message_ = !fin;
DCHECK(!fin || state == StreamingUtf8Validator::VALID_ENDPOINT);
}
- current_send_quota_ -= buffer_size;
// TODO(ricea): If current_send_quota_ has dropped below
// send_quota_low_water_mark_, it might be good to increase the "low
// water mark" and "high water mark", but only if the link to the WebSocket
@@ -599,21 +590,7 @@
return WriteFrames();
} else {
data_being_sent_.reset();
- if (current_send_quota_ < send_quota_low_water_mark_) {
- // TODO(ricea): Increase low_water_mark and high_water_mark if
- // throughput is high, reduce them if throughput is low. Low water
- // mark needs to be >= the bandwidth delay product *of the IPC
- // channel*. Because factors like context-switch time, thread wake-up
- // time, and bus speed come into play it is complex and probably needs
- // to be determined empirically.
- DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_);
- // TODO(ricea): Truncate quota by the quota specified by the remote
- // server, if the protocol in use supports quota.
- int fresh_quota = send_quota_high_water_mark_ - current_send_quota_;
- current_send_quota_ += fresh_quota;
- event_interface_->OnSendFlowControlQuotaAdded(fresh_quota);
- return CHANNEL_ALIVE;
- }
+ event_interface_->OnSendDataFrameDone();
}
return CHANNEL_ALIVE;
diff --git a/net/websockets/websocket_channel.h b/net/websockets/websocket_channel.h
index d05207e8..579d802 100644
--- a/net/websockets/websocket_channel.h
+++ b/net/websockets/websocket_channel.h
@@ -359,10 +359,6 @@
// during the connection process.
std::unique_ptr<WebSocketStreamRequest> stream_request_;
- // If the renderer's send quota reaches this level, it is sent a quota
- // refresh. "quota units" are currently bytes. TODO(ricea): Update the
- // definition of quota units when necessary.
- int send_quota_low_water_mark_;
// The level the quota is refreshed to when it reaches the low_water_mark
// (quota units).
int send_quota_high_water_mark_;
diff --git a/net/websockets/websocket_channel_test.cc b/net/websockets/websocket_channel_test.cc
index 07f90d5..1bd6d25 100644
--- a/net/websockets/websocket_channel_test.cc
+++ b/net/websockets/websocket_channel_test.cc
@@ -135,10 +135,6 @@
// TODO(ricea): If kDefaultSendQuotaHighWaterMark changes, then this value will
// need to be updated.
const size_t kDefaultInitialQuota = 1 << 17;
-// The amount of bytes we need to send after the initial connection to trigger a
-// quota refresh. TODO(ricea): Change this if kDefaultSendQuotaHighWaterMark or
-// kDefaultSendQuotaLowWaterMark change.
-const size_t kDefaultQuotaRefreshTrigger = (1 << 16) + 1;
const int kVeryBigTimeoutMillis = 60 * 60 * 24 * 1000;
@@ -177,7 +173,7 @@
WebSocketMessageType,
const std::vector<char>&)); // NOLINT
MOCK_METHOD0(HasPendingDataFrames, bool(void)); // NOLINT
- MOCK_METHOD1(OnSendFlowControlQuotaAdded, void(int64_t)); // NOLINT
+ MOCK_METHOD0(OnSendDataFrameDone, void(void)); // NOLINT
MOCK_METHOD0(OnClosingHandshake, void(void)); // NOLINT
MOCK_METHOD1(OnFailChannel, void(const std::string&)); // NOLINT
MOCK_METHOD3(OnDropChannel,
@@ -230,8 +226,8 @@
void OnDataFrame(bool fin,
WebSocketMessageType type,
base::span<const char> data_span) override {}
+ void OnSendDataFrameDone() override {}
bool HasPendingDataFrames() override { return false; }
- void OnSendFlowControlQuotaAdded(int64_t quota) override {}
void OnClosingHandshake() override {}
void OnFailChannel(const std::string& message) override {}
void OnDropChannel(bool was_clean,
@@ -935,6 +931,7 @@
EXPECT_CALL(*event_interface_, OnDataFrameVector(_, _, _))
.Times(AnyNumber());
EXPECT_CALL(*event_interface_, OnClosingHandshake()).Times(AnyNumber());
+ EXPECT_CALL(*event_interface_, OnSendDataFrameDone()).Times(AnyNumber());
EXPECT_CALL(*event_interface_, OnFailChannel(_)).Times(AnyNumber());
EXPECT_CALL(*event_interface_, OnDropChannel(_, _, _)).Times(AnyNumber());
}
@@ -963,6 +960,7 @@
// whether these methods are called or not.
EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _, _, _))
.Times(AnyNumber());
+ EXPECT_CALL(*event_interface_, OnSendDataFrameDone()).Times(AnyNumber());
}
};
@@ -1411,98 +1409,6 @@
base::RunLoop().RunUntilIdle();
}
-// If the renderer sends lots of small writes, we don't want to update the quota
-// for each one.
-TEST_F(WebSocketChannelEventInterfaceTest, SmallWriteDoesntUpdateQuota) {
- set_stream(std::make_unique<WriteableFakeWebSocketStream>());
- {
- InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _, _, _));
- }
-
- CreateChannelAndConnectSuccessfully();
- EXPECT_EQ(channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText,
- AsIOBuffer("B"), 1U),
- WebSocketChannel::CHANNEL_ALIVE);
-}
-
-// If we send enough to go below |send_quota_low_water_mark_| we should get our
-// quota refreshed.
-TEST_F(WebSocketChannelEventInterfaceTest, LargeWriteUpdatesQuota) {
- set_stream(std::make_unique<WriteableFakeWebSocketStream>());
- // We use this checkpoint object to verify that the quota update comes after
- // the write.
- Checkpoint checkpoint;
- {
- InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _, _, _));
- EXPECT_CALL(checkpoint, Call(1));
- EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
- EXPECT_CALL(checkpoint, Call(2));
- }
-
- CreateChannelAndConnectSuccessfully();
- checkpoint.Call(1);
- EXPECT_EQ(
- channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText,
- AsIOBuffer(std::string(kDefaultInitialQuota, 'B')),
- kDefaultInitialQuota),
- WebSocketChannel::CHANNEL_ALIVE);
- checkpoint.Call(2);
-}
-
-// Verify that our quota actually is refreshed when we are told it is.
-TEST_F(WebSocketChannelEventInterfaceTest, QuotaReallyIsRefreshed) {
- set_stream(std::make_unique<WriteableFakeWebSocketStream>());
- Checkpoint checkpoint;
- {
- InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _, _, _));
- EXPECT_CALL(checkpoint, Call(1));
- EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
- EXPECT_CALL(checkpoint, Call(2));
- // If quota was not really refreshed, we would get an OnDropChannel()
- // message.
- EXPECT_CALL(*event_interface_, OnSendFlowControlQuotaAdded(_));
- EXPECT_CALL(checkpoint, Call(3));
- }
-
- CreateChannelAndConnectSuccessfully();
- checkpoint.Call(1);
- EXPECT_EQ(channel_->SendFrame(
- true, WebSocketFrameHeader::kOpCodeText,
- AsIOBuffer(std::string(kDefaultQuotaRefreshTrigger, 'D')),
- kDefaultQuotaRefreshTrigger),
- WebSocketChannel::CHANNEL_ALIVE);
- checkpoint.Call(2);
- // We should have received more quota at this point.
- EXPECT_EQ(channel_->SendFrame(
- true, WebSocketFrameHeader::kOpCodeText,
- AsIOBuffer(std::string(kDefaultQuotaRefreshTrigger, 'E')),
- kDefaultQuotaRefreshTrigger),
- WebSocketChannel::CHANNEL_ALIVE);
- checkpoint.Call(3);
-}
-
-// If we send more than the available quota then the connection will be closed
-// with an error.
-TEST_F(WebSocketChannelEventInterfaceTest, WriteOverQuotaIsRejected) {
- set_stream(std::make_unique<WriteableFakeWebSocketStream>());
- {
- InSequence s;
- EXPECT_CALL(*event_interface_,
- OnAddChannelResponse(_, _, _, kDefaultInitialQuota));
- EXPECT_CALL(*event_interface_, OnFailChannel("Send quota exceeded"));
- }
-
- CreateChannelAndConnectSuccessfully();
- EXPECT_EQ(channel_->SendFrame(
- true, WebSocketFrameHeader::kOpCodeText,
- AsIOBuffer(std::string(kDefaultInitialQuota + 1, 'C')),
- kDefaultInitialQuota + 1),
- WebSocketChannel::CHANNEL_DELETED);
-}
-
// If a write fails, the channel is dropped.
TEST_F(WebSocketChannelEventInterfaceTest, FailedWrite) {
set_stream(std::make_unique<UnWriteableFakeWebSocketStream>());
@@ -1531,6 +1437,7 @@
{
InSequence s;
EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _, _, _));
+ EXPECT_CALL(*event_interface_, OnSendDataFrameDone());
EXPECT_CALL(*event_interface_,
OnDropChannel(true, kWebSocketNormalClosure, "Fred"));
}
@@ -2283,26 +2190,6 @@
std::move(write_callback).Run(OK);
}
-// When the renderer sends more on a channel than it has quota for, we send the
-// remote server a kWebSocketErrorGoingAway error code.
-TEST_F(WebSocketChannelStreamTest, SendGoingAwayOnRendererQuotaExceeded) {
- static const InitFrame expected[] = {
- {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
- MASKED, CLOSE_DATA(GOING_AWAY, "")}};
- EXPECT_CALL(*mock_stream_, ReadFramesInternal(_, _))
- .WillOnce(Return(ERR_IO_PENDING));
- EXPECT_CALL(*mock_stream_, WriteFramesInternal(EqualsFrames(expected), _))
- .WillOnce(Return(OK));
- EXPECT_CALL(*mock_stream_, Close());
-
- CreateChannelAndConnectSuccessfully();
- EXPECT_EQ(channel_->SendFrame(
- true, WebSocketFrameHeader::kOpCodeText,
- AsIOBuffer(std::string(kDefaultInitialQuota + 1, 'C')),
- kDefaultInitialQuota + 1),
- WebSocketChannel::CHANNEL_DELETED);
-}
-
// For convenience, most of these tests use Text frames. However, the WebSocket
// protocol also has Binary frames and those need to be 8-bit clean. For the
// sake of completeness, this test verifies that they are.
@@ -2925,30 +2812,5 @@
completion.WaitForResult();
}
-// Verify that current_send_quota() returns a non-zero value for a newly
-// connected channel.
-TEST_F(WebSocketChannelTest, CurrentSendQuotaNonZero) {
- CreateChannelAndConnectSuccessfully();
- EXPECT_GT(channel_->current_send_quota(), 0);
-}
-
-// Verify that current_send_quota() is updated when SendFrame() is called.
-TEST_F(WebSocketChannelTest, CurrentSendQuotaUpdated) {
- const int kMessageSize = 5;
- set_stream(std::make_unique<WriteableFakeWebSocketStream>());
- CreateChannelAndConnectSuccessfully();
-
- int initial_send_quota = channel_->current_send_quota();
- EXPECT_GE(initial_send_quota, kMessageSize);
-
- EXPECT_EQ(channel_->SendFrame(
- true, WebSocketFrameHeader::kOpCodeText,
- AsIOBuffer(std::string(static_cast<size_t>(kMessageSize), 'a')),
- static_cast<size_t>(kMessageSize)),
- WebSocketChannel::CHANNEL_ALIVE);
- int new_send_quota = channel_->current_send_quota();
- EXPECT_EQ(kMessageSize, initial_send_quota - new_send_quota);
-}
-
} // namespace
} // namespace net
diff --git a/net/websockets/websocket_end_to_end_test.cc b/net/websockets/websocket_end_to_end_test.cc
index caf50d9..1766d9b8 100644
--- a/net/websockets/websocket_end_to_end_test.cc
+++ b/net/websockets/websocket_end_to_end_test.cc
@@ -109,7 +109,7 @@
bool HasPendingDataFrames() override { return false; }
- void OnSendFlowControlQuotaAdded(int64_t quota) override;
+ void OnSendDataFrameDone() override;
void OnClosingHandshake() override;
@@ -182,7 +182,7 @@
base::span<const char> payload) {
}
-void ConnectTestingEventInterface::OnSendFlowControlQuotaAdded(int64_t quota) {}
+void ConnectTestingEventInterface::OnSendDataFrameDone() {}
void ConnectTestingEventInterface::OnClosingHandshake() {}
diff --git a/net/websockets/websocket_event_interface.h b/net/websockets/websocket_event_interface.h
index 63eb1b4..fb98d2e 100644
--- a/net/websockets/websocket_event_interface.h
+++ b/net/websockets/websocket_event_interface.h
@@ -64,9 +64,9 @@
// out. The network service should not read more from network until that.
virtual bool HasPendingDataFrames() = 0;
- // Called to provide more send quota for this channel to the renderer
- // process.
- virtual void OnSendFlowControlQuotaAdded(int64_t quota) = 0;
+ // Called once for each call to SendFrame() once the frame has been passed to
+ // the OS.
+ virtual void OnSendDataFrameDone() = 0;
// Called when the remote server has Started the WebSocket Closing
// Handshake. The client should not attempt to send any more messages after