HELLO·Android
系统源代码
IT资讯
技术文章
我的收藏
注册
登录
-
我收藏的文章
创建代码块
我的代码块
我的账号
Kitkat
|
4.4.4_r1
下载
查看原文件
收藏
根目录
external
chromium
net
spdy
spdy_network_transaction_unittest.cc
// Copyright (c) 2011 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/http/http_network_transaction.h" #include
#include
#include "net/base/auth.h" #include "net/base/net_log_unittest.h" #include "net/http/http_network_session_peer.h" #include "net/http/http_transaction_unittest.h" #include "net/socket/client_socket_pool_base.h" #include "net/spdy/spdy_http_stream.h" #include "net/spdy/spdy_http_utils.h" #include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" #include "net/spdy/spdy_test_util.h" #include "net/url_request/url_request_test_util.h" #include "testing/platform_test.h" //----------------------------------------------------------------------------- namespace net { // This is the expected list of advertised protocols from the browser's NPN // list. static const char kExpectedNPNString[] = "\x08http/1.1\x06spdy/2"; enum SpdyNetworkTransactionTestTypes { SPDYNPN, SPDYNOSSL, SPDYSSL, }; class SpdyNetworkTransactionTest : public ::testing::TestWithParam
{ protected: virtual void SetUp() { // By default, all tests turn off compression. EnableCompression(false); google_get_request_initialized_ = false; google_post_request_initialized_ = false; google_chunked_post_request_initialized_ = false; } virtual void TearDown() { // Empty the current queue. MessageLoop::current()->RunAllPending(); } struct TransactionHelperResult { int rv; std::string status_line; std::string response_data; HttpResponseInfo response_info; }; void EnableCompression(bool enabled) { spdy::SpdyFramer::set_enable_compression_default(enabled); } class StartTransactionCallback; class DeleteSessionCallback; // A helper class that handles all the initial npn/ssl setup. class NormalSpdyTransactionHelper { public: NormalSpdyTransactionHelper(const HttpRequestInfo& request, const BoundNetLog& log, SpdyNetworkTransactionTestTypes test_type) : request_(request), session_deps_(new SpdySessionDependencies()), session_(SpdySessionDependencies::SpdyCreateSession( session_deps_.get())), log_(log), test_type_(test_type), deterministic_(false), spdy_enabled_(true) { switch (test_type_) { case SPDYNOSSL: case SPDYSSL: port_ = 80; break; case SPDYNPN: port_ = 443; break; default: NOTREACHED(); } } ~NormalSpdyTransactionHelper() { // Any test which doesn't close the socket by sending it an EOF will // have a valid session left open, which leaks the entire session pool. // This is just fine - in fact, some of our tests intentionally do this // so that we can check consistency of the SpdySessionPool as the test // finishes. If we had put an EOF on the socket, the SpdySession would // have closed and we wouldn't be able to check the consistency. // Forcefully close existing sessions here. session()->spdy_session_pool()->CloseAllSessions(); } void SetDeterministic() { session_ = SpdySessionDependencies::SpdyCreateSessionDeterministic( session_deps_.get()); deterministic_ = true; } void SetSpdyDisabled() { spdy_enabled_ = false; } void RunPreTestSetup() { if (!session_deps_.get()) session_deps_.reset(new SpdySessionDependencies()); if (!session_.get()) session_ = SpdySessionDependencies::SpdyCreateSession( session_deps_.get()); HttpStreamFactory::set_use_alternate_protocols(false); HttpStreamFactory::set_force_spdy_over_ssl(false); HttpStreamFactory::set_force_spdy_always(false); switch (test_type_) { case SPDYNPN: session_->mutable_alternate_protocols()->SetAlternateProtocolFor( HostPortPair("www.google.com", 80), 443, HttpAlternateProtocols::NPN_SPDY_2); HttpStreamFactory::set_use_alternate_protocols(true); HttpStreamFactory::set_next_protos(kExpectedNPNString); break; case SPDYNOSSL: HttpStreamFactory::set_force_spdy_over_ssl(false); HttpStreamFactory::set_force_spdy_always(true); break; case SPDYSSL: HttpStreamFactory::set_force_spdy_over_ssl(true); HttpStreamFactory::set_force_spdy_always(true); break; default: NOTREACHED(); } // We're now ready to use SSL-npn SPDY. trans_.reset(new HttpNetworkTransaction(session_)); } // Start the transaction, read some data, finish. void RunDefaultTest() { output_.rv = trans_->Start(&request_, &callback, log_); // We expect an IO Pending or some sort of error. EXPECT_LT(output_.rv, 0); if (output_.rv != ERR_IO_PENDING) return; output_.rv = callback.WaitForResult(); if (output_.rv != OK) { session_->spdy_session_pool()->CloseCurrentSessions(); return; } // Verify responses. const HttpResponseInfo* response = trans_->GetResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_TRUE(response->headers != NULL); EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); EXPECT_EQ(spdy_enabled_, response->was_fetched_via_spdy); if (test_type_ == SPDYNPN && spdy_enabled_) { EXPECT_TRUE(response->was_npn_negotiated); } else { EXPECT_TRUE(!response->was_npn_negotiated); } // If SPDY is not enabled, a HTTP request should not be diverted // over a SSL session. if (!spdy_enabled_) { EXPECT_EQ(request_.url.SchemeIs("https"), response->was_npn_negotiated); } EXPECT_EQ("192.0.2.33", response->socket_address.host()); EXPECT_EQ(0, response->socket_address.port()); output_.status_line = response->headers->GetStatusLine(); output_.response_info = *response; // Make a copy so we can verify. output_.rv = ReadTransaction(trans_.get(), &output_.response_data); } // Most tests will want to call this function. In particular, the MockReads // should end with an empty read, and that read needs to be processed to // ensure proper deletion of the spdy_session_pool. void VerifyDataConsumed() { for (DataVector::iterator it = data_vector_.begin(); it != data_vector_.end(); ++it) { EXPECT_TRUE((*it)->at_read_eof()) << "Read count: " << (*it)->read_count() << " Read index: " << (*it)->read_index(); EXPECT_TRUE((*it)->at_write_eof()) << "Write count: " << (*it)->write_count() << " Write index: " << (*it)->write_index(); } } // Occasionally a test will expect to error out before certain reads are // processed. In that case we want to explicitly ensure that the reads were // not processed. void VerifyDataNotConsumed() { for (DataVector::iterator it = data_vector_.begin(); it != data_vector_.end(); ++it) { EXPECT_TRUE(!(*it)->at_read_eof()) << "Read count: " << (*it)->read_count() << " Read index: " << (*it)->read_index(); EXPECT_TRUE(!(*it)->at_write_eof()) << "Write count: " << (*it)->write_count() << " Write index: " << (*it)->write_index(); } } void RunToCompletion(StaticSocketDataProvider* data) { RunPreTestSetup(); AddData(data); RunDefaultTest(); VerifyDataConsumed(); } void AddData(StaticSocketDataProvider* data) { DCHECK(!deterministic_); data_vector_.push_back(data); linked_ptr
ssl_( new SSLSocketDataProvider(true, OK)); if (test_type_ == SPDYNPN) { ssl_->next_proto_status = SSLClientSocket::kNextProtoNegotiated; ssl_->next_proto = "spdy/2"; ssl_->was_npn_negotiated = true; } ssl_vector_.push_back(ssl_); if (test_type_ == SPDYNPN || test_type_ == SPDYSSL) session_deps_->socket_factory->AddSSLSocketDataProvider(ssl_.get()); session_deps_->socket_factory->AddSocketDataProvider(data); if (test_type_ == SPDYNPN) { MockConnect never_finishing_connect(false, ERR_IO_PENDING); linked_ptr
hanging_non_alternate_protocol_socket( new StaticSocketDataProvider(NULL, 0, NULL, 0)); hanging_non_alternate_protocol_socket->set_connect_data( never_finishing_connect); session_deps_->socket_factory->AddSocketDataProvider( hanging_non_alternate_protocol_socket.get()); alternate_vector_.push_back(hanging_non_alternate_protocol_socket); } } void AddDeterministicData(DeterministicSocketData* data) { DCHECK(deterministic_); data_vector_.push_back(data); linked_ptr
ssl_( new SSLSocketDataProvider(true, OK)); if (test_type_ == SPDYNPN) { ssl_->next_proto_status = SSLClientSocket::kNextProtoNegotiated; ssl_->next_proto = "spdy/2"; ssl_->was_npn_negotiated = true; } ssl_vector_.push_back(ssl_); if (test_type_ == SPDYNPN || test_type_ == SPDYSSL) { session_deps_->deterministic_socket_factory-> AddSSLSocketDataProvider(ssl_.get()); } session_deps_->deterministic_socket_factory->AddSocketDataProvider(data); if (test_type_ == SPDYNPN) { MockConnect never_finishing_connect(false, ERR_IO_PENDING); scoped_refptr
hanging_non_alternate_protocol_socket( new DeterministicSocketData(NULL, 0, NULL, 0)); hanging_non_alternate_protocol_socket->set_connect_data( never_finishing_connect); session_deps_->deterministic_socket_factory->AddSocketDataProvider( hanging_non_alternate_protocol_socket); alternate_deterministic_vector_.push_back( hanging_non_alternate_protocol_socket); } } // This can only be called after RunPreTestSetup. It adds a Data Provider, // but not a corresponding SSL data provider void AddDataNoSSL(StaticSocketDataProvider* data) { DCHECK(!deterministic_); session_deps_->socket_factory->AddSocketDataProvider(data); } void AddDataNoSSL(DeterministicSocketData* data) { DCHECK(deterministic_); session_deps_->deterministic_socket_factory->AddSocketDataProvider(data); } void SetSession(const scoped_refptr
& session) { session_ = session; } HttpNetworkTransaction* trans() { return trans_.get(); } void ResetTrans() { trans_.reset(); } TransactionHelperResult& output() { return output_; } const HttpRequestInfo& request() const { return request_; } const scoped_refptr
& session() const { return session_; } scoped_ptr
& session_deps() { return session_deps_; } int port() const { return port_; } SpdyNetworkTransactionTestTypes test_type() const { return test_type_; } private: typedef std::vector
DataVector; typedef std::vector
> SSLVector; typedef std::vector
> AlternateVector; typedef std::vector
> AlternateDeterministicVector; HttpRequestInfo request_; scoped_ptr
session_deps_; scoped_refptr
session_; TransactionHelperResult output_; scoped_ptr
first_transaction_; SSLVector ssl_vector_; TestCompletionCallback callback; scoped_ptr
trans_; scoped_ptr
trans_http_; DataVector data_vector_; AlternateVector alternate_vector_; AlternateDeterministicVector alternate_deterministic_vector_; const BoundNetLog& log_; SpdyNetworkTransactionTestTypes test_type_; int port_; bool deterministic_; bool spdy_enabled_; }; void ConnectStatusHelperWithExpectedStatus(const MockRead& status, int expected_status); void ConnectStatusHelper(const MockRead& status); const HttpRequestInfo& CreateGetPushRequest() { google_get_push_request_.method = "GET"; google_get_push_request_.url = GURL("http://www.google.com/foo.dat"); google_get_push_request_.load_flags = 0; return google_get_push_request_; } const HttpRequestInfo& CreateGetRequest() { if (!google_get_request_initialized_) { google_get_request_.method = "GET"; google_get_request_.url = GURL(kDefaultURL); google_get_request_.load_flags = 0; google_get_request_initialized_ = true; } return google_get_request_; } const HttpRequestInfo& CreateGetRequestWithUserAgent() { if (!google_get_request_initialized_) { google_get_request_.method = "GET"; google_get_request_.url = GURL(kDefaultURL); google_get_request_.load_flags = 0; google_get_request_.extra_headers.SetHeader("User-Agent", "Chrome"); google_get_request_initialized_ = true; } return google_get_request_; } const HttpRequestInfo& CreatePostRequest() { if (!google_post_request_initialized_) { google_post_request_.method = "POST"; google_post_request_.url = GURL(kDefaultURL); google_post_request_.upload_data = new UploadData(); google_post_request_.upload_data->AppendBytes(kUploadData, kUploadDataSize); google_post_request_initialized_ = true; } return google_post_request_; } const HttpRequestInfo& CreateChunkedPostRequest() { if (!google_chunked_post_request_initialized_) { google_chunked_post_request_.method = "POST"; google_chunked_post_request_.url = GURL(kDefaultURL); google_chunked_post_request_.upload_data = new UploadData(); google_chunked_post_request_.upload_data->set_is_chunked(true); google_chunked_post_request_.upload_data->AppendChunk( kUploadData, kUploadDataSize, false); google_chunked_post_request_.upload_data->AppendChunk( kUploadData, kUploadDataSize, true); google_chunked_post_request_initialized_ = true; } return google_chunked_post_request_; } // Read the result of a particular transaction, knowing that we've got // multiple transactions in the read pipeline; so as we read, we may have // to skip over data destined for other transactions while we consume // the data for |trans|. int ReadResult(HttpNetworkTransaction* trans, StaticSocketDataProvider* data, std::string* result) { const int kSize = 3000; int bytes_read = 0; scoped_refptr
buf(new net::IOBufferWithSize(kSize)); TestCompletionCallback callback; while (true) { int rv = trans->Read(buf, kSize, &callback); if (rv == ERR_IO_PENDING) { // Multiple transactions may be in the data set. Keep pulling off // reads until we complete our callback. while (!callback.have_result()) { data->CompleteRead(); MessageLoop::current()->RunAllPending(); } rv = callback.WaitForResult(); } else if (rv <= 0) { break; } result->append(buf->data(), rv); bytes_read += rv; } return bytes_read; } void VerifyStreamsClosed(const NormalSpdyTransactionHelper& helper) { // This lengthy block is reaching into the pool to dig out the active // session. Once we have the session, we verify that the streams are // all closed and not leaked at this point. const GURL& url = helper.request().url; int port = helper.test_type() == SPDYNPN ? 443 : 80; HostPortPair host_port_pair(url.host(), port); HostPortProxyPair pair(host_port_pair, ProxyServer::Direct()); BoundNetLog log; const scoped_refptr
& session = helper.session(); SpdySessionPool* pool(session->spdy_session_pool()); EXPECT_TRUE(pool->HasSession(pair)); scoped_refptr
spdy_session(pool->Get(pair, log)); ASSERT_TRUE(spdy_session.get() != NULL); EXPECT_EQ(0u, spdy_session->num_active_streams()); EXPECT_EQ(0u, spdy_session->num_unclaimed_pushed_streams()); } void RunServerPushTest(OrderedSocketData* data, HttpResponseInfo* response, HttpResponseInfo* push_response, std::string& expected) { NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(data); HttpNetworkTransaction* trans = helper.trans(); // Start the transaction with basic parameters. TestCompletionCallback callback; int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); // Request the pushed path. scoped_ptr
trans2( new HttpNetworkTransaction(helper.session())); rv = trans2->Start(&CreateGetPushRequest(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); MessageLoop::current()->RunAllPending(); // The data for the pushed path may be coming in more than 1 packet. Compile // the results into a single string. // Read the server push body. std::string result2; ReadResult(trans2.get(), data, &result2); // Read the response body. std::string result; ReadResult(trans, data, &result); // Verify that we consumed all test data. EXPECT_TRUE(data->at_read_eof()); EXPECT_TRUE(data->at_write_eof()); // Verify that the received push data is same as the expected push data. EXPECT_EQ(result2.compare(expected), 0) << "Received data: " << result2 << "||||| Expected data: " << expected; // Verify the SYN_REPLY. // Copy the response info, because trans goes away. *response = *trans->GetResponseInfo(); *push_response = *trans2->GetResponseInfo(); VerifyStreamsClosed(helper); } private: bool google_get_request_initialized_; bool google_post_request_initialized_; bool google_chunked_post_request_initialized_; HttpRequestInfo google_get_request_; HttpRequestInfo google_post_request_; HttpRequestInfo google_chunked_post_request_; HttpRequestInfo google_get_push_request_; }; //----------------------------------------------------------------------------- // All tests are run with three different connection types: SPDY after NPN // negotiation, SPDY without SSL, and SPDY with SSL. INSTANTIATE_TEST_CASE_P(Spdy, SpdyNetworkTransactionTest, ::testing::Values(SPDYNOSSL, SPDYSSL, SPDYNPN)); // Verify HttpNetworkTransaction constructor. TEST_P(SpdyNetworkTransactionTest, Constructor) { SpdySessionDependencies session_deps; scoped_refptr
session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); scoped_ptr
trans(new HttpNetworkTransaction(session)); } TEST_P(SpdyNetworkTransactionTest, Get) { // Construct the request. scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(1, reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunToCompletion(data.get()); TransactionHelperResult out = helper.output(); EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); EXPECT_EQ("hello!", out.response_data); } TEST_P(SpdyNetworkTransactionTest, GetAtEachPriority) { for (RequestPriority p = HIGHEST; p < NUM_PRIORITIES; p = RequestPriority(p+1)) { // Construct the request. scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, p)); MockWrite writes[] = { CreateMockWrite(*req) }; const int spdy_prio = reinterpret_cast
( req.get())->priority(); // this repeats the RequestPriority-->SpdyPriority mapping from // SpdyFramer::ConvertRequestPriorityToSpdyPriority to make // sure it's being done right. switch(p) { case HIGHEST: EXPECT_EQ(0, spdy_prio); break; case MEDIUM: EXPECT_EQ(1, spdy_prio); break; case LOW: case LOWEST: EXPECT_EQ(2, spdy_prio); break; case IDLE: EXPECT_EQ(3, spdy_prio); break; default: FAIL(); } scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(1, reads, arraysize(reads), writes, arraysize(writes))); HttpRequestInfo http_req = CreateGetRequest(); http_req.priority = p; NormalSpdyTransactionHelper helper(http_req, BoundNetLog(), GetParam()); helper.RunToCompletion(data.get()); TransactionHelperResult out = helper.output(); EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); 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_P(SpdyNetworkTransactionTest, ThreeGets) { scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body(ConstructSpdyBodyFrame(1, false)); scoped_ptr
fbody(ConstructSpdyBodyFrame(1, true)); scoped_ptr
req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); scoped_ptr
resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); scoped_ptr
body2(ConstructSpdyBodyFrame(3, false)); scoped_ptr
fbody2(ConstructSpdyBodyFrame(3, true)); scoped_ptr
req3(ConstructSpdyGet(NULL, 0, false, 5, LOWEST)); scoped_ptr
resp3(ConstructSpdyGetSynReply(NULL, 0, 5)); scoped_ptr
body3(ConstructSpdyBodyFrame(5, false)); scoped_ptr
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
data( new OrderedSocketData(reads, arraysize(reads), writes, arraysize(writes))); scoped_refptr
data_placeholder( new OrderedSocketData(NULL, 0, NULL, 0)); BoundNetLog log; TransactionHelperResult out; NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(data.get()); // We require placeholder data because three get requests are sent out, so // there needs to be three sets of SSL connection data. helper.AddData(data_placeholder.get()); helper.AddData(data_placeholder.get()); scoped_ptr
trans1( new HttpNetworkTransaction(helper.session())); scoped_ptr
trans2( new HttpNetworkTransaction(helper.session())); scoped_ptr
trans3( new HttpNetworkTransaction(helper.session())); 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); helper.VerifyDataConsumed(); EXPECT_EQ(OK, out.rv); EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); EXPECT_EQ("hello!hello!", out.response_data); } TEST_P(SpdyNetworkTransactionTest, TwoGetsLateBinding) { scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body(ConstructSpdyBodyFrame(1, false)); scoped_ptr
fbody(ConstructSpdyBodyFrame(1, true)); scoped_ptr
req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); scoped_ptr
resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); scoped_ptr
body2(ConstructSpdyBodyFrame(3, false)); scoped_ptr
fbody2(ConstructSpdyBodyFrame(3, true)); MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*req2), }; MockRead reads[] = { CreateMockRead(*resp, 1), CreateMockRead(*body), CreateMockRead(*resp2, 4), CreateMockRead(*body2), CreateMockRead(*fbody), CreateMockRead(*fbody2), MockRead(true, 0, 0), // EOF }; scoped_refptr
data( new OrderedSocketData(reads, arraysize(reads), writes, arraysize(writes))); MockConnect never_finishing_connect(false, ERR_IO_PENDING); scoped_refptr
data_placeholder( new OrderedSocketData(NULL, 0, NULL, 0)); data_placeholder->set_connect_data(never_finishing_connect); BoundNetLog log; TransactionHelperResult out; NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(data.get()); // We require placeholder data because two get requests are sent out, so // there needs to be two sets of SSL connection data. helper.AddData(data_placeholder.get()); scoped_ptr
trans1( new HttpNetworkTransaction(helper.session())); scoped_ptr
trans2( new HttpNetworkTransaction(helper.session())); TestCompletionCallback callback1; TestCompletionCallback callback2; HttpRequestInfo httpreq1 = CreateGetRequest(); HttpRequestInfo httpreq2 = 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 = callback1.WaitForResult(); ASSERT_EQ(OK, out.rv); out.rv = callback2.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(); EXPECT_TRUE(response2->headers != NULL); EXPECT_TRUE(response2->was_fetched_via_spdy); 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); helper.VerifyDataConsumed(); } TEST_P(SpdyNetworkTransactionTest, TwoGetsLateBindingFromPreconnect) { scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body(ConstructSpdyBodyFrame(1, false)); scoped_ptr
fbody(ConstructSpdyBodyFrame(1, true)); scoped_ptr
req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); scoped_ptr
resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); scoped_ptr
body2(ConstructSpdyBodyFrame(3, false)); scoped_ptr
fbody2(ConstructSpdyBodyFrame(3, true)); MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*req2), }; MockRead reads[] = { CreateMockRead(*resp, 1), CreateMockRead(*body), CreateMockRead(*resp2, 4), CreateMockRead(*body2), CreateMockRead(*fbody), CreateMockRead(*fbody2), MockRead(true, 0, 0), // EOF }; scoped_refptr
preconnect_data( new OrderedSocketData(reads, arraysize(reads), writes, arraysize(writes))); MockConnect never_finishing_connect(true, ERR_IO_PENDING); scoped_refptr
data_placeholder( new OrderedSocketData(NULL, 0, NULL, 0)); data_placeholder->set_connect_data(never_finishing_connect); BoundNetLog log; TransactionHelperResult out; NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(preconnect_data.get()); // We require placeholder data because 3 connections are attempted (first is // the preconnect, 2nd and 3rd are the never finished connections. helper.AddData(data_placeholder.get()); helper.AddData(data_placeholder.get()); scoped_ptr
trans1( new HttpNetworkTransaction(helper.session())); scoped_ptr
trans2( new HttpNetworkTransaction(helper.session())); TestCompletionCallback callback1; TestCompletionCallback callback2; HttpRequestInfo httpreq = CreateGetRequest(); // Preconnect the first. SSLConfig preconnect_ssl_config; helper.session()->ssl_config_service()->GetSSLConfig(&preconnect_ssl_config); HttpStreamFactory* http_stream_factory = helper.session()->http_stream_factory(); if (http_stream_factory->next_protos()) { preconnect_ssl_config.next_protos = *http_stream_factory->next_protos(); } http_stream_factory->PreconnectStreams( 1, httpreq, preconnect_ssl_config, log); out.rv = trans1->Start(&httpreq, &callback1, log); ASSERT_EQ(ERR_IO_PENDING, out.rv); out.rv = trans2->Start(&httpreq, &callback2, log); ASSERT_EQ(ERR_IO_PENDING, out.rv); out.rv = callback1.WaitForResult(); ASSERT_EQ(OK, out.rv); out.rv = callback2.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(); EXPECT_TRUE(response2->headers != NULL); EXPECT_TRUE(response2->was_fetched_via_spdy); 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); helper.VerifyDataConsumed(); } // 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_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrent) { // Construct the request. scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body(ConstructSpdyBodyFrame(1, false)); scoped_ptr
fbody(ConstructSpdyBodyFrame(1, true)); scoped_ptr
req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); scoped_ptr
resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); scoped_ptr
body2(ConstructSpdyBodyFrame(3, false)); scoped_ptr
fbody2(ConstructSpdyBodyFrame(3, true)); scoped_ptr
req3(ConstructSpdyGet(NULL, 0, false, 5, LOWEST)); scoped_ptr
resp3(ConstructSpdyGetSynReply(NULL, 0, 5)); scoped_ptr
body3(ConstructSpdyBodyFrame(5, false)); scoped_ptr
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
settings_frame(ConstructSpdySettings(settings)); MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*req2), CreateMockWrite(*req3), }; MockRead reads[] = { CreateMockRead(*settings_frame, 1), CreateMockRead(*resp), CreateMockRead(*body), CreateMockRead(*fbody), CreateMockRead(*resp2, 7), CreateMockRead(*body2), CreateMockRead(*fbody2), CreateMockRead(*resp3, 12), CreateMockRead(*body3), CreateMockRead(*fbody3), MockRead(true, 0, 0), // EOF }; scoped_refptr
data( new OrderedSocketData(reads, arraysize(reads), writes, arraysize(writes))); scoped_refptr
data_placeholder( new OrderedSocketData(NULL, 0, NULL, 0)); BoundNetLog log; TransactionHelperResult out; { NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(data.get()); // We require placeholder data because three get requests are sent out, so // there needs to be three sets of SSL connection data. helper.AddData(data_placeholder.get()); helper.AddData(data_placeholder.get()); scoped_ptr
trans1( new HttpNetworkTransaction(helper.session())); scoped_ptr
trans2( new HttpNetworkTransaction(helper.session())); scoped_ptr
trans3( new HttpNetworkTransaction(helper.session())); 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(); ASSERT_EQ(OK, out.rv); 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(); ASSERT_TRUE(response1 != NULL); 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); helper.VerifyDataConsumed(); } EXPECT_EQ(OK, out.rv); } // 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_P(SpdyNetworkTransactionTest, FourGetsWithMaxConcurrentPriority) { // Construct the request. scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body(ConstructSpdyBodyFrame(1, false)); scoped_ptr
fbody(ConstructSpdyBodyFrame(1, true)); scoped_ptr
req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); scoped_ptr
resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); scoped_ptr
body2(ConstructSpdyBodyFrame(3, false)); scoped_ptr
fbody2(ConstructSpdyBodyFrame(3, true)); scoped_ptr
req4( ConstructSpdyGet(NULL, 0, false, 5, HIGHEST)); scoped_ptr
resp4(ConstructSpdyGetSynReply(NULL, 0, 5)); scoped_ptr
fbody4(ConstructSpdyBodyFrame(5, true)); scoped_ptr
req3(ConstructSpdyGet(NULL, 0, false, 7, LOWEST)); scoped_ptr
resp3(ConstructSpdyGetSynReply(NULL, 0, 7)); scoped_ptr
body3(ConstructSpdyBodyFrame(7, false)); scoped_ptr
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
settings_frame(ConstructSpdySettings(settings)); MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*req2), CreateMockWrite(*req4), CreateMockWrite(*req3), }; MockRead reads[] = { CreateMockRead(*settings_frame, 1), CreateMockRead(*resp), CreateMockRead(*body), CreateMockRead(*fbody), CreateMockRead(*resp2, 7), CreateMockRead(*body2), CreateMockRead(*fbody2), CreateMockRead(*resp4, 13), CreateMockRead(*fbody4), CreateMockRead(*resp3, 16), CreateMockRead(*body3), CreateMockRead(*fbody3), MockRead(true, 0, 0), // EOF }; scoped_refptr
data( new OrderedSocketData(reads, arraysize(reads), writes, arraysize(writes))); scoped_refptr
data_placeholder( new OrderedSocketData(NULL, 0, NULL, 0)); BoundNetLog log; TransactionHelperResult out; NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(data.get()); // We require placeholder data because four get requests are sent out, so // there needs to be four sets of SSL connection data. helper.AddData(data_placeholder.get()); helper.AddData(data_placeholder.get()); helper.AddData(data_placeholder.get()); scoped_ptr
trans1( new HttpNetworkTransaction(helper.session())); scoped_ptr
trans2( new HttpNetworkTransaction(helper.session())); scoped_ptr
trans3( new HttpNetworkTransaction(helper.session())); scoped_ptr
trans4( new HttpNetworkTransaction(helper.session())); 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); helper.VerifyDataConsumed(); EXPECT_EQ(OK, out.rv); } // 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_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentDelete) { // Construct the request. scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body(ConstructSpdyBodyFrame(1, false)); scoped_ptr
fbody(ConstructSpdyBodyFrame(1, true)); scoped_ptr
req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); scoped_ptr
resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); scoped_ptr
body2(ConstructSpdyBodyFrame(3, false)); scoped_ptr
fbody2(ConstructSpdyBodyFrame(3, 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
settings_frame(ConstructSpdySettings(settings)); MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*req2), }; MockRead reads[] = { CreateMockRead(*settings_frame, 1), CreateMockRead(*resp), CreateMockRead(*body), CreateMockRead(*fbody), CreateMockRead(*resp2, 7), CreateMockRead(*body2), CreateMockRead(*fbody2), MockRead(true, 0, 0), // EOF }; scoped_refptr
data( new OrderedSocketData(reads, arraysize(reads), writes, arraysize(writes))); scoped_refptr
data_placeholder( new OrderedSocketData(NULL, 0, NULL, 0)); BoundNetLog log; TransactionHelperResult out; NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(data.get()); // We require placeholder data because three get requests are sent out, so // there needs to be three sets of SSL connection data. helper.AddData(data_placeholder.get()); helper.AddData(data_placeholder.get()); scoped_ptr
trans1( new HttpNetworkTransaction(helper.session())); scoped_ptr
trans2( new HttpNetworkTransaction(helper.session())); scoped_ptr
trans3( new HttpNetworkTransaction(helper.session())); 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(); ASSERT_EQ(OK, out.rv); 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(); ASSERT_EQ(OK, out.rv); EXPECT_EQ(8U, data->read_index()); const HttpResponseInfo* response1 = trans1->GetResponseInfo(); ASSERT_TRUE(response1 != NULL); 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(); ASSERT_TRUE(response2 != NULL); 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); helper.VerifyDataConsumed(); EXPECT_EQ(OK, out.rv); } // The KillerCallback will delete the transaction on error as part of the // callback. class KillerCallback : public TestCompletionCallback { public: explicit KillerCallback(HttpNetworkTransaction* transaction) : transaction_(transaction) {} virtual void RunWithParams(const Tuple1
& params) { if (params.a < 0) delete transaction_; TestCompletionCallback::RunWithParams(params); } private: HttpNetworkTransaction* transaction_; }; // Similar to ThreeGetsMaxConcurrrentDelete above, however, this test // closes the socket while we have a pending transaction waiting for // a pending stream creation. http://crbug.com/52901 TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentSocketClose) { // Construct the request. scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body(ConstructSpdyBodyFrame(1, false)); scoped_ptr
fin_body(ConstructSpdyBodyFrame(1, true)); scoped_ptr
req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); scoped_ptr
resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); 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
settings_frame(ConstructSpdySettings(settings)); MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*req2), }; MockRead reads[] = { CreateMockRead(*settings_frame, 1), CreateMockRead(*resp), CreateMockRead(*body), CreateMockRead(*fin_body), CreateMockRead(*resp2, 7), MockRead(true, ERR_CONNECTION_RESET, 0), // Abort! }; scoped_refptr
data( new OrderedSocketData(reads, arraysize(reads), writes, arraysize(writes))); scoped_refptr
data_placeholder( new OrderedSocketData(NULL, 0, NULL, 0)); BoundNetLog log; TransactionHelperResult out; NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(data.get()); // We require placeholder data because three get requests are sent out, so // there needs to be three sets of SSL connection data. helper.AddData(data_placeholder.get()); helper.AddData(data_placeholder.get()); HttpNetworkTransaction trans1(helper.session()); HttpNetworkTransaction trans2(helper.session()); HttpNetworkTransaction* trans3(new HttpNetworkTransaction(helper.session())); TestCompletionCallback callback1; TestCompletionCallback callback2; KillerCallback callback3(trans3); 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(); ASSERT_EQ(OK, out.rv); 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 = callback3.WaitForResult(); ASSERT_EQ(ERR_ABORTED, out.rv); EXPECT_EQ(6U, data->read_index()); const HttpResponseInfo* response1 = trans1.GetResponseInfo(); ASSERT_TRUE(response1 != NULL); 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, &out.response_data); EXPECT_EQ(OK, out.rv); const HttpResponseInfo* response2 = trans2.GetResponseInfo(); ASSERT_TRUE(response2 != NULL); out.status_line = response2->headers->GetStatusLine(); out.response_info = *response2; out.rv = ReadTransaction(&trans2, &out.response_data); EXPECT_EQ(ERR_CONNECTION_RESET, out.rv); helper.VerifyDataConsumed(); } // Test that a simple PUT request works. TEST_P(SpdyNetworkTransactionTest, Put) { // Setup the request HttpRequestInfo request; request.method = "PUT"; request.url = GURL("http://www.google.com/"); const SpdyHeaderInfo kSynStartHeader = { spdy::SYN_STREAM, // Kind = Syn 1, // Stream ID 0, // Associated stream ID net::ConvertRequestPriorityToSpdyPriority(LOWEST), // Priority spdy::CONTROL_FLAG_FIN, // Control Flags false, // Compressed spdy::INVALID, // Status NULL, // Data 0, // Length spdy::DATA_FLAG_NONE // Data Flags }; const char* const kPutHeaders[] = { "method", "PUT", "url", "/", "host", "www.google.com", "scheme", "http", "version", "HTTP/1.1", "content-length", "0" }; scoped_ptr
req(ConstructSpdyPacket(kSynStartHeader, NULL, 0, kPutHeaders, arraysize(kPutHeaders) / 2)); MockWrite writes[] = { CreateMockWrite(*req) }; scoped_ptr
body(ConstructSpdyBodyFrame(1, true)); const SpdyHeaderInfo kSynReplyHeader = { spdy::SYN_REPLY, // Kind = SynReply 1, // Stream ID 0, // Associated stream ID net::ConvertRequestPriorityToSpdyPriority(LOWEST), // Priority spdy::CONTROL_FLAG_NONE, // Control Flags false, // Compressed spdy::INVALID, // Status NULL, // Data 0, // Length spdy::DATA_FLAG_NONE // Data Flags }; static const char* const kStandardGetHeaders[] = { "status", "200", "version", "HTTP/1.1" "content-length", "1234" }; scoped_ptr
resp(ConstructSpdyPacket(kSynReplyHeader, NULL, 0, kStandardGetHeaders, arraysize(kStandardGetHeaders) / 2)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(1, reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(request, BoundNetLog(), GetParam()); helper.RunToCompletion(data.get()); TransactionHelperResult out = helper.output(); EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); } // Test that a simple HEAD request works. TEST_P(SpdyNetworkTransactionTest, Head) { // Setup the request HttpRequestInfo request; request.method = "HEAD"; request.url = GURL("http://www.google.com/"); const SpdyHeaderInfo kSynStartHeader = { spdy::SYN_STREAM, // Kind = Syn 1, // Stream ID 0, // Associated stream ID net::ConvertRequestPriorityToSpdyPriority(LOWEST), // Priority spdy::CONTROL_FLAG_FIN, // Control Flags false, // Compressed spdy::INVALID, // Status NULL, // Data 0, // Length spdy::DATA_FLAG_NONE // Data Flags }; const char* const kHeadHeaders[] = { "method", "HEAD", "url", "/", "host", "www.google.com", "scheme", "http", "version", "HTTP/1.1", "content-length", "0" }; scoped_ptr
req(ConstructSpdyPacket(kSynStartHeader, NULL, 0, kHeadHeaders, arraysize(kHeadHeaders) / 2)); MockWrite writes[] = { CreateMockWrite(*req) }; scoped_ptr
body(ConstructSpdyBodyFrame(1, true)); const SpdyHeaderInfo kSynReplyHeader = { spdy::SYN_REPLY, // Kind = SynReply 1, // Stream ID 0, // Associated stream ID net::ConvertRequestPriorityToSpdyPriority(LOWEST), // Priority spdy::CONTROL_FLAG_NONE, // Control Flags false, // Compressed spdy::INVALID, // Status NULL, // Data 0, // Length spdy::DATA_FLAG_NONE // Data Flags }; static const char* const kStandardGetHeaders[] = { "status", "200", "version", "HTTP/1.1" "content-length", "1234" }; scoped_ptr
resp(ConstructSpdyPacket(kSynReplyHeader, NULL, 0, kStandardGetHeaders, arraysize(kStandardGetHeaders) / 2)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(1, reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(request, BoundNetLog(), GetParam()); helper.RunToCompletion(data.get()); TransactionHelperResult out = helper.output(); EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); } // Test that a simple POST works. TEST_P(SpdyNetworkTransactionTest, Post) { scoped_ptr
req(ConstructSpdyPost(kUploadDataSize, NULL, 0)); scoped_ptr
body(ConstructSpdyBodyFrame(1, true)); MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*body), // POST upload frame }; scoped_ptr
resp(ConstructSpdyPostSynReply(NULL, 0)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(2, reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(CreatePostRequest(), BoundNetLog(), GetParam()); helper.RunToCompletion(data.get()); TransactionHelperResult out = helper.output(); EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); EXPECT_EQ("hello!", out.response_data); } // Test that a chunked POST works. TEST_P(SpdyNetworkTransactionTest, ChunkedPost) { UploadDataStream::set_merge_chunks(false); scoped_ptr
req(ConstructChunkedSpdyPost(NULL, 0)); scoped_ptr
chunk1(ConstructSpdyBodyFrame(1, false)); scoped_ptr
chunk2(ConstructSpdyBodyFrame(1, true)); MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*chunk1), CreateMockWrite(*chunk2), }; scoped_ptr
resp(ConstructSpdyPostSynReply(NULL, 0)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*chunk1), CreateMockRead(*chunk2), MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(2, reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), BoundNetLog(), GetParam()); helper.RunToCompletion(data.get()); TransactionHelperResult out = helper.output(); EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); EXPECT_EQ("hello!hello!", out.response_data); } // Test that a POST without any post data works. TEST_P(SpdyNetworkTransactionTest, NullPost) { // Setup the request HttpRequestInfo request; request.method = "POST"; request.url = GURL("http://www.google.com/"); // Create an empty UploadData. request.upload_data = NULL; // When request.upload_data is NULL for post, content-length is // expected to be 0. scoped_ptr
req(ConstructSpdyPost(0, NULL, 0)); // Set the FIN bit since there will be no body. req->set_flags(spdy::CONTROL_FLAG_FIN); MockWrite writes[] = { CreateMockWrite(*req), }; scoped_ptr
resp(ConstructSpdyPostSynReply(NULL, 0)); scoped_ptr
body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(1, reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(request, BoundNetLog(), GetParam()); helper.RunToCompletion(data.get()); TransactionHelperResult out = helper.output(); EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); EXPECT_EQ("hello!", out.response_data); } // Test that a simple POST works. TEST_P(SpdyNetworkTransactionTest, EmptyPost) { // Setup the request HttpRequestInfo request; request.method = "POST"; request.url = GURL("http://www.google.com/"); // Create an empty UploadData. request.upload_data = new UploadData(); // Http POST Content-Length is using UploadDataStream::size(). // It is the same as request.upload_data->GetContentLength(). scoped_ptr
stream(UploadDataStream::Create( request.upload_data, NULL)); ASSERT_EQ(request.upload_data->GetContentLength(), stream->size()); scoped_ptr
req(ConstructSpdyPost(request.upload_data->GetContentLength(), NULL, 0)); // Set the FIN bit since there will be no body. req->set_flags(spdy::CONTROL_FLAG_FIN); MockWrite writes[] = { CreateMockWrite(*req), }; scoped_ptr
resp(ConstructSpdyPostSynReply(NULL, 0)); scoped_ptr
body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(1, reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(request, BoundNetLog(), GetParam()); helper.RunToCompletion(data.get()); TransactionHelperResult out = helper.output(); EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); EXPECT_EQ("hello!", out.response_data); } // While we're doing a post, the server sends back a SYN_REPLY. TEST_P(SpdyNetworkTransactionTest, PostWithEarlySynReply) { static const char upload[] = { "hello!" }; // Setup the request HttpRequestInfo request; request.method = "POST"; request.url = GURL("http://www.google.com/"); request.upload_data = new UploadData(); request.upload_data->AppendBytes(upload, sizeof(upload)); // Http POST Content-Length is using UploadDataStream::size(). // It is the same as request.upload_data->GetContentLength(). scoped_ptr
stream(UploadDataStream::Create( request.upload_data, NULL)); ASSERT_EQ(request.upload_data->GetContentLength(), stream->size()); scoped_ptr
stream_reply(ConstructSpdyPostSynReply(NULL, 0)); scoped_ptr
stream_body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*stream_reply, 2), CreateMockRead(*stream_body, 3), MockRead(false, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(0, reads, arraysize(reads), NULL, 0)); NormalSpdyTransactionHelper helper(request, BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(data.get()); helper.RunDefaultTest(); helper.VerifyDataConsumed(); TransactionHelperResult out = helper.output(); EXPECT_EQ(ERR_SYN_REPLY_NOT_RECEIVED, out.rv); } // The client upon cancellation tries to send a RST_STREAM frame. The mock // socket causes the TCP write to return zero. This test checks that the client // tries to queue up the RST_STREAM frame again. TEST_P(SpdyNetworkTransactionTest, SocketWriteReturnsZero) { scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); scoped_ptr
rst( ConstructSpdyRstStream(1, spdy::CANCEL)); MockWrite writes[] = { CreateMockWrite(*req.get(), 0, false), MockWrite(false, 0, 0, 2), CreateMockWrite(*rst.get(), 3, false), }; scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); MockRead reads[] = { CreateMockRead(*resp.get(), 1, true), MockRead(true, 0, 0, 4) // EOF }; scoped_refptr
data( new DeterministicSocketData(reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.SetDeterministic(); helper.RunPreTestSetup(); helper.AddDeterministicData(data.get()); HttpNetworkTransaction* trans = helper.trans(); TestCompletionCallback callback; int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); data->SetStop(2); data->Run(); helper.ResetTrans(); data->SetStop(20); data->Run(); helper.VerifyDataConsumed(); } // Test that the transaction doesn't crash when we don't have a reply. TEST_P(SpdyNetworkTransactionTest, ResponseWithoutSynReply) { scoped_ptr
body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*body), MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(1, reads, arraysize(reads), NULL, 0)); NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunToCompletion(data.get()); TransactionHelperResult out = helper.output(); EXPECT_EQ(ERR_SYN_REPLY_NOT_RECEIVED, out.rv); } // Test that the transaction doesn't crash when we get two replies on the same // stream ID. See http://crbug.com/45639. TEST_P(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) { scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*resp), CreateMockRead(*body), MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(1, reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(data.get()); HttpNetworkTransaction* trans = helper.trans(); TestCompletionCallback callback; int rv = trans->Start(&helper.request(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(response->headers != NULL); EXPECT_TRUE(response->was_fetched_via_spdy); std::string response_data; rv = ReadTransaction(trans, &response_data); EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, rv); helper.VerifyDataConsumed(); } // Test that sent data frames and received WINDOW_UPDATE frames change // the send_window_size_ correctly. // WINDOW_UPDATE is different than most other frames in that it can arrive // while the client is still sending the request body. In order to enforce // this scenario, we feed a couple of dummy frames and give a delay of 0 to // socket data provider, so that initial read that is done as soon as the // stream is created, succeeds and schedules another read. This way reads // and writes are interleaved; after doing a full frame write, SpdyStream // will break out of DoLoop and will read and process a WINDOW_UPDATE. // Once our WINDOW_UPDATE is read, we cannot send SYN_REPLY right away // since request has not been completely written, therefore we feed // enough number of WINDOW_UPDATEs to finish the first read and cause a // write, leading to a complete write of request body; after that we send // a reply with a body, to cause a graceful shutdown. // TODO(agayev): develop a socket data provider where both, reads and // writes are ordered so that writing tests like these are easy and rewrite // all these tests using it. Right now we are working around the // limitations as described above and it's not deterministic, tests may // fail under specific circumstances. TEST_P(SpdyNetworkTransactionTest, WindowUpdateReceived) { SpdySession::set_flow_control(true); static int kFrameCount = 2; scoped_ptr
content( new std::string(kMaxSpdyFrameChunkSize, 'a')); scoped_ptr
req(ConstructSpdyPost( kMaxSpdyFrameChunkSize * kFrameCount, NULL, 0)); scoped_ptr
body( ConstructSpdyBodyFrame(1, content->c_str(), content->size(), false)); scoped_ptr
body_end( ConstructSpdyBodyFrame(1, content->c_str(), content->size(), true)); MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*body), CreateMockWrite(*body_end), }; static const int kDeltaWindowSize = 0xff; static const int kDeltaCount = 4; scoped_ptr
window_update( ConstructSpdyWindowUpdate(1, kDeltaWindowSize)); scoped_ptr
window_update_dummy( ConstructSpdyWindowUpdate(2, kDeltaWindowSize)); scoped_ptr
resp(ConstructSpdyPostSynReply(NULL, 0)); MockRead reads[] = { CreateMockRead(*window_update_dummy), CreateMockRead(*window_update_dummy), CreateMockRead(*window_update_dummy), CreateMockRead(*window_update), // Four updates, therefore window CreateMockRead(*window_update), // size should increase by CreateMockRead(*window_update), // kDeltaWindowSize * 4 CreateMockRead(*window_update), CreateMockRead(*resp), CreateMockRead(*body_end), MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(0, reads, arraysize(reads), writes, arraysize(writes))); // Setup the request HttpRequestInfo request; request.method = "POST"; request.url = GURL(kDefaultURL); request.upload_data = new UploadData(); for (int i = 0; i < kFrameCount; ++i) request.upload_data->AppendBytes(content->c_str(), content->size()); NormalSpdyTransactionHelper helper(request, BoundNetLog(), GetParam()); helper.AddData(data.get()); helper.RunPreTestSetup(); HttpNetworkTransaction* trans = helper.trans(); TestCompletionCallback callback; int rv = trans->Start(&helper.request(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); SpdyHttpStream* stream = static_cast
(trans->stream_.get()); ASSERT_TRUE(stream != NULL); ASSERT_TRUE(stream->stream() != NULL); EXPECT_EQ(static_cast
(spdy::kSpdyStreamInitialWindowSize) + kDeltaWindowSize * kDeltaCount - kMaxSpdyFrameChunkSize * kFrameCount, stream->stream()->send_window_size()); helper.VerifyDataConsumed(); SpdySession::set_flow_control(false); } // Test that received data frames and sent WINDOW_UPDATE frames change // the recv_window_size_ correctly. TEST_P(SpdyNetworkTransactionTest, WindowUpdateSent) { SpdySession::set_flow_control(true); scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); scoped_ptr
window_update( ConstructSpdyWindowUpdate(1, kUploadDataSize)); MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*window_update), }; scoped_ptr
resp( ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body_no_fin( ConstructSpdyBodyFrame(1, false)); scoped_ptr
body_fin( ConstructSpdyBodyFrame(1, NULL, 0, true)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body_no_fin), MockRead(true, ERR_IO_PENDING, 0), // Force a pause CreateMockRead(*body_fin), MockRead(true, ERR_IO_PENDING, 0), // Force a pause MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(1, reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.AddData(data.get()); helper.RunPreTestSetup(); HttpNetworkTransaction* trans = helper.trans(); TestCompletionCallback callback; int rv = trans->Start(&helper.request(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); SpdyHttpStream* stream = static_cast
(trans->stream_.get()); ASSERT_TRUE(stream != NULL); ASSERT_TRUE(stream->stream() != NULL); EXPECT_EQ( static_cast
(spdy::kSpdyStreamInitialWindowSize) - kUploadDataSize, stream->stream()->recv_window_size()); const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_TRUE(response->headers != NULL); EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); EXPECT_TRUE(response->was_fetched_via_spdy); // Issue a read which will cause a WINDOW_UPDATE to be sent and window // size increased to default. scoped_refptr
buf(new net::IOBuffer(kUploadDataSize)); rv = trans->Read(buf, kUploadDataSize, NULL); EXPECT_EQ(kUploadDataSize, rv); std::string content(buf->data(), buf->data()+kUploadDataSize); EXPECT_STREQ(kUploadData, content.c_str()); // Schedule the reading of empty data frame with FIN data->CompleteRead(); // Force write of WINDOW_UPDATE which was scheduled during the above // read. MessageLoop::current()->RunAllPending(); // Read EOF. data->CompleteRead(); helper.VerifyDataConsumed(); SpdySession::set_flow_control(false); } // Test that WINDOW_UPDATE frame causing overflow is handled correctly. We // use the same trick as in the above test to enforce our scenario. TEST_P(SpdyNetworkTransactionTest, WindowUpdateOverflow) { SpdySession::set_flow_control(true); // number of full frames we hope to write (but will not, used to // set content-length header correctly) static int kFrameCount = 3; scoped_ptr
content( new std::string(kMaxSpdyFrameChunkSize, 'a')); scoped_ptr
req(ConstructSpdyPost( kMaxSpdyFrameChunkSize * kFrameCount, NULL, 0)); scoped_ptr
body( ConstructSpdyBodyFrame(1, content->c_str(), content->size(), false)); scoped_ptr
rst( ConstructSpdyRstStream(1, spdy::FLOW_CONTROL_ERROR)); // We're not going to write a data frame with FIN, we'll receive a bad // WINDOW_UPDATE while sending a request and will send a RST_STREAM frame. MockWrite writes[] = { CreateMockWrite(*req), CreateMockWrite(*body), CreateMockWrite(*rst), }; static const int kDeltaWindowSize = 0x7fffffff; // cause an overflow scoped_ptr
window_update( ConstructSpdyWindowUpdate(1, kDeltaWindowSize)); scoped_ptr
window_update2( ConstructSpdyWindowUpdate(2, kDeltaWindowSize)); scoped_ptr
reply(ConstructSpdyPostSynReply(NULL, 0)); MockRead reads[] = { CreateMockRead(*window_update2), CreateMockRead(*window_update2), CreateMockRead(*window_update), CreateMockRead(*window_update), CreateMockRead(*window_update), MockRead(true, ERR_IO_PENDING, 0), // Wait for the RST to be written. MockRead(true, 0, 0) // EOF }; scoped_refptr
data( new DelayedSocketData(0, reads, arraysize(reads), writes, arraysize(writes))); // Setup the request HttpRequestInfo request; request.method = "POST"; request.url = GURL("http://www.google.com/"); request.upload_data = new UploadData(); for (int i = 0; i < kFrameCount; ++i) request.upload_data->AppendBytes(content->c_str(), content->size()); NormalSpdyTransactionHelper helper(request, BoundNetLog(), GetParam()); helper.AddData(data.get()); helper.RunPreTestSetup(); HttpNetworkTransaction* trans = helper.trans(); TestCompletionCallback callback; int rv = trans->Start(&helper.request(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, rv); data->CompleteRead(); ASSERT_TRUE(helper.session() != NULL); ASSERT_TRUE(helper.session()->spdy_session_pool() != NULL); helper.session()->spdy_session_pool()->CloseAllSessions(); helper.VerifyDataConsumed(); SpdySession::set_flow_control(false); } // Test that after hitting a send window size of 0, the write process // stalls and upon receiving WINDOW_UPDATE frame write resumes. // This test constructs a POST request followed by enough data frames // containing 'a' that would make the window size 0, followed by another // data frame containing default content (which is "hello!") and this frame // also contains a FIN flag. DelayedSocketData is used to enforce all // writes go through before a read could happen. However, the last frame // ("hello!") is not supposed to go through since by the time its turn // arrives, window size is 0. At this point MessageLoop::Run() called via // callback would block. Therefore we call MessageLoop::RunAllPending() // which returns after performing all possible writes. We use DCHECKS to // ensure that last data frame is still there and stream has stalled. // After that, next read is artifically enforced, which causes a // WINDOW_UPDATE to be read and I/O process resumes. TEST_P(SpdyNetworkTransactionTest, FlowControlStallResume) { SpdySession::set_flow_control(true); // Number of frames we need to send to zero out the window size: data // frames plus SYN_STREAM plus the last data frame; also we need another // data frame that we will send once the WINDOW_UPDATE is received, // therefore +3. size_t nwrites = spdy::kSpdyStreamInitialWindowSize / kMaxSpdyFrameChunkSize + 3; // Calculate last frame's size; 0 size data frame is legal. size_t last_frame_size = spdy::kSpdyStreamInitialWindowSize % kMaxSpdyFrameChunkSize; // Construct content for a data frame of maximum size. scoped_ptr
content( new std::string(kMaxSpdyFrameChunkSize, 'a')); scoped_ptr
req(ConstructSpdyPost( spdy::kSpdyStreamInitialWindowSize + kUploadDataSize, NULL, 0)); // Full frames. scoped_ptr
body1( ConstructSpdyBodyFrame(1, content->c_str(), content->size(), false)); // Last frame to zero out the window size. scoped_ptr
body2( ConstructSpdyBodyFrame(1, content->c_str(), last_frame_size, false)); // Data frame to be sent once WINDOW_UPDATE frame is received. scoped_ptr
body3(ConstructSpdyBodyFrame(1, true)); // Fill in mock writes. scoped_array
writes(new MockWrite[nwrites]); size_t i = 0; writes[i] = CreateMockWrite(*req); for (i = 1; i < nwrites-2; i++) writes[i] = CreateMockWrite(*body1); writes[i++] = CreateMockWrite(*body2); writes[i] = CreateMockWrite(*body3); // Construct read frame, give enough space to upload the rest of the // data. scoped_ptr
window_update( ConstructSpdyWindowUpdate(1, kUploadDataSize)); scoped_ptr
reply(ConstructSpdyPostSynReply(NULL, 0)); MockRead reads[] = { CreateMockRead(*window_update), CreateMockRead(*window_update), CreateMockRead(*reply), CreateMockRead(*body2), CreateMockRead(*body3), MockRead(true, 0, 0) // EOF }; // Force all writes to happen before any read, last write will not // actually queue a frame, due to window size being 0. scoped_refptr
data( new DelayedSocketData(nwrites, reads, arraysize(reads), writes.get(), nwrites)); HttpRequestInfo request; request.method = "POST"; request.url = GURL("http://www.google.com/"); request.upload_data = new UploadData(); scoped_ptr
upload_data( new std::string(spdy::kSpdyStreamInitialWindowSize, 'a')); upload_data->append(kUploadData, kUploadDataSize); request.upload_data->AppendBytes(upload_data->c_str(), upload_data->size()); NormalSpdyTransactionHelper helper(request, BoundNetLog(), GetParam()); helper.AddData(data.get()); helper.RunPreTestSetup(); HttpNetworkTransaction* trans = helper.trans(); TestCompletionCallback callback; int rv = trans->Start(&helper.request(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); MessageLoop::current()->RunAllPending(); // Write as much as we can. SpdyHttpStream* stream = static_cast
(trans->stream_.get()); ASSERT_TRUE(stream != NULL); ASSERT_TRUE(stream->stream() != NULL); EXPECT_EQ(0, stream->stream()->send_window_size()); EXPECT_FALSE(stream->request_body_stream_->eof()); data->ForceNextRead(); // Read in WINDOW_UPDATE frame. rv = callback.WaitForResult(); helper.VerifyDataConsumed(); SpdySession::set_flow_control(false); } TEST_P(SpdyNetworkTransactionTest, CancelledTransaction) { // Construct the request. scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req), }; scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); MockRead reads[] = { CreateMockRead(*resp), // This following read isn't used by the test, except during the // RunAllPending() call at the end since the SpdySession survives the // HttpNetworkTransaction and still tries to continue Read()'ing. Any // MockRead will do here. MockRead(true, 0, 0) // EOF }; StaticSocketDataProvider data(reads, arraysize(reads), writes, arraysize(writes)); NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(&data); HttpNetworkTransaction* trans = helper.trans(); TestCompletionCallback callback; int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); helper.ResetTrans(); // Cancel the transaction. // Flush the MessageLoop while the SpdySessionDependencies (in particular, the // MockClientSocketFactory) are still alive. MessageLoop::current()->RunAllPending(); helper.VerifyDataNotConsumed(); } // Verify that the client sends a Rst Frame upon cancelling the stream. TEST_P(SpdyNetworkTransactionTest, CancelledTransactionSendRst) { scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); scoped_ptr
rst( ConstructSpdyRstStream(1, spdy::CANCEL)); MockWrite writes[] = { CreateMockWrite(*req, 0, false), CreateMockWrite(*rst, 2, false), }; scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); MockRead reads[] = { CreateMockRead(*resp, 1, true), MockRead(true, 0, 0, 3) // EOF }; scoped_refptr
data( new DeterministicSocketData(reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.SetDeterministic(); helper.RunPreTestSetup(); helper.AddDeterministicData(data.get()); HttpNetworkTransaction* trans = helper.trans(); TestCompletionCallback callback; int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); data->SetStop(2); data->Run(); helper.ResetTrans(); data->SetStop(20); data->Run(); helper.VerifyDataConsumed(); } class SpdyNetworkTransactionTest::StartTransactionCallback : public CallbackRunner< Tuple1
> { public: explicit StartTransactionCallback( const scoped_refptr
& session, NormalSpdyTransactionHelper& helper) : session_(session), helper_(helper) {} // We try to start another transaction, which should succeed. virtual void RunWithParams(const Tuple1
& params) { scoped_ptr
trans( new HttpNetworkTransaction(session_)); TestCompletionCallback callback; HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; int rv = trans->Start(&request, &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); } private: const scoped_refptr
& session_; NormalSpdyTransactionHelper& helper_; }; // Verify that the client can correctly deal with the user callback attempting // to start another transaction on a session that is closing down. See // http://crbug.com/47455 TEST_P(SpdyNetworkTransactionTest, StartTransactionOnReadCallback) { scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; MockWrite writes2[] = { CreateMockWrite(*req) }; // The indicated length of this packet is longer than its actual length. When // the session receives an empty packet after this one, it shuts down the // session, and calls the read callback with the incomplete data. const uint8 kGetBodyFrame2[] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x07, 'h', 'e', 'l', 'l', 'o', '!', }; scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); MockRead reads[] = { CreateMockRead(*resp, 2), MockRead(true, ERR_IO_PENDING, 3), // Force a pause MockRead(true, reinterpret_cast
(kGetBodyFrame2), arraysize(kGetBodyFrame2), 4), MockRead(true, ERR_IO_PENDING, 5), // Force a pause MockRead(true, 0, 0, 6), // EOF }; MockRead reads2[] = { CreateMockRead(*resp, 2), MockRead(true, 0, 0, 3), // EOF }; scoped_refptr
data( new OrderedSocketData(reads, arraysize(reads), writes, arraysize(writes))); scoped_refptr
data2( new DelayedSocketData(1, reads2, arraysize(reads2), writes2, arraysize(writes2))); NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(data.get()); helper.AddData(data2.get()); HttpNetworkTransaction* trans = helper.trans(); // Start the transaction with basic parameters. TestCompletionCallback callback; int rv = trans->Start(&helper.request(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); StartTransactionCallback callback2(helper.session(), helper); const int kSize = 3000; scoped_refptr
buf(new net::IOBuffer(kSize)); rv = trans->Read(buf, kSize, &callback2); // This forces an err_IO_pending, which sets the callback. data->CompleteRead(); // This finishes the read. data->CompleteRead(); helper.VerifyDataConsumed(); } class SpdyNetworkTransactionTest::DeleteSessionCallback : public CallbackRunner< Tuple1
> { public: explicit DeleteSessionCallback(NormalSpdyTransactionHelper& helper) : helper_(helper) {} // We kill the transaction, which deletes the session and stream. virtual void RunWithParams(const Tuple1
& params) { helper_.ResetTrans(); } private: NormalSpdyTransactionHelper& helper_; }; // Verify that the client can correctly deal with the user callback deleting the // transaction. Failures will usually be valgrind errors. See // http://crbug.com/46925 TEST_P(SpdyNetworkTransactionTest, DeleteSessionOnReadCallback) { scoped_ptr
req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); MockWrite writes[] = { CreateMockWrite(*req) }; scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp.get(), 2), MockRead(true, ERR_IO_PENDING, 3), // Force a pause CreateMockRead(*body.get(), 4), MockRead(true, 0, 0, 5), // EOF }; scoped_refptr
data( new OrderedSocketData(reads, arraysize(reads), writes, arraysize(writes))); NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.RunPreTestSetup(); helper.AddData(data.get()); HttpNetworkTransaction* trans = helper.trans(); // Start the transaction with basic parameters. TestCompletionCallback callback; int rv = trans->Start(&helper.request(), &callback, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); // Setup a user callback which will delete the session, and clear out the // memory holding the stream object. Note that the callback deletes trans. DeleteSessionCallback callback2(helper); const int kSize = 3000; scoped_refptr
buf(new net::IOBuffer(kSize)); rv = trans->Read(buf, kSize, &callback2); ASSERT_EQ(ERR_IO_PENDING, rv); data->CompleteRead(); // Finish running rest of tasks. MessageLoop::current()->RunAllPending(); helper.VerifyDataConsumed(); } // Send a spdy request to www.google.com that gets redirected to www.foo.com. TEST_P(SpdyNetworkTransactionTest, RedirectGetRequest) { // These are headers which the net::URLRequest tacks on. const char* const kExtraHeaders[] = { "accept-encoding", "gzip,deflate", }; const SpdyHeaderInfo kSynStartHeader = make_spdy_header(spdy::SYN_STREAM); const char* const kStandardGetHeaders[] = { "host", "www.google.com", "method", "GET", "scheme", "http", "url", "/", "user-agent", "", "version", "HTTP/1.1" }; const char* const kStandardGetHeaders2[] = { "host", "www.foo.com", "method", "GET", "scheme", "http", "url", "/index.php", "user-agent", "", "version", "HTTP/1.1" }; // Setup writes/reads to www.google.com scoped_ptr
req(ConstructSpdyPacket( kSynStartHeader, kExtraHeaders, arraysize(kExtraHeaders) / 2, kStandardGetHeaders, arraysize(kStandardGetHeaders) / 2)); scoped_ptr
req2(ConstructSpdyPacket( kSynStartHeader, kExtraHeaders, arraysize(kExtraHeaders) / 2, kStandardGetHeaders2, arraysize(kStandardGetHeaders2) / 2)); scoped_ptr
resp(ConstructSpdyGetSynReplyRedirect(1)); MockWrite writes[] = { CreateMockWrite(*req, 1), }; MockRead reads[] = { CreateMockRead(*resp, 2), MockRead(true, 0, 0, 3) // EOF }; // Setup writes/reads to www.foo.com scoped_ptr
resp2(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
body2(ConstructSpdyBodyFrame(1, true)); MockWrite writes2[] = { CreateMockWrite(*req2, 1), }; MockRead reads2[] = { CreateMockRead(*resp2, 2), CreateMockRead(*body2, 3), MockRead(true, 0, 0, 4) // EOF }; scoped_refptr
data( new OrderedSocketData(reads, arraysize(reads), writes, arraysize(writes))); scoped_refptr
data2( new OrderedSocketData(reads2, arraysize(reads2), writes2, arraysize(writes2))); // TODO(erikchen): Make test support SPDYSSL, SPDYNPN HttpStreamFactory::set_force_spdy_over_ssl(false); HttpStreamFactory::set_force_spdy_always(true); TestDelegate d; { net::URLRequest r(GURL("http://www.google.com/"), &d); SpdyURLRequestContext* spdy_url_request_context = new SpdyURLRequestContext(); r.set_context(spdy_url_request_context); spdy_url_request_context->socket_factory(). AddSocketDataProvider(data.get()); spdy_url_request_context->socket_factory(). AddSocketDataProvider(data2.get()); d.set_quit_on_redirect(true); r.Start(); MessageLoop::current()->Run(); EXPECT_EQ(1, d.received_redirect_count()); r.FollowDeferredRedirect(); MessageLoop::current()->Run(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(net::URLRequestStatus::SUCCESS, r.status().status()); std::string contents("hello!"); EXPECT_EQ(contents, d.data_received()); } EXPECT_TRUE(data->at_read_eof()); EXPECT_TRUE(data->at_write_eof()); EXPECT_TRUE(data2->at_read_eof()); EXPECT_TRUE(data2->at_write_eof()); } // Send a spdy request to www.google.com. Get a pushed stream that redirects to // www.foo.com. TEST_P(SpdyNetworkTransactionTest, RedirectServerPush) { // These are headers which the net::URLRequest tacks on. const char* const kExtraHeaders[] = { "accept-encoding", "gzip,deflate", }; const SpdyHeaderInfo kSynStartHeader = make_spdy_header(spdy::SYN_STREAM); const char* const kStandardGetHeaders[] = { "host", "www.google.com", "method", "GET", "scheme", "http", "url", "/", "user-agent", "", "version", "HTTP/1.1" }; // Setup writes/reads to www.google.com scoped_ptr
req( ConstructSpdyPacket(kSynStartHeader, kExtraHeaders, arraysize(kExtraHeaders) / 2, kStandardGetHeaders, arraysize(kStandardGetHeaders) / 2)); scoped_ptr
resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr
rep( ConstructSpdyPush(NULL, 0, 2, 1, "http://www.google.com/foo.dat", "301 Moved Permanently", "http://www.foo.com/index.php")); scoped_ptr
body(ConstructSpdyBodyFrame(1, true)); scoped_ptr
rst(ConstructSpdyRstStream(2, spdy::CANCEL)); MockWrite writes[] = { CreateMockWrite(*req, 1), CreateMockWrite(*rst, 6), }; MockRead reads[] = { CreateMockRead(*resp, 2), CreateMockRead(*rep, 3), CreateMockRead(*body, 4), MockRead(true, ERR_IO_PENDING, 5), // Force a pause MockRead(true, 0, 0, 7) // EOF }; // Setup writes/reads to www.foo.com const char* const kStandardGetHeaders2[] = { "host", "www.foo.com", "method", "GET", "scheme", "http", "url", "/index.php", "user-agent", "", "version", "HTTP/1.1" }; scoped_ptr