summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-11 19:43:20 +0000
committerrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-11 19:43:20 +0000
commitca6459f86fd72f3491b867725890aec5ed85bafd (patch)
tree3eab34dccfda695c4c175e781f8850493ff35792 /net
parent492aea51e163cbf78126180752812978f9c28f72 (diff)
downloadchromium_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.h12
-rw-r--r--net/http/http_network_transaction.h4
-rw-r--r--net/spdy/spdy_http_stream.h4
-rw-r--r--net/spdy/spdy_network_transaction_spdy3_unittest.cc224
-rw-r--r--net/spdy/spdy_session.cc23
-rw-r--r--net/spdy/spdy_session.h5
-rw-r--r--net/spdy/spdy_session_spdy3_unittest.cc88
-rw-r--r--net/spdy/spdy_stream.cc15
-rw-r--r--net/spdy/spdy_stream.h10
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.