diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-08 05:49:36 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-08 05:49:36 +0000 |
commit | f54a4a4f889ac4e99b3a4b0a3ee9b2507dde18ae (patch) | |
tree | 3319eb0d0ff265adc467d5702c2c0287bc10e6ac | |
parent | 5aad680df5d54c9f9f618912e391cc1c06aee8c1 (diff) | |
download | chromium_src-f54a4a4f889ac4e99b3a4b0a3ee9b2507dde18ae.zip chromium_src-f54a4a4f889ac4e99b3a4b0a3ee9b2507dde18ae.tar.gz chromium_src-f54a4a4f889ac4e99b3a4b0a3ee9b2507dde18ae.tar.bz2 |
When SpdySession is initialized with a socket that negotiated a specific SPDY version, use that version to determine if flow control should be used.
Remove flow control tests from spdy/2 tests, since flow control is not part
of spdy/2 (only spdy/2.1 and spdy/3)
Review URL: http://codereview.chromium.org/9632004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@125572 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/spdy/spdy_network_transaction_spdy2_unittest.cc | 379 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 12 |
2 files changed, 5 insertions, 386 deletions
diff --git a/net/spdy/spdy_network_transaction_spdy2_unittest.cc b/net/spdy/spdy_network_transaction_spdy2_unittest.cc index 8a8e001..5859a98 100644 --- a/net/spdy/spdy_network_transaction_spdy2_unittest.cc +++ b/net/spdy/spdy_network_transaction_spdy2_unittest.cc @@ -1870,385 +1870,6 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, ResponseWithTwoSynReplies) { helper.VerifyDataConsumed(); } -// Test that sent data frames and received WINDOW_UPDATE frames change -// the send_window_size_ correctly. - -// WINDOW_UPDATE is different than most other frames in that it can arrive -// while the client is still sending the request body. In order to enforce -// this scenario, we feed a couple of dummy frames and give a delay of 0 to -// socket data provider, so that initial read that is done as soon as the -// stream is created, succeeds and schedules another read. This way reads -// and writes are interleaved; after doing a full frame write, SpdyStream -// will break out of DoLoop and will read and process a WINDOW_UPDATE. -// Once our WINDOW_UPDATE is read, we cannot send SYN_REPLY right away -// since request has not been completely written, therefore we feed -// enough number of WINDOW_UPDATEs to finish the first read and cause a -// write, leading to a complete write of request body; after that we send -// a reply with a body, to cause a graceful shutdown. - -// TODO(agayev): develop a socket data provider where both, reads and -// writes are ordered so that writing tests like these are easy and rewrite -// all these tests using it. Right now we are working around the -// limitations as described above and it's not deterministic, tests may -// fail under specific circumstances. -TEST_P(SpdyNetworkTransactionSpdy2Test, WindowUpdateReceived) { - SpdySession::set_use_flow_control(SpdySession::kEnableFlowControl); - - static int kFrameCount = 2; - scoped_ptr<std::string> content( - new std::string(kMaxSpdyFrameChunkSize, 'a')); - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPost( - kMaxSpdyFrameChunkSize * kFrameCount, NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body( - ConstructSpdyBodyFrame(1, content->c_str(), content->size(), false)); - scoped_ptr<spdy::SpdyFrame> body_end( - ConstructSpdyBodyFrame(1, content->c_str(), content->size(), true)); - - MockWrite writes[] = { - CreateMockWrite(*req), - CreateMockWrite(*body), - CreateMockWrite(*body_end), - }; - - static const int32 kDeltaWindowSize = 0xff; - static const int kDeltaCount = 4; - scoped_ptr<spdy::SpdyFrame> window_update( - ConstructSpdyWindowUpdate(1, kDeltaWindowSize)); - scoped_ptr<spdy::SpdyFrame> window_update_dummy( - ConstructSpdyWindowUpdate(2, kDeltaWindowSize)); - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyPostSynReply(NULL, 0)); - MockRead reads[] = { - CreateMockRead(*window_update_dummy), - CreateMockRead(*window_update_dummy), - CreateMockRead(*window_update_dummy), - CreateMockRead(*window_update), // Four updates, therefore window - CreateMockRead(*window_update), // size should increase by - CreateMockRead(*window_update), // kDeltaWindowSize * 4 - CreateMockRead(*window_update), - CreateMockRead(*resp), - CreateMockRead(*body_end), - MockRead(ASYNC, 0, 0) // EOF - }; - - scoped_ptr<DelayedSocketData> data( - new DelayedSocketData(0, reads, arraysize(reads), - writes, arraysize(writes))); - - // Setup the request - HttpRequestInfo request; - request.method = "POST"; - request.url = GURL(kDefaultURL); - request.upload_data = new UploadData(); - for (int i = 0; i < kFrameCount; ++i) - request.upload_data->AppendBytes(content->c_str(), content->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); - rv = callback.WaitForResult(); - EXPECT_EQ(OK, rv); - - SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); - ASSERT_TRUE(stream != NULL); - ASSERT_TRUE(stream->stream() != NULL); - EXPECT_EQ(static_cast<int>(spdy::kSpdyStreamInitialWindowSize) + - kDeltaWindowSize * kDeltaCount - - kMaxSpdyFrameChunkSize * kFrameCount, - stream->stream()->send_window_size()); - helper.VerifyDataConsumed(); - - SpdySession::set_use_flow_control(SpdySession::kFlowControlBasedOnNPN); -} - -// Test that received data frames and sent WINDOW_UPDATE frames change -// the recv_window_size_ correctly. -TEST_P(SpdyNetworkTransactionSpdy2Test, WindowUpdateSent) { - SpdySession::set_use_flow_control(SpdySession::kEnableFlowControl); - - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); - scoped_ptr<spdy::SpdyFrame> window_update( - ConstructSpdyWindowUpdate(1, kUploadDataSize)); - - MockWrite writes[] = { - CreateMockWrite(*req), - CreateMockWrite(*window_update), - }; - - scoped_ptr<spdy::SpdyFrame> resp( - ConstructSpdyGetSynReply(NULL, 0, 1)); - scoped_ptr<spdy::SpdyFrame> body_no_fin( - ConstructSpdyBodyFrame(1, false)); - scoped_ptr<spdy::SpdyFrame> body_fin( - ConstructSpdyBodyFrame(1, NULL, 0, true)); - MockRead reads[] = { - CreateMockRead(*resp), - CreateMockRead(*body_no_fin), - MockRead(ASYNC, ERR_IO_PENDING, 0), // Force a pause - CreateMockRead(*body_fin), - MockRead(ASYNC, ERR_IO_PENDING, 0), // Force a pause - MockRead(ASYNC, 0, 0) // EOF - }; - - scoped_ptr<DelayedSocketData> data( - new DelayedSocketData(1, reads, arraysize(reads), - writes, arraysize(writes))); - - NormalSpdyTransactionHelper helper(CreateGetRequest(), - 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); - rv = callback.WaitForResult(); - EXPECT_EQ(OK, rv); - - SpdyHttpStream* stream = - static_cast<SpdyHttpStream*>(trans->stream_.get()); - ASSERT_TRUE(stream != NULL); - ASSERT_TRUE(stream->stream() != NULL); - - EXPECT_EQ( - static_cast<int>(spdy::kSpdyStreamInitialWindowSize) - kUploadDataSize, - stream->stream()->recv_window_size()); - - const HttpResponseInfo* response = trans->GetResponseInfo(); - ASSERT_TRUE(response != NULL); - ASSERT_TRUE(response->headers != NULL); - EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); - EXPECT_TRUE(response->was_fetched_via_spdy); - - // Force sending of WINDOW_UPDATE by setting initial_recv_window_size to a - // small value. - stream->stream()->set_initial_recv_window_size(kUploadDataSize / 2); - - // Issue a read which will cause a WINDOW_UPDATE to be sent and window - // size increased to default. - scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kUploadDataSize)); - rv = trans->Read(buf, kUploadDataSize, CompletionCallback()); - EXPECT_EQ(kUploadDataSize, rv); - std::string content(buf->data(), buf->data()+kUploadDataSize); - EXPECT_STREQ(kUploadData, content.c_str()); - - // Schedule the reading of empty data frame with FIN - data->CompleteRead(); - - // Force write of WINDOW_UPDATE which was scheduled during the above - // read. - MessageLoop::current()->RunAllPending(); - - // Read EOF. - data->CompleteRead(); - - helper.VerifyDataConsumed(); - - SpdySession::set_use_flow_control(SpdySession::kFlowControlBasedOnNPN); -} - -// Test that WINDOW_UPDATE frame causing overflow is handled correctly. We -// use the same trick as in the above test to enforce our scenario. -TEST_P(SpdyNetworkTransactionSpdy2Test, WindowUpdateOverflow) { - SpdySession::set_use_flow_control(SpdySession::kEnableFlowControl); - - // number of full frames we hope to write (but will not, used to - // set content-length header correctly) - static int kFrameCount = 3; - - scoped_ptr<std::string> content( - new std::string(kMaxSpdyFrameChunkSize, 'a')); - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPost( - kMaxSpdyFrameChunkSize * kFrameCount, NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body( - ConstructSpdyBodyFrame(1, content->c_str(), content->size(), false)); - scoped_ptr<spdy::SpdyFrame> rst( - ConstructSpdyRstStream(1, spdy::FLOW_CONTROL_ERROR)); - - // We're not going to write a data frame with FIN, we'll receive a bad - // WINDOW_UPDATE while sending a request and will send a RST_STREAM frame. - MockWrite writes[] = { - CreateMockWrite(*req), - CreateMockWrite(*body), - CreateMockWrite(*rst), - }; - - static const int32 kDeltaWindowSize = 0x7fffffff; // cause an overflow - scoped_ptr<spdy::SpdyFrame> window_update( - ConstructSpdyWindowUpdate(1, kDeltaWindowSize)); - scoped_ptr<spdy::SpdyFrame> window_update2( - ConstructSpdyWindowUpdate(2, kDeltaWindowSize)); - scoped_ptr<spdy::SpdyFrame> reply(ConstructSpdyPostSynReply(NULL, 0)); - - MockRead reads[] = { - CreateMockRead(*window_update2), - CreateMockRead(*window_update2), - CreateMockRead(*window_update), - CreateMockRead(*window_update), - CreateMockRead(*window_update), - MockRead(ASYNC, ERR_IO_PENDING, 0), // Wait for the RST to be written. - MockRead(ASYNC, 0, 0) // EOF - }; - - scoped_ptr<DelayedSocketData> data( - new DelayedSocketData(0, reads, arraysize(reads), - writes, arraysize(writes))); - - // Setup the request - HttpRequestInfo request; - request.method = "POST"; - request.url = GURL("http://www.google.com/"); - request.upload_data = new UploadData(); - for (int i = 0; i < kFrameCount; ++i) - request.upload_data->AppendBytes(content->c_str(), content->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); - rv = callback.WaitForResult(); - EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, rv); - - data->CompleteRead(); - - ASSERT_TRUE(helper.session() != NULL); - ASSERT_TRUE(helper.session()->spdy_session_pool() != NULL); - helper.session()->spdy_session_pool()->CloseAllSessions(); - helper.VerifyDataConsumed(); - - SpdySession::set_use_flow_control(SpdySession::kFlowControlBasedOnNPN); -} - -// Test that after hitting a send window size of 0, the write process -// stalls and upon receiving WINDOW_UPDATE frame write resumes. - -// This test constructs a POST request followed by enough data frames -// containing 'a' that would make the window size 0, followed by another -// data frame containing default content (which is "hello!") and this frame -// also contains a FIN flag. DelayedSocketData is used to enforce all -// writes go through before a read could happen. However, the last frame -// ("hello!") is not supposed to go through since by the time its turn -// arrives, window size is 0. At this point MessageLoop::Run() called via -// callback would block. Therefore we call MessageLoop::RunAllPending() -// which returns after performing all possible writes. We use DCHECKS to -// ensure that last data frame is still there and stream has stalled. -// After that, next read is artifically enforced, which causes a -// WINDOW_UPDATE to be read and I/O process resumes. -TEST_P(SpdyNetworkTransactionSpdy2Test, FlowControlStallResume) { - SpdySession::set_use_flow_control(SpdySession::kEnableFlowControl); - - // 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 WINDOW_UPDATE is received, - // therefore +3. - size_t nwrites = - spdy::kSpdyStreamInitialWindowSize / kMaxSpdyFrameChunkSize + 3; - - // Calculate last frame's size; 0 size data frame is legal. - size_t last_frame_size = - spdy::kSpdyStreamInitialWindowSize % kMaxSpdyFrameChunkSize; - - // Construct content for a data frame of maximum size. - scoped_ptr<std::string> content( - new std::string(kMaxSpdyFrameChunkSize, 'a')); - - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPost( - spdy::kSpdyStreamInitialWindowSize + kUploadDataSize, NULL, 0)); - - // Full frames. - scoped_ptr<spdy::SpdyFrame> body1( - ConstructSpdyBodyFrame(1, content->c_str(), content->size(), false)); - - // Last frame to zero out the window size. - scoped_ptr<spdy::SpdyFrame> body2( - ConstructSpdyBodyFrame(1, content->c_str(), last_frame_size, false)); - - // Data frame to be sent once WINDOW_UPDATE frame is received. - scoped_ptr<spdy::SpdyFrame> body3(ConstructSpdyBodyFrame(1, true)); - - // Fill in mock writes. - scoped_array<MockWrite> writes(new MockWrite[nwrites]); - size_t i = 0; - writes[i] = CreateMockWrite(*req); - for (i = 1; i < nwrites-2; i++) - writes[i] = CreateMockWrite(*body1); - writes[i++] = CreateMockWrite(*body2); - writes[i] = CreateMockWrite(*body3); - - // Construct read frame, give enough space to upload the rest of the - // data. - scoped_ptr<spdy::SpdyFrame> window_update( - ConstructSpdyWindowUpdate(1, kUploadDataSize)); - scoped_ptr<spdy::SpdyFrame> reply(ConstructSpdyPostSynReply(NULL, 0)); - MockRead reads[] = { - CreateMockRead(*window_update), - CreateMockRead(*window_update), - 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(nwrites, reads, arraysize(reads), - writes.get(), nwrites)); - - HttpRequestInfo request; - request.method = "POST"; - request.url = GURL("http://www.google.com/"); - request.upload_data = new UploadData(); - scoped_ptr<std::string> upload_data( - new std::string(spdy::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 ("hello!" is not yet sent). - EXPECT_FALSE(stream->stream()->body_sent()); - - data->ForceNextRead(); // Read in WINDOW_UPDATE frame. - rv = callback.WaitForResult(); - helper.VerifyDataConsumed(); - - SpdySession::set_use_flow_control(SpdySession::kFlowControlBasedOnNPN); -} - TEST_P(SpdyNetworkTransactionSpdy2Test, CancelledTransaction) { // Construct the request. scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index c9b83d1..772c041 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -390,13 +390,11 @@ net::Error SpdySession::InitializeWithSocket( reinterpret_cast<SSLClientSocket*>(connection_->socket()); DCHECK(ssl_socket); - // For SPDY 2.1 and above versions, flow control is enabled by default and - // for older versions, flow control is disabled by default. Unit tests can - // either enable or disable flow_control_ by setting the use_flow_control_. - if (ssl_socket->protocol_negotiated() >= SSLClientSocket::kProtoSPDY21) - flow_control_ = (use_flow_control_ != SpdySession::kDisableFlowControl); - else - flow_control_ = (use_flow_control_ == SpdySession::kEnableFlowControl); + SSLClientSocket::NextProto protocol_negotiated = + ssl_socket->protocol_negotiated(); + if (protocol_negotiated != SSLClientSocket::kProtoUnknown) + flow_control_ = (protocol_negotiated >= SSLClientSocket::kProtoSPDY21); + if (ssl_socket->WasOriginBoundCertSent()) { // According to the SPDY spec, the credential associated with the TLS // connection is stored in slot[0]. |