diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-11 19:43:20 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-11 19:43:20 +0000 |
commit | ca6459f86fd72f3491b867725890aec5ed85bafd (patch) | |
tree | 3eab34dccfda695c4c175e781f8850493ff35792 /net | |
parent | 492aea51e163cbf78126180752812978f9c28f72 (diff) | |
download | chromium_src-ca6459f86fd72f3491b867725890aec5ed85bafd.zip chromium_src-ca6459f86fd72f3491b867725890aec5ed85bafd.tar.gz chromium_src-ca6459f86fd72f3491b867725890aec5ed85bafd.tar.bz2 |
SPDY - Added the following flow control unitests.
* We update the send window size for all streams, current and future, on receipt of SETTINGS
* We correctly handle the case where the SETTINGS frame results in a negative send window size
* We correctly handle the case where the SETTINGS frame results in unstalling the send window
- In SETTINGS frame if SETTINGS_INITIAL_WINDOW_SIZE is less than zero, then NetLog the value
and ignore it because it is a server bug.
- Adding a NetLog event that we're updating streams send window sizes by |delta_window_size|
when we receive SETTINGS_INITIAL_WINDOW_SIZE.
- Added code to unstall the stream if it is stalled and when |delta_window_size| makes
send_window_size positive when we receive a SETTINGS frame.
BUG=113091
R=willchan
TEST=network unit tests
Review URL: http://codereview.chromium.org/9549010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@131811 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/net_log_event_type_list.h | 12 | ||||
-rw-r--r-- | net/http/http_network_transaction.h | 4 | ||||
-rw-r--r-- | net/spdy/spdy_http_stream.h | 4 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_spdy3_unittest.cc | 224 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 23 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 5 | ||||
-rw-r--r-- | net/spdy/spdy_session_spdy3_unittest.cc | 88 | ||||
-rw-r--r-- | net/spdy/spdy_stream.cc | 15 | ||||
-rw-r--r-- | net/spdy/spdy_stream.h | 10 |
9 files changed, 357 insertions, 28 deletions
diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h index 9e00980..71e02f0 100644 --- a/net/base/net_log_event_type_list.h +++ b/net/base/net_log_event_type_list.h @@ -1070,6 +1070,18 @@ EVENT_TYPE(SPDY_SESSION_CLOSE) // the maximum number of concurrent streams. EVENT_TYPE(SPDY_SESSION_STALLED_MAX_STREAMS) +// Received a negative value for initial window size in SETTINGS frame. +// { +// "initial_window_size" : <The initial window size>, +// } +EVENT_TYPE(SPDY_SESSION_NEGATIVE_INITIAL_WINDOW_SIZE) + +// Updating streams send window size by the delta window size. +// { +// "delta_window_size" : <The delta window size>, +// } +EVENT_TYPE(SPDY_SESSION_UPDATE_STREAMS_SEND_WINDOW_SIZE) + // ------------------------------------------------------------------------ // SpdySessionPool // ------------------------------------------------------------------------ diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index 779d0e3..73ff419 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -105,6 +105,10 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction WindowUpdateOverflow); FRIEND_TEST_ALL_PREFIXES(SpdyNetworkTransactionSpdy3Test, FlowControlStallResume); + FRIEND_TEST_ALL_PREFIXES(SpdyNetworkTransactionSpdy3Test, + FlowControlStallResumeAfterSettings); + FRIEND_TEST_ALL_PREFIXES(SpdyNetworkTransactionSpdy3Test, + FlowControlNegativeSendWindowSize); enum State { STATE_CREATE_STREAM, diff --git a/net/spdy/spdy_http_stream.h b/net/spdy/spdy_http_stream.h index 42c612d..da99777 100644 --- a/net/spdy/spdy_http_stream.h +++ b/net/spdy/spdy_http_stream.h @@ -90,6 +90,10 @@ class NET_EXPORT_PRIVATE SpdyHttpStream : public SpdyStream::Delegate, FlowControlStallResume); FRIEND_TEST_ALL_PREFIXES(SpdyNetworkTransactionSpdy3Test, FlowControlStallResume); + FRIEND_TEST_ALL_PREFIXES(SpdyNetworkTransactionSpdy3Test, + FlowControlStallResumeAfterSettings); + FRIEND_TEST_ALL_PREFIXES(SpdyNetworkTransactionSpdy3Test, + FlowControlNegativeSendWindowSize); // Call the user callback. void DoCallback(int rv); diff --git a/net/spdy/spdy_network_transaction_spdy3_unittest.cc b/net/spdy/spdy_network_transaction_spdy3_unittest.cc index 92fab4c..666cf54 100644 --- a/net/spdy/spdy_network_transaction_spdy3_unittest.cc +++ b/net/spdy/spdy_network_transaction_spdy3_unittest.cc @@ -34,6 +34,7 @@ enum SpdyNetworkTransactionSpdy3TestTypes { SPDYNOSSL, SPDYSSL, }; + class SpdyNetworkTransactionSpdy3Test : public ::testing::TestWithParam<SpdyNetworkTransactionSpdy3TestTypes> { protected: @@ -2135,36 +2136,34 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, FlowControlStallResume) { // frames plus SYN_STREAM plus the last data frame; also we need another // data frame that we will send once the WINDOW_UPDATE is received, // therefore +3. - size_t nwrites = - kSpdyStreamInitialWindowSize / kMaxSpdyFrameChunkSize + 3; + size_t num_writes = kSpdyStreamInitialWindowSize / kMaxSpdyFrameChunkSize + 3; // Calculate last frame's size; 0 size data frame is legal. size_t last_frame_size = kSpdyStreamInitialWindowSize % kMaxSpdyFrameChunkSize; // Construct content for a data frame of maximum size. - scoped_ptr<std::string> content( - new std::string(kMaxSpdyFrameChunkSize, 'a')); + std::string content(kMaxSpdyFrameChunkSize, 'a'); scoped_ptr<SpdyFrame> req(ConstructSpdyPost( kSpdyStreamInitialWindowSize + kUploadDataSize, NULL, 0)); // Full frames. scoped_ptr<SpdyFrame> body1( - ConstructSpdyBodyFrame(1, content->c_str(), content->size(), false)); + ConstructSpdyBodyFrame(1, content.c_str(), content.size(), false)); // Last frame to zero out the window size. scoped_ptr<SpdyFrame> body2( - ConstructSpdyBodyFrame(1, content->c_str(), last_frame_size, false)); + ConstructSpdyBodyFrame(1, content.c_str(), last_frame_size, false)); // Data frame to be sent once WINDOW_UPDATE frame is received. scoped_ptr<SpdyFrame> body3(ConstructSpdyBodyFrame(1, true)); // Fill in mock writes. - scoped_array<MockWrite> writes(new MockWrite[nwrites]); + scoped_array<MockWrite> writes(new MockWrite[num_writes]); size_t i = 0; writes[i] = CreateMockWrite(*req); - for (i = 1; i < nwrites-2; i++) + for (i = 1; i < num_writes - 2; i++) writes[i] = CreateMockWrite(*body1); writes[i++] = CreateMockWrite(*body2); writes[i] = CreateMockWrite(*body3); @@ -2186,8 +2185,8 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, FlowControlStallResume) { // Force all writes to happen before any read, last write will not // actually queue a frame, due to window size being 0. scoped_ptr<DelayedSocketData> data( - new DelayedSocketData(nwrites, reads, arraysize(reads), - writes.get(), nwrites)); + new DelayedSocketData(num_writes, reads, arraysize(reads), + writes.get(), num_writes)); HttpRequestInfo request; request.method = "POST"; @@ -2197,8 +2196,7 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, FlowControlStallResume) { new std::string(kSpdyStreamInitialWindowSize, 'a')); upload_data->append(kUploadData, kUploadDataSize); request.upload_data->AppendBytes(upload_data->c_str(), upload_data->size()); - NormalSpdyTransactionHelper helper(request, - BoundNetLog(), GetParam()); + NormalSpdyTransactionHelper helper(request, BoundNetLog(), GetParam()); helper.AddData(data.get()); helper.RunPreTestSetup(); @@ -2208,7 +2206,7 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, FlowControlStallResume) { int rv = trans->Start(&helper.request(), callback.callback(), BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); - MessageLoop::current()->RunAllPending(); // Write as much as we can. + MessageLoop::current()->RunAllPending(); // Write as much as we can. SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); ASSERT_TRUE(stream != NULL); @@ -2218,7 +2216,7 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, FlowControlStallResume) { // TODO(satorux): This is because of the weirdness in reading the request // body in OnSendBodyComplete(). See crbug.com/113107. EXPECT_TRUE(stream->request_body_stream_->IsEOF()); - // But the body is not yet fully sent ("hello!" is not yet sent). + // But the body is not yet fully sent (kUploadData is not yet sent). EXPECT_FALSE(stream->stream()->body_sent()); data->ForceNextRead(); // Read in WINDOW_UPDATE frame. @@ -2226,6 +2224,204 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, FlowControlStallResume) { helper.VerifyDataConsumed(); } +// Test we correctly handle the case where the SETTINGS frame results in +// unstalling the send window. +TEST_P(SpdyNetworkTransactionSpdy3Test, FlowControlStallResumeAfterSettings) { + // Number of frames we need to send to zero out the window size: data + // frames plus SYN_STREAM plus the last data frame; also we need another + // data frame that we will send once the SETTING is received, therefore +3. + size_t num_writes = kSpdyStreamInitialWindowSize / kMaxSpdyFrameChunkSize + 3; + + // Calculate last frame's size; 0 size data frame is legal. + size_t last_frame_size = + kSpdyStreamInitialWindowSize % kMaxSpdyFrameChunkSize; + + // Construct content for a data frame of maximum size. + std::string content(kMaxSpdyFrameChunkSize, 'a'); + + scoped_ptr<SpdyFrame> req(ConstructSpdyPost( + kSpdyStreamInitialWindowSize + kUploadDataSize, NULL, 0)); + + // Full frames. + scoped_ptr<SpdyFrame> body1( + ConstructSpdyBodyFrame(1, content.c_str(), content.size(), false)); + + // Last frame to zero out the window size. + scoped_ptr<SpdyFrame> body2( + ConstructSpdyBodyFrame(1, content.c_str(), last_frame_size, false)); + + // Data frame to be sent once SETTINGS frame is received. + scoped_ptr<SpdyFrame> body3(ConstructSpdyBodyFrame(1, true)); + + // Fill in mock writes. + scoped_array<MockWrite> writes(new MockWrite[num_writes]); + size_t i = 0; + writes[i] = CreateMockWrite(*req); + for (i = 1; i < num_writes - 2; i++) + writes[i] = CreateMockWrite(*body1); + writes[i++] = CreateMockWrite(*body2); + writes[i] = CreateMockWrite(*body3); + + // Construct read frame for SETTINGS that gives enough space to upload the + // rest of the data. + SpdySettings settings; + SettingsFlagsAndId id(0, SETTINGS_INITIAL_WINDOW_SIZE); + settings.push_back(SpdySetting(id, kSpdyStreamInitialWindowSize * 2)); + scoped_ptr<SpdyFrame> settings_frame_large(ConstructSpdySettings(settings)); + scoped_ptr<SpdyFrame> reply(ConstructSpdyPostSynReply(NULL, 0)); + MockRead reads[] = { + CreateMockRead(*settings_frame_large), + CreateMockRead(*reply), + CreateMockRead(*body2), + CreateMockRead(*body3), + MockRead(ASYNC, 0, 0) // EOF + }; + + // Force all writes to happen before any read, last write will not + // actually queue a frame, due to window size being 0. + scoped_ptr<DelayedSocketData> data( + new DelayedSocketData(num_writes, reads, arraysize(reads), + writes.get(), num_writes)); + + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.google.com/"); + request.upload_data = new UploadData(); + std::string upload_data(kSpdyStreamInitialWindowSize, 'a'); + upload_data.append(kUploadData, kUploadDataSize); + request.upload_data->AppendBytes(upload_data.c_str(), upload_data.size()); + NormalSpdyTransactionHelper helper(request, BoundNetLog(), GetParam()); + helper.AddData(data.get()); + helper.RunPreTestSetup(); + + HttpNetworkTransaction* trans = helper.trans(); + + TestCompletionCallback callback; + int rv = trans->Start(&helper.request(), callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + MessageLoop::current()->RunAllPending(); // Write as much as we can. + + SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); + ASSERT_TRUE(stream != NULL); + ASSERT_TRUE(stream->stream() != NULL); + EXPECT_EQ(0, stream->stream()->send_window_size()); + + // All the body data should have been read. + // TODO(satorux): This is because of the weirdness in reading the request + // body in OnSendBodyComplete(). See crbug.com/113107. + EXPECT_TRUE(stream->request_body_stream_->IsEOF()); + // But the body is not yet fully sent (kUploadData is not yet sent). + EXPECT_FALSE(stream->stream()->body_sent()); + EXPECT_TRUE(stream->stream()->stalled_by_flow_control()); + + data->ForceNextRead(); // Read in SETTINGS frame to unstall. + rv = callback.WaitForResult(); + helper.VerifyDataConsumed(); + EXPECT_FALSE(stream->stream()->stalled_by_flow_control()); +} + +// Test we correctly handle the case where the SETTINGS frame results in a +// negative send window size. +TEST_P(SpdyNetworkTransactionSpdy3Test, FlowControlNegativeSendWindowSize) { + // Number of frames we need to send to zero out the window size: data + // frames plus SYN_STREAM plus the last data frame; also we need another + // data frame that we will send once the SETTING is received, therefore +3. + size_t num_writes = kSpdyStreamInitialWindowSize / kMaxSpdyFrameChunkSize + 3; + + // Calculate last frame's size; 0 size data frame is legal. + size_t last_frame_size = + kSpdyStreamInitialWindowSize % kMaxSpdyFrameChunkSize; + + // Construct content for a data frame of maximum size. + std::string content(kMaxSpdyFrameChunkSize, 'a'); + + scoped_ptr<SpdyFrame> req(ConstructSpdyPost( + kSpdyStreamInitialWindowSize + kUploadDataSize, NULL, 0)); + + // Full frames. + scoped_ptr<SpdyFrame> body1( + ConstructSpdyBodyFrame(1, content.c_str(), content.size(), false)); + + // Last frame to zero out the window size. + scoped_ptr<SpdyFrame> body2( + ConstructSpdyBodyFrame(1, content.c_str(), last_frame_size, false)); + + // Data frame to be sent once SETTINGS frame is received. + scoped_ptr<SpdyFrame> body3(ConstructSpdyBodyFrame(1, true)); + + // Fill in mock writes. + scoped_array<MockWrite> writes(new MockWrite[num_writes]); + size_t i = 0; + writes[i] = CreateMockWrite(*req); + for (i = 1; i < num_writes - 2; i++) + writes[i] = CreateMockWrite(*body1); + writes[i++] = CreateMockWrite(*body2); + writes[i] = CreateMockWrite(*body3); + + // Construct read frame for SETTINGS that makes the send_window_size + // negative. + SpdySettings new_settings; + SettingsFlagsAndId new_id(0, SETTINGS_INITIAL_WINDOW_SIZE); + new_settings.push_back(SpdySetting(new_id, kSpdyStreamInitialWindowSize / 2)); + scoped_ptr<SpdyFrame> settings_frame_small( + ConstructSpdySettings(new_settings)); + // Construct read frame for WINDOW_UPDATE that makes the send_window_size + // postive. + scoped_ptr<SpdyFrame> window_update_init_size( + ConstructSpdyWindowUpdate(1, kSpdyStreamInitialWindowSize)); + scoped_ptr<SpdyFrame> reply(ConstructSpdyPostSynReply(NULL, 0)); + MockRead reads[] = { + CreateMockRead(*settings_frame_small), + CreateMockRead(*window_update_init_size), + CreateMockRead(*reply), + CreateMockRead(*body2), + CreateMockRead(*body3), + MockRead(ASYNC, 0, 0) // EOF + }; + + // Force all writes to happen before any read, last write will not actually + // queue a frame, due to window size being 0. + scoped_ptr<DelayedSocketData> data( + new DelayedSocketData(num_writes, reads, arraysize(reads), + writes.get(), num_writes)); + + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.google.com/"); + request.upload_data = new UploadData(); + std::string upload_data(kSpdyStreamInitialWindowSize, 'a'); + upload_data.append(kUploadData, kUploadDataSize); + request.upload_data->AppendBytes(upload_data.c_str(), upload_data.size()); + NormalSpdyTransactionHelper helper(request, BoundNetLog(), GetParam()); + helper.AddData(data.get()); + helper.RunPreTestSetup(); + + HttpNetworkTransaction* trans = helper.trans(); + + TestCompletionCallback callback; + int rv = trans->Start(&helper.request(), callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + MessageLoop::current()->RunAllPending(); // Write as much as we can. + + SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); + ASSERT_TRUE(stream != NULL); + ASSERT_TRUE(stream->stream() != NULL); + EXPECT_EQ(0, stream->stream()->send_window_size()); + + // All the body data should have been read. + // TODO(satorux): This is because of the weirdness in reading the request + // body in OnSendBodyComplete(). See crbug.com/113107. + EXPECT_TRUE(stream->request_body_stream_->IsEOF()); + // But the body is not yet fully sent (kUploadData is not yet sent). + EXPECT_FALSE(stream->stream()->body_sent()); + + data->ForceNextRead(); // Read in WINDOW_UPDATE or SETTINGS frame. + rv = callback.WaitForResult(); + helper.VerifyDataConsumed(); +} + TEST_P(SpdyNetworkTransactionSpdy3Test, ResetReplyWithTransferEncoding) { // Construct the request. scoped_ptr<SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index f6b360dc..ee0918b 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -1753,14 +1753,21 @@ void SpdySession::HandleSetting(uint32 id, uint32 value) { ProcessPendingCreateStreams(); break; case SETTINGS_INITIAL_WINDOW_SIZE: - // INITIAL_WINDOW_SIZE updates initial_send_window_size_ only. - // TODO(rtenneti): discuss with the server team about - // initial_recv_window_size_. - int32 prev_initial_send_window_size = initial_send_window_size_; - initial_send_window_size_ = value; - int32 delta_window_size = - initial_send_window_size_ - prev_initial_send_window_size; - UpdateStreamsSendWindowSize(delta_window_size); + if (static_cast<int32>(value) < 0) { + net_log().AddEvent( + NetLog::TYPE_SPDY_SESSION_NEGATIVE_INITIAL_WINDOW_SIZE, + make_scoped_refptr(new NetLogIntegerParameter( + "initial_window_size", value))); + } else { + // SETTINGS_INITIAL_WINDOW_SIZE updates initial_send_window_size_ only. + int32 delta_window_size = value - initial_send_window_size_; + initial_send_window_size_ = value; + UpdateStreamsSendWindowSize(delta_window_size); + net_log().AddEvent( + NetLog::TYPE_SPDY_SESSION_UPDATE_STREAMS_SEND_WINDOW_SIZE, + make_scoped_refptr(new NetLogIntegerParameter( + "delta_window_size", delta_window_size))); + } break; } } diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index f03436c..68a1a42 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -244,6 +244,11 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, return flow_control_; } + // Returns the current |initial_send_window_size_|. + int32 initial_send_window_size() const { + return initial_send_window_size_; + } + // Returns the current |initial_recv_window_size_|. int32 initial_recv_window_size() const { return initial_recv_window_size_; } diff --git a/net/spdy/spdy_session_spdy3_unittest.cc b/net/spdy/spdy_session_spdy3_unittest.cc index 4d78f37..e66b309 100644 --- a/net/spdy/spdy_session_spdy3_unittest.cc +++ b/net/spdy/spdy_session_spdy3_unittest.cc @@ -1132,4 +1132,92 @@ TEST_F(SpdySessionSpdy3Test, CloseSessionOnError) { EXPECT_EQ(ERR_CONNECTION_CLOSED, request_params->status()); } +TEST_F(SpdySessionSpdy3Test, UpdateStreamsSendWindowSize) { + // Set SETTINGS_INITIAL_WINDOW_SIZE to a small number so that WINDOW_UPDATE + // gets sent. + SpdySettings new_settings; + SettingsFlagsAndId id(0, SETTINGS_INITIAL_WINDOW_SIZE); + int32 window_size = 1; + new_settings.push_back(SpdySetting(id, window_size)); + + // Set up the socket so we read a SETTINGS frame that sets + // INITIAL_WINDOW_SIZE. + MockConnect connect_data(SYNCHRONOUS, OK); + scoped_ptr<SpdyFrame> settings_frame(ConstructSpdySettings(new_settings)); + MockRead reads[] = { + CreateMockRead(*settings_frame), + MockRead(SYNCHRONOUS, 0, 0) // EOF + }; + + SpdySessionDependencies session_deps; + session_deps.host_resolver->set_synchronous_mode(true); + + StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); + data.set_connect_data(connect_data); + session_deps.socket_factory->AddSocketDataProvider(&data); + + SSLSocketDataProvider ssl(SYNCHRONOUS, OK); + session_deps.socket_factory->AddSSLSocketDataProvider(&ssl); + + scoped_refptr<HttpNetworkSession> http_session( + SpdySessionDependencies::SpdyCreateSession(&session_deps)); + + const std::string kTestHost("www.foo.com"); + const int kTestPort = 80; + HostPortPair test_host_port_pair(kTestHost, kTestPort); + HostPortProxyPair pair(test_host_port_pair, ProxyServer::Direct()); + + SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool()); + + // Create a session. + EXPECT_FALSE(spdy_session_pool->HasSession(pair)); + scoped_refptr<SpdySession> session = + spdy_session_pool->Get(pair, BoundNetLog()); + ASSERT_TRUE(spdy_session_pool->HasSession(pair)); + + scoped_refptr<TransportSocketParams> transport_params( + new TransportSocketParams(test_host_port_pair, + MEDIUM, + false, + false)); + scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); + EXPECT_EQ(OK, connection->Init(test_host_port_pair.ToString(), + transport_params, MEDIUM, CompletionCallback(), + http_session->GetTransportSocketPool( + HttpNetworkSession::NORMAL_SOCKET_POOL), + BoundNetLog())); + EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), false, OK)); + + scoped_refptr<SpdyStream> spdy_stream1; + TestCompletionCallback callback1; + GURL url("http://www.google.com"); + EXPECT_EQ(OK, + session->CreateStream(url, + MEDIUM, /* priority, not important */ + &spdy_stream1, + BoundNetLog(), + callback1.callback())); + EXPECT_NE(spdy_stream1->send_window_size(), window_size); + + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(session->initial_send_window_size(), window_size); + EXPECT_EQ(spdy_stream1->send_window_size(), window_size); + + // Release the first one, this will allow the second to be created. + spdy_stream1->Cancel(); + spdy_stream1 = NULL; + + scoped_refptr<SpdyStream> spdy_stream2; + EXPECT_EQ(OK, + session->CreateStream(url, + MEDIUM, /* priority, not important */ + &spdy_stream2, + BoundNetLog(), + callback1.callback())); + + EXPECT_EQ(spdy_stream2->send_window_size(), window_size); + spdy_stream2->Cancel(); + spdy_stream2 = NULL; +} + } // namespace net diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc index 0fa62901..c8238fc 100644 --- a/net/spdy/spdy_stream.cc +++ b/net/spdy/spdy_stream.cc @@ -166,8 +166,17 @@ void SpdyStream::set_initial_recv_window_size(int32 window_size) { session_->set_initial_recv_window_size(window_size); } +void SpdyStream::PossiblyResumeIfStalled() { + if (send_window_size_ > 0 && stalled_by_flow_control_) { + stalled_by_flow_control_ = false; + io_state_ = STATE_SEND_BODY; + DoLoop(OK); + } +} + void SpdyStream::AdjustSendWindowSize(int32 delta_window_size) { send_window_size_ += delta_window_size; + PossiblyResumeIfStalled(); } void SpdyStream::IncreaseSendWindowSize(int32 delta_window_size) { @@ -201,11 +210,7 @@ void SpdyStream::IncreaseSendWindowSize(int32 delta_window_size) { NetLog::TYPE_SPDY_STREAM_UPDATE_SEND_WINDOW, make_scoped_refptr(new NetLogSpdyStreamWindowUpdateParameter( stream_id_, delta_window_size, send_window_size_))); - if (send_window_size_ > 0 && stalled_by_flow_control_) { - stalled_by_flow_control_ = false; - io_state_ = STATE_SEND_BODY; - DoLoop(OK); - } + PossiblyResumeIfStalled(); } void SpdyStream::DecreaseSendWindowSize(int32 delta_window_size) { diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h index 9798845..55e8777 100644 --- a/net/spdy/spdy_stream.h +++ b/net/spdy/spdy_stream.h @@ -138,11 +138,15 @@ class NET_EXPORT_PRIVATE SpdyStream // Set session_'s initial_recv_window_size. Used by unittests. void set_initial_recv_window_size(int32 window_size); + bool stalled_by_flow_control() { return stalled_by_flow_control_; } + void set_stalled_by_flow_control(bool stalled) { stalled_by_flow_control_ = stalled; } - // Adjust the |send_window_size_| by |delta_window_size|. + // Adjusts the |send_window_size_| by |delta_window_size|. |delta_window_size| + // is the difference between the SETTINGS_INITIAL_WINDOW_SIZE in SETTINGS + // frame and the previous initial_send_window_size. void AdjustSendWindowSize(int32 delta_window_size); // Increases |send_window_size_| with delta extracted from a WINDOW_UPDATE @@ -274,6 +278,10 @@ class NET_EXPORT_PRIVATE SpdyStream friend class base::RefCounted<SpdyStream>; virtual ~SpdyStream(); + // If the stream is stalled and if |send_window_size_| is positive, then set + // |stalled_by_flow_control_| to false and unstall the stream. + void PossiblyResumeIfStalled(); + void OnGetDomainBoundCertComplete(int result); // Try to make progress sending/receiving the request/response. |