summaryrefslogtreecommitdiffstats
path: root/net/spdy
diff options
context:
space:
mode:
authorgavinp@google.com <gavinp@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-17 00:58:44 +0000
committergavinp@google.com <gavinp@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-17 00:58:44 +0000
commit2bd930265ee663668e49d41eb35fddd94934eae2 (patch)
tree995f47c031908b58b5780b228fa405de1b0ff407 /net/spdy
parent485cd456a503e6698c75ea6185ed93de832d493a (diff)
downloadchromium_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.cc1
-rw-r--r--net/spdy/spdy_http_stream.cc46
-rw-r--r--net/spdy/spdy_http_stream.h14
-rw-r--r--net/spdy/spdy_http_stream_unittest.cc9
-rw-r--r--net/spdy/spdy_network_transaction.cc70
-rw-r--r--net/spdy/spdy_network_transaction.h4
-rw-r--r--net/spdy/spdy_network_transaction_unittest.cc613
-rw-r--r--net/spdy/spdy_session.cc92
-rw-r--r--net/spdy/spdy_session.h50
-rw-r--r--net/spdy/spdy_test_util.cc31
-rw-r--r--net/spdy/spdy_test_util.h20
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_