diff options
author | gavinp@google.com <gavinp@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-17 00:58:44 +0000 |
---|---|---|
committer | gavinp@google.com <gavinp@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-17 00:58:44 +0000 |
commit | 2bd930265ee663668e49d41eb35fddd94934eae2 (patch) | |
tree | 995f47c031908b58b5780b228fa405de1b0ff407 /net/spdy | |
parent | 485cd456a503e6698c75ea6185ed93de832d493a (diff) | |
download | chromium_src-2bd930265ee663668e49d41eb35fddd94934eae2.zip chromium_src-2bd930265ee663668e49d41eb35fddd94934eae2.tar.gz chromium_src-2bd930265ee663668e49d41eb35fddd94934eae2.tar.bz2 |
Implement MAX_CONCURRENT_STREAMS SETTINGS header
This CL helps chrome respect the SETTINGS header
MAX_CONCURRENT_STREAMS. Note that this means that
SpdySession::CreateStream can now return ERR_IO_PENDING, so it
requires a callback. There's a noted TODO that if an
http_network_transaction dissapears betweeen STATE_SPDY_GET_STREAM and
STATE_SPDY_SEND_REQUEST I don't know if we end up with an orphan stream
in our spdy_session.
As well, spdy_test_util.cc had a lot of functions with default arguments;
I didn't fix them all, but the functions I modified no longer take default
arguments and meet the coding standard. I'd like to circle back at some point
and possibly make the tests call SpdyFramer directly: these test utils seem
sometimes more trouble than they're worth if the framer was a bit more
convenient for direct use.
BUG=34750
TEST=net_unittests Spdy.ThreeGets*
Review URL: http://codereview.chromium.org/2919011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52791 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/spdy')
-rw-r--r-- | net/spdy/spdy_framer.cc | 1 | ||||
-rw-r--r-- | net/spdy/spdy_http_stream.cc | 46 | ||||
-rw-r--r-- | net/spdy/spdy_http_stream.h | 14 | ||||
-rw-r--r-- | net/spdy/spdy_http_stream_unittest.cc | 9 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction.cc | 70 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction.h | 4 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 613 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 92 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 50 | ||||
-rw-r--r-- | net/spdy/spdy_test_util.cc | 31 | ||||
-rw-r--r-- | net/spdy/spdy_test_util.h | 20 |
11 files changed, 805 insertions, 145 deletions
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index bb5d0fe..c0f4a86 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -1112,4 +1112,3 @@ void SpdyFramer::set_enable_compression_default(bool value) { } } // namespace spdy - diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc index bb22b9d..68e5e2a 100644 --- a/net/spdy/spdy_http_stream.cc +++ b/net/spdy/spdy_http_stream.cc @@ -4,7 +4,9 @@ #include "net/spdy/spdy_http_stream.h" +#include <algorithm> #include <list> +#include <string> #include "base/logging.h" #include "base/message_loop.h" @@ -126,28 +128,49 @@ void CreateSpdyHeadersFromHttpRequest( namespace net { -SpdyHttpStream::SpdyHttpStream(const scoped_refptr<SpdyStream>& stream) +SpdyHttpStream::SpdyHttpStream() : ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_factory_(this)), - stream_(stream), + stream_(NULL), + spdy_session_(NULL), response_info_(NULL), download_finished_(false), user_callback_(NULL), user_buffer_len_(0), buffered_read_callback_pending_(false), - more_read_data_pending_(false) { - CHECK(stream_.get()); - stream_->SetDelegate(this); -} + more_read_data_pending_(false) { } SpdyHttpStream::~SpdyHttpStream() { - stream_->DetachDelegate(); + if (stream_) + stream_->DetachDelegate(); } -void SpdyHttpStream::InitializeRequest( +int SpdyHttpStream::InitializeStream( + SpdySession* spdy_session, const HttpRequestInfo& request_info, + const BoundNetLog& stream_net_log, + CompletionCallback* callback) { + spdy_session_ = spdy_session; + request_info_ = request_info; + if (request_info_.method == "GET") { + int error = spdy_session_->GetPushStream(request_info.url, &stream_, + stream_net_log); + if (error != OK) + return error; + } + + if (stream_.get()) + return OK; + else + return spdy_session_->CreateStream(request_info_.url, + request_info_.priority, &stream_, + stream_net_log, callback, this); +} + +void SpdyHttpStream::InitializeRequest( base::Time request_time, UploadDataStream* upload_data) { - request_info_ = request_info; + CHECK(stream_.get()); + stream_->SetDelegate(this); linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock); CreateSpdyHeadersFromHttpRequest(request_info_, headers.get()); stream_->set_spdy_headers(headers); @@ -278,8 +301,11 @@ int SpdyHttpStream::SendRequest(HttpResponseInfo* response, } void SpdyHttpStream::Cancel() { + if (spdy_session_) + spdy_session_->CancelPendingCreateStreams(this); user_callback_ = NULL; - stream_->Cancel(); + if (stream_) + stream_->Cancel(); } bool SpdyHttpStream::OnSendHeadersComplete(int status) { diff --git a/net/spdy/spdy_http_stream.h b/net/spdy/spdy_http_stream.h index 495cac9e..a16c3ca 100644 --- a/net/spdy/spdy_http_stream.h +++ b/net/spdy/spdy_http_stream.h @@ -28,15 +28,20 @@ class UploadDataStream; class SpdyHttpStream : public SpdyStream::Delegate { public: // SpdyHttpStream constructor - explicit SpdyHttpStream(const scoped_refptr<SpdyStream>& stream); + SpdyHttpStream(); virtual ~SpdyHttpStream(); SpdyStream* stream() { return stream_.get(); } + // Initialize stream. Must be called before calling InitializeRequest(). + int InitializeStream(SpdySession* spdy_session, + const HttpRequestInfo& request_info, + const BoundNetLog& stream_net_log, + CompletionCallback* callback); + // Initialize request. Must be called before calling SendRequest(). // SpdyHttpStream takes ownership of |upload_data|. |upload_data| may be NULL. - void InitializeRequest(const HttpRequestInfo& request_info, - base::Time request_time, + void InitializeRequest(base::Time request_time, UploadDataStream* upload_data); const HttpResponseInfo* GetResponseInfo() const; @@ -104,7 +109,8 @@ class SpdyHttpStream : public SpdyStream::Delegate { bool ShouldWaitForMoreBufferedData() const; ScopedRunnableMethodFactory<SpdyHttpStream> read_callback_factory_; - const scoped_refptr<SpdyStream> stream_; + scoped_refptr<SpdyStream> stream_; + scoped_refptr<SpdySession> spdy_session_; // The request to send. HttpRequestInfo request_info_; diff --git a/net/spdy/spdy_http_stream_unittest.cc b/net/spdy/spdy_http_stream_unittest.cc index a2fdf58..5fab0e2 100644 --- a/net/spdy/spdy_http_stream_unittest.cc +++ b/net/spdy/spdy_http_stream_unittest.cc @@ -115,14 +115,11 @@ TEST_F(SpdyHttpStreamTest, SendRequest) { request.url = GURL("http://www.google.com/"); TestCompletionCallback callback; HttpResponseInfo response; - - scoped_refptr<SpdyStream> stream; + scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream()); ASSERT_EQ( OK, - session->CreateStream(request.url, HIGHEST, &stream, BoundNetLog())); - - scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(stream.get())); - http_stream->InitializeRequest(request, base::Time::Now(), NULL); + http_stream->InitializeStream(session, request, BoundNetLog(), NULL)); + http_stream->InitializeRequest(base::Time::Now(), NULL); EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(&response, &callback)); diff --git a/net/spdy/spdy_network_transaction.cc b/net/spdy/spdy_network_transaction.cc index 05a4a5a..7814553 100644 --- a/net/spdy/spdy_network_transaction.cc +++ b/net/spdy/spdy_network_transaction.cc @@ -112,6 +112,7 @@ LoadState SpdyNetworkTransaction::GetLoadState() const { if (spdy_.get()) return spdy_->GetLoadState(); return LOAD_STATE_CONNECTING; + case STATE_GET_STREAM_COMPLETE: case STATE_SEND_REQUEST_COMPLETE: return LOAD_STATE_SENDING_REQUEST; case STATE_READ_HEADERS_COMPLETE: @@ -168,6 +169,12 @@ int SpdyNetworkTransaction::DoLoop(int result) { net_log_.EndEvent(NetLog::TYPE_SPDY_TRANSACTION_INIT_CONNECTION, NULL); rv = DoInitConnectionComplete(rv); break; + case STATE_GET_STREAM: + DCHECK_EQ(OK, rv); + rv = DoGetStream(); + break; + case STATE_GET_STREAM_COMPLETE: + rv = DoGetStreamComplete(rv); case STATE_SEND_REQUEST: DCHECK_EQ(OK, rv); net_log_.BeginEvent(NetLog::TYPE_SPDY_TRANSACTION_SEND_REQUEST, NULL); @@ -243,57 +250,54 @@ int SpdyNetworkTransaction::DoInitConnectionComplete(int result) { if (result < 0) return result; - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_GET_STREAM; return OK; } -int SpdyNetworkTransaction::DoSendRequest() { - next_state_ = STATE_SEND_REQUEST_COMPLETE; - CHECK(!stream_.get()); +int SpdyNetworkTransaction::DoGetStream() { + next_state_ = STATE_GET_STREAM_COMPLETE; // It is possible that the spdy session was shut down while it was // asynchronously waiting to connect. - if(spdy_->IsClosed()) + if (spdy_->IsClosed()) return ERR_CONNECTION_CLOSED; - UploadDataStream* upload_data = NULL; + CHECK(!stream_.get()); + + stream_.reset(new SpdyHttpStream()); + return stream_->InitializeStream(spdy_, *request_, + net_log_, &io_callback_); +} + +int SpdyNetworkTransaction::DoGetStreamComplete(int result) { + if (result < 0) { + return result; + } + + next_state_ = STATE_SEND_REQUEST; + return OK; +} + +int SpdyNetworkTransaction::DoSendRequest() { + next_state_ = STATE_SEND_REQUEST_COMPLETE; + + UploadDataStream* upload_data_stream = NULL; if (request_->upload_data) { int error_code; - upload_data = UploadDataStream::Create(request_->upload_data, &error_code); - if (!upload_data) + upload_data_stream = UploadDataStream::Create(request_->upload_data, + &error_code); + if (!upload_data_stream) return error_code; } - scoped_refptr<SpdyStream> spdy_stream; - if (request_->method == "GET") { - int error = spdy_->GetPushStream(request_->url, &spdy_stream, net_log_); - if (error != OK) - return error; - } - if (spdy_stream.get()) { - DCHECK(spdy_stream->pushed()); - CHECK(spdy_stream->GetDelegate() == NULL); - stream_.reset(new SpdyHttpStream(spdy_stream)); - stream_->InitializeRequest(*request_, base::Time::Now(), NULL); - // "vary" field? - } else { - int error = spdy_->CreateStream(request_->url, - request_->priority, - &spdy_stream, - net_log_); - if (error != OK) - return error; - DCHECK(!spdy_stream->pushed()); - CHECK(spdy_stream->GetDelegate() == NULL); - stream_.reset(new SpdyHttpStream(spdy_stream)); - stream_->InitializeRequest(*request_, base::Time::Now(), upload_data); - } + stream_->InitializeRequest(base::Time::Now(), upload_data_stream); spdy_ = NULL; + return stream_->SendRequest(&response_, &io_callback_); } int SpdyNetworkTransaction::DoSendRequestComplete(int result) { if (result < 0) { - stream_.reset() ; + stream_.reset(); return result; } diff --git a/net/spdy/spdy_network_transaction.h b/net/spdy/spdy_network_transaction.h index 12b055c..686c3f7 100644 --- a/net/spdy/spdy_network_transaction.h +++ b/net/spdy/spdy_network_transaction.h @@ -56,6 +56,8 @@ class SpdyNetworkTransaction : public HttpTransaction { enum State { STATE_INIT_CONNECTION, STATE_INIT_CONNECTION_COMPLETE, + STATE_GET_STREAM, + STATE_GET_STREAM_COMPLETE, STATE_SEND_REQUEST, STATE_SEND_REQUEST_COMPLETE, STATE_READ_HEADERS, @@ -77,6 +79,8 @@ class SpdyNetworkTransaction : public HttpTransaction { // next state method as the result arg. int DoInitConnection(); int DoInitConnectionComplete(int result); + int DoGetStream(); + int DoGetStreamComplete(int result); int DoSendRequest(); int DoSendRequestComplete(int result); int DoReadHeaders(); diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index 59cabb6..54453bf 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -1,5 +1,5 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be +// Copyright (c) 2010 The Chromium Authors. All rights reserved. Use +// of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/spdy/spdy_network_transaction.h" @@ -10,6 +10,7 @@ #include "net/base/completion_callback.h" #include "net/base/mock_host_resolver.h" #include "net/base/net_log_unittest.h" +#include "net/base/request_priority.h" #include "net/base/ssl_config_service_defaults.h" #include "net/base/test_completion_callback.h" #include "net/base/upload_data.h" @@ -300,11 +301,11 @@ TEST_F(SpdyNetworkTransactionTest, Constructor) { TEST_F(SpdyNetworkTransactionTest, Get) { // Construct the request. - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), @@ -323,6 +324,500 @@ TEST_F(SpdyNetworkTransactionTest, Get) { EXPECT_EQ("hello!", out.response_data); } +// Start three gets simultaniously; making sure that multiplexed +// streams work properly. + +// This can't use the TransactionHelper method, since it only +// handles a single transaction, and finishes them as soon +// as it launches them. + +// TODO(gavinp): create a working generalized TransactionHelper that +// can allow multiple streams in flight. + +TEST_F(SpdyNetworkTransactionTest, ThreeGets) { + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, false)); + scoped_ptr<spdy::SpdyFrame> fbody(ConstructSpdyBodyFrame(1, true)); + + scoped_ptr<spdy::SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); + scoped_ptr<spdy::SpdyFrame> body2(ConstructSpdyBodyFrame(3, false)); + scoped_ptr<spdy::SpdyFrame> fbody2(ConstructSpdyBodyFrame(3, true)); + + scoped_ptr<spdy::SpdyFrame> req3(ConstructSpdyGet(NULL, 0, false, 5, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp3(ConstructSpdyGetSynReply(NULL, 0, 5)); + scoped_ptr<spdy::SpdyFrame> body3(ConstructSpdyBodyFrame(5, false)); + scoped_ptr<spdy::SpdyFrame> fbody3(ConstructSpdyBodyFrame(5, true)); + + MockWrite writes[] = { CreateMockWrite(*req), + CreateMockWrite(*req2), + CreateMockWrite(*req3), + }; + MockRead reads[] = { + CreateMockRead(*resp, 1), + CreateMockRead(*body), + CreateMockRead(*resp2, 4), + CreateMockRead(*body2), + CreateMockRead(*resp3, 7), + CreateMockRead(*body3), + + CreateMockRead(*fbody), + CreateMockRead(*fbody2), + CreateMockRead(*fbody3), + + MockRead(true, 0, 0), // EOF + }; + + scoped_refptr<OrderedSocketData> data( + new OrderedSocketData(reads, arraysize(reads), + writes, arraysize(writes))); + + BoundNetLog log; + TransactionHelperResult out; + { + SessionDependencies session_deps; + HttpNetworkSession* session = CreateSession(&session_deps); + SpdySession::SetSSLMode(false); + scoped_ptr<SpdyNetworkTransaction> trans1( + new SpdyNetworkTransaction(session)); + scoped_ptr<SpdyNetworkTransaction> trans2( + new SpdyNetworkTransaction(session)); + scoped_ptr<SpdyNetworkTransaction> trans3( + new SpdyNetworkTransaction(session)); + + session_deps.socket_factory.AddSocketDataProvider(data); + + TestCompletionCallback callback1; + TestCompletionCallback callback2; + TestCompletionCallback callback3; + + HttpRequestInfo httpreq1 = CreateGetRequest(); + HttpRequestInfo httpreq2 = CreateGetRequest(); + HttpRequestInfo httpreq3 = CreateGetRequest(); + + out.rv = trans1->Start(&httpreq1, &callback1, log); + ASSERT_EQ(ERR_IO_PENDING, out.rv); + out.rv = trans2->Start(&httpreq2, &callback2, log); + ASSERT_EQ(ERR_IO_PENDING, out.rv); + out.rv = trans3->Start(&httpreq3, &callback3, log); + ASSERT_EQ(ERR_IO_PENDING, out.rv); + + out.rv = callback1.WaitForResult(); + ASSERT_EQ(OK, out.rv); + out.rv = callback3.WaitForResult(); + ASSERT_EQ(OK, out.rv); + + const HttpResponseInfo* response1 = trans1->GetResponseInfo(); + EXPECT_TRUE(response1->headers != NULL); + EXPECT_TRUE(response1->was_fetched_via_spdy); + out.status_line = response1->headers->GetStatusLine(); + out.response_info = *response1; + + trans2->GetResponseInfo(); + + out.rv = ReadTransaction(trans1.get(), &out.response_data); + } + EXPECT_EQ(OK, out.rv); + + EXPECT_TRUE(data->at_read_eof()); + EXPECT_TRUE(data->at_write_eof()); + + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!hello!", out.response_data); +} + + +// Similar to ThreeGets above, however this test adds a SETTINGS +// frame. The SETTINGS frame is read during the IO loop waiting on +// the first transaction completion, and sets a maximum concurrent +// stream limit of 1. This means that our IO loop exists after the +// second transaction completes, so we can assert on read_index(). +TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrent) { + // Construct the request. + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, false)); + scoped_ptr<spdy::SpdyFrame> fbody(ConstructSpdyBodyFrame(1, true)); + + scoped_ptr<spdy::SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); + scoped_ptr<spdy::SpdyFrame> body2(ConstructSpdyBodyFrame(3, false)); + scoped_ptr<spdy::SpdyFrame> fbody2(ConstructSpdyBodyFrame(3, true)); + + scoped_ptr<spdy::SpdyFrame> req3(ConstructSpdyGet(NULL, 0, false, 5, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp3(ConstructSpdyGetSynReply(NULL, 0, 5)); + scoped_ptr<spdy::SpdyFrame> body3(ConstructSpdyBodyFrame(5, false)); + scoped_ptr<spdy::SpdyFrame> fbody3(ConstructSpdyBodyFrame(5, true)); + + spdy::SpdySettings settings; + spdy::SettingsFlagsAndId id(0); + id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS); + const size_t max_concurrent_streams = 1; + + settings.push_back(spdy::SpdySetting(id, max_concurrent_streams)); + scoped_ptr<spdy::SpdyFrame> settings_frame(ConstructSpdySettings(settings)); + + MockWrite writes[] = { CreateMockWrite(*req), + CreateMockWrite(*req2), + CreateMockWrite(*req3), + }; + MockRead reads[] = { + CreateMockRead(*settings_frame, 0), + CreateMockRead(*resp), + CreateMockRead(*body), + CreateMockRead(*fbody), + CreateMockRead(*resp2, 6), + CreateMockRead(*body2), + CreateMockRead(*fbody2), + CreateMockRead(*resp3, 11), + CreateMockRead(*body3), + CreateMockRead(*fbody3), + + MockRead(true, 0, 0), // EOF + }; + + scoped_refptr<OrderedSocketData> data( + new OrderedSocketData(reads, arraysize(reads), + writes, arraysize(writes))); + + BoundNetLog log; + TransactionHelperResult out; + { + SessionDependencies session_deps; + HttpNetworkSession* session = CreateSession(&session_deps); + SpdySession::SetSSLMode(false); + scoped_ptr<SpdyNetworkTransaction> trans1( + new SpdyNetworkTransaction(session)); + scoped_ptr<SpdyNetworkTransaction> trans2( + new SpdyNetworkTransaction(session)); + scoped_ptr<SpdyNetworkTransaction> trans3( + new SpdyNetworkTransaction(session)); + + session_deps.socket_factory.AddSocketDataProvider(data); + + TestCompletionCallback callback1; + TestCompletionCallback callback2; + TestCompletionCallback callback3; + + HttpRequestInfo httpreq1 = CreateGetRequest(); + HttpRequestInfo httpreq2 = CreateGetRequest(); + HttpRequestInfo httpreq3 = CreateGetRequest(); + + out.rv = trans1->Start(&httpreq1, &callback1, log); + ASSERT_EQ(out.rv, ERR_IO_PENDING); + // run transaction 1 through quickly to force a read of our SETTINGS + // frame + out.rv = callback1.WaitForResult(); + + out.rv = trans2->Start(&httpreq2, &callback2, log); + ASSERT_EQ(out.rv, ERR_IO_PENDING); + out.rv = trans3->Start(&httpreq3, &callback3, log); + ASSERT_EQ(out.rv, ERR_IO_PENDING); + out.rv = callback2.WaitForResult(); + ASSERT_EQ(OK, out.rv); + EXPECT_EQ(7U, data->read_index()); // i.e. the third trans was queued + + out.rv = callback3.WaitForResult(); + ASSERT_EQ(OK, out.rv); + + const HttpResponseInfo* response1 = trans1->GetResponseInfo(); + EXPECT_TRUE(response1->headers != NULL); + EXPECT_TRUE(response1->was_fetched_via_spdy); + out.status_line = response1->headers->GetStatusLine(); + out.response_info = *response1; + out.rv = ReadTransaction(trans1.get(), &out.response_data); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!hello!", out.response_data); + + const HttpResponseInfo* response2 = trans2->GetResponseInfo(); + out.status_line = response2->headers->GetStatusLine(); + out.response_info = *response2; + out.rv = ReadTransaction(trans2.get(), &out.response_data); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!hello!", out.response_data); + + const HttpResponseInfo* response3 = trans3->GetResponseInfo(); + out.status_line = response3->headers->GetStatusLine(); + out.response_info = *response3; + out.rv = ReadTransaction(trans3.get(), &out.response_data); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!hello!", out.response_data); + } + EXPECT_EQ(OK, out.rv); + + EXPECT_TRUE(data->at_read_eof()); + EXPECT_TRUE(data->at_write_eof()); +} + +// Similar to ThreeGetsWithMaxConcurrent above, however this test adds +// a fourth transaction. The third and fourth transactions have +// different data ("hello!" vs "hello!hello!") and because of the +// user specified priority, we expect to see them inverted in +// the response from the server. +TEST_F(SpdyNetworkTransactionTest, FourGetsWithMaxConcurrentPriority) { + // Construct the request. + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, false)); + scoped_ptr<spdy::SpdyFrame> fbody(ConstructSpdyBodyFrame(1, true)); + + scoped_ptr<spdy::SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); + scoped_ptr<spdy::SpdyFrame> body2(ConstructSpdyBodyFrame(3, false)); + scoped_ptr<spdy::SpdyFrame> fbody2(ConstructSpdyBodyFrame(3, true)); + + scoped_ptr<spdy::SpdyFrame> req4( + ConstructSpdyGet(NULL, 0, false, 5, HIGHEST)); + scoped_ptr<spdy::SpdyFrame> resp4(ConstructSpdyGetSynReply(NULL, 0, 5)); + scoped_ptr<spdy::SpdyFrame> fbody4(ConstructSpdyBodyFrame(5, true)); + + scoped_ptr<spdy::SpdyFrame> req3(ConstructSpdyGet(NULL, 0, false, 7, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp3(ConstructSpdyGetSynReply(NULL, 0, 7)); + scoped_ptr<spdy::SpdyFrame> body3(ConstructSpdyBodyFrame(7, false)); + scoped_ptr<spdy::SpdyFrame> fbody3(ConstructSpdyBodyFrame(7, true)); + + + spdy::SpdySettings settings; + spdy::SettingsFlagsAndId id(0); + id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS); + const size_t max_concurrent_streams = 1; + + settings.push_back(spdy::SpdySetting(id, max_concurrent_streams)); + scoped_ptr<spdy::SpdyFrame> settings_frame(ConstructSpdySettings(settings)); + + MockWrite writes[] = { CreateMockWrite(*req), + CreateMockWrite(*req2), + CreateMockWrite(*req4), + CreateMockWrite(*req3), + }; + MockRead reads[] = { + CreateMockRead(*settings_frame, 0), + CreateMockRead(*resp), + CreateMockRead(*body), + CreateMockRead(*fbody), + CreateMockRead(*resp2, 6), + CreateMockRead(*body2), + CreateMockRead(*fbody2), + CreateMockRead(*resp4, 12), + CreateMockRead(*fbody4), + CreateMockRead(*resp3, 15), + CreateMockRead(*body3), + CreateMockRead(*fbody3), + + MockRead(true, 0, 0), // EOF + }; + + scoped_refptr<OrderedSocketData> data( + new OrderedSocketData(reads, arraysize(reads), + writes, arraysize(writes))); + + BoundNetLog log; + TransactionHelperResult out; + { + SessionDependencies session_deps; + HttpNetworkSession* session = CreateSession(&session_deps); + SpdySession::SetSSLMode(false); + scoped_ptr<SpdyNetworkTransaction> trans1( + new SpdyNetworkTransaction(session)); + scoped_ptr<SpdyNetworkTransaction> trans2( + new SpdyNetworkTransaction(session)); + scoped_ptr<SpdyNetworkTransaction> trans3( + new SpdyNetworkTransaction(session)); + scoped_ptr<SpdyNetworkTransaction> trans4( + new SpdyNetworkTransaction(session)); + + session_deps.socket_factory.AddSocketDataProvider(data); + + TestCompletionCallback callback1; + TestCompletionCallback callback2; + TestCompletionCallback callback3; + TestCompletionCallback callback4; + + HttpRequestInfo httpreq1 = CreateGetRequest(); + HttpRequestInfo httpreq2 = CreateGetRequest(); + HttpRequestInfo httpreq3 = CreateGetRequest(); + HttpRequestInfo httpreq4 = CreateGetRequest(); + httpreq4.priority = HIGHEST; + + out.rv = trans1->Start(&httpreq1, &callback1, log); + ASSERT_EQ(ERR_IO_PENDING, out.rv); + // run transaction 1 through quickly to force a read of our SETTINGS + // frame + out.rv = callback1.WaitForResult(); + ASSERT_EQ(OK, out.rv); + + out.rv = trans2->Start(&httpreq2, &callback2, log); + ASSERT_EQ(ERR_IO_PENDING, out.rv); + out.rv = trans3->Start(&httpreq3, &callback3, log); + ASSERT_EQ(ERR_IO_PENDING, out.rv); + out.rv = trans4->Start(&httpreq4, &callback4, log); + ASSERT_EQ(ERR_IO_PENDING, out.rv); + + out.rv = callback2.WaitForResult(); + ASSERT_EQ(OK, out.rv); + EXPECT_EQ(data->read_index(), 7U); // i.e. the third & fourth trans queued + + out.rv = callback3.WaitForResult(); + ASSERT_EQ(OK, out.rv); + + const HttpResponseInfo* response1 = trans1->GetResponseInfo(); + EXPECT_TRUE(response1->headers != NULL); + EXPECT_TRUE(response1->was_fetched_via_spdy); + out.status_line = response1->headers->GetStatusLine(); + out.response_info = *response1; + out.rv = ReadTransaction(trans1.get(), &out.response_data); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!hello!", out.response_data); + + const HttpResponseInfo* response2 = trans2->GetResponseInfo(); + out.status_line = response2->headers->GetStatusLine(); + out.response_info = *response2; + out.rv = ReadTransaction(trans2.get(), &out.response_data); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!hello!", out.response_data); + + // notice: response3 gets two hellos, response4 gets one + // hello, so we know dequeuing priority was respected. + const HttpResponseInfo* response3 = trans3->GetResponseInfo(); + out.status_line = response3->headers->GetStatusLine(); + out.response_info = *response3; + out.rv = ReadTransaction(trans3.get(), &out.response_data); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!hello!", out.response_data); + + out.rv = callback4.WaitForResult(); + EXPECT_EQ(OK, out.rv); + const HttpResponseInfo* response4 = trans4->GetResponseInfo(); + out.status_line = response4->headers->GetStatusLine(); + out.response_info = *response4; + out.rv = ReadTransaction(trans4.get(), &out.response_data); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!", out.response_data); + } + EXPECT_EQ(OK, out.rv); + + EXPECT_TRUE(data->at_read_eof()); + EXPECT_TRUE(data->at_write_eof()); +} + +// Similar to ThreeGetsMaxConcurrrent above, however, this test +// deletes a session in the middle of the transaction to insure +// that we properly remove pendingcreatestream objects from +// the spdy_session +TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentDelete) { + // Construct the request. + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, false)); + scoped_ptr<spdy::SpdyFrame> fbody(ConstructSpdyBodyFrame(1, true)); + + scoped_ptr<spdy::SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); + scoped_ptr<spdy::SpdyFrame> body2(ConstructSpdyBodyFrame(3, false)); + scoped_ptr<spdy::SpdyFrame> fbody2(ConstructSpdyBodyFrame(3, true)); + + scoped_ptr<spdy::SpdyFrame> req3(ConstructSpdyGet(NULL, 0, false, 5, LOWEST)); + scoped_ptr<spdy::SpdyFrame> resp3(ConstructSpdyGetSynReply(NULL, 0, 5)); + scoped_ptr<spdy::SpdyFrame> body3(ConstructSpdyBodyFrame(5, false)); + scoped_ptr<spdy::SpdyFrame> fbody3(ConstructSpdyBodyFrame(5, true)); + + spdy::SpdySettings settings; + spdy::SettingsFlagsAndId id(0); + id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS); + const size_t max_concurrent_streams = 1; + + settings.push_back(spdy::SpdySetting(id, max_concurrent_streams)); + scoped_ptr<spdy::SpdyFrame> settings_frame(ConstructSpdySettings(settings)); + + MockWrite writes[] = { CreateMockWrite(*req), + CreateMockWrite(*req2), + }; + MockRead reads[] = { + CreateMockRead(*settings_frame, 0), + CreateMockRead(*resp), + CreateMockRead(*body), + CreateMockRead(*fbody), + CreateMockRead(*resp2, 6), + CreateMockRead(*body2), + CreateMockRead(*fbody2), + MockRead(true, 0, 0), // EOF + }; + + scoped_refptr<OrderedSocketData> data( + new OrderedSocketData(reads, arraysize(reads), + writes, arraysize(writes))); + + BoundNetLog log; + TransactionHelperResult out; + { + SessionDependencies session_deps; + HttpNetworkSession* session = CreateSession(&session_deps); + SpdySession::SetSSLMode(false); + scoped_ptr<SpdyNetworkTransaction> trans1( + new SpdyNetworkTransaction(session)); + scoped_ptr<SpdyNetworkTransaction> trans2( + new SpdyNetworkTransaction(session)); + scoped_ptr<SpdyNetworkTransaction> trans3( + new SpdyNetworkTransaction(session)); + + session_deps.socket_factory.AddSocketDataProvider(data); + + TestCompletionCallback callback1; + TestCompletionCallback callback2; + TestCompletionCallback callback3; + + HttpRequestInfo httpreq1 = CreateGetRequest(); + HttpRequestInfo httpreq2 = CreateGetRequest(); + HttpRequestInfo httpreq3 = CreateGetRequest(); + + out.rv = trans1->Start(&httpreq1, &callback1, log); + ASSERT_EQ(out.rv, ERR_IO_PENDING); + // run transaction 1 through quickly to force a read of our SETTINGS + // frame + out.rv = callback1.WaitForResult(); + + out.rv = trans2->Start(&httpreq2, &callback2, log); + ASSERT_EQ(out.rv, ERR_IO_PENDING); + out.rv = trans3->Start(&httpreq3, &callback3, log); + delete trans3.release(); + ASSERT_EQ(out.rv, ERR_IO_PENDING); + out.rv = callback2.WaitForResult(); + + EXPECT_EQ(8U, data->read_index()); + + const HttpResponseInfo* response1 = trans1->GetResponseInfo(); + EXPECT_TRUE(response1->headers != NULL); + EXPECT_TRUE(response1->was_fetched_via_spdy); + out.status_line = response1->headers->GetStatusLine(); + out.response_info = *response1; + out.rv = ReadTransaction(trans1.get(), &out.response_data); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!hello!", out.response_data); + + const HttpResponseInfo* response2 = trans2->GetResponseInfo(); + out.status_line = response2->headers->GetStatusLine(); + out.response_info = *response2; + out.rv = ReadTransaction(trans2.get(), &out.response_data); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!hello!", out.response_data); + } + EXPECT_EQ(OK, out.rv); + + EXPECT_TRUE(data->at_read_eof()); + EXPECT_TRUE(data->at_write_eof()); +} + // Test that a simple POST works. TEST_F(SpdyNetworkTransactionTest, Post) { static const char upload[] = { "hello!" }; @@ -335,7 +830,7 @@ TEST_F(SpdyNetworkTransactionTest, Post) { request.upload_data->AppendBytes(upload, strlen(upload)); scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPost(NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*body), // POST upload frame @@ -377,7 +872,7 @@ TEST_F(SpdyNetworkTransactionTest, EmptyPost) { }; scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyPostSynReply(NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), @@ -409,7 +904,7 @@ TEST_F(SpdyNetworkTransactionTest, PostWithEarlySynReply) { request.upload_data->AppendBytes(upload, sizeof(upload)); scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPost(NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockWrite writes[] = { CreateMockWrite(*req.get(), 2), CreateMockWrite(*body.get(), 3), // POST upload frame @@ -437,7 +932,7 @@ TEST_F(SpdyNetworkTransactionTest, PostWithEarlySynReply) { // Test that the transaction doesn't crash when we don't have a reply. TEST_F(SpdyNetworkTransactionTest, ResponseWithoutSynReply) { - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*body), MockRead(true, 0, 0) // EOF @@ -455,11 +950,11 @@ TEST_F(SpdyNetworkTransactionTest, ResponseWithoutSynReply) { // Test that the transaction doesn't crash when we get two replies on the same // stream ID. See http://crbug.com/45639. TEST_F(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) { - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*resp), @@ -496,12 +991,12 @@ TEST_F(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) { TEST_F(SpdyNetworkTransactionTest, CancelledTransaction) { // Construct the request. - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req), }; - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); MockRead reads[] = { CreateMockRead(*resp), // This following read isn't used by the test, except during the @@ -562,7 +1057,7 @@ class SpdyNetworkTransactionTest::StartTransactionCallback // to start another transaction on a session that is closing down. See // http://crbug.com/47455 TEST_F(SpdyNetworkTransactionTest, StartTransactionOnReadCallback) { - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; MockWrite writes2[] = { CreateMockWrite(*req) }; @@ -575,7 +1070,7 @@ TEST_F(SpdyNetworkTransactionTest, StartTransactionOnReadCallback) { 'h', 'e', 'l', 'l', 'o', '!', }; - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); MockRead reads[] = { CreateMockRead(*resp, 2), MockRead(true, ERR_IO_PENDING, 3), // Force a pause @@ -639,11 +1134,11 @@ class SpdyNetworkTransactionTest::DeleteSessionCallback // transaction. Failures will usually be valgrind errors. See // http://crbug.com/46925 TEST_F(SpdyNetworkTransactionTest, DeleteSessionOnReadCallback) { - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp.get(), 2), MockRead(true, ERR_IO_PENDING, 3), // Force a pause @@ -724,13 +1219,15 @@ TEST_F(SpdyNetworkTransactionTest, SynReplyHeaders) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req( + ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; scoped_ptr<spdy::SpdyFrame> resp( ConstructSpdyGetSynReply(test_cases[i].extra_headers, - test_cases[i].num_headers)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + test_cases[i].num_headers, + 1)); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), @@ -853,7 +1350,8 @@ TEST_F(SpdyNetworkTransactionTest, SynReplyHeadersVary) { // Construct the request. scoped_ptr<spdy::SpdyFrame> frame_req( ConstructSpdyGet(test_cases[i].extra_headers[0], - test_cases[i].num_headers[0])); + test_cases[i].num_headers[0], + false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*frame_req), @@ -867,7 +1365,7 @@ TEST_F(SpdyNetworkTransactionTest, SynReplyHeadersVary) { NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*frame_reply), CreateMockRead(*body), @@ -971,7 +1469,8 @@ TEST_F(SpdyNetworkTransactionTest, InvalidSynReply) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req( + ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req), }; @@ -981,7 +1480,7 @@ TEST_F(SpdyNetworkTransactionTest, InvalidSynReply) { NULL, 0, test_cases[i].headers, test_cases[i].num_headers)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), @@ -1004,7 +1503,7 @@ TEST_F(SpdyNetworkTransactionTest, InvalidSynReply) { TEST_F(SpdyNetworkTransactionTest, DISABLED_CorruptFrameSessionError) { // This is the length field with a big number scoped_ptr<spdy::SpdyFrame> syn_reply_massive_length( - ConstructSpdyGetSynReply(NULL, 0)); + ConstructSpdyGetSynReply(NULL, 0, 1)); syn_reply_massive_length->set_length(0x111126); struct SynReplyTests { @@ -1014,13 +1513,14 @@ TEST_F(SpdyNetworkTransactionTest, DISABLED_CorruptFrameSessionError) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req( + ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req), MockWrite(true, 0, 0) // EOF }; - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*test_cases[i].syn_reply), CreateMockRead(*body), @@ -1040,7 +1540,7 @@ TEST_F(SpdyNetworkTransactionTest, DISABLED_CorruptFrameSessionError) { // Test that we shutdown correctly on write errors. TEST_F(SpdyNetworkTransactionTest, WriteError) { - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { // We'll write 10 bytes successfully MockWrite(true, req->data(), 10), @@ -1062,12 +1562,12 @@ TEST_F(SpdyNetworkTransactionTest, WriteError) { // Test that partial writes work. TEST_F(SpdyNetworkTransactionTest, PartialWrite) { // Chop the SYN_STREAM frame into 5 chunks. - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); const int kChunks = 5; scoped_array<MockWrite> writes(ChopWriteFrame(*req.get(), kChunks)); - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), @@ -1092,13 +1592,14 @@ TEST_F(SpdyNetworkTransactionTest, DecompressFailureOnSynReply) { // For this test, we turn on the normal compression. EnableCompression(true); - scoped_ptr<spdy::SpdyFrame> compressed(ConstructSpdyGet(NULL, 0, true)); + scoped_ptr<spdy::SpdyFrame> compressed( + ConstructSpdyGet(NULL, 0, true, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*compressed), }; - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), @@ -1120,11 +1621,11 @@ TEST_F(SpdyNetworkTransactionTest, DecompressFailureOnSynReply) { // Test that the NetLog contains good data for a simple GET request. TEST_F(SpdyNetworkTransactionTest, NetLog) { - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), @@ -1177,7 +1678,7 @@ TEST_F(SpdyNetworkTransactionTest, NetLog) { TEST_F(SpdyNetworkTransactionTest, BufferFull) { spdy::SpdyFramer framer; - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; // 2 data frames in a single read. @@ -1196,7 +1697,7 @@ TEST_F(SpdyNetworkTransactionTest, BufferFull) { scoped_ptr<spdy::SpdyFrame> last_frame( framer.CreateDataFrame(1, "d", 1, spdy::DATA_FLAG_FIN)); - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); MockRead reads[] = { CreateMockRead(*resp), MockRead(true, ERR_IO_PENDING), // Force a pause @@ -1271,7 +1772,7 @@ TEST_F(SpdyNetworkTransactionTest, BufferFull) { TEST_F(SpdyNetworkTransactionTest, Buffering) { spdy::SpdyFramer framer; - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; // 4 data frames in a single read. @@ -1290,7 +1791,7 @@ TEST_F(SpdyNetworkTransactionTest, Buffering) { CombineFrames(data_frames, arraysize(data_frames), combined_data_frames, arraysize(combined_data_frames)); - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); MockRead reads[] = { CreateMockRead(*resp), MockRead(true, ERR_IO_PENDING), // Force a pause @@ -1365,12 +1866,12 @@ TEST_F(SpdyNetworkTransactionTest, Buffering) { TEST_F(SpdyNetworkTransactionTest, BufferedAll) { spdy::SpdyFramer framer; - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; // 5 data frames in a single read. scoped_ptr<spdy::SpdyFrame> syn_reply( - ConstructSpdyGetSynReply(NULL, 0)); + ConstructSpdyGetSynReply(NULL, 0, 1)); syn_reply->set_flags(spdy::CONTROL_FLAG_NONE); // turn off FIN bit scoped_ptr<spdy::SpdyFrame> data_frame( framer.CreateDataFrame(1, "message", 7, spdy::DATA_FLAG_NONE)); @@ -1456,7 +1957,7 @@ TEST_F(SpdyNetworkTransactionTest, BufferedAll) { TEST_F(SpdyNetworkTransactionTest, BufferedClosed) { spdy::SpdyFramer framer; - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; // All data frames in a single read. @@ -1473,7 +1974,7 @@ TEST_F(SpdyNetworkTransactionTest, BufferedClosed) { int combined_data_frames_len = CombineFrames(data_frames, arraysize(data_frames), combined_data_frames, arraysize(combined_data_frames)); - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); MockRead reads[] = { CreateMockRead(*resp), MockRead(true, ERR_IO_PENDING), // Force a wait @@ -1546,14 +2047,14 @@ TEST_F(SpdyNetworkTransactionTest, BufferedClosed) { TEST_F(SpdyNetworkTransactionTest, BufferedCancelled) { spdy::SpdyFramer framer; - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; // NOTE: We don't FIN the stream. scoped_ptr<spdy::SpdyFrame> data_frame( framer.CreateDataFrame(1, "message", 7, spdy::DATA_FLAG_NONE)); - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); MockRead reads[] = { CreateMockRead(*resp), MockRead(true, ERR_IO_PENDING), // Force a wait @@ -1640,7 +2141,7 @@ TEST_F(SpdyNetworkTransactionTest, SettingsSaved) { EXPECT_TRUE(helper.session()->spdy_settings().Get(host_port_pair).empty()); // Construct the request. - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; // Construct the reply. @@ -1677,7 +2178,7 @@ TEST_F(SpdyNetworkTransactionTest, SettingsSaved) { settings_frame.reset(ConstructSpdySettings(settings)); } - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*reply), CreateMockRead(*body), @@ -1771,7 +2272,7 @@ TEST_F(SpdyNetworkTransactionTest, SettingsPlayback) { scoped_ptr<spdy::SpdyFrame> settings_frame(ConstructSpdySettings(settings)); // Construct the request. - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*settings_frame), @@ -1786,7 +2287,7 @@ TEST_F(SpdyNetworkTransactionTest, SettingsPlayback) { NULL, 0)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame()); + scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*reply), CreateMockRead(*body), @@ -1825,7 +2326,7 @@ TEST_F(SpdyNetworkTransactionTest, SettingsPlayback) { } TEST_F(SpdyNetworkTransactionTest, GoAwayWithActiveStream) { - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; scoped_ptr<spdy::SpdyFrame> go_away(ConstructSpdyGoAway()); @@ -1845,10 +2346,10 @@ TEST_F(SpdyNetworkTransactionTest, GoAwayWithActiveStream) { } TEST_F(SpdyNetworkTransactionTest, CloseWithActiveStream) { - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); MockRead reads[] = { CreateMockRead(*resp), MockRead(false, 0, 0) // EOF diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 13a54d5..b938f53 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -156,6 +156,7 @@ SpdySession::SpdySession(const HostPortPair& host_port_pair, certificate_error_code_(OK), error_(OK), state_(IDLE), + max_concurrent_streams_(kDefaultMaxConcurrentStreams), streams_initiated_count_(0), streams_pushed_count_(0), streams_pushed_and_claimed_count_(0), @@ -292,6 +293,65 @@ int SpdySession::CreateStream( const GURL& url, RequestPriority priority, scoped_refptr<SpdyStream>* spdy_stream, + const BoundNetLog& stream_net_log, + CompletionCallback* callback, + const SpdyHttpStream* spdy_http_stream) { + if (!max_concurrent_streams_ || + active_streams_.size() < max_concurrent_streams_) { + return CreateStreamImpl(url, priority, spdy_stream, stream_net_log); + } + + create_stream_queues_[priority].push( + PendingCreateStream(url, priority, spdy_stream, + stream_net_log, callback, spdy_http_stream)); + return ERR_IO_PENDING; +} + +void SpdySession::ProcessPendingCreateStreams() { + while (!max_concurrent_streams_ || + active_streams_.size() < max_concurrent_streams_) { + bool no_pending_create_streams = true; + for (int i = 0;i < NUM_PRIORITIES;++i) { + if (!create_stream_queues_[i].empty()) { + PendingCreateStream& pending_create = create_stream_queues_[i].front(); + no_pending_create_streams = false; + int error = CreateStreamImpl(*pending_create.url, + pending_create.priority, + pending_create.spdy_stream, + *pending_create.stream_net_log); + pending_create.callback->Run(error); + create_stream_queues_[i].pop(); + break; + } + } + if (no_pending_create_streams) + return; // there were no streams in any queue + } +} + +void SpdySession::CancelPendingCreateStreams( + const SpdyHttpStream *const spdy_http_stream) { + for (int i = 0;i < NUM_PRIORITIES;++i) { + PendingCreateStreamQueue tmp; + // Make a copy removing this trans + while (!create_stream_queues_[i].empty()) { + PendingCreateStream& pending_create = create_stream_queues_[i].front(); + if (pending_create.spdy_http_stream != spdy_http_stream) + tmp.push(pending_create); + create_stream_queues_[i].pop(); + } + // Now copy it back + while (!tmp.empty()) { + create_stream_queues_[i].push(tmp.front()); + tmp.pop(); + } + } +} + +int SpdySession::CreateStreamImpl( + const GURL& url, + RequestPriority priority, + scoped_refptr<SpdyStream>* spdy_stream, const BoundNetLog& stream_net_log) { // Make sure that we don't try to send https/wss over an unauthenticated, but // encrypted SSL socket. @@ -707,6 +767,14 @@ void SpdySession::CloseAllStreams(net::Error status) { abandoned_push_streams.Add(pushed_streams_.size()); } + for (int i = 0;i < NUM_PRIORITIES;++i) { + while (!create_stream_queues_[i].empty()) { + PendingCreateStream& pending_create = create_stream_queues_[i].front(); + pending_create.callback->Run(ERR_ABORTED); + create_stream_queues_[i].pop(); + } + } + while (!active_streams_.empty()) { ActiveStreamMap::iterator it = active_streams_.begin(); const scoped_refptr<SpdyStream>& stream = it->second; @@ -719,8 +787,7 @@ void SpdySession::CloseAllStreams(net::Error status) { // TODO(erikchen): ideally stream->OnClose() is only ever called by // DeleteStream, but pending streams fall into their own category for now. PendingStreamMap::iterator it; - for (it = pending_streams_.begin(); it != pending_streams_.end(); ++it) - { + for (it = pending_streams_.begin(); it != pending_streams_.end(); ++it) { const scoped_refptr<SpdyStream>& stream = it->second; if (stream) stream->OnClose(ERR_ABORTED); @@ -799,6 +866,7 @@ void SpdySession::DeleteStream(spdy::SpdyStreamId id, int status) { active_streams_.erase(it2); if (stream) stream->OnClose(status); + ProcessPendingCreateStreams(); } void SpdySession::RemoveFromPool() { @@ -1118,6 +1186,7 @@ void SpdySession::OnGoAway(const spdy::SpdyGoAwayControlFrame& frame) { void SpdySession::OnSettings(const spdy::SpdySettingsControlFrame& frame) { spdy::SpdySettings settings; if (spdy_framer_.ParseSettings(&frame, &settings)) { + HandleSettings(settings); SpdySettingsStorage* settings_storage = session_->mutable_spdy_settings(); settings_storage->Set(host_port_pair_, settings); } @@ -1134,6 +1203,7 @@ void SpdySession::SendSettings() { const spdy::SpdySettings& settings = settings_storage.Get(host_port_pair_); if (settings.empty()) return; + HandleSettings(settings); net_log_.AddEvent( NetLog::TYPE_SPDY_SESSION_SEND_SETTINGS, @@ -1146,6 +1216,20 @@ void SpdySession::SendSettings() { QueueFrame(settings_frame.get(), 0, NULL); } +void SpdySession::HandleSettings(const spdy::SpdySettings& settings) { + for (spdy::SpdySettings::const_iterator i = settings.begin(), + end = settings.end(); i != end; ++i) { + const uint32 id = i->first.id(); + const uint32 val = i->second; + switch (id) { + case spdy::SETTINGS_MAX_CONCURRENT_STREAMS: + max_concurrent_streams_ = val; + ProcessPendingCreateStreams(); + break; + } + } +} + void SpdySession::RecordHistograms() { UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPerSession", streams_initiated_count_, @@ -1168,10 +1252,6 @@ void SpdySession::RecordHistograms() { // Enumerate the saved settings, and set histograms for it. const SpdySettingsStorage& settings_storage = session_->spdy_settings(); const spdy::SpdySettings& settings = settings_storage.Get(host_port_pair_); - if (settings.empty()) { - NOTREACHED(); // If we lost our settings already, something is wrong! - return; - } spdy::SpdySettings::const_iterator it; for (it = settings.begin(); it != settings.end(); ++it) { diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index e129d57..b306831 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -31,6 +31,7 @@ namespace net { +class SpdyHttpStream; class SpdyStream; class HttpNetworkSession; class BoundNetLog; @@ -67,12 +68,17 @@ class SpdySession : public base::RefCounted<SpdySession>, const BoundNetLog& stream_net_log); // Create a new stream for a given |url|. Writes it out to |spdy_stream|. - // Returns a net error code. + // Returns a net error code, possibly ERR_IO_PENDING. int CreateStream( const GURL& url, RequestPriority priority, scoped_refptr<SpdyStream>* spdy_stream, - const BoundNetLog& stream_net_log); + const BoundNetLog& stream_net_log, + CompletionCallback* callback, + const SpdyHttpStream* spdy_http_stream); + + // Remove PendingCreateStream objects on transaction deletion + void CancelPendingCreateStreams(const SpdyHttpStream* trans); // Used by SpdySessionPool to initialize with a pre-existing SSL socket. // Returns OK on success, or an error on failure. @@ -128,6 +134,28 @@ class SpdySession : public base::RefCounted<SpdySession>, CLOSED }; + enum { kDefaultMaxConcurrentStreams = 100 }; // TODO(mbelshe) remove this + + struct PendingCreateStream { + const GURL* url; + RequestPriority priority; + scoped_refptr<SpdyStream>* spdy_stream; + const BoundNetLog* stream_net_log; + CompletionCallback* callback; + + const SpdyHttpStream* spdy_http_stream; + + PendingCreateStream(const GURL& url, RequestPriority priority, + scoped_refptr<SpdyStream>* spdy_stream, + const BoundNetLog& stream_net_log, + CompletionCallback* callback, + const SpdyHttpStream* spdy_http_stream) + : url(&url), priority(priority), spdy_stream(spdy_stream), + stream_net_log(&stream_net_log), callback(callback), + spdy_http_stream(spdy_http_stream) { } + }; + typedef std::queue<PendingCreateStream, std::list< PendingCreateStream> > + PendingCreateStreamQueue; typedef std::map<int, scoped_refptr<SpdyStream> > ActiveStreamMap; // Only HTTP push a stream. typedef std::list<scoped_refptr<SpdyStream> > ActivePushedStreamList; @@ -136,6 +164,13 @@ class SpdySession : public base::RefCounted<SpdySession>, virtual ~SpdySession(); + void ProcessPendingCreateStreams(); + int CreateStreamImpl( + const GURL& url, + RequestPriority priority, + scoped_refptr<SpdyStream>* spdy_stream, + const BoundNetLog& stream_net_log); + // SpdyFramerVisitorInterface virtual void OnError(spdy::SpdyFramer*); virtual void OnStreamFrameData(spdy::SpdyStreamId stream_id, @@ -161,6 +196,10 @@ class SpdySession : public base::RefCounted<SpdySession>, // Send relevant SETTINGS. This is generally called on connection setup. void SendSettings(); + // Handle SETTINGS. Either when we send settings, or when we receive a + // SETTINGS ontrol frame, update our SpdySession accordingly. + void HandleSettings(const spdy::SpdySettings& settings); + // Start reading from the socket. // Returns OK on success, or an error on failure. net::Error ReadSocket(); @@ -223,6 +262,10 @@ class SpdySession : public base::RefCounted<SpdySession>, int stream_hi_water_mark_; // The next stream id to use. + // Queue, for each priority, of pending Create Streams that have not + // yet been satisfied + PendingCreateStreamQueue create_stream_queues_[NUM_PRIORITIES]; + // TODO(mbelshe): We need to track these stream lists better. // I suspect it is possible to remove a stream from // one list, but not the other. @@ -269,6 +312,9 @@ class SpdySession : public base::RefCounted<SpdySession>, net::Error error_; State state_; + // Limits + size_t max_concurrent_streams_; // 0 if no limit + // Some statistics counters for the session. int streams_initiated_count_; int streams_pushed_count_; diff --git a/net/spdy/spdy_test_util.cc b/net/spdy/spdy_test_util.cc index 697a3a6..89e3343 100644 --- a/net/spdy/spdy_test_util.cc +++ b/net/spdy/spdy_test_util.cc @@ -4,6 +4,8 @@ #include "net/spdy/spdy_test_util.h" +#include <string> + #include "base/basictypes.h" #include "base/string_util.h" @@ -235,12 +237,15 @@ int ConstructSpdyHeader(const char* const extra_headers[], // will vary the most between calls. // Returns a SpdyFrame. spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], - int extra_header_count, bool compressed) { + int extra_header_count, + bool compressed, + int stream_id, + RequestPriority request_priority) { const SpdyHeaderInfo kSynStartHeader = { spdy::SYN_STREAM, // Kind = Syn - 1, // Stream ID + stream_id, // Stream ID 0, // Associated stream ID - SPDY_PRIORITY_LOWEST, // Priority + request_priority, // Priority spdy::CONTROL_FLAG_FIN, // Control Flags compressed, // Compressed spdy::INVALID, // Status @@ -264,24 +269,16 @@ spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], arraysize(kStandardGetHeaders) / 2); } -// Constructs a standard SPDY GET SYN packet, not compressed. -// |extra_headers| are the extra header-value pairs, which typically -// will vary the most between calls. -// Returns a SpdyFrame. -spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], - int extra_header_count) { - return ConstructSpdyGet(extra_headers, extra_header_count, false); -} - // Constructs a standard SPDY SYN_REPLY packet to match the SPDY GET. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. spdy::SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[], - int extra_header_count) { + int extra_header_count, + int stream_id) { const SpdyHeaderInfo kSynStartHeader = { spdy::SYN_REPLY, // Kind = SynReply - 1, // Stream ID + stream_id, // Stream ID 0, // Associated stream ID SPDY_PRIORITY_LOWEST, // Priority spdy::CONTROL_FLAG_NONE, // Control Flags @@ -380,9 +377,11 @@ spdy::SpdyFrame* ConstructSpdyPostSynReply(const char* const extra_headers[], } // Constructs a single SPDY data frame with the contents "hello!" -spdy::SpdyFrame* ConstructSpdyBodyFrame() { +spdy::SpdyFrame* ConstructSpdyBodyFrame(int stream_id, bool fin) { spdy::SpdyFramer framer; - return framer.CreateDataFrame(1, "hello!", 6, spdy::DATA_FLAG_FIN); + return + framer.CreateDataFrame(stream_id, "hello!", 6, + fin ? spdy::DATA_FLAG_FIN : spdy::DATA_FLAG_NONE); } // Construct an expected SPDY reply string. diff --git a/net/spdy/spdy_test_util.h b/net/spdy/spdy_test_util.h index 02981ce..b01f7bb 100644 --- a/net/spdy/spdy_test_util.h +++ b/net/spdy/spdy_test_util.h @@ -6,6 +6,7 @@ #define NET_SPDY_SPDY_TEST_UTIL_H_ #include "base/basictypes.h" +#include "net/base/request_priority.h" #include "net/socket/socket_test_util.h" #include "net/spdy/spdy_framer.h" @@ -137,21 +138,17 @@ int ConstructSpdyHeader(const char* const extra_headers[], // Returns a SpdyFrame. spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], int extra_header_count, - bool compressed); - -// Constructs a standard SPDY GET SYN packet, with no compression. -// |extra_headers| are the extra header-value pairs, which typically -// will vary the most between calls. -// Returns a SpdyFrame. -spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], - int extra_header_count); + bool compressed, + int stream_id, + RequestPriority request_priority); // Constructs a standard SPDY SYN_REPLY packet to match the SPDY GET. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. spdy::SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[], - int extra_header_count); + int extra_header_count, + int stream_id); // Constructs a standard SPDY POST SYN packet. // |extra_headers| are the extra header-value pairs, which typically @@ -168,7 +165,8 @@ spdy::SpdyFrame* ConstructSpdyPostSynReply(const char* const extra_headers[], int extra_header_count); // Constructs a single SPDY data frame with the contents "hello!" -spdy::SpdyFrame* ConstructSpdyBodyFrame(); +spdy::SpdyFrame* ConstructSpdyBodyFrame(int stream_id, + bool fin); // Create an async MockWrite from the given SpdyFrame. MockWrite CreateMockWrite(const spdy::SpdyFrame& req); @@ -189,4 +187,4 @@ int CombineFrames(const spdy::SpdyFrame** frames, int num_frames, } // namespace net -#endif // NET_SPDY_SPDY_TEST_UTIL_H_ +#endif // NET_SPDY_SPDY_TEST_UTIL_H_ |