| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <cmath> | |
| 6 #include <memory> | |
| 7 #include <utility> | |
| 8 #include <vector> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/bind_helpers.h" | |
| 12 #include "base/files/file_util.h" | |
| 13 #include "base/files/scoped_temp_dir.h" | |
| 14 #include "base/memory/ptr_util.h" | |
| 15 #include "base/run_loop.h" | |
| 16 #include "base/test/test_file_util.h" | |
| 17 #include "base/threading/thread_task_runner_handle.h" | |
| 18 #include "net/base/auth.h" | |
| 19 #include "net/base/chunked_upload_data_stream.h" | |
| 20 #include "net/base/elements_upload_data_stream.h" | |
| 21 #include "net/base/proxy_delegate.h" | |
| 22 #include "net/base/request_priority.h" | |
| 23 #include "net/base/test_proxy_delegate.h" | |
| 24 #include "net/base/upload_bytes_element_reader.h" | |
| 25 #include "net/base/upload_file_element_reader.h" | |
| 26 #include "net/http/http_auth_scheme.h" | |
| 27 #include "net/http/http_network_session.h" | |
| 28 #include "net/http/http_network_session_peer.h" | |
| 29 #include "net/http/http_network_transaction.h" | |
| 30 #include "net/http/http_server_properties.h" | |
| 31 #include "net/http/http_transaction_test_util.h" | |
| 32 #include "net/log/net_log_event_type.h" | |
| 33 #include "net/log/net_log_with_source.h" | |
| 34 #include "net/log/test_net_log.h" | |
| 35 #include "net/log/test_net_log_entry.h" | |
| 36 #include "net/log/test_net_log_util.h" | |
| 37 #include "net/proxy/proxy_server.h" | |
| 38 #include "net/socket/client_socket_pool_base.h" | |
| 39 #include "net/socket/next_proto.h" | |
| 40 #include "net/spdy/buffered_spdy_framer.h" | |
| 41 #include "net/spdy/platform/api/spdy_string.h" | |
| 42 #include "net/spdy/platform/api/spdy_string_piece.h" | |
| 43 #include "net/spdy/spdy_http_stream.h" | |
| 44 #include "net/spdy/spdy_http_utils.h" | |
| 45 #include "net/spdy/spdy_protocol.h" | |
| 46 #include "net/spdy/spdy_session.h" | |
| 47 #include "net/spdy/spdy_session_pool.h" | |
| 48 #include "net/spdy/spdy_test_util_common.h" | |
| 49 #include "net/spdy/spdy_test_utils.h" | |
| 50 #include "net/ssl/ssl_connection_status_flags.h" | |
| 51 #include "net/test/cert_test_util.h" | |
| 52 #include "net/test/gtest_util.h" | |
| 53 #include "net/test/test_data_directory.h" | |
| 54 #include "net/url_request/url_request_test_util.h" | |
| 55 #include "testing/gmock/include/gmock/gmock.h" | |
| 56 #include "testing/platform_test.h" | |
| 57 | |
| 58 using net::test::IsError; | |
| 59 using net::test::IsOk; | |
| 60 | |
| 61 //----------------------------------------------------------------------------- | |
| 62 | |
| 63 namespace net { | |
| 64 | |
| 65 namespace { | |
| 66 | |
| 67 using testing::Each; | |
| 68 using testing::Eq; | |
| 69 | |
| 70 const int32_t kBufferSize = SpdyHttpStream::kRequestBodyBufferSize; | |
| 71 | |
| 72 } // namespace | |
| 73 | |
| 74 class SpdyNetworkTransactionTest : public ::testing::Test { | |
| 75 protected: | |
| 76 SpdyNetworkTransactionTest() | |
| 77 : default_url_(kDefaultUrl), | |
| 78 host_port_pair_(HostPortPair::FromURL(default_url_)) {} | |
| 79 | |
| 80 ~SpdyNetworkTransactionTest() override { | |
| 81 // UploadDataStream may post a deletion tasks back to the message loop on | |
| 82 // destruction. | |
| 83 upload_data_stream_.reset(); | |
| 84 base::RunLoop().RunUntilIdle(); | |
| 85 } | |
| 86 | |
| 87 void SetUp() override { | |
| 88 get_request_initialized_ = false; | |
| 89 post_request_initialized_ = false; | |
| 90 chunked_post_request_initialized_ = false; | |
| 91 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | |
| 92 } | |
| 93 | |
| 94 struct TransactionHelperResult { | |
| 95 int rv; | |
| 96 SpdyString status_line; | |
| 97 SpdyString response_data; | |
| 98 HttpResponseInfo response_info; | |
| 99 }; | |
| 100 | |
| 101 // A helper class that handles all the initial npn/ssl setup. | |
| 102 class NormalSpdyTransactionHelper { | |
| 103 public: | |
| 104 NormalSpdyTransactionHelper( | |
| 105 const HttpRequestInfo& request, | |
| 106 RequestPriority priority, | |
| 107 const NetLogWithSource& log, | |
| 108 std::unique_ptr<SpdySessionDependencies> session_deps) | |
| 109 : request_(request), | |
| 110 priority_(priority), | |
| 111 session_deps_(session_deps.get() == nullptr | |
| 112 ? base::MakeUnique<SpdySessionDependencies>() | |
| 113 : std::move(session_deps)), | |
| 114 log_(log) { | |
| 115 session_deps_->net_log = log.net_log(); | |
| 116 session_ = | |
| 117 SpdySessionDependencies::SpdyCreateSession(session_deps_.get()); | |
| 118 } | |
| 119 | |
| 120 ~NormalSpdyTransactionHelper() { | |
| 121 // Any test which doesn't close the socket by sending it an EOF will | |
| 122 // have a valid session left open, which leaks the entire session pool. | |
| 123 // This is just fine - in fact, some of our tests intentionally do this | |
| 124 // so that we can check consistency of the SpdySessionPool as the test | |
| 125 // finishes. If we had put an EOF on the socket, the SpdySession would | |
| 126 // have closed and we wouldn't be able to check the consistency. | |
| 127 | |
| 128 // Forcefully close existing sessions here. | |
| 129 session()->spdy_session_pool()->CloseAllSessions(); | |
| 130 } | |
| 131 | |
| 132 void RunPreTestSetup() { | |
| 133 // We're now ready to use SSL-npn SPDY. | |
| 134 trans_.reset(new HttpNetworkTransaction(priority_, session_.get())); | |
| 135 } | |
| 136 | |
| 137 // Start the transaction, read some data, finish. | |
| 138 void RunDefaultTest() { | |
| 139 if (!StartDefaultTest()) | |
| 140 return; | |
| 141 FinishDefaultTest(); | |
| 142 } | |
| 143 | |
| 144 bool StartDefaultTest() { | |
| 145 output_.rv = trans_->Start(&request_, callback_.callback(), log_); | |
| 146 | |
| 147 // We expect an IO Pending or some sort of error. | |
| 148 EXPECT_LT(output_.rv, 0); | |
| 149 return output_.rv == ERR_IO_PENDING; | |
| 150 } | |
| 151 | |
| 152 void FinishDefaultTest() { | |
| 153 output_.rv = callback_.WaitForResult(); | |
| 154 // Finish async network reads/writes. | |
| 155 base::RunLoop().RunUntilIdle(); | |
| 156 if (output_.rv != OK) { | |
| 157 session_->spdy_session_pool()->CloseCurrentSessions(ERR_ABORTED); | |
| 158 return; | |
| 159 } | |
| 160 | |
| 161 // Verify responses. | |
| 162 const HttpResponseInfo* response = trans_->GetResponseInfo(); | |
| 163 ASSERT_TRUE(response); | |
| 164 ASSERT_TRUE(response->headers); | |
| 165 EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2, | |
| 166 response->connection_info); | |
| 167 EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); | |
| 168 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 169 EXPECT_TRUE(response->was_alpn_negotiated); | |
| 170 EXPECT_EQ("127.0.0.1", response->socket_address.host()); | |
| 171 EXPECT_EQ(443, response->socket_address.port()); | |
| 172 output_.status_line = response->headers->GetStatusLine(); | |
| 173 output_.response_info = *response; // Make a copy so we can verify. | |
| 174 output_.rv = ReadTransaction(trans_.get(), &output_.response_data); | |
| 175 } | |
| 176 | |
| 177 void FinishDefaultTestWithoutVerification() { | |
| 178 output_.rv = callback_.WaitForResult(); | |
| 179 // Finish async network reads/writes. | |
| 180 base::RunLoop().RunUntilIdle(); | |
| 181 if (output_.rv != OK) | |
| 182 session_->spdy_session_pool()->CloseCurrentSessions(ERR_ABORTED); | |
| 183 } | |
| 184 | |
| 185 void WaitForCallbackToComplete() { output_.rv = callback_.WaitForResult(); } | |
| 186 | |
| 187 // Most tests will want to call this function. In particular, the MockReads | |
| 188 // should end with an empty read, and that read needs to be processed to | |
| 189 // ensure proper deletion of the spdy_session_pool. | |
| 190 void VerifyDataConsumed() { | |
| 191 for (const SocketDataProvider* provider : data_vector_) { | |
| 192 EXPECT_TRUE(provider->AllReadDataConsumed()); | |
| 193 EXPECT_TRUE(provider->AllWriteDataConsumed()); | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 // Occasionally a test will expect to error out before certain reads are | |
| 198 // processed. In that case we want to explicitly ensure that the reads were | |
| 199 // not processed. | |
| 200 void VerifyDataNotConsumed() { | |
| 201 for (const SocketDataProvider* provider : data_vector_) { | |
| 202 EXPECT_FALSE(provider->AllReadDataConsumed()); | |
| 203 EXPECT_FALSE(provider->AllWriteDataConsumed()); | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 void RunToCompletion(SocketDataProvider* data) { | |
| 208 RunPreTestSetup(); | |
| 209 AddData(data); | |
| 210 RunDefaultTest(); | |
| 211 VerifyDataConsumed(); | |
| 212 } | |
| 213 | |
| 214 void RunToCompletionWithSSLData( | |
| 215 SocketDataProvider* data, | |
| 216 std::unique_ptr<SSLSocketDataProvider> ssl_provider) { | |
| 217 RunPreTestSetup(); | |
| 218 AddDataWithSSLSocketDataProvider(data, std::move(ssl_provider)); | |
| 219 RunDefaultTest(); | |
| 220 VerifyDataConsumed(); | |
| 221 } | |
| 222 | |
| 223 void AddData(SocketDataProvider* data) { | |
| 224 std::unique_ptr<SSLSocketDataProvider> ssl_provider( | |
| 225 new SSLSocketDataProvider(ASYNC, OK)); | |
| 226 ssl_provider->cert = | |
| 227 ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); | |
| 228 AddDataWithSSLSocketDataProvider(data, std::move(ssl_provider)); | |
| 229 } | |
| 230 | |
| 231 void AddDataWithSSLSocketDataProvider( | |
| 232 SocketDataProvider* data, | |
| 233 std::unique_ptr<SSLSocketDataProvider> ssl_provider) { | |
| 234 data_vector_.push_back(data); | |
| 235 if (ssl_provider->next_proto == kProtoUnknown) | |
| 236 ssl_provider->next_proto = kProtoHTTP2; | |
| 237 | |
| 238 session_deps_->socket_factory->AddSSLSocketDataProvider( | |
| 239 ssl_provider.get()); | |
| 240 ssl_vector_.push_back(std::move(ssl_provider)); | |
| 241 | |
| 242 session_deps_->socket_factory->AddSocketDataProvider(data); | |
| 243 } | |
| 244 | |
| 245 HttpNetworkTransaction* trans() { return trans_.get(); } | |
| 246 void ResetTrans() { trans_.reset(); } | |
| 247 const TransactionHelperResult& output() { return output_; } | |
| 248 const HttpRequestInfo& request() const { return request_; } | |
| 249 HttpNetworkSession* session() const { return session_.get(); } | |
| 250 SpdySessionDependencies* session_deps() { return session_deps_.get(); } | |
| 251 | |
| 252 private: | |
| 253 typedef std::vector<SocketDataProvider*> DataVector; | |
| 254 typedef std::vector<std::unique_ptr<SSLSocketDataProvider>> SSLVector; | |
| 255 typedef std::vector<std::unique_ptr<SocketDataProvider>> AlternateVector; | |
| 256 HttpRequestInfo request_; | |
| 257 RequestPriority priority_; | |
| 258 std::unique_ptr<SpdySessionDependencies> session_deps_; | |
| 259 std::unique_ptr<HttpNetworkSession> session_; | |
| 260 TransactionHelperResult output_; | |
| 261 SSLVector ssl_vector_; | |
| 262 TestCompletionCallback callback_; | |
| 263 std::unique_ptr<HttpNetworkTransaction> trans_; | |
| 264 DataVector data_vector_; | |
| 265 const NetLogWithSource log_; | |
| 266 }; | |
| 267 | |
| 268 void ConnectStatusHelperWithExpectedStatus(const MockRead& status, | |
| 269 int expected_status); | |
| 270 | |
| 271 void ConnectStatusHelper(const MockRead& status); | |
| 272 | |
| 273 const HttpRequestInfo& CreateGetPushRequest() { | |
| 274 get_push_request_.method = "GET"; | |
| 275 get_push_request_.url = GURL(GetDefaultUrlWithPath("/foo.dat")); | |
| 276 get_push_request_.load_flags = 0; | |
| 277 return get_push_request_; | |
| 278 } | |
| 279 | |
| 280 const HttpRequestInfo& CreateGetRequest() { | |
| 281 if (!get_request_initialized_) { | |
| 282 get_request_.method = "GET"; | |
| 283 get_request_.url = default_url_; | |
| 284 get_request_.load_flags = 0; | |
| 285 get_request_initialized_ = true; | |
| 286 } | |
| 287 return get_request_; | |
| 288 } | |
| 289 | |
| 290 const HttpRequestInfo& CreateGetRequestWithUserAgent() { | |
| 291 if (!get_request_initialized_) { | |
| 292 get_request_.method = "GET"; | |
| 293 get_request_.url = default_url_; | |
| 294 get_request_.load_flags = 0; | |
| 295 get_request_.extra_headers.SetHeader("User-Agent", "Chrome"); | |
| 296 get_request_initialized_ = true; | |
| 297 } | |
| 298 return get_request_; | |
| 299 } | |
| 300 | |
| 301 const HttpRequestInfo& CreatePostRequest() { | |
| 302 if (!post_request_initialized_) { | |
| 303 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
| 304 element_readers.push_back(base::MakeUnique<UploadBytesElementReader>( | |
| 305 kUploadData, kUploadDataSize)); | |
| 306 upload_data_stream_.reset( | |
| 307 new ElementsUploadDataStream(std::move(element_readers), 0)); | |
| 308 | |
| 309 post_request_.method = "POST"; | |
| 310 post_request_.url = default_url_; | |
| 311 post_request_.upload_data_stream = upload_data_stream_.get(); | |
| 312 post_request_initialized_ = true; | |
| 313 } | |
| 314 return post_request_; | |
| 315 } | |
| 316 | |
| 317 const HttpRequestInfo& CreateFilePostRequest() { | |
| 318 if (!post_request_initialized_) { | |
| 319 base::FilePath file_path; | |
| 320 CHECK(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &file_path)); | |
| 321 CHECK_EQ(static_cast<int>(kUploadDataSize), | |
| 322 base::WriteFile(file_path, kUploadData, kUploadDataSize)); | |
| 323 | |
| 324 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
| 325 element_readers.push_back(base::MakeUnique<UploadFileElementReader>( | |
| 326 base::ThreadTaskRunnerHandle::Get().get(), file_path, 0, | |
| 327 kUploadDataSize, base::Time())); | |
| 328 upload_data_stream_.reset( | |
| 329 new ElementsUploadDataStream(std::move(element_readers), 0)); | |
| 330 | |
| 331 post_request_.method = "POST"; | |
| 332 post_request_.url = default_url_; | |
| 333 post_request_.upload_data_stream = upload_data_stream_.get(); | |
| 334 post_request_initialized_ = true; | |
| 335 } | |
| 336 return post_request_; | |
| 337 } | |
| 338 | |
| 339 const HttpRequestInfo& CreateUnreadableFilePostRequest() { | |
| 340 if (post_request_initialized_) | |
| 341 return post_request_; | |
| 342 | |
| 343 base::FilePath file_path; | |
| 344 CHECK(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &file_path)); | |
| 345 CHECK_EQ(static_cast<int>(kUploadDataSize), | |
| 346 base::WriteFile(file_path, kUploadData, kUploadDataSize)); | |
| 347 CHECK(base::MakeFileUnreadable(file_path)); | |
| 348 | |
| 349 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
| 350 element_readers.push_back(base::MakeUnique<UploadFileElementReader>( | |
| 351 base::ThreadTaskRunnerHandle::Get().get(), file_path, 0, | |
| 352 kUploadDataSize, base::Time())); | |
| 353 upload_data_stream_.reset( | |
| 354 new ElementsUploadDataStream(std::move(element_readers), 0)); | |
| 355 | |
| 356 post_request_.method = "POST"; | |
| 357 post_request_.url = default_url_; | |
| 358 post_request_.upload_data_stream = upload_data_stream_.get(); | |
| 359 post_request_initialized_ = true; | |
| 360 return post_request_; | |
| 361 } | |
| 362 | |
| 363 const HttpRequestInfo& CreateComplexPostRequest() { | |
| 364 if (!post_request_initialized_) { | |
| 365 const int kFileRangeOffset = 1; | |
| 366 const int kFileRangeLength = 3; | |
| 367 CHECK_LT(kFileRangeOffset + kFileRangeLength, kUploadDataSize); | |
| 368 | |
| 369 base::FilePath file_path; | |
| 370 CHECK(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &file_path)); | |
| 371 CHECK_EQ(static_cast<int>(kUploadDataSize), | |
| 372 base::WriteFile(file_path, kUploadData, kUploadDataSize)); | |
| 373 | |
| 374 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
| 375 element_readers.push_back(base::MakeUnique<UploadBytesElementReader>( | |
| 376 kUploadData, kFileRangeOffset)); | |
| 377 element_readers.push_back(base::MakeUnique<UploadFileElementReader>( | |
| 378 base::ThreadTaskRunnerHandle::Get().get(), file_path, | |
| 379 kFileRangeOffset, kFileRangeLength, base::Time())); | |
| 380 element_readers.push_back(base::MakeUnique<UploadBytesElementReader>( | |
| 381 kUploadData + kFileRangeOffset + kFileRangeLength, | |
| 382 kUploadDataSize - (kFileRangeOffset + kFileRangeLength))); | |
| 383 upload_data_stream_.reset( | |
| 384 new ElementsUploadDataStream(std::move(element_readers), 0)); | |
| 385 | |
| 386 post_request_.method = "POST"; | |
| 387 post_request_.url = default_url_; | |
| 388 post_request_.upload_data_stream = upload_data_stream_.get(); | |
| 389 post_request_initialized_ = true; | |
| 390 } | |
| 391 return post_request_; | |
| 392 } | |
| 393 | |
| 394 const HttpRequestInfo& CreateChunkedPostRequest() { | |
| 395 if (!chunked_post_request_initialized_) { | |
| 396 upload_chunked_data_stream_.reset(new ChunkedUploadDataStream(0)); | |
| 397 chunked_post_request_.method = "POST"; | |
| 398 chunked_post_request_.url = default_url_; | |
| 399 chunked_post_request_.upload_data_stream = | |
| 400 upload_chunked_data_stream_.get(); | |
| 401 chunked_post_request_initialized_ = true; | |
| 402 } | |
| 403 return chunked_post_request_; | |
| 404 } | |
| 405 | |
| 406 // Read the result of a particular transaction, knowing that we've got | |
| 407 // multiple transactions in the read pipeline; so as we read, we may have | |
| 408 // to skip over data destined for other transactions while we consume | |
| 409 // the data for |trans|. | |
| 410 int ReadResult(HttpNetworkTransaction* trans, SpdyString* result) { | |
| 411 const int kSize = 3000; | |
| 412 | |
| 413 int bytes_read = 0; | |
| 414 scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(kSize)); | |
| 415 TestCompletionCallback callback; | |
| 416 while (true) { | |
| 417 int rv = trans->Read(buf.get(), kSize, callback.callback()); | |
| 418 if (rv == ERR_IO_PENDING) { | |
| 419 rv = callback.WaitForResult(); | |
| 420 } else if (rv <= 0) { | |
| 421 break; | |
| 422 } | |
| 423 result->append(buf->data(), rv); | |
| 424 bytes_read += rv; | |
| 425 } | |
| 426 return bytes_read; | |
| 427 } | |
| 428 | |
| 429 void VerifyStreamsClosed(const NormalSpdyTransactionHelper& helper) { | |
| 430 // This lengthy block is reaching into the pool to dig out the active | |
| 431 // session. Once we have the session, we verify that the streams are | |
| 432 // all closed and not leaked at this point. | |
| 433 const GURL& url = helper.request().url; | |
| 434 SpdySessionKey key(HostPortPair::FromURL(url), ProxyServer::Direct(), | |
| 435 PRIVACY_MODE_DISABLED); | |
| 436 NetLogWithSource log; | |
| 437 HttpNetworkSession* session = helper.session(); | |
| 438 base::WeakPtr<SpdySession> spdy_session = | |
| 439 session->spdy_session_pool()->FindAvailableSession( | |
| 440 key, url, | |
| 441 /* enable_ip_based_pooling = */ true, log); | |
| 442 ASSERT_TRUE(spdy_session); | |
| 443 EXPECT_EQ(0u, spdy_session->num_active_streams()); | |
| 444 EXPECT_EQ(0u, spdy_session->num_unclaimed_pushed_streams()); | |
| 445 } | |
| 446 | |
| 447 void RunServerPushTest(SequencedSocketData* data, | |
| 448 HttpResponseInfo* response, | |
| 449 HttpResponseInfo* push_response, | |
| 450 const SpdyString& expected) { | |
| 451 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 452 NetLogWithSource(), nullptr); | |
| 453 helper.RunPreTestSetup(); | |
| 454 helper.AddData(data); | |
| 455 | |
| 456 HttpNetworkTransaction* trans = helper.trans(); | |
| 457 | |
| 458 // Start the transaction with basic parameters. | |
| 459 TestCompletionCallback callback; | |
| 460 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 461 NetLogWithSource()); | |
| 462 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 463 rv = callback.WaitForResult(); | |
| 464 | |
| 465 // Finish async network reads/writes. | |
| 466 base::RunLoop().RunUntilIdle(); | |
| 467 | |
| 468 // Request the pushed path. | |
| 469 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
| 470 rv = trans2.Start(&CreateGetPushRequest(), callback.callback(), | |
| 471 NetLogWithSource()); | |
| 472 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 473 base::RunLoop().RunUntilIdle(); | |
| 474 | |
| 475 // The data for the pushed path may be coming in more than 1 frame. Compile | |
| 476 // the results into a single string. | |
| 477 | |
| 478 // Read the server push body. | |
| 479 SpdyString result2; | |
| 480 ReadResult(&trans2, &result2); | |
| 481 // Read the response body. | |
| 482 SpdyString result; | |
| 483 ReadResult(trans, &result); | |
| 484 | |
| 485 // Verify that we consumed all test data. | |
| 486 EXPECT_TRUE(data->AllReadDataConsumed()); | |
| 487 EXPECT_TRUE(data->AllWriteDataConsumed()); | |
| 488 | |
| 489 LoadTimingInfo load_timing_info; | |
| 490 EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); | |
| 491 EXPECT_TRUE(load_timing_info.push_start.is_null()); | |
| 492 EXPECT_TRUE(load_timing_info.push_end.is_null()); | |
| 493 | |
| 494 LoadTimingInfo load_timing_info2; | |
| 495 EXPECT_TRUE(trans2.GetLoadTimingInfo(&load_timing_info2)); | |
| 496 EXPECT_FALSE(load_timing_info2.push_start.is_null()); | |
| 497 EXPECT_FALSE(load_timing_info2.push_end.is_null()); | |
| 498 | |
| 499 // Verify that the received push data is same as the expected push data. | |
| 500 EXPECT_EQ(result2.compare(expected), 0) << "Received data: " | |
| 501 << result2 | |
| 502 << "||||| Expected data: " | |
| 503 << expected; | |
| 504 | |
| 505 // Verify the response HEADERS. | |
| 506 // Copy the response info, because trans goes away. | |
| 507 *response = *trans->GetResponseInfo(); | |
| 508 *push_response = *trans2.GetResponseInfo(); | |
| 509 | |
| 510 VerifyStreamsClosed(helper); | |
| 511 } | |
| 512 | |
| 513 void RunBrokenPushTest(SequencedSocketData* data, int expected_rv) { | |
| 514 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 515 NetLogWithSource(), nullptr); | |
| 516 helper.RunPreTestSetup(); | |
| 517 helper.AddData(data); | |
| 518 | |
| 519 HttpNetworkTransaction* trans = helper.trans(); | |
| 520 | |
| 521 // Start the transaction with basic parameters. | |
| 522 TestCompletionCallback callback; | |
| 523 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 524 NetLogWithSource()); | |
| 525 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 526 rv = callback.WaitForResult(); | |
| 527 EXPECT_EQ(expected_rv, rv); | |
| 528 | |
| 529 // Finish async network reads/writes. | |
| 530 base::RunLoop().RunUntilIdle(); | |
| 531 | |
| 532 // Verify that we consumed all test data. | |
| 533 EXPECT_TRUE(data->AllReadDataConsumed()); | |
| 534 EXPECT_TRUE(data->AllWriteDataConsumed()); | |
| 535 | |
| 536 if (expected_rv == OK) { | |
| 537 // Expected main request to succeed, even if push failed. | |
| 538 HttpResponseInfo response = *trans->GetResponseInfo(); | |
| 539 EXPECT_TRUE(response.headers); | |
| 540 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 541 } | |
| 542 } | |
| 543 | |
| 544 static void DeleteSessionCallback(NormalSpdyTransactionHelper* helper, | |
| 545 int result) { | |
| 546 helper->ResetTrans(); | |
| 547 } | |
| 548 | |
| 549 static void StartTransactionCallback(HttpNetworkSession* session, | |
| 550 GURL url, | |
| 551 int result) { | |
| 552 HttpRequestInfo request; | |
| 553 HttpNetworkTransaction trans(DEFAULT_PRIORITY, session); | |
| 554 TestCompletionCallback callback; | |
| 555 request.method = "GET"; | |
| 556 request.url = url; | |
| 557 request.load_flags = 0; | |
| 558 int rv = trans.Start(&request, callback.callback(), NetLogWithSource()); | |
| 559 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 560 callback.WaitForResult(); | |
| 561 } | |
| 562 | |
| 563 ChunkedUploadDataStream* upload_chunked_data_stream() const { | |
| 564 return upload_chunked_data_stream_.get(); | |
| 565 } | |
| 566 | |
| 567 SpdyString GetDefaultUrlWithPath(const char* path) { | |
| 568 return SpdyString(kDefaultUrl) + path; | |
| 569 } | |
| 570 | |
| 571 const GURL default_url_; | |
| 572 const HostPortPair host_port_pair_; | |
| 573 SpdyTestUtil spdy_util_; | |
| 574 | |
| 575 private: | |
| 576 std::unique_ptr<ChunkedUploadDataStream> upload_chunked_data_stream_; | |
| 577 std::unique_ptr<UploadDataStream> upload_data_stream_; | |
| 578 bool get_request_initialized_; | |
| 579 bool post_request_initialized_; | |
| 580 bool chunked_post_request_initialized_; | |
| 581 HttpRequestInfo get_request_; | |
| 582 HttpRequestInfo post_request_; | |
| 583 HttpRequestInfo chunked_post_request_; | |
| 584 HttpRequestInfo get_push_request_; | |
| 585 base::ScopedTempDir temp_dir_; | |
| 586 }; | |
| 587 | |
| 588 // Verify HttpNetworkTransaction constructor. | |
| 589 TEST_F(SpdyNetworkTransactionTest, Constructor) { | |
| 590 auto session_deps = base::MakeUnique<SpdySessionDependencies>(); | |
| 591 std::unique_ptr<HttpNetworkSession> session( | |
| 592 SpdySessionDependencies::SpdyCreateSession(session_deps.get())); | |
| 593 auto trans = | |
| 594 base::MakeUnique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get()); | |
| 595 } | |
| 596 | |
| 597 TEST_F(SpdyNetworkTransactionTest, Get) { | |
| 598 // Construct the request. | |
| 599 SpdySerializedFrame req( | |
| 600 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 601 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 602 | |
| 603 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 604 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 605 MockRead reads[] = { | |
| 606 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 607 MockRead(ASYNC, 0, 3) // EOF | |
| 608 }; | |
| 609 | |
| 610 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 611 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 612 NetLogWithSource(), nullptr); | |
| 613 helper.RunToCompletion(&data); | |
| 614 TransactionHelperResult out = helper.output(); | |
| 615 EXPECT_THAT(out.rv, IsOk()); | |
| 616 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 617 EXPECT_EQ("hello!", out.response_data); | |
| 618 } | |
| 619 | |
| 620 TEST_F(SpdyNetworkTransactionTest, GetAtEachPriority) { | |
| 621 for (RequestPriority p = MINIMUM_PRIORITY; p <= MAXIMUM_PRIORITY; | |
| 622 p = RequestPriority(p + 1)) { | |
| 623 SpdyTestUtil spdy_test_util; | |
| 624 | |
| 625 // Construct the request. | |
| 626 SpdySerializedFrame req( | |
| 627 spdy_test_util.ConstructSpdyGet(nullptr, 0, 1, p, true)); | |
| 628 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 629 | |
| 630 SpdyPriority spdy_prio = 0; | |
| 631 EXPECT_TRUE(GetSpdyPriority(req, &spdy_prio)); | |
| 632 // this repeats the RequestPriority-->SpdyPriority mapping from | |
| 633 // SpdyFramer::ConvertRequestPriorityToSpdyPriority to make | |
| 634 // sure it's being done right. | |
| 635 switch (p) { | |
| 636 case HIGHEST: | |
| 637 EXPECT_EQ(0, spdy_prio); | |
| 638 break; | |
| 639 case MEDIUM: | |
| 640 EXPECT_EQ(1, spdy_prio); | |
| 641 break; | |
| 642 case LOW: | |
| 643 EXPECT_EQ(2, spdy_prio); | |
| 644 break; | |
| 645 case LOWEST: | |
| 646 EXPECT_EQ(3, spdy_prio); | |
| 647 break; | |
| 648 case IDLE: | |
| 649 EXPECT_EQ(4, spdy_prio); | |
| 650 break; | |
| 651 case THROTTLED: | |
| 652 EXPECT_EQ(5, spdy_prio); | |
| 653 break; | |
| 654 default: | |
| 655 FAIL(); | |
| 656 } | |
| 657 | |
| 658 SpdySerializedFrame resp( | |
| 659 spdy_test_util.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 660 SpdySerializedFrame body(spdy_test_util.ConstructSpdyDataFrame(1, true)); | |
| 661 MockRead reads[] = { | |
| 662 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 663 MockRead(ASYNC, 0, 3) // EOF | |
| 664 }; | |
| 665 | |
| 666 SequencedSocketData data(reads, arraysize(reads), writes, | |
| 667 arraysize(writes)); | |
| 668 HttpRequestInfo http_req = CreateGetRequest(); | |
| 669 | |
| 670 NormalSpdyTransactionHelper helper(http_req, p, NetLogWithSource(), | |
| 671 nullptr); | |
| 672 helper.RunToCompletion(&data); | |
| 673 TransactionHelperResult out = helper.output(); | |
| 674 EXPECT_THAT(out.rv, IsOk()); | |
| 675 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 676 EXPECT_EQ("hello!", out.response_data); | |
| 677 } | |
| 678 } | |
| 679 | |
| 680 // Start three gets simultaniously; making sure that multiplexed | |
| 681 // streams work properly. | |
| 682 | |
| 683 // This can't use the TransactionHelper method, since it only | |
| 684 // handles a single transaction, and finishes them as soon | |
| 685 // as it launches them. | |
| 686 | |
| 687 // TODO(gavinp): create a working generalized TransactionHelper that | |
| 688 // can allow multiple streams in flight. | |
| 689 | |
| 690 TEST_F(SpdyNetworkTransactionTest, ThreeGets) { | |
| 691 SpdySerializedFrame req( | |
| 692 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 693 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 694 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 695 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 696 | |
| 697 SpdySerializedFrame req2( | |
| 698 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
| 699 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 700 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
| 701 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
| 702 | |
| 703 SpdySerializedFrame req3( | |
| 704 spdy_util_.ConstructSpdyGet(nullptr, 0, 5, LOWEST, true)); | |
| 705 SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5)); | |
| 706 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, false)); | |
| 707 SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(5, true)); | |
| 708 | |
| 709 MockWrite writes[] = { | |
| 710 CreateMockWrite(req, 0), CreateMockWrite(req2, 3), | |
| 711 CreateMockWrite(req3, 6), | |
| 712 }; | |
| 713 MockRead reads[] = { | |
| 714 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 715 CreateMockRead(resp2, 4), CreateMockRead(body2, 5), | |
| 716 CreateMockRead(resp3, 7), CreateMockRead(body3, 8), | |
| 717 | |
| 718 CreateMockRead(fbody, 9), CreateMockRead(fbody2, 10), | |
| 719 CreateMockRead(fbody3, 11), | |
| 720 | |
| 721 MockRead(ASYNC, 0, 12), // EOF | |
| 722 }; | |
| 723 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 724 SequencedSocketData data_placeholder1(nullptr, 0, nullptr, 0); | |
| 725 SequencedSocketData data_placeholder2(nullptr, 0, nullptr, 0); | |
| 726 | |
| 727 NetLogWithSource log; | |
| 728 TransactionHelperResult out; | |
| 729 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 730 NetLogWithSource(), nullptr); | |
| 731 helper.RunPreTestSetup(); | |
| 732 helper.AddData(&data); | |
| 733 // We require placeholder data because three get requests are sent out at | |
| 734 // the same time which results in three sockets being connected. The first | |
| 735 // on will negotiate SPDY and will be used for all requests. | |
| 736 helper.AddData(&data_placeholder1); | |
| 737 helper.AddData(&data_placeholder2); | |
| 738 TestCompletionCallback callback1; | |
| 739 TestCompletionCallback callback2; | |
| 740 TestCompletionCallback callback3; | |
| 741 | |
| 742 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
| 743 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
| 744 HttpRequestInfo httpreq3 = CreateGetRequest(); | |
| 745 | |
| 746 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
| 747 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
| 748 HttpNetworkTransaction trans3(DEFAULT_PRIORITY, helper.session()); | |
| 749 | |
| 750 out.rv = trans1.Start(&httpreq1, callback1.callback(), log); | |
| 751 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
| 752 out.rv = trans2.Start(&httpreq2, callback2.callback(), log); | |
| 753 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
| 754 out.rv = trans3.Start(&httpreq3, callback3.callback(), log); | |
| 755 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
| 756 | |
| 757 out.rv = callback1.WaitForResult(); | |
| 758 ASSERT_THAT(out.rv, IsOk()); | |
| 759 out.rv = callback3.WaitForResult(); | |
| 760 ASSERT_THAT(out.rv, IsOk()); | |
| 761 | |
| 762 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
| 763 EXPECT_TRUE(response1->headers); | |
| 764 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
| 765 out.status_line = response1->headers->GetStatusLine(); | |
| 766 out.response_info = *response1; | |
| 767 | |
| 768 trans2.GetResponseInfo(); | |
| 769 | |
| 770 out.rv = ReadTransaction(&trans1, &out.response_data); | |
| 771 helper.VerifyDataConsumed(); | |
| 772 EXPECT_THAT(out.rv, IsOk()); | |
| 773 | |
| 774 EXPECT_THAT(out.rv, IsOk()); | |
| 775 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 776 EXPECT_EQ("hello!hello!", out.response_data); | |
| 777 } | |
| 778 | |
| 779 TEST_F(SpdyNetworkTransactionTest, TwoGetsLateBinding) { | |
| 780 SpdySerializedFrame req( | |
| 781 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 782 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 783 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 784 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 785 | |
| 786 SpdySerializedFrame req2( | |
| 787 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
| 788 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 789 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
| 790 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
| 791 | |
| 792 MockWrite writes[] = { | |
| 793 CreateMockWrite(req, 0), CreateMockWrite(req2, 3), | |
| 794 }; | |
| 795 MockRead reads[] = { | |
| 796 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 797 CreateMockRead(resp2, 4), CreateMockRead(body2, 5), | |
| 798 CreateMockRead(fbody, 6), CreateMockRead(fbody2, 7), | |
| 799 MockRead(ASYNC, 0, 8), // EOF | |
| 800 }; | |
| 801 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 802 | |
| 803 MockConnect never_finishing_connect(SYNCHRONOUS, ERR_IO_PENDING); | |
| 804 SequencedSocketData data_placeholder(nullptr, 0, nullptr, 0); | |
| 805 data_placeholder.set_connect_data(never_finishing_connect); | |
| 806 | |
| 807 NetLogWithSource log; | |
| 808 TransactionHelperResult out; | |
| 809 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 810 NetLogWithSource(), nullptr); | |
| 811 helper.RunPreTestSetup(); | |
| 812 helper.AddData(&data); | |
| 813 // We require placeholder data because two requests are sent out at | |
| 814 // the same time which results in two sockets being connected. The first | |
| 815 // on will negotiate SPDY and will be used for all requests. | |
| 816 helper.AddData(&data_placeholder); | |
| 817 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
| 818 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
| 819 | |
| 820 TestCompletionCallback callback1; | |
| 821 TestCompletionCallback callback2; | |
| 822 | |
| 823 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
| 824 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
| 825 | |
| 826 out.rv = trans1.Start(&httpreq1, callback1.callback(), log); | |
| 827 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
| 828 out.rv = trans2.Start(&httpreq2, callback2.callback(), log); | |
| 829 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
| 830 | |
| 831 out.rv = callback1.WaitForResult(); | |
| 832 ASSERT_THAT(out.rv, IsOk()); | |
| 833 out.rv = callback2.WaitForResult(); | |
| 834 ASSERT_THAT(out.rv, IsOk()); | |
| 835 | |
| 836 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
| 837 EXPECT_TRUE(response1->headers); | |
| 838 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
| 839 out.status_line = response1->headers->GetStatusLine(); | |
| 840 out.response_info = *response1; | |
| 841 out.rv = ReadTransaction(&trans1, &out.response_data); | |
| 842 EXPECT_THAT(out.rv, IsOk()); | |
| 843 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 844 EXPECT_EQ("hello!hello!", out.response_data); | |
| 845 | |
| 846 const HttpResponseInfo* response2 = trans2.GetResponseInfo(); | |
| 847 EXPECT_TRUE(response2->headers); | |
| 848 EXPECT_TRUE(response2->was_fetched_via_spdy); | |
| 849 out.status_line = response2->headers->GetStatusLine(); | |
| 850 out.response_info = *response2; | |
| 851 out.rv = ReadTransaction(&trans2, &out.response_data); | |
| 852 EXPECT_THAT(out.rv, IsOk()); | |
| 853 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 854 EXPECT_EQ("hello!hello!", out.response_data); | |
| 855 | |
| 856 helper.VerifyDataConsumed(); | |
| 857 } | |
| 858 | |
| 859 TEST_F(SpdyNetworkTransactionTest, TwoGetsLateBindingFromPreconnect) { | |
| 860 SpdySerializedFrame req( | |
| 861 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 862 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 863 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 864 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 865 | |
| 866 SpdySerializedFrame req2( | |
| 867 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
| 868 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 869 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
| 870 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
| 871 | |
| 872 MockWrite writes[] = { | |
| 873 CreateMockWrite(req, 0), CreateMockWrite(req2, 3), | |
| 874 }; | |
| 875 MockRead reads[] = { | |
| 876 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 877 CreateMockRead(resp2, 4), CreateMockRead(body2, 5), | |
| 878 CreateMockRead(fbody, 6), CreateMockRead(fbody2, 7), | |
| 879 MockRead(ASYNC, 0, 8), // EOF | |
| 880 }; | |
| 881 SequencedSocketData preconnect_data(reads, arraysize(reads), writes, | |
| 882 arraysize(writes)); | |
| 883 | |
| 884 MockConnect never_finishing_connect(ASYNC, ERR_IO_PENDING); | |
| 885 | |
| 886 SequencedSocketData data_placeholder(nullptr, 0, nullptr, 0); | |
| 887 data_placeholder.set_connect_data(never_finishing_connect); | |
| 888 | |
| 889 NetLogWithSource log; | |
| 890 TransactionHelperResult out; | |
| 891 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 892 NetLogWithSource(), nullptr); | |
| 893 helper.RunPreTestSetup(); | |
| 894 helper.AddData(&preconnect_data); | |
| 895 // We require placeholder data because 3 connections are attempted (first is | |
| 896 // the preconnect, 2nd and 3rd are the never finished connections. | |
| 897 helper.AddData(&data_placeholder); | |
| 898 helper.AddData(&data_placeholder); | |
| 899 | |
| 900 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
| 901 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
| 902 | |
| 903 TestCompletionCallback callback1; | |
| 904 TestCompletionCallback callback2; | |
| 905 | |
| 906 HttpRequestInfo httpreq = CreateGetRequest(); | |
| 907 | |
| 908 // Preconnect the first. | |
| 909 HttpStreamFactory* http_stream_factory = | |
| 910 helper.session()->http_stream_factory(); | |
| 911 | |
| 912 http_stream_factory->PreconnectStreams(1, httpreq); | |
| 913 | |
| 914 out.rv = trans1.Start(&httpreq, callback1.callback(), log); | |
| 915 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
| 916 out.rv = trans2.Start(&httpreq, callback2.callback(), log); | |
| 917 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
| 918 | |
| 919 out.rv = callback1.WaitForResult(); | |
| 920 ASSERT_THAT(out.rv, IsOk()); | |
| 921 out.rv = callback2.WaitForResult(); | |
| 922 ASSERT_THAT(out.rv, IsOk()); | |
| 923 | |
| 924 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
| 925 EXPECT_TRUE(response1->headers); | |
| 926 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
| 927 out.status_line = response1->headers->GetStatusLine(); | |
| 928 out.response_info = *response1; | |
| 929 out.rv = ReadTransaction(&trans1, &out.response_data); | |
| 930 EXPECT_THAT(out.rv, IsOk()); | |
| 931 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 932 EXPECT_EQ("hello!hello!", out.response_data); | |
| 933 | |
| 934 const HttpResponseInfo* response2 = trans2.GetResponseInfo(); | |
| 935 EXPECT_TRUE(response2->headers); | |
| 936 EXPECT_TRUE(response2->was_fetched_via_spdy); | |
| 937 out.status_line = response2->headers->GetStatusLine(); | |
| 938 out.response_info = *response2; | |
| 939 out.rv = ReadTransaction(&trans2, &out.response_data); | |
| 940 EXPECT_THAT(out.rv, IsOk()); | |
| 941 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 942 EXPECT_EQ("hello!hello!", out.response_data); | |
| 943 | |
| 944 helper.VerifyDataConsumed(); | |
| 945 } | |
| 946 | |
| 947 // Similar to ThreeGets above, however this test adds a SETTINGS | |
| 948 // frame. The SETTINGS frame is read during the IO loop waiting on | |
| 949 // the first transaction completion, and sets a maximum concurrent | |
| 950 // stream limit of 1. This means that our IO loop exists after the | |
| 951 // second transaction completes, so we can assert on read_index(). | |
| 952 TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrent) { | |
| 953 // Construct the request. | |
| 954 // Each request fully completes before the next starts. | |
| 955 SpdySerializedFrame req( | |
| 956 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 957 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 958 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 959 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 960 spdy_util_.UpdateWithStreamDestruction(1); | |
| 961 | |
| 962 SpdySerializedFrame req2( | |
| 963 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
| 964 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 965 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
| 966 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
| 967 spdy_util_.UpdateWithStreamDestruction(3); | |
| 968 | |
| 969 SpdySerializedFrame req3( | |
| 970 spdy_util_.ConstructSpdyGet(nullptr, 0, 5, LOWEST, true)); | |
| 971 SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5)); | |
| 972 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, false)); | |
| 973 SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(5, true)); | |
| 974 | |
| 975 SettingsMap settings; | |
| 976 const uint32_t max_concurrent_streams = 1; | |
| 977 settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; | |
| 978 SpdySerializedFrame settings_frame( | |
| 979 spdy_util_.ConstructSpdySettings(settings)); | |
| 980 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
| 981 | |
| 982 MockWrite writes[] = { | |
| 983 CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5), | |
| 984 CreateMockWrite(req2, 6), CreateMockWrite(req3, 10), | |
| 985 }; | |
| 986 | |
| 987 MockRead reads[] = { | |
| 988 CreateMockRead(settings_frame, 1), | |
| 989 CreateMockRead(resp, 2), | |
| 990 CreateMockRead(body, 3), | |
| 991 CreateMockRead(fbody, 4), | |
| 992 CreateMockRead(resp2, 7), | |
| 993 CreateMockRead(body2, 8), | |
| 994 CreateMockRead(fbody2, 9), | |
| 995 CreateMockRead(resp3, 11), | |
| 996 CreateMockRead(body3, 12), | |
| 997 CreateMockRead(fbody3, 13), | |
| 998 | |
| 999 MockRead(ASYNC, 0, 14), // EOF | |
| 1000 }; | |
| 1001 | |
| 1002 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1003 | |
| 1004 NetLogWithSource log; | |
| 1005 TransactionHelperResult out; | |
| 1006 { | |
| 1007 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 1008 NetLogWithSource(), nullptr); | |
| 1009 helper.RunPreTestSetup(); | |
| 1010 helper.AddData(&data); | |
| 1011 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
| 1012 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
| 1013 HttpNetworkTransaction trans3(DEFAULT_PRIORITY, helper.session()); | |
| 1014 | |
| 1015 TestCompletionCallback callback1; | |
| 1016 TestCompletionCallback callback2; | |
| 1017 TestCompletionCallback callback3; | |
| 1018 | |
| 1019 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
| 1020 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
| 1021 HttpRequestInfo httpreq3 = CreateGetRequest(); | |
| 1022 | |
| 1023 out.rv = trans1.Start(&httpreq1, callback1.callback(), log); | |
| 1024 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
| 1025 // Run transaction 1 through quickly to force a read of our SETTINGS | |
| 1026 // frame. | |
| 1027 out.rv = callback1.WaitForResult(); | |
| 1028 ASSERT_THAT(out.rv, IsOk()); | |
| 1029 | |
| 1030 out.rv = trans2.Start(&httpreq2, callback2.callback(), log); | |
| 1031 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
| 1032 out.rv = trans3.Start(&httpreq3, callback3.callback(), log); | |
| 1033 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
| 1034 out.rv = callback2.WaitForResult(); | |
| 1035 ASSERT_THAT(out.rv, IsOk()); | |
| 1036 | |
| 1037 out.rv = callback3.WaitForResult(); | |
| 1038 ASSERT_THAT(out.rv, IsOk()); | |
| 1039 | |
| 1040 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
| 1041 ASSERT_TRUE(response1); | |
| 1042 EXPECT_TRUE(response1->headers); | |
| 1043 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
| 1044 out.status_line = response1->headers->GetStatusLine(); | |
| 1045 out.response_info = *response1; | |
| 1046 out.rv = ReadTransaction(&trans1, &out.response_data); | |
| 1047 EXPECT_THAT(out.rv, IsOk()); | |
| 1048 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1049 EXPECT_EQ("hello!hello!", out.response_data); | |
| 1050 | |
| 1051 const HttpResponseInfo* response2 = trans2.GetResponseInfo(); | |
| 1052 out.status_line = response2->headers->GetStatusLine(); | |
| 1053 out.response_info = *response2; | |
| 1054 out.rv = ReadTransaction(&trans2, &out.response_data); | |
| 1055 EXPECT_THAT(out.rv, IsOk()); | |
| 1056 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1057 EXPECT_EQ("hello!hello!", out.response_data); | |
| 1058 | |
| 1059 const HttpResponseInfo* response3 = trans3.GetResponseInfo(); | |
| 1060 out.status_line = response3->headers->GetStatusLine(); | |
| 1061 out.response_info = *response3; | |
| 1062 out.rv = ReadTransaction(&trans3, &out.response_data); | |
| 1063 EXPECT_THAT(out.rv, IsOk()); | |
| 1064 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1065 EXPECT_EQ("hello!hello!", out.response_data); | |
| 1066 | |
| 1067 helper.VerifyDataConsumed(); | |
| 1068 } | |
| 1069 EXPECT_THAT(out.rv, IsOk()); | |
| 1070 } | |
| 1071 | |
| 1072 // Similar to ThreeGetsWithMaxConcurrent above, however this test adds | |
| 1073 // a fourth transaction. The third and fourth transactions have | |
| 1074 // different data ("hello!" vs "hello!hello!") and because of the | |
| 1075 // user specified priority, we expect to see them inverted in | |
| 1076 // the response from the server. | |
| 1077 TEST_F(SpdyNetworkTransactionTest, FourGetsWithMaxConcurrentPriority) { | |
| 1078 // Construct the request. | |
| 1079 SpdySerializedFrame req( | |
| 1080 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 1081 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 1082 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 1083 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1084 spdy_util_.UpdateWithStreamDestruction(1); | |
| 1085 | |
| 1086 SpdySerializedFrame req2( | |
| 1087 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
| 1088 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 1089 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
| 1090 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
| 1091 spdy_util_.UpdateWithStreamDestruction(3); | |
| 1092 | |
| 1093 SpdySerializedFrame req4( | |
| 1094 spdy_util_.ConstructSpdyGet(nullptr, 0, 5, HIGHEST, true)); | |
| 1095 SpdySerializedFrame resp4(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5)); | |
| 1096 SpdySerializedFrame fbody4(spdy_util_.ConstructSpdyDataFrame(5, true)); | |
| 1097 spdy_util_.UpdateWithStreamDestruction(5); | |
| 1098 | |
| 1099 SpdySerializedFrame req3( | |
| 1100 spdy_util_.ConstructSpdyGet(nullptr, 0, 7, LOWEST, true)); | |
| 1101 SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 7)); | |
| 1102 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(7, false)); | |
| 1103 SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(7, true)); | |
| 1104 | |
| 1105 SettingsMap settings; | |
| 1106 const uint32_t max_concurrent_streams = 1; | |
| 1107 settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; | |
| 1108 SpdySerializedFrame settings_frame( | |
| 1109 spdy_util_.ConstructSpdySettings(settings)); | |
| 1110 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
| 1111 MockWrite writes[] = { | |
| 1112 CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5), | |
| 1113 // By making these synchronous, it guarantees that they are not *started* | |
| 1114 // before their sequence number, which in turn verifies that only a single | |
| 1115 // request is in-flight at a time. | |
| 1116 CreateMockWrite(req2, 6, SYNCHRONOUS), | |
| 1117 CreateMockWrite(req4, 10, SYNCHRONOUS), | |
| 1118 CreateMockWrite(req3, 13, SYNCHRONOUS), | |
| 1119 }; | |
| 1120 MockRead reads[] = { | |
| 1121 CreateMockRead(settings_frame, 1), | |
| 1122 CreateMockRead(resp, 2), | |
| 1123 CreateMockRead(body, 3), | |
| 1124 CreateMockRead(fbody, 4), | |
| 1125 CreateMockRead(resp2, 7), | |
| 1126 CreateMockRead(body2, 8), | |
| 1127 CreateMockRead(fbody2, 9), | |
| 1128 CreateMockRead(resp4, 11), | |
| 1129 CreateMockRead(fbody4, 12), | |
| 1130 CreateMockRead(resp3, 14), | |
| 1131 CreateMockRead(body3, 15), | |
| 1132 CreateMockRead(fbody3, 16), | |
| 1133 | |
| 1134 MockRead(ASYNC, 0, 17), // EOF | |
| 1135 }; | |
| 1136 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1137 NetLogWithSource log; | |
| 1138 TransactionHelperResult out; | |
| 1139 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 1140 NetLogWithSource(), nullptr); | |
| 1141 helper.RunPreTestSetup(); | |
| 1142 helper.AddData(&data); | |
| 1143 | |
| 1144 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
| 1145 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
| 1146 HttpNetworkTransaction trans3(DEFAULT_PRIORITY, helper.session()); | |
| 1147 HttpNetworkTransaction trans4(HIGHEST, helper.session()); | |
| 1148 | |
| 1149 TestCompletionCallback callback1; | |
| 1150 TestCompletionCallback callback2; | |
| 1151 TestCompletionCallback callback3; | |
| 1152 TestCompletionCallback callback4; | |
| 1153 | |
| 1154 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
| 1155 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
| 1156 HttpRequestInfo httpreq3 = CreateGetRequest(); | |
| 1157 HttpRequestInfo httpreq4 = CreateGetRequest(); | |
| 1158 | |
| 1159 out.rv = trans1.Start(&httpreq1, callback1.callback(), log); | |
| 1160 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
| 1161 // Run transaction 1 through quickly to force a read of our SETTINGS frame. | |
| 1162 out.rv = callback1.WaitForResult(); | |
| 1163 ASSERT_THAT(out.rv, IsOk()); | |
| 1164 | |
| 1165 // Finish async network reads and writes associated with |trans1|. | |
| 1166 base::RunLoop().RunUntilIdle(); | |
| 1167 | |
| 1168 out.rv = trans2.Start(&httpreq2, callback2.callback(), log); | |
| 1169 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
| 1170 out.rv = trans3.Start(&httpreq3, callback3.callback(), log); | |
| 1171 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
| 1172 out.rv = trans4.Start(&httpreq4, callback4.callback(), log); | |
| 1173 ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING)); | |
| 1174 | |
| 1175 out.rv = callback2.WaitForResult(); | |
| 1176 ASSERT_THAT(out.rv, IsOk()); | |
| 1177 | |
| 1178 out.rv = callback3.WaitForResult(); | |
| 1179 ASSERT_THAT(out.rv, IsOk()); | |
| 1180 | |
| 1181 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
| 1182 EXPECT_TRUE(response1->headers); | |
| 1183 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
| 1184 out.status_line = response1->headers->GetStatusLine(); | |
| 1185 out.response_info = *response1; | |
| 1186 out.rv = ReadTransaction(&trans1, &out.response_data); | |
| 1187 EXPECT_THAT(out.rv, IsOk()); | |
| 1188 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1189 EXPECT_EQ("hello!hello!", out.response_data); | |
| 1190 | |
| 1191 const HttpResponseInfo* response2 = trans2.GetResponseInfo(); | |
| 1192 out.status_line = response2->headers->GetStatusLine(); | |
| 1193 out.response_info = *response2; | |
| 1194 out.rv = ReadTransaction(&trans2, &out.response_data); | |
| 1195 EXPECT_THAT(out.rv, IsOk()); | |
| 1196 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1197 EXPECT_EQ("hello!hello!", out.response_data); | |
| 1198 | |
| 1199 // notice: response3 gets two hellos, response4 gets one | |
| 1200 // hello, so we know dequeuing priority was respected. | |
| 1201 const HttpResponseInfo* response3 = trans3.GetResponseInfo(); | |
| 1202 out.status_line = response3->headers->GetStatusLine(); | |
| 1203 out.response_info = *response3; | |
| 1204 out.rv = ReadTransaction(&trans3, &out.response_data); | |
| 1205 EXPECT_THAT(out.rv, IsOk()); | |
| 1206 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1207 EXPECT_EQ("hello!hello!", out.response_data); | |
| 1208 | |
| 1209 out.rv = callback4.WaitForResult(); | |
| 1210 EXPECT_THAT(out.rv, IsOk()); | |
| 1211 const HttpResponseInfo* response4 = trans4.GetResponseInfo(); | |
| 1212 out.status_line = response4->headers->GetStatusLine(); | |
| 1213 out.response_info = *response4; | |
| 1214 out.rv = ReadTransaction(&trans4, &out.response_data); | |
| 1215 EXPECT_THAT(out.rv, IsOk()); | |
| 1216 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1217 EXPECT_EQ("hello!", out.response_data); | |
| 1218 helper.VerifyDataConsumed(); | |
| 1219 EXPECT_THAT(out.rv, IsOk()); | |
| 1220 } | |
| 1221 | |
| 1222 // Similar to ThreeGetsMaxConcurrrent above, however, this test | |
| 1223 // deletes a session in the middle of the transaction to ensure | |
| 1224 // that we properly remove pendingcreatestream objects from | |
| 1225 // the spdy_session | |
| 1226 TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentDelete) { | |
| 1227 // Construct the request. | |
| 1228 SpdySerializedFrame req( | |
| 1229 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 1230 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 1231 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 1232 SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1233 spdy_util_.UpdateWithStreamDestruction(1); | |
| 1234 | |
| 1235 SpdySerializedFrame req2( | |
| 1236 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
| 1237 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 1238 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false)); | |
| 1239 SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
| 1240 | |
| 1241 SettingsMap settings; | |
| 1242 const uint32_t max_concurrent_streams = 1; | |
| 1243 settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; | |
| 1244 SpdySerializedFrame settings_frame( | |
| 1245 spdy_util_.ConstructSpdySettings(settings)); | |
| 1246 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
| 1247 | |
| 1248 MockWrite writes[] = { | |
| 1249 CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5), | |
| 1250 CreateMockWrite(req2, 6), | |
| 1251 }; | |
| 1252 MockRead reads[] = { | |
| 1253 CreateMockRead(settings_frame, 1), CreateMockRead(resp, 2), | |
| 1254 CreateMockRead(body, 3), CreateMockRead(fbody, 4), | |
| 1255 CreateMockRead(resp2, 7), CreateMockRead(body2, 8), | |
| 1256 CreateMockRead(fbody2, 9), MockRead(ASYNC, 0, 10), // EOF | |
| 1257 }; | |
| 1258 | |
| 1259 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1260 | |
| 1261 NetLogWithSource log; | |
| 1262 TransactionHelperResult out; | |
| 1263 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 1264 NetLogWithSource(), nullptr); | |
| 1265 helper.RunPreTestSetup(); | |
| 1266 helper.AddData(&data); | |
| 1267 std::unique_ptr<HttpNetworkTransaction> trans1( | |
| 1268 new HttpNetworkTransaction(DEFAULT_PRIORITY, helper.session())); | |
| 1269 std::unique_ptr<HttpNetworkTransaction> trans2( | |
| 1270 new HttpNetworkTransaction(DEFAULT_PRIORITY, helper.session())); | |
| 1271 std::unique_ptr<HttpNetworkTransaction> trans3( | |
| 1272 new HttpNetworkTransaction(DEFAULT_PRIORITY, helper.session())); | |
| 1273 | |
| 1274 TestCompletionCallback callback1; | |
| 1275 TestCompletionCallback callback2; | |
| 1276 TestCompletionCallback callback3; | |
| 1277 | |
| 1278 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
| 1279 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
| 1280 HttpRequestInfo httpreq3 = CreateGetRequest(); | |
| 1281 | |
| 1282 out.rv = trans1->Start(&httpreq1, callback1.callback(), log); | |
| 1283 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
| 1284 // Run transaction 1 through quickly to force a read of our SETTINGS frame. | |
| 1285 out.rv = callback1.WaitForResult(); | |
| 1286 ASSERT_THAT(out.rv, IsOk()); | |
| 1287 | |
| 1288 out.rv = trans2->Start(&httpreq2, callback2.callback(), log); | |
| 1289 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
| 1290 out.rv = trans3->Start(&httpreq3, callback3.callback(), log); | |
| 1291 trans3.reset(); | |
| 1292 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
| 1293 out.rv = callback2.WaitForResult(); | |
| 1294 ASSERT_THAT(out.rv, IsOk()); | |
| 1295 | |
| 1296 const HttpResponseInfo* response1 = trans1->GetResponseInfo(); | |
| 1297 ASSERT_TRUE(response1); | |
| 1298 EXPECT_TRUE(response1->headers); | |
| 1299 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
| 1300 out.status_line = response1->headers->GetStatusLine(); | |
| 1301 out.response_info = *response1; | |
| 1302 out.rv = ReadTransaction(trans1.get(), &out.response_data); | |
| 1303 EXPECT_THAT(out.rv, IsOk()); | |
| 1304 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1305 EXPECT_EQ("hello!hello!", out.response_data); | |
| 1306 | |
| 1307 const HttpResponseInfo* response2 = trans2->GetResponseInfo(); | |
| 1308 ASSERT_TRUE(response2); | |
| 1309 out.status_line = response2->headers->GetStatusLine(); | |
| 1310 out.response_info = *response2; | |
| 1311 out.rv = ReadTransaction(trans2.get(), &out.response_data); | |
| 1312 EXPECT_THAT(out.rv, IsOk()); | |
| 1313 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1314 EXPECT_EQ("hello!hello!", out.response_data); | |
| 1315 helper.VerifyDataConsumed(); | |
| 1316 EXPECT_THAT(out.rv, IsOk()); | |
| 1317 } | |
| 1318 | |
| 1319 namespace { | |
| 1320 | |
| 1321 // The KillerCallback will delete the transaction on error as part of the | |
| 1322 // callback. | |
| 1323 class KillerCallback : public TestCompletionCallbackBase { | |
| 1324 public: | |
| 1325 explicit KillerCallback(HttpNetworkTransaction* transaction) | |
| 1326 : transaction_(transaction), | |
| 1327 callback_(base::Bind(&KillerCallback::OnComplete, | |
| 1328 base::Unretained(this))) { | |
| 1329 } | |
| 1330 | |
| 1331 ~KillerCallback() override {} | |
| 1332 | |
| 1333 const CompletionCallback& callback() const { return callback_; } | |
| 1334 | |
| 1335 private: | |
| 1336 void OnComplete(int result) { | |
| 1337 if (result < 0) | |
| 1338 delete transaction_; | |
| 1339 | |
| 1340 SetResult(result); | |
| 1341 } | |
| 1342 | |
| 1343 HttpNetworkTransaction* transaction_; | |
| 1344 CompletionCallback callback_; | |
| 1345 }; | |
| 1346 | |
| 1347 } // namespace | |
| 1348 | |
| 1349 // Similar to ThreeGetsMaxConcurrrentDelete above, however, this test | |
| 1350 // closes the socket while we have a pending transaction waiting for | |
| 1351 // a pending stream creation. http://crbug.com/52901 | |
| 1352 TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentSocketClose) { | |
| 1353 // Construct the request. | |
| 1354 SpdySerializedFrame req( | |
| 1355 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 1356 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 1357 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 1358 SpdySerializedFrame fin_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1359 spdy_util_.UpdateWithStreamDestruction(1); | |
| 1360 | |
| 1361 SpdySerializedFrame req2( | |
| 1362 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
| 1363 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 1364 | |
| 1365 SettingsMap settings; | |
| 1366 const uint32_t max_concurrent_streams = 1; | |
| 1367 settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; | |
| 1368 SpdySerializedFrame settings_frame( | |
| 1369 spdy_util_.ConstructSpdySettings(settings)); | |
| 1370 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
| 1371 | |
| 1372 MockWrite writes[] = { | |
| 1373 CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5), | |
| 1374 CreateMockWrite(req2, 6), | |
| 1375 }; | |
| 1376 MockRead reads[] = { | |
| 1377 CreateMockRead(settings_frame, 1), | |
| 1378 CreateMockRead(resp, 2), | |
| 1379 CreateMockRead(body, 3), | |
| 1380 CreateMockRead(fin_body, 4), | |
| 1381 CreateMockRead(resp2, 7), | |
| 1382 MockRead(ASYNC, ERR_CONNECTION_RESET, 8), // Abort! | |
| 1383 }; | |
| 1384 | |
| 1385 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1386 SequencedSocketData data_placeholder(nullptr, 0, nullptr, 0); | |
| 1387 | |
| 1388 NetLogWithSource log; | |
| 1389 TransactionHelperResult out; | |
| 1390 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 1391 NetLogWithSource(), nullptr); | |
| 1392 helper.RunPreTestSetup(); | |
| 1393 helper.AddData(&data); | |
| 1394 // We require placeholder data because three get requests are sent out, so | |
| 1395 // there needs to be three sets of SSL connection data. | |
| 1396 helper.AddData(&data_placeholder); | |
| 1397 helper.AddData(&data_placeholder); | |
| 1398 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
| 1399 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
| 1400 HttpNetworkTransaction* trans3( | |
| 1401 new HttpNetworkTransaction(DEFAULT_PRIORITY, helper.session())); | |
| 1402 | |
| 1403 TestCompletionCallback callback1; | |
| 1404 TestCompletionCallback callback2; | |
| 1405 KillerCallback callback3(trans3); | |
| 1406 | |
| 1407 HttpRequestInfo httpreq1 = CreateGetRequest(); | |
| 1408 HttpRequestInfo httpreq2 = CreateGetRequest(); | |
| 1409 HttpRequestInfo httpreq3 = CreateGetRequest(); | |
| 1410 | |
| 1411 out.rv = trans1.Start(&httpreq1, callback1.callback(), log); | |
| 1412 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
| 1413 // Run transaction 1 through quickly to force a read of our SETTINGS frame. | |
| 1414 out.rv = callback1.WaitForResult(); | |
| 1415 ASSERT_THAT(out.rv, IsOk()); | |
| 1416 | |
| 1417 out.rv = trans2.Start(&httpreq2, callback2.callback(), log); | |
| 1418 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
| 1419 out.rv = trans3->Start(&httpreq3, callback3.callback(), log); | |
| 1420 ASSERT_EQ(out.rv, ERR_IO_PENDING); | |
| 1421 out.rv = callback3.WaitForResult(); | |
| 1422 ASSERT_THAT(out.rv, IsError(ERR_ABORTED)); | |
| 1423 | |
| 1424 const HttpResponseInfo* response1 = trans1.GetResponseInfo(); | |
| 1425 ASSERT_TRUE(response1); | |
| 1426 EXPECT_TRUE(response1->headers); | |
| 1427 EXPECT_TRUE(response1->was_fetched_via_spdy); | |
| 1428 out.status_line = response1->headers->GetStatusLine(); | |
| 1429 out.response_info = *response1; | |
| 1430 out.rv = ReadTransaction(&trans1, &out.response_data); | |
| 1431 EXPECT_THAT(out.rv, IsOk()); | |
| 1432 | |
| 1433 const HttpResponseInfo* response2 = trans2.GetResponseInfo(); | |
| 1434 ASSERT_TRUE(response2); | |
| 1435 out.status_line = response2->headers->GetStatusLine(); | |
| 1436 out.response_info = *response2; | |
| 1437 out.rv = ReadTransaction(&trans2, &out.response_data); | |
| 1438 EXPECT_THAT(out.rv, IsError(ERR_CONNECTION_RESET)); | |
| 1439 | |
| 1440 helper.VerifyDataConsumed(); | |
| 1441 } | |
| 1442 | |
| 1443 // Test that a simple PUT request works. | |
| 1444 TEST_F(SpdyNetworkTransactionTest, Put) { | |
| 1445 // Setup the request | |
| 1446 HttpRequestInfo request; | |
| 1447 request.method = "PUT"; | |
| 1448 request.url = default_url_; | |
| 1449 | |
| 1450 SpdyHeaderBlock put_headers( | |
| 1451 spdy_util_.ConstructPutHeaderBlock(kDefaultUrl, 0)); | |
| 1452 SpdySerializedFrame req( | |
| 1453 spdy_util_.ConstructSpdyHeaders(1, std::move(put_headers), LOWEST, true)); | |
| 1454 MockWrite writes[] = { | |
| 1455 CreateMockWrite(req, 0), | |
| 1456 }; | |
| 1457 | |
| 1458 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 1459 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1460 MockRead reads[] = { | |
| 1461 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 1462 MockRead(ASYNC, 0, 3) // EOF | |
| 1463 }; | |
| 1464 | |
| 1465 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1466 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 1467 NetLogWithSource(), nullptr); | |
| 1468 helper.RunToCompletion(&data); | |
| 1469 TransactionHelperResult out = helper.output(); | |
| 1470 | |
| 1471 EXPECT_THAT(out.rv, IsOk()); | |
| 1472 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1473 } | |
| 1474 | |
| 1475 // Test that a simple HEAD request works. | |
| 1476 TEST_F(SpdyNetworkTransactionTest, Head) { | |
| 1477 // Setup the request | |
| 1478 HttpRequestInfo request; | |
| 1479 request.method = "HEAD"; | |
| 1480 request.url = default_url_; | |
| 1481 | |
| 1482 SpdyHeaderBlock head_headers( | |
| 1483 spdy_util_.ConstructHeadHeaderBlock(kDefaultUrl, 0)); | |
| 1484 SpdySerializedFrame req(spdy_util_.ConstructSpdyHeaders( | |
| 1485 1, std::move(head_headers), LOWEST, true)); | |
| 1486 MockWrite writes[] = { | |
| 1487 CreateMockWrite(req, 0), | |
| 1488 }; | |
| 1489 | |
| 1490 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 1491 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1492 MockRead reads[] = { | |
| 1493 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 1494 MockRead(ASYNC, 0, 3) // EOF | |
| 1495 }; | |
| 1496 | |
| 1497 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1498 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 1499 NetLogWithSource(), nullptr); | |
| 1500 helper.RunToCompletion(&data); | |
| 1501 TransactionHelperResult out = helper.output(); | |
| 1502 | |
| 1503 EXPECT_THAT(out.rv, IsOk()); | |
| 1504 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1505 } | |
| 1506 | |
| 1507 // Test that a simple POST works. | |
| 1508 TEST_F(SpdyNetworkTransactionTest, Post) { | |
| 1509 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
| 1510 kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0)); | |
| 1511 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1512 MockWrite writes[] = { | |
| 1513 CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame | |
| 1514 }; | |
| 1515 | |
| 1516 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 1517 MockRead reads[] = { | |
| 1518 CreateMockRead(resp, 2), CreateMockRead(body, 3), | |
| 1519 MockRead(ASYNC, 0, 4) // EOF | |
| 1520 }; | |
| 1521 | |
| 1522 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1523 NormalSpdyTransactionHelper helper(CreatePostRequest(), DEFAULT_PRIORITY, | |
| 1524 NetLogWithSource(), nullptr); | |
| 1525 helper.RunToCompletion(&data); | |
| 1526 TransactionHelperResult out = helper.output(); | |
| 1527 EXPECT_THAT(out.rv, IsOk()); | |
| 1528 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1529 EXPECT_EQ("hello!", out.response_data); | |
| 1530 } | |
| 1531 | |
| 1532 // Test that a POST with a file works. | |
| 1533 TEST_F(SpdyNetworkTransactionTest, FilePost) { | |
| 1534 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
| 1535 kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0)); | |
| 1536 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1537 MockWrite writes[] = { | |
| 1538 CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame | |
| 1539 }; | |
| 1540 | |
| 1541 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 1542 MockRead reads[] = { | |
| 1543 CreateMockRead(resp, 2), CreateMockRead(body, 3), | |
| 1544 MockRead(ASYNC, 0, 4) // EOF | |
| 1545 }; | |
| 1546 | |
| 1547 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1548 NormalSpdyTransactionHelper helper(CreateFilePostRequest(), DEFAULT_PRIORITY, | |
| 1549 NetLogWithSource(), nullptr); | |
| 1550 helper.RunToCompletion(&data); | |
| 1551 TransactionHelperResult out = helper.output(); | |
| 1552 EXPECT_THAT(out.rv, IsOk()); | |
| 1553 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1554 EXPECT_EQ("hello!", out.response_data); | |
| 1555 } | |
| 1556 | |
| 1557 // Test that a POST with a unreadable file fails. | |
| 1558 TEST_F(SpdyNetworkTransactionTest, UnreadableFilePost) { | |
| 1559 MockWrite writes[] = { | |
| 1560 MockWrite(ASYNC, 0, 0) // EOF | |
| 1561 }; | |
| 1562 MockRead reads[] = { | |
| 1563 MockRead(ASYNC, 0, 1) // EOF | |
| 1564 }; | |
| 1565 | |
| 1566 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1567 NormalSpdyTransactionHelper helper(CreateUnreadableFilePostRequest(), | |
| 1568 DEFAULT_PRIORITY, NetLogWithSource(), | |
| 1569 nullptr); | |
| 1570 helper.RunPreTestSetup(); | |
| 1571 helper.AddData(&data); | |
| 1572 helper.RunDefaultTest(); | |
| 1573 | |
| 1574 base::RunLoop().RunUntilIdle(); | |
| 1575 helper.VerifyDataNotConsumed(); | |
| 1576 EXPECT_THAT(helper.output().rv, IsError(ERR_ACCESS_DENIED)); | |
| 1577 } | |
| 1578 | |
| 1579 // Test that a complex POST works. | |
| 1580 TEST_F(SpdyNetworkTransactionTest, ComplexPost) { | |
| 1581 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
| 1582 kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0)); | |
| 1583 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1584 MockWrite writes[] = { | |
| 1585 CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame | |
| 1586 }; | |
| 1587 | |
| 1588 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 1589 MockRead reads[] = { | |
| 1590 CreateMockRead(resp, 2), CreateMockRead(body, 3), | |
| 1591 MockRead(ASYNC, 0, 4) // EOF | |
| 1592 }; | |
| 1593 | |
| 1594 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1595 NormalSpdyTransactionHelper helper(CreateComplexPostRequest(), | |
| 1596 DEFAULT_PRIORITY, NetLogWithSource(), | |
| 1597 nullptr); | |
| 1598 helper.RunToCompletion(&data); | |
| 1599 TransactionHelperResult out = helper.output(); | |
| 1600 EXPECT_THAT(out.rv, IsOk()); | |
| 1601 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1602 EXPECT_EQ("hello!", out.response_data); | |
| 1603 } | |
| 1604 | |
| 1605 // Test that a chunked POST works. | |
| 1606 TEST_F(SpdyNetworkTransactionTest, ChunkedPost) { | |
| 1607 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 1608 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1609 MockWrite writes[] = { | |
| 1610 CreateMockWrite(req, 0), CreateMockWrite(body, 1), | |
| 1611 }; | |
| 1612 | |
| 1613 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 1614 MockRead reads[] = { | |
| 1615 CreateMockRead(resp, 2), CreateMockRead(body, 3), | |
| 1616 MockRead(ASYNC, 0, 4) // EOF | |
| 1617 }; | |
| 1618 | |
| 1619 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1620 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
| 1621 DEFAULT_PRIORITY, NetLogWithSource(), | |
| 1622 nullptr); | |
| 1623 | |
| 1624 // These chunks get merged into a single frame when being sent. | |
| 1625 const int kFirstChunkSize = kUploadDataSize/2; | |
| 1626 upload_chunked_data_stream()->AppendData(kUploadData, kFirstChunkSize, false); | |
| 1627 upload_chunked_data_stream()->AppendData( | |
| 1628 kUploadData + kFirstChunkSize, kUploadDataSize - kFirstChunkSize, true); | |
| 1629 | |
| 1630 helper.RunToCompletion(&data); | |
| 1631 TransactionHelperResult out = helper.output(); | |
| 1632 EXPECT_THAT(out.rv, IsOk()); | |
| 1633 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1634 EXPECT_EQ(kUploadData, out.response_data); | |
| 1635 } | |
| 1636 | |
| 1637 // Test that a chunked POST works with chunks appended after transaction starts. | |
| 1638 TEST_F(SpdyNetworkTransactionTest, DelayedChunkedPost) { | |
| 1639 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 1640 SpdySerializedFrame chunk1(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 1641 SpdySerializedFrame chunk2(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 1642 SpdySerializedFrame chunk3(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1643 MockWrite writes[] = { | |
| 1644 CreateMockWrite(req, 0), CreateMockWrite(chunk1, 1), | |
| 1645 CreateMockWrite(chunk2, 2), CreateMockWrite(chunk3, 3), | |
| 1646 }; | |
| 1647 | |
| 1648 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 1649 MockRead reads[] = { | |
| 1650 CreateMockRead(resp, 4), CreateMockRead(chunk1, 5), | |
| 1651 CreateMockRead(chunk2, 6), CreateMockRead(chunk3, 7), | |
| 1652 MockRead(ASYNC, 0, 8) // EOF | |
| 1653 }; | |
| 1654 | |
| 1655 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1656 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
| 1657 DEFAULT_PRIORITY, NetLogWithSource(), | |
| 1658 nullptr); | |
| 1659 | |
| 1660 upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, false); | |
| 1661 | |
| 1662 helper.RunPreTestSetup(); | |
| 1663 helper.AddData(&data); | |
| 1664 ASSERT_TRUE(helper.StartDefaultTest()); | |
| 1665 | |
| 1666 base::RunLoop().RunUntilIdle(); | |
| 1667 upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, false); | |
| 1668 base::RunLoop().RunUntilIdle(); | |
| 1669 upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, true); | |
| 1670 | |
| 1671 helper.FinishDefaultTest(); | |
| 1672 helper.VerifyDataConsumed(); | |
| 1673 | |
| 1674 SpdyString expected_response; | |
| 1675 expected_response += kUploadData; | |
| 1676 expected_response += kUploadData; | |
| 1677 expected_response += kUploadData; | |
| 1678 | |
| 1679 TransactionHelperResult out = helper.output(); | |
| 1680 EXPECT_THAT(out.rv, IsOk()); | |
| 1681 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1682 EXPECT_EQ(expected_response, out.response_data); | |
| 1683 } | |
| 1684 | |
| 1685 // Test that a POST without any post data works. | |
| 1686 TEST_F(SpdyNetworkTransactionTest, NullPost) { | |
| 1687 // Setup the request | |
| 1688 HttpRequestInfo request; | |
| 1689 request.method = "POST"; | |
| 1690 request.url = default_url_; | |
| 1691 // Create an empty UploadData. | |
| 1692 request.upload_data_stream = nullptr; | |
| 1693 | |
| 1694 // When request.upload_data_stream is NULL for post, content-length is | |
| 1695 // expected to be 0. | |
| 1696 SpdyHeaderBlock req_block( | |
| 1697 spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0)); | |
| 1698 SpdySerializedFrame req( | |
| 1699 spdy_util_.ConstructSpdyHeaders(1, std::move(req_block), LOWEST, true)); | |
| 1700 | |
| 1701 MockWrite writes[] = { | |
| 1702 CreateMockWrite(req, 0), | |
| 1703 }; | |
| 1704 | |
| 1705 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 1706 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1707 MockRead reads[] = { | |
| 1708 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 1709 MockRead(ASYNC, 0, 3) // EOF | |
| 1710 }; | |
| 1711 | |
| 1712 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1713 | |
| 1714 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 1715 NetLogWithSource(), nullptr); | |
| 1716 helper.RunToCompletion(&data); | |
| 1717 TransactionHelperResult out = helper.output(); | |
| 1718 EXPECT_THAT(out.rv, IsOk()); | |
| 1719 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1720 EXPECT_EQ("hello!", out.response_data); | |
| 1721 } | |
| 1722 | |
| 1723 // Test that a simple POST works. | |
| 1724 TEST_F(SpdyNetworkTransactionTest, EmptyPost) { | |
| 1725 // Create an empty UploadDataStream. | |
| 1726 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
| 1727 ElementsUploadDataStream stream(std::move(element_readers), 0); | |
| 1728 | |
| 1729 // Setup the request | |
| 1730 HttpRequestInfo request; | |
| 1731 request.method = "POST"; | |
| 1732 request.url = default_url_; | |
| 1733 request.upload_data_stream = &stream; | |
| 1734 | |
| 1735 const uint64_t kContentLength = 0; | |
| 1736 | |
| 1737 SpdyHeaderBlock req_block( | |
| 1738 spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, kContentLength)); | |
| 1739 SpdySerializedFrame req( | |
| 1740 spdy_util_.ConstructSpdyHeaders(1, std::move(req_block), LOWEST, true)); | |
| 1741 | |
| 1742 MockWrite writes[] = { | |
| 1743 CreateMockWrite(req, 0), | |
| 1744 }; | |
| 1745 | |
| 1746 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 1747 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1748 MockRead reads[] = { | |
| 1749 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 1750 MockRead(ASYNC, 0, 3) // EOF | |
| 1751 }; | |
| 1752 | |
| 1753 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1754 | |
| 1755 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 1756 NetLogWithSource(), nullptr); | |
| 1757 helper.RunToCompletion(&data); | |
| 1758 TransactionHelperResult out = helper.output(); | |
| 1759 EXPECT_THAT(out.rv, IsOk()); | |
| 1760 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1761 EXPECT_EQ("hello!", out.response_data); | |
| 1762 } | |
| 1763 | |
| 1764 // While we're doing a post, the server sends the reply before upload completes. | |
| 1765 TEST_F(SpdyNetworkTransactionTest, ResponseBeforePostCompletes) { | |
| 1766 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 1767 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1768 MockWrite writes[] = { | |
| 1769 CreateMockWrite(req, 0), CreateMockWrite(body, 3), | |
| 1770 }; | |
| 1771 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 1772 MockRead reads[] = { | |
| 1773 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 1774 MockRead(ASYNC, 0, 4) // EOF | |
| 1775 }; | |
| 1776 | |
| 1777 // Write the request headers, and read the complete response | |
| 1778 // while still waiting for chunked request data. | |
| 1779 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1780 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
| 1781 DEFAULT_PRIORITY, NetLogWithSource(), | |
| 1782 nullptr); | |
| 1783 helper.RunPreTestSetup(); | |
| 1784 helper.AddData(&data); | |
| 1785 | |
| 1786 ASSERT_TRUE(helper.StartDefaultTest()); | |
| 1787 | |
| 1788 base::RunLoop().RunUntilIdle(); | |
| 1789 | |
| 1790 // Process the request headers, response headers, and response body. | |
| 1791 // The request body is still in flight. | |
| 1792 const HttpResponseInfo* response = helper.trans()->GetResponseInfo(); | |
| 1793 EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); | |
| 1794 | |
| 1795 // Finish sending the request body. | |
| 1796 upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, true); | |
| 1797 helper.WaitForCallbackToComplete(); | |
| 1798 EXPECT_THAT(helper.output().rv, IsOk()); | |
| 1799 | |
| 1800 SpdyString response_body; | |
| 1801 EXPECT_THAT(ReadTransaction(helper.trans(), &response_body), IsOk()); | |
| 1802 EXPECT_EQ(kUploadData, response_body); | |
| 1803 | |
| 1804 // Finish async network reads/writes. | |
| 1805 base::RunLoop().RunUntilIdle(); | |
| 1806 helper.VerifyDataConsumed(); | |
| 1807 } | |
| 1808 | |
| 1809 // The client upon cancellation tries to send a RST_STREAM frame. The mock | |
| 1810 // socket causes the TCP write to return zero. This test checks that the client | |
| 1811 // tries to queue up the RST_STREAM frame again. | |
| 1812 TEST_F(SpdyNetworkTransactionTest, SocketWriteReturnsZero) { | |
| 1813 SpdySerializedFrame req( | |
| 1814 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 1815 SpdySerializedFrame rst( | |
| 1816 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_CANCEL)); | |
| 1817 MockWrite writes[] = { | |
| 1818 CreateMockWrite(req, 0, SYNCHRONOUS), MockWrite(SYNCHRONOUS, 0, 0, 2), | |
| 1819 CreateMockWrite(rst, 3, SYNCHRONOUS), | |
| 1820 }; | |
| 1821 | |
| 1822 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 1823 MockRead reads[] = { | |
| 1824 CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 0, 4) // EOF | |
| 1825 }; | |
| 1826 | |
| 1827 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1828 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 1829 NetLogWithSource(), nullptr); | |
| 1830 helper.RunPreTestSetup(); | |
| 1831 helper.AddData(&data); | |
| 1832 helper.StartDefaultTest(); | |
| 1833 EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING)); | |
| 1834 | |
| 1835 helper.WaitForCallbackToComplete(); | |
| 1836 EXPECT_THAT(helper.output().rv, IsOk()); | |
| 1837 | |
| 1838 helper.ResetTrans(); | |
| 1839 base::RunLoop().RunUntilIdle(); | |
| 1840 | |
| 1841 helper.VerifyDataConsumed(); | |
| 1842 } | |
| 1843 | |
| 1844 // Test that the transaction doesn't crash when we don't have a reply. | |
| 1845 TEST_F(SpdyNetworkTransactionTest, ResponseWithoutHeaders) { | |
| 1846 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1847 MockRead reads[] = { | |
| 1848 CreateMockRead(body, 1), MockRead(ASYNC, 0, 3) // EOF | |
| 1849 }; | |
| 1850 | |
| 1851 SpdySerializedFrame req( | |
| 1852 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 1853 SpdySerializedFrame rst( | |
| 1854 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
| 1855 MockWrite writes[] = { | |
| 1856 CreateMockWrite(req, 0), CreateMockWrite(rst, 2), | |
| 1857 }; | |
| 1858 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1859 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 1860 NetLogWithSource(), nullptr); | |
| 1861 helper.RunToCompletion(&data); | |
| 1862 TransactionHelperResult out = helper.output(); | |
| 1863 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
| 1864 } | |
| 1865 | |
| 1866 // Test that the transaction doesn't crash when we get two replies on the same | |
| 1867 // stream ID. See http://crbug.com/45639. | |
| 1868 TEST_F(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) { | |
| 1869 SpdySerializedFrame req( | |
| 1870 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 1871 SpdySerializedFrame rst( | |
| 1872 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
| 1873 MockWrite writes[] = { | |
| 1874 CreateMockWrite(req, 0), CreateMockWrite(rst, 4), | |
| 1875 }; | |
| 1876 | |
| 1877 SpdySerializedFrame resp0(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 1878 SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 1879 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1880 MockRead reads[] = { | |
| 1881 CreateMockRead(resp0, 1), CreateMockRead(resp1, 2), | |
| 1882 CreateMockRead(body, 3), MockRead(ASYNC, 0, 5) // EOF | |
| 1883 }; | |
| 1884 | |
| 1885 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1886 | |
| 1887 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 1888 NetLogWithSource(), nullptr); | |
| 1889 helper.RunPreTestSetup(); | |
| 1890 helper.AddData(&data); | |
| 1891 | |
| 1892 HttpNetworkTransaction* trans = helper.trans(); | |
| 1893 | |
| 1894 TestCompletionCallback callback; | |
| 1895 int rv = | |
| 1896 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
| 1897 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 1898 rv = callback.WaitForResult(); | |
| 1899 EXPECT_THAT(rv, IsOk()); | |
| 1900 | |
| 1901 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
| 1902 ASSERT_TRUE(response); | |
| 1903 EXPECT_TRUE(response->headers); | |
| 1904 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 1905 SpdyString response_data; | |
| 1906 rv = ReadTransaction(trans, &response_data); | |
| 1907 EXPECT_THAT(rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
| 1908 | |
| 1909 helper.VerifyDataConsumed(); | |
| 1910 } | |
| 1911 | |
| 1912 TEST_F(SpdyNetworkTransactionTest, ResetReplyWithTransferEncoding) { | |
| 1913 // Construct the request. | |
| 1914 SpdySerializedFrame req( | |
| 1915 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 1916 SpdySerializedFrame rst( | |
| 1917 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
| 1918 MockWrite writes[] = { | |
| 1919 CreateMockWrite(req, 0), CreateMockWrite(rst, 2), | |
| 1920 }; | |
| 1921 | |
| 1922 const char* const headers[] = { | |
| 1923 "transfer-encoding", "chunked" | |
| 1924 }; | |
| 1925 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(headers, 1, 1)); | |
| 1926 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1927 MockRead reads[] = { | |
| 1928 CreateMockRead(resp, 1), CreateMockRead(body, 3), | |
| 1929 MockRead(ASYNC, 0, 4) // EOF | |
| 1930 }; | |
| 1931 | |
| 1932 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1933 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 1934 NetLogWithSource(), nullptr); | |
| 1935 helper.RunToCompletion(&data); | |
| 1936 TransactionHelperResult out = helper.output(); | |
| 1937 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
| 1938 | |
| 1939 helper.session()->spdy_session_pool()->CloseAllSessions(); | |
| 1940 helper.VerifyDataConsumed(); | |
| 1941 } | |
| 1942 | |
| 1943 TEST_F(SpdyNetworkTransactionTest, ResetPushWithTransferEncoding) { | |
| 1944 // Construct the request. | |
| 1945 SpdySerializedFrame req( | |
| 1946 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 1947 SpdySerializedFrame priority( | |
| 1948 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 1949 SpdySerializedFrame rst( | |
| 1950 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_PROTOCOL_ERROR)); | |
| 1951 MockWrite writes[] = { | |
| 1952 CreateMockWrite(req, 0), CreateMockWrite(priority, 3), | |
| 1953 CreateMockWrite(rst, 5), | |
| 1954 }; | |
| 1955 | |
| 1956 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 1957 const char* const headers[] = { | |
| 1958 "transfer-encoding", "chunked" | |
| 1959 }; | |
| 1960 SpdySerializedFrame push( | |
| 1961 spdy_util_.ConstructSpdyPush(headers, arraysize(headers) / 2, 2, 1, | |
| 1962 GetDefaultUrlWithPath("/1").c_str())); | |
| 1963 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 1964 MockRead reads[] = { | |
| 1965 CreateMockRead(resp, 1), CreateMockRead(push, 2), CreateMockRead(body, 4), | |
| 1966 MockRead(ASYNC, 0, 6) // EOF | |
| 1967 }; | |
| 1968 | |
| 1969 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 1970 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 1971 NetLogWithSource(), nullptr); | |
| 1972 helper.RunToCompletion(&data); | |
| 1973 TransactionHelperResult out = helper.output(); | |
| 1974 EXPECT_THAT(out.rv, IsOk()); | |
| 1975 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 1976 EXPECT_EQ("hello!", out.response_data); | |
| 1977 | |
| 1978 helper.session()->spdy_session_pool()->CloseAllSessions(); | |
| 1979 helper.VerifyDataConsumed(); | |
| 1980 } | |
| 1981 | |
| 1982 TEST_F(SpdyNetworkTransactionTest, CancelledTransaction) { | |
| 1983 // Construct the request. | |
| 1984 SpdySerializedFrame req( | |
| 1985 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 1986 MockWrite writes[] = { | |
| 1987 CreateMockWrite(req), | |
| 1988 }; | |
| 1989 | |
| 1990 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 1991 MockRead reads[] = { | |
| 1992 CreateMockRead(resp), | |
| 1993 // This following read isn't used by the test, except during the | |
| 1994 // RunUntilIdle() call at the end since the SpdySession survives the | |
| 1995 // HttpNetworkTransaction and still tries to continue Read()'ing. Any | |
| 1996 // MockRead will do here. | |
| 1997 MockRead(ASYNC, 0, 0) // EOF | |
| 1998 }; | |
| 1999 | |
| 2000 StaticSocketDataProvider data(reads, arraysize(reads), | |
| 2001 writes, arraysize(writes)); | |
| 2002 | |
| 2003 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 2004 NetLogWithSource(), nullptr); | |
| 2005 helper.RunPreTestSetup(); | |
| 2006 helper.AddData(&data); | |
| 2007 HttpNetworkTransaction* trans = helper.trans(); | |
| 2008 | |
| 2009 TestCompletionCallback callback; | |
| 2010 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 2011 NetLogWithSource()); | |
| 2012 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 2013 helper.ResetTrans(); // Cancel the transaction. | |
| 2014 | |
| 2015 // Flush the MessageLoop while the SpdySessionDependencies (in particular, the | |
| 2016 // MockClientSocketFactory) are still alive. | |
| 2017 base::RunLoop().RunUntilIdle(); | |
| 2018 helper.VerifyDataNotConsumed(); | |
| 2019 } | |
| 2020 | |
| 2021 // Verify that the client sends a Rst Frame upon cancelling the stream. | |
| 2022 TEST_F(SpdyNetworkTransactionTest, CancelledTransactionSendRst) { | |
| 2023 SpdySerializedFrame req( | |
| 2024 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2025 SpdySerializedFrame rst( | |
| 2026 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_CANCEL)); | |
| 2027 MockWrite writes[] = { | |
| 2028 CreateMockWrite(req, 0, SYNCHRONOUS), | |
| 2029 CreateMockWrite(rst, 2, SYNCHRONOUS), | |
| 2030 }; | |
| 2031 | |
| 2032 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2033 MockRead reads[] = { | |
| 2034 CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 0, 3) // EOF | |
| 2035 }; | |
| 2036 | |
| 2037 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2038 | |
| 2039 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 2040 NetLogWithSource(), nullptr); | |
| 2041 helper.RunPreTestSetup(); | |
| 2042 helper.AddData(&data); | |
| 2043 HttpNetworkTransaction* trans = helper.trans(); | |
| 2044 | |
| 2045 TestCompletionCallback callback; | |
| 2046 | |
| 2047 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 2048 NetLogWithSource()); | |
| 2049 EXPECT_THAT(callback.GetResult(rv), IsOk()); | |
| 2050 | |
| 2051 helper.ResetTrans(); | |
| 2052 base::RunLoop().RunUntilIdle(); | |
| 2053 | |
| 2054 helper.VerifyDataConsumed(); | |
| 2055 } | |
| 2056 | |
| 2057 // Verify that the client can correctly deal with the user callback attempting | |
| 2058 // to start another transaction on a session that is closing down. See | |
| 2059 // http://crbug.com/47455 | |
| 2060 TEST_F(SpdyNetworkTransactionTest, StartTransactionOnReadCallback) { | |
| 2061 SpdySerializedFrame req( | |
| 2062 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2063 MockWrite writes[] = {CreateMockWrite(req)}; | |
| 2064 MockWrite writes2[] = {CreateMockWrite(req, 0), | |
| 2065 MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 3)}; | |
| 2066 | |
| 2067 // The indicated length of this frame is longer than its actual length. When | |
| 2068 // the session receives an empty frame after this one, it shuts down the | |
| 2069 // session, and calls the read callback with the incomplete data. | |
| 2070 const uint8_t kGetBodyFrame2[] = { | |
| 2071 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, | |
| 2072 0x07, 'h', 'e', 'l', 'l', 'o', '!', | |
| 2073 }; | |
| 2074 | |
| 2075 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2076 MockRead reads[] = { | |
| 2077 CreateMockRead(resp, 1), | |
| 2078 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause | |
| 2079 MockRead(ASYNC, reinterpret_cast<const char*>(kGetBodyFrame2), | |
| 2080 arraysize(kGetBodyFrame2), 3), | |
| 2081 MockRead(ASYNC, ERR_IO_PENDING, 4), // Force a pause | |
| 2082 MockRead(ASYNC, 0, 0, 5), // EOF | |
| 2083 }; | |
| 2084 MockRead reads2[] = { | |
| 2085 CreateMockRead(resp, 1), MockRead(ASYNC, 0, 0, 2), // EOF | |
| 2086 }; | |
| 2087 | |
| 2088 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2089 SequencedSocketData data2(reads2, arraysize(reads2), writes2, | |
| 2090 arraysize(writes2)); | |
| 2091 | |
| 2092 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 2093 NetLogWithSource(), nullptr); | |
| 2094 helper.RunPreTestSetup(); | |
| 2095 helper.AddData(&data); | |
| 2096 helper.AddData(&data2); | |
| 2097 HttpNetworkTransaction* trans = helper.trans(); | |
| 2098 | |
| 2099 // Start the transaction with basic parameters. | |
| 2100 TestCompletionCallback callback; | |
| 2101 int rv = | |
| 2102 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
| 2103 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 2104 rv = callback.WaitForResult(); | |
| 2105 | |
| 2106 const int kSize = 3000; | |
| 2107 scoped_refptr<IOBuffer> buf(new IOBuffer(kSize)); | |
| 2108 rv = trans->Read( | |
| 2109 buf.get(), kSize, | |
| 2110 base::Bind(&SpdyNetworkTransactionTest::StartTransactionCallback, | |
| 2111 helper.session(), default_url_)); | |
| 2112 ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 2113 // This forces an err_IO_pending, which sets the callback. | |
| 2114 data.Resume(); | |
| 2115 data.RunUntilPaused(); | |
| 2116 | |
| 2117 // This finishes the read. | |
| 2118 data.Resume(); | |
| 2119 base::RunLoop().RunUntilIdle(); | |
| 2120 helper.VerifyDataConsumed(); | |
| 2121 } | |
| 2122 | |
| 2123 // Verify that the client can correctly deal with the user callback deleting the | |
| 2124 // transaction. Failures will usually be valgrind errors. See | |
| 2125 // http://crbug.com/46925 | |
| 2126 TEST_F(SpdyNetworkTransactionTest, DeleteSessionOnReadCallback) { | |
| 2127 SpdySerializedFrame req( | |
| 2128 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2129 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 2130 | |
| 2131 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2132 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2133 MockRead reads[] = { | |
| 2134 CreateMockRead(resp, 1), | |
| 2135 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause | |
| 2136 CreateMockRead(body, 3), MockRead(ASYNC, 0, 0, 4), // EOF | |
| 2137 }; | |
| 2138 | |
| 2139 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2140 | |
| 2141 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 2142 NetLogWithSource(), nullptr); | |
| 2143 helper.RunPreTestSetup(); | |
| 2144 helper.AddData(&data); | |
| 2145 HttpNetworkTransaction* trans = helper.trans(); | |
| 2146 | |
| 2147 // Start the transaction with basic parameters. | |
| 2148 TestCompletionCallback callback; | |
| 2149 int rv = | |
| 2150 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
| 2151 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 2152 rv = callback.WaitForResult(); | |
| 2153 | |
| 2154 // Setup a user callback which will delete the session, and clear out the | |
| 2155 // memory holding the stream object. Note that the callback deletes trans. | |
| 2156 const int kSize = 3000; | |
| 2157 scoped_refptr<IOBuffer> buf(new IOBuffer(kSize)); | |
| 2158 rv = trans->Read( | |
| 2159 buf.get(), | |
| 2160 kSize, | |
| 2161 base::Bind(&SpdyNetworkTransactionTest::DeleteSessionCallback, | |
| 2162 base::Unretained(&helper))); | |
| 2163 ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 2164 data.Resume(); | |
| 2165 | |
| 2166 // Finish running rest of tasks. | |
| 2167 base::RunLoop().RunUntilIdle(); | |
| 2168 helper.VerifyDataConsumed(); | |
| 2169 } | |
| 2170 | |
| 2171 TEST_F(SpdyNetworkTransactionTest, TestRawHeaderSizeSuccessfullRequest) { | |
| 2172 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
| 2173 headers["user-agent"] = ""; | |
| 2174 headers["accept-encoding"] = "gzip, deflate"; | |
| 2175 | |
| 2176 SpdySerializedFrame req( | |
| 2177 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
| 2178 MockWrite writes[] = { | |
| 2179 CreateMockWrite(req, 0), | |
| 2180 }; | |
| 2181 | |
| 2182 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2183 | |
| 2184 SpdySerializedFrame response_body_frame( | |
| 2185 spdy_util_.ConstructSpdyDataFrame(1, "should not include", 18, true)); | |
| 2186 | |
| 2187 MockRead response_headers(CreateMockRead(resp, 1)); | |
| 2188 MockRead reads[] = { | |
| 2189 response_headers, CreateMockRead(response_body_frame, 2), | |
| 2190 MockRead(ASYNC, 0, 0, 3) // EOF | |
| 2191 }; | |
| 2192 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2193 | |
| 2194 TestDelegate delegate; | |
| 2195 SpdyURLRequestContext spdy_url_request_context; | |
| 2196 TestNetworkDelegate network_delegate; | |
| 2197 spdy_url_request_context.set_network_delegate(&network_delegate); | |
| 2198 SSLSocketDataProvider ssl_data(ASYNC, OK); | |
| 2199 ssl_data.next_proto = kProtoHTTP2; | |
| 2200 | |
| 2201 std::unique_ptr<URLRequest> request(spdy_url_request_context.CreateRequest( | |
| 2202 GURL(kDefaultUrl), DEFAULT_PRIORITY, &delegate)); | |
| 2203 spdy_url_request_context.socket_factory().AddSSLSocketDataProvider(&ssl_data); | |
| 2204 spdy_url_request_context.socket_factory().AddSocketDataProvider(&data); | |
| 2205 | |
| 2206 request->Start(); | |
| 2207 base::RunLoop().Run(); | |
| 2208 | |
| 2209 EXPECT_LT(0, request->GetTotalSentBytes()); | |
| 2210 EXPECT_LT(0, request->GetTotalReceivedBytes()); | |
| 2211 EXPECT_EQ(network_delegate.total_network_bytes_sent(), | |
| 2212 request->GetTotalSentBytes()); | |
| 2213 EXPECT_EQ(network_delegate.total_network_bytes_received(), | |
| 2214 request->GetTotalReceivedBytes()); | |
| 2215 EXPECT_EQ(response_headers.data_len, request->raw_header_size()); | |
| 2216 EXPECT_TRUE(data.AllReadDataConsumed()); | |
| 2217 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
| 2218 } | |
| 2219 | |
| 2220 TEST_F(SpdyNetworkTransactionTest, | |
| 2221 TestRawHeaderSizeSuccessfullPushHeadersFirst) { | |
| 2222 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
| 2223 headers["user-agent"] = ""; | |
| 2224 headers["accept-encoding"] = "gzip, deflate"; | |
| 2225 | |
| 2226 SpdySerializedFrame req( | |
| 2227 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
| 2228 SpdySerializedFrame priority( | |
| 2229 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 2230 MockWrite writes[] = { | |
| 2231 CreateMockWrite(req, 0), CreateMockWrite(priority, 2), | |
| 2232 }; | |
| 2233 | |
| 2234 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2235 SpdySerializedFrame response_body_frame( | |
| 2236 spdy_util_.ConstructSpdyDataFrame(1, "should not include", 18, true)); | |
| 2237 | |
| 2238 SpdyHeaderBlock push_headers; | |
| 2239 spdy_util_.AddUrlToHeaderBlock(SpdyString(kDefaultUrl) + "b.dat", | |
| 2240 &push_headers); | |
| 2241 | |
| 2242 SpdySerializedFrame push_init_frame( | |
| 2243 spdy_util_.ConstructInitialSpdyPushFrame(std::move(push_headers), 2, 1)); | |
| 2244 | |
| 2245 SpdySerializedFrame push_headers_frame( | |
| 2246 spdy_util_.ConstructSpdyPushHeaders(2, nullptr, 0)); | |
| 2247 | |
| 2248 SpdySerializedFrame push_body_frame(spdy_util_.ConstructSpdyDataFrame( | |
| 2249 2, "should not include either", 25, false)); | |
| 2250 | |
| 2251 MockRead push_init_read(CreateMockRead(push_init_frame, 1)); | |
| 2252 MockRead response_headers(CreateMockRead(resp, 5)); | |
| 2253 // raw_header_size() will contain the size of the push promise frame | |
| 2254 // initialization. | |
| 2255 int expected_response_headers_size = | |
| 2256 response_headers.data_len + push_init_read.data_len; | |
| 2257 | |
| 2258 MockRead reads[] = { | |
| 2259 push_init_read, | |
| 2260 CreateMockRead(push_headers_frame, 3), | |
| 2261 CreateMockRead(push_body_frame, 4), | |
| 2262 response_headers, | |
| 2263 CreateMockRead(response_body_frame, 6), | |
| 2264 MockRead(ASYNC, 0, 7) // EOF | |
| 2265 }; | |
| 2266 | |
| 2267 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2268 | |
| 2269 TestDelegate delegate; | |
| 2270 SpdyURLRequestContext spdy_url_request_context; | |
| 2271 TestNetworkDelegate network_delegate; | |
| 2272 spdy_url_request_context.set_network_delegate(&network_delegate); | |
| 2273 SSLSocketDataProvider ssl_data(ASYNC, OK); | |
| 2274 ssl_data.next_proto = kProtoHTTP2; | |
| 2275 | |
| 2276 std::unique_ptr<URLRequest> request(spdy_url_request_context.CreateRequest( | |
| 2277 GURL(kDefaultUrl), DEFAULT_PRIORITY, &delegate)); | |
| 2278 spdy_url_request_context.socket_factory().AddSSLSocketDataProvider(&ssl_data); | |
| 2279 spdy_url_request_context.socket_factory().AddSocketDataProvider(&data); | |
| 2280 | |
| 2281 request->Start(); | |
| 2282 base::RunLoop().Run(); | |
| 2283 | |
| 2284 EXPECT_LT(0, request->GetTotalSentBytes()); | |
| 2285 EXPECT_LT(0, request->GetTotalReceivedBytes()); | |
| 2286 EXPECT_EQ(network_delegate.total_network_bytes_sent(), | |
| 2287 request->GetTotalSentBytes()); | |
| 2288 EXPECT_EQ(network_delegate.total_network_bytes_received(), | |
| 2289 request->GetTotalReceivedBytes()); | |
| 2290 EXPECT_EQ(expected_response_headers_size, request->raw_header_size()); | |
| 2291 EXPECT_TRUE(data.AllReadDataConsumed()); | |
| 2292 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
| 2293 } | |
| 2294 | |
| 2295 // Send a spdy request to www.example.org that gets redirected to www.foo.com. | |
| 2296 TEST_F(SpdyNetworkTransactionTest, DISABLED_RedirectGetRequest) { | |
| 2297 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
| 2298 headers["user-agent"] = ""; | |
| 2299 headers["accept-encoding"] = "gzip, deflate"; | |
| 2300 | |
| 2301 // Setup writes/reads to www.example.org | |
| 2302 SpdySerializedFrame req( | |
| 2303 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
| 2304 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReplyRedirect(1)); | |
| 2305 MockWrite writes[] = { | |
| 2306 CreateMockWrite(req, 1), | |
| 2307 }; | |
| 2308 MockRead reads[] = { | |
| 2309 CreateMockRead(resp, 2), MockRead(ASYNC, 0, 0, 3) // EOF | |
| 2310 }; | |
| 2311 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2312 | |
| 2313 // Setup writes/reads to www.foo.com | |
| 2314 SpdyHeaderBlock headers2( | |
| 2315 spdy_util_.ConstructGetHeaderBlock("http://www.foo.com/index.php")); | |
| 2316 headers2["user-agent"] = ""; | |
| 2317 headers2["accept-encoding"] = "gzip, deflate"; | |
| 2318 SpdySerializedFrame req2( | |
| 2319 spdy_util_.ConstructSpdyHeaders(1, std::move(headers2), LOWEST, true)); | |
| 2320 MockWrite writes2[] = { | |
| 2321 CreateMockWrite(req2, 1), | |
| 2322 }; | |
| 2323 | |
| 2324 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2325 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2326 MockRead reads2[] = { | |
| 2327 CreateMockRead(resp2, 2), CreateMockRead(body2, 3), | |
| 2328 MockRead(ASYNC, 0, 0, 4) // EOF | |
| 2329 }; | |
| 2330 | |
| 2331 SequencedSocketData data2(reads2, arraysize(reads2), writes2, | |
| 2332 arraysize(writes2)); | |
| 2333 | |
| 2334 // TODO(erikchen): Make test support SPDYSSL, SPDYNPN | |
| 2335 TestDelegate d; | |
| 2336 { | |
| 2337 SpdyURLRequestContext spdy_url_request_context; | |
| 2338 std::unique_ptr<URLRequest> r(spdy_url_request_context.CreateRequest( | |
| 2339 default_url_, DEFAULT_PRIORITY, &d)); | |
| 2340 spdy_url_request_context.socket_factory(). | |
| 2341 AddSocketDataProvider(&data); | |
| 2342 spdy_url_request_context.socket_factory(). | |
| 2343 AddSocketDataProvider(&data2); | |
| 2344 | |
| 2345 d.set_quit_on_redirect(true); | |
| 2346 r->Start(); | |
| 2347 base::RunLoop().Run(); | |
| 2348 | |
| 2349 EXPECT_EQ(1, d.received_redirect_count()); | |
| 2350 | |
| 2351 r->FollowDeferredRedirect(); | |
| 2352 base::RunLoop().Run(); | |
| 2353 EXPECT_EQ(1, d.response_started_count()); | |
| 2354 EXPECT_FALSE(d.received_data_before_response()); | |
| 2355 EXPECT_EQ(OK, d.request_status()); | |
| 2356 SpdyString contents("hello!"); | |
| 2357 EXPECT_EQ(contents, d.data_received()); | |
| 2358 } | |
| 2359 EXPECT_TRUE(data.AllReadDataConsumed()); | |
| 2360 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
| 2361 EXPECT_TRUE(data2.AllReadDataConsumed()); | |
| 2362 EXPECT_TRUE(data2.AllWriteDataConsumed()); | |
| 2363 } | |
| 2364 | |
| 2365 // Send a spdy request to www.example.org. Get a pushed stream that redirects to | |
| 2366 // www.foo.com. | |
| 2367 TEST_F(SpdyNetworkTransactionTest, DISABLED_RedirectServerPush) { | |
| 2368 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
| 2369 headers["user-agent"] = ""; | |
| 2370 headers["accept-encoding"] = "gzip, deflate"; | |
| 2371 | |
| 2372 // Setup writes/reads to www.example.org | |
| 2373 SpdySerializedFrame req( | |
| 2374 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
| 2375 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2376 SpdySerializedFrame rep(spdy_util_.ConstructSpdyPush( | |
| 2377 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str(), | |
| 2378 "301 Moved Permanently", "http://www.foo.com/index.php")); | |
| 2379 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2380 SpdySerializedFrame rst( | |
| 2381 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_CANCEL)); | |
| 2382 MockWrite writes[] = { | |
| 2383 CreateMockWrite(req, 1), CreateMockWrite(rst, 6), | |
| 2384 }; | |
| 2385 MockRead reads[] = { | |
| 2386 CreateMockRead(resp, 2), CreateMockRead(rep, 3), CreateMockRead(body, 4), | |
| 2387 MockRead(ASYNC, ERR_IO_PENDING, 5), // Force a pause | |
| 2388 MockRead(ASYNC, 0, 0, 7) // EOF | |
| 2389 }; | |
| 2390 | |
| 2391 // Setup writes/reads to www.foo.com | |
| 2392 SpdyHeaderBlock headers2( | |
| 2393 spdy_util_.ConstructGetHeaderBlock("http://www.foo.com/index.php")); | |
| 2394 headers2["user-agent"] = ""; | |
| 2395 headers2["accept-encoding"] = "gzip, deflate"; | |
| 2396 SpdySerializedFrame req2( | |
| 2397 spdy_util_.ConstructSpdyHeaders(1, std::move(headers2), LOWEST, true)); | |
| 2398 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2399 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2400 MockWrite writes2[] = { | |
| 2401 CreateMockWrite(req2, 1), | |
| 2402 }; | |
| 2403 MockRead reads2[] = { | |
| 2404 CreateMockRead(resp2, 2), CreateMockRead(body2, 3), | |
| 2405 MockRead(ASYNC, 0, 0, 5) // EOF | |
| 2406 }; | |
| 2407 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2408 SequencedSocketData data2(reads2, arraysize(reads2), writes2, | |
| 2409 arraysize(writes2)); | |
| 2410 | |
| 2411 TestDelegate d; | |
| 2412 TestDelegate d2; | |
| 2413 SpdyURLRequestContext spdy_url_request_context; | |
| 2414 { | |
| 2415 std::unique_ptr<URLRequest> r(spdy_url_request_context.CreateRequest( | |
| 2416 default_url_, DEFAULT_PRIORITY, &d)); | |
| 2417 spdy_url_request_context.socket_factory(). | |
| 2418 AddSocketDataProvider(&data); | |
| 2419 | |
| 2420 r->Start(); | |
| 2421 base::RunLoop().Run(); | |
| 2422 | |
| 2423 EXPECT_EQ(0, d.received_redirect_count()); | |
| 2424 SpdyString contents("hello!"); | |
| 2425 EXPECT_EQ(contents, d.data_received()); | |
| 2426 | |
| 2427 std::unique_ptr<URLRequest> r2(spdy_url_request_context.CreateRequest( | |
| 2428 GURL(GetDefaultUrlWithPath("/foo.dat")), DEFAULT_PRIORITY, &d2)); | |
| 2429 spdy_url_request_context.socket_factory(). | |
| 2430 AddSocketDataProvider(&data2); | |
| 2431 | |
| 2432 d2.set_quit_on_redirect(true); | |
| 2433 r2->Start(); | |
| 2434 base::RunLoop().Run(); | |
| 2435 EXPECT_EQ(1, d2.received_redirect_count()); | |
| 2436 | |
| 2437 r2->FollowDeferredRedirect(); | |
| 2438 base::RunLoop().Run(); | |
| 2439 EXPECT_EQ(1, d2.response_started_count()); | |
| 2440 EXPECT_FALSE(d2.received_data_before_response()); | |
| 2441 EXPECT_EQ(OK, d2.request_status()); | |
| 2442 SpdyString contents2("hello!"); | |
| 2443 EXPECT_EQ(contents2, d2.data_received()); | |
| 2444 } | |
| 2445 EXPECT_TRUE(data.AllReadDataConsumed()); | |
| 2446 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
| 2447 EXPECT_TRUE(data2.AllReadDataConsumed()); | |
| 2448 EXPECT_TRUE(data2.AllWriteDataConsumed()); | |
| 2449 } | |
| 2450 | |
| 2451 TEST_F(SpdyNetworkTransactionTest, ServerPushSingleDataFrame) { | |
| 2452 SpdySerializedFrame stream1_syn( | |
| 2453 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2454 SpdySerializedFrame stream2_priority( | |
| 2455 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 2456 MockWrite writes[] = { | |
| 2457 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
| 2458 }; | |
| 2459 | |
| 2460 SpdySerializedFrame stream1_reply( | |
| 2461 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2462 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 2463 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 2464 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2465 const char kPushedData[] = "pushed"; | |
| 2466 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
| 2467 2, kPushedData, strlen(kPushedData), true)); | |
| 2468 MockRead reads[] = { | |
| 2469 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
| 2470 CreateMockRead(stream1_body, 4), CreateMockRead(stream2_body, 5), | |
| 2471 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause | |
| 2472 }; | |
| 2473 | |
| 2474 HttpResponseInfo response; | |
| 2475 HttpResponseInfo response2; | |
| 2476 SpdyString expected_push_result("pushed"); | |
| 2477 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2478 RunServerPushTest(&data, | |
| 2479 &response, | |
| 2480 &response2, | |
| 2481 expected_push_result); | |
| 2482 | |
| 2483 // Verify the response headers. | |
| 2484 EXPECT_TRUE(response.headers); | |
| 2485 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 2486 | |
| 2487 // Verify the pushed stream. | |
| 2488 EXPECT_TRUE(response2.headers); | |
| 2489 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
| 2490 } | |
| 2491 | |
| 2492 TEST_F(SpdyNetworkTransactionTest, ServerPushBeforeHeaders) { | |
| 2493 SpdySerializedFrame stream1_syn( | |
| 2494 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2495 SpdySerializedFrame stream2_priority( | |
| 2496 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 2497 MockWrite writes[] = { | |
| 2498 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 2), | |
| 2499 }; | |
| 2500 | |
| 2501 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 2502 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 2503 SpdySerializedFrame stream1_reply( | |
| 2504 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2505 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2506 const char kPushedData[] = "pushed"; | |
| 2507 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
| 2508 2, kPushedData, strlen(kPushedData), true)); | |
| 2509 MockRead reads[] = { | |
| 2510 CreateMockRead(stream2_syn, 1), | |
| 2511 CreateMockRead(stream1_reply, 3), | |
| 2512 CreateMockRead(stream1_body, 4, SYNCHRONOUS), | |
| 2513 CreateMockRead(stream2_body, 5), | |
| 2514 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause | |
| 2515 }; | |
| 2516 | |
| 2517 HttpResponseInfo response; | |
| 2518 HttpResponseInfo response2; | |
| 2519 SpdyString expected_push_result("pushed"); | |
| 2520 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2521 RunServerPushTest(&data, | |
| 2522 &response, | |
| 2523 &response2, | |
| 2524 expected_push_result); | |
| 2525 | |
| 2526 // Verify the response headers. | |
| 2527 EXPECT_TRUE(response.headers); | |
| 2528 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 2529 | |
| 2530 // Verify the pushed stream. | |
| 2531 EXPECT_TRUE(response2.headers); | |
| 2532 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
| 2533 } | |
| 2534 | |
| 2535 TEST_F(SpdyNetworkTransactionTest, ServerPushSingleDataFrame2) { | |
| 2536 SpdySerializedFrame stream1_syn( | |
| 2537 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2538 SpdySerializedFrame stream2_priority( | |
| 2539 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 2540 MockWrite writes[] = { | |
| 2541 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
| 2542 }; | |
| 2543 | |
| 2544 SpdySerializedFrame stream1_reply( | |
| 2545 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2546 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 2547 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 2548 const char kPushedData[] = "pushed"; | |
| 2549 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
| 2550 2, kPushedData, strlen(kPushedData), true)); | |
| 2551 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2552 MockRead reads[] = { | |
| 2553 CreateMockRead(stream1_reply, 1), | |
| 2554 CreateMockRead(stream2_syn, 2), | |
| 2555 CreateMockRead(stream2_body, 4), | |
| 2556 CreateMockRead(stream1_body, 5, SYNCHRONOUS), | |
| 2557 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause | |
| 2558 }; | |
| 2559 | |
| 2560 HttpResponseInfo response; | |
| 2561 HttpResponseInfo response2; | |
| 2562 SpdyString expected_push_result("pushed"); | |
| 2563 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2564 RunServerPushTest(&data, | |
| 2565 &response, | |
| 2566 &response2, | |
| 2567 expected_push_result); | |
| 2568 | |
| 2569 // Verify the response headers. | |
| 2570 EXPECT_TRUE(response.headers); | |
| 2571 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 2572 | |
| 2573 // Verify the pushed stream. | |
| 2574 EXPECT_TRUE(response2.headers); | |
| 2575 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
| 2576 } | |
| 2577 | |
| 2578 TEST_F(SpdyNetworkTransactionTest, ServerPushUpdatesPriority) { | |
| 2579 SpdySerializedFrame stream1_headers( | |
| 2580 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST, true)); | |
| 2581 SpdySerializedFrame stream3_headers( | |
| 2582 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, MEDIUM, true)); | |
| 2583 SpdySerializedFrame stream5_headers( | |
| 2584 spdy_util_.ConstructSpdyGet(nullptr, 0, 5, MEDIUM, true)); | |
| 2585 | |
| 2586 // Stream 1 pushes two streams that are initially prioritized below stream 5. | |
| 2587 // Stream 2 is later prioritized below stream 1 after it matches a request. | |
| 2588 SpdySerializedFrame stream2_priority( | |
| 2589 spdy_util_.ConstructSpdyPriority(2, 5, IDLE, true)); | |
| 2590 SpdySerializedFrame stream4_priority( | |
| 2591 spdy_util_.ConstructSpdyPriority(4, 2, IDLE, true)); | |
| 2592 SpdySerializedFrame stream4_priority_update( | |
| 2593 spdy_util_.ConstructSpdyPriority(4, 5, IDLE, true)); | |
| 2594 SpdySerializedFrame stream2_priority_update( | |
| 2595 spdy_util_.ConstructSpdyPriority(2, 1, HIGHEST, true)); | |
| 2596 | |
| 2597 MockWrite writes[] = { | |
| 2598 CreateMockWrite(stream1_headers, 0), | |
| 2599 CreateMockWrite(stream3_headers, 1), | |
| 2600 CreateMockWrite(stream5_headers, 2), | |
| 2601 CreateMockWrite(stream2_priority, 7), | |
| 2602 CreateMockWrite(stream4_priority, 9), | |
| 2603 CreateMockWrite(stream4_priority_update, 11), | |
| 2604 CreateMockWrite(stream2_priority_update, 12), | |
| 2605 }; | |
| 2606 | |
| 2607 SpdySerializedFrame stream1_reply( | |
| 2608 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2609 SpdySerializedFrame stream3_reply( | |
| 2610 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 2611 SpdySerializedFrame stream5_reply( | |
| 2612 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5)); | |
| 2613 | |
| 2614 SpdySerializedFrame stream2_push(spdy_util_.ConstructSpdyPush( | |
| 2615 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 2616 SpdySerializedFrame stream4_push(spdy_util_.ConstructSpdyPush( | |
| 2617 nullptr, 0, 4, 1, GetDefaultUrlWithPath("/bar.dat").c_str())); | |
| 2618 | |
| 2619 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2620 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame(2, true)); | |
| 2621 SpdySerializedFrame stream3_body(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
| 2622 SpdySerializedFrame stream5_body(spdy_util_.ConstructSpdyDataFrame(5, true)); | |
| 2623 | |
| 2624 MockRead reads[] = { | |
| 2625 CreateMockRead(stream1_reply, 3), | |
| 2626 CreateMockRead(stream3_reply, 4), | |
| 2627 CreateMockRead(stream5_reply, 5), | |
| 2628 CreateMockRead(stream2_push, 6), | |
| 2629 CreateMockRead(stream4_push, 8), | |
| 2630 MockRead(ASYNC, ERR_IO_PENDING, 10), | |
| 2631 CreateMockRead(stream1_body, 13), | |
| 2632 CreateMockRead(stream2_body, 14), | |
| 2633 CreateMockRead(stream3_body, 15), | |
| 2634 CreateMockRead(stream5_body, 16), | |
| 2635 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 17), // Force a pause | |
| 2636 }; | |
| 2637 | |
| 2638 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2639 SequencedSocketData data_placeholder1(nullptr, 0, nullptr, 0); | |
| 2640 SequencedSocketData data_placeholder2(nullptr, 0, nullptr, 0); | |
| 2641 SequencedSocketData data_placeholder3(nullptr, 0, nullptr, 0); | |
| 2642 | |
| 2643 NormalSpdyTransactionHelper helper(CreateGetRequest(), LOWEST, | |
| 2644 NetLogWithSource(), nullptr); | |
| 2645 helper.RunPreTestSetup(); | |
| 2646 helper.AddData(&data); | |
| 2647 helper.AddData(&data_placeholder1); // other requests reuse the same socket | |
| 2648 helper.AddData(&data_placeholder2); | |
| 2649 helper.AddData(&data_placeholder3); | |
| 2650 HttpNetworkTransaction trans1(HIGHEST, helper.session()); | |
| 2651 HttpNetworkTransaction trans3(MEDIUM, helper.session()); | |
| 2652 HttpNetworkTransaction trans5(MEDIUM, helper.session()); | |
| 2653 | |
| 2654 TestCompletionCallback callback1; | |
| 2655 TestCompletionCallback callback3; | |
| 2656 TestCompletionCallback callback5; | |
| 2657 | |
| 2658 // Start the ordinary requests. | |
| 2659 NetLogWithSource log; | |
| 2660 ASSERT_THAT(trans1.Start(&CreateGetRequest(), callback1.callback(), log), | |
| 2661 IsError(ERR_IO_PENDING)); | |
| 2662 ASSERT_THAT(trans3.Start(&CreateGetRequest(), callback3.callback(), log), | |
| 2663 IsError(ERR_IO_PENDING)); | |
| 2664 ASSERT_THAT(trans5.Start(&CreateGetRequest(), callback5.callback(), log), | |
| 2665 IsError(ERR_IO_PENDING)); | |
| 2666 data.RunUntilPaused(); | |
| 2667 | |
| 2668 // Start a request that matches the push. | |
| 2669 HttpRequestInfo push_req = CreateGetRequest(); | |
| 2670 push_req.url = GURL(GetDefaultUrlWithPath("/foo.dat")); | |
| 2671 | |
| 2672 HttpNetworkTransaction trans2(HIGHEST, helper.session()); | |
| 2673 TestCompletionCallback callback2; | |
| 2674 ASSERT_THAT(trans2.Start(&push_req, callback2.callback(), log), | |
| 2675 IsError(ERR_IO_PENDING)); | |
| 2676 data.Resume(); | |
| 2677 | |
| 2678 base::RunLoop().RunUntilIdle(); | |
| 2679 ASSERT_THAT(callback1.WaitForResult(), IsOk()); | |
| 2680 ASSERT_THAT(callback2.WaitForResult(), IsOk()); | |
| 2681 ASSERT_THAT(callback3.WaitForResult(), IsOk()); | |
| 2682 ASSERT_THAT(callback5.WaitForResult(), IsOk()); | |
| 2683 helper.VerifyDataConsumed(); | |
| 2684 } | |
| 2685 | |
| 2686 TEST_F(SpdyNetworkTransactionTest, ServerPushServerAborted) { | |
| 2687 SpdySerializedFrame stream1_syn( | |
| 2688 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2689 SpdySerializedFrame stream2_priority( | |
| 2690 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 2691 MockWrite writes[] = { | |
| 2692 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
| 2693 }; | |
| 2694 | |
| 2695 SpdySerializedFrame stream1_reply( | |
| 2696 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2697 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 2698 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 2699 SpdySerializedFrame stream2_rst( | |
| 2700 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_PROTOCOL_ERROR)); | |
| 2701 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2702 MockRead reads[] = { | |
| 2703 CreateMockRead(stream1_reply, 1), | |
| 2704 CreateMockRead(stream2_syn, 2, SYNCHRONOUS), | |
| 2705 CreateMockRead(stream2_rst, 4), | |
| 2706 CreateMockRead(stream1_body, 5, SYNCHRONOUS), | |
| 2707 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause | |
| 2708 }; | |
| 2709 | |
| 2710 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2711 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 2712 NetLogWithSource(), nullptr); | |
| 2713 | |
| 2714 helper.RunPreTestSetup(); | |
| 2715 helper.AddData(&data); | |
| 2716 | |
| 2717 HttpNetworkTransaction* trans = helper.trans(); | |
| 2718 | |
| 2719 // Start the transaction with basic parameters. | |
| 2720 TestCompletionCallback callback; | |
| 2721 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 2722 NetLogWithSource()); | |
| 2723 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 2724 rv = callback.WaitForResult(); | |
| 2725 EXPECT_THAT(rv, IsOk()); | |
| 2726 | |
| 2727 // Verify that we consumed all test data. | |
| 2728 base::RunLoop().RunUntilIdle(); | |
| 2729 EXPECT_TRUE(data.AllReadDataConsumed()); | |
| 2730 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
| 2731 | |
| 2732 // Verify the response headers. | |
| 2733 HttpResponseInfo response = *trans->GetResponseInfo(); | |
| 2734 EXPECT_TRUE(response.headers); | |
| 2735 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 2736 } | |
| 2737 | |
| 2738 // Verify that we don't leak streams and that we properly send a reset | |
| 2739 // if the server pushes the same stream twice. | |
| 2740 TEST_F(SpdyNetworkTransactionTest, ServerPushDuplicate) { | |
| 2741 SpdySerializedFrame stream1_syn( | |
| 2742 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2743 SpdySerializedFrame stream2_priority( | |
| 2744 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 2745 SpdySerializedFrame stream3_rst( | |
| 2746 spdy_util_.ConstructSpdyRstStream(4, ERROR_CODE_PROTOCOL_ERROR)); | |
| 2747 MockWrite writes[] = { | |
| 2748 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
| 2749 CreateMockWrite(stream3_rst, 5), | |
| 2750 }; | |
| 2751 | |
| 2752 SpdySerializedFrame stream1_reply( | |
| 2753 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2754 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 2755 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 2756 SpdySerializedFrame stream3_syn(spdy_util_.ConstructSpdyPush( | |
| 2757 nullptr, 0, 4, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 2758 | |
| 2759 const char kPushedData[] = "pushed"; | |
| 2760 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2761 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
| 2762 2, kPushedData, strlen(kPushedData), true)); | |
| 2763 | |
| 2764 MockRead reads[] = { | |
| 2765 CreateMockRead(stream1_reply, 1), | |
| 2766 CreateMockRead(stream2_syn, 2), | |
| 2767 CreateMockRead(stream3_syn, 4), | |
| 2768 CreateMockRead(stream1_body, 6), | |
| 2769 CreateMockRead(stream2_body, 7), | |
| 2770 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 8), // Force a pause | |
| 2771 }; | |
| 2772 | |
| 2773 HttpResponseInfo response; | |
| 2774 HttpResponseInfo response2; | |
| 2775 SpdyString expected_push_result("pushed"); | |
| 2776 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2777 RunServerPushTest(&data, | |
| 2778 &response, | |
| 2779 &response2, | |
| 2780 expected_push_result); | |
| 2781 | |
| 2782 // Verify the response headers. | |
| 2783 EXPECT_TRUE(response.headers); | |
| 2784 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 2785 | |
| 2786 // Verify the pushed stream. | |
| 2787 EXPECT_TRUE(response2.headers); | |
| 2788 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
| 2789 } | |
| 2790 | |
| 2791 TEST_F(SpdyNetworkTransactionTest, ServerPushMultipleDataFrame) { | |
| 2792 SpdySerializedFrame stream1_syn( | |
| 2793 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2794 SpdySerializedFrame stream2_priority( | |
| 2795 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 2796 MockWrite writes[] = { | |
| 2797 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
| 2798 }; | |
| 2799 | |
| 2800 SpdySerializedFrame stream1_reply( | |
| 2801 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2802 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 2803 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 2804 static const char kPushedData[] = "pushed my darling hello my baby"; | |
| 2805 SpdySerializedFrame stream2_body_base(spdy_util_.ConstructSpdyDataFrame( | |
| 2806 2, kPushedData, strlen(kPushedData), true)); | |
| 2807 const size_t kChunkSize = strlen(kPushedData) / 4; | |
| 2808 SpdySerializedFrame stream2_body1(stream2_body_base.data(), kChunkSize, | |
| 2809 false); | |
| 2810 SpdySerializedFrame stream2_body2(stream2_body_base.data() + kChunkSize, | |
| 2811 kChunkSize, false); | |
| 2812 SpdySerializedFrame stream2_body3(stream2_body_base.data() + 2 * kChunkSize, | |
| 2813 kChunkSize, false); | |
| 2814 SpdySerializedFrame stream2_body4(stream2_body_base.data() + 3 * kChunkSize, | |
| 2815 stream2_body_base.size() - 3 * kChunkSize, | |
| 2816 false); | |
| 2817 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2818 MockRead reads[] = { | |
| 2819 CreateMockRead(stream1_reply, 1), | |
| 2820 CreateMockRead(stream2_syn, 2), | |
| 2821 CreateMockRead(stream2_body1, 4), | |
| 2822 CreateMockRead(stream2_body2, 5), | |
| 2823 CreateMockRead(stream2_body3, 6), | |
| 2824 CreateMockRead(stream2_body4, 7), | |
| 2825 CreateMockRead(stream1_body, 8, SYNCHRONOUS), | |
| 2826 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 9), // Force a pause | |
| 2827 }; | |
| 2828 | |
| 2829 HttpResponseInfo response; | |
| 2830 HttpResponseInfo response2; | |
| 2831 SpdyString expected_push_result("pushed my darling hello my baby"); | |
| 2832 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2833 RunServerPushTest(&data, &response, &response2, kPushedData); | |
| 2834 | |
| 2835 // Verify the response headers. | |
| 2836 EXPECT_TRUE(response.headers); | |
| 2837 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 2838 | |
| 2839 // Verify the pushed stream. | |
| 2840 EXPECT_TRUE(response2.headers); | |
| 2841 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
| 2842 } | |
| 2843 | |
| 2844 TEST_F(SpdyNetworkTransactionTest, ServerPushMultipleDataFrameInterrupted) { | |
| 2845 SpdySerializedFrame stream1_syn( | |
| 2846 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2847 SpdySerializedFrame stream2_priority( | |
| 2848 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 2849 MockWrite writes[] = { | |
| 2850 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
| 2851 }; | |
| 2852 | |
| 2853 SpdySerializedFrame stream1_reply( | |
| 2854 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2855 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 2856 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 2857 static const char kPushedData[] = "pushed my darling hello my baby"; | |
| 2858 SpdySerializedFrame stream2_body_base(spdy_util_.ConstructSpdyDataFrame( | |
| 2859 2, kPushedData, strlen(kPushedData), true)); | |
| 2860 const size_t kChunkSize = strlen(kPushedData) / 4; | |
| 2861 SpdySerializedFrame stream2_body1(stream2_body_base.data(), kChunkSize, | |
| 2862 false); | |
| 2863 SpdySerializedFrame stream2_body2(stream2_body_base.data() + kChunkSize, | |
| 2864 kChunkSize, false); | |
| 2865 SpdySerializedFrame stream2_body3(stream2_body_base.data() + 2 * kChunkSize, | |
| 2866 kChunkSize, false); | |
| 2867 SpdySerializedFrame stream2_body4(stream2_body_base.data() + 3 * kChunkSize, | |
| 2868 stream2_body_base.size() - 3 * kChunkSize, | |
| 2869 false); | |
| 2870 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2871 MockRead reads[] = { | |
| 2872 CreateMockRead(stream1_reply, 1), | |
| 2873 CreateMockRead(stream2_syn, 2), | |
| 2874 CreateMockRead(stream2_body1, 4), | |
| 2875 CreateMockRead(stream2_body2, 5), | |
| 2876 CreateMockRead(stream2_body3, 6), | |
| 2877 CreateMockRead(stream2_body4, 7), | |
| 2878 CreateMockRead(stream1_body, 8, SYNCHRONOUS), | |
| 2879 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 9) // Force a pause. | |
| 2880 }; | |
| 2881 | |
| 2882 HttpResponseInfo response; | |
| 2883 HttpResponseInfo response2; | |
| 2884 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2885 RunServerPushTest(&data, &response, &response2, kPushedData); | |
| 2886 | |
| 2887 // Verify the response headers. | |
| 2888 EXPECT_TRUE(response.headers); | |
| 2889 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 2890 | |
| 2891 // Verify the pushed stream. | |
| 2892 EXPECT_TRUE(response2.headers); | |
| 2893 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
| 2894 } | |
| 2895 | |
| 2896 TEST_F(SpdyNetworkTransactionTest, ServerPushInvalidUrl) { | |
| 2897 // Coverage on how a non-empty invalid GURL in a PUSH_PROMISE is handled. | |
| 2898 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
| 2899 SpdySerializedFrame req( | |
| 2900 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
| 2901 | |
| 2902 // Can't use ConstructSpdyPush here since it wants to parse a URL and | |
| 2903 // split it into the appropriate :header pieces. So we have to hand-fill | |
| 2904 // those pieces in. | |
| 2905 SpdyFramer response_spdy_framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 2906 SpdyHeaderBlock push_promise_header_block; | |
| 2907 push_promise_header_block[spdy_util_.GetHostKey()] = ""; | |
| 2908 push_promise_header_block[spdy_util_.GetSchemeKey()] = ""; | |
| 2909 push_promise_header_block[spdy_util_.GetPathKey()] = "/index.html"; | |
| 2910 | |
| 2911 SpdyPushPromiseIR push_promise(1, 2, std::move(push_promise_header_block)); | |
| 2912 SpdySerializedFrame push_promise_frame( | |
| 2913 response_spdy_framer.SerializeFrame(push_promise)); | |
| 2914 | |
| 2915 SpdySerializedFrame stream2_rst( | |
| 2916 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_PROTOCOL_ERROR)); | |
| 2917 | |
| 2918 MockWrite writes[] = {CreateMockWrite(req, 0), | |
| 2919 CreateMockWrite(stream2_rst, 2)}; | |
| 2920 MockRead reads[] = { | |
| 2921 CreateMockRead(push_promise_frame, 1), MockRead(ASYNC, 0, 3) /* EOF */ | |
| 2922 }; | |
| 2923 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2924 RunBrokenPushTest(&data, ERR_CONNECTION_CLOSED); | |
| 2925 } | |
| 2926 | |
| 2927 TEST_F(SpdyNetworkTransactionTest, ServerPushInvalidAssociatedStreamID0) { | |
| 2928 SpdySerializedFrame stream1_syn( | |
| 2929 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2930 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
| 2931 0, ERROR_CODE_PROTOCOL_ERROR, "Framer error: 1 (INVALID_STREAM_ID).")); | |
| 2932 MockWrite writes[] = { | |
| 2933 CreateMockWrite(stream1_syn, 0), CreateMockWrite(goaway, 3), | |
| 2934 }; | |
| 2935 | |
| 2936 SpdySerializedFrame stream1_reply( | |
| 2937 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2938 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 2939 nullptr, 0, 2, 0, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 2940 MockRead reads[] = { | |
| 2941 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
| 2942 }; | |
| 2943 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2944 RunBrokenPushTest(&data, OK); | |
| 2945 } | |
| 2946 | |
| 2947 TEST_F(SpdyNetworkTransactionTest, ServerPushInvalidAssociatedStreamID9) { | |
| 2948 SpdySerializedFrame stream1_syn( | |
| 2949 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2950 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2951 SpdySerializedFrame stream2_rst( | |
| 2952 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_STREAM_CLOSED)); | |
| 2953 MockWrite writes[] = { | |
| 2954 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_rst, 3), | |
| 2955 }; | |
| 2956 | |
| 2957 SpdySerializedFrame stream1_reply( | |
| 2958 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2959 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 2960 nullptr, 0, 2, 9, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 2961 MockRead reads[] = { | |
| 2962 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
| 2963 CreateMockRead(stream1_body, 4), | |
| 2964 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5), // Force a pause | |
| 2965 }; | |
| 2966 | |
| 2967 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2968 RunBrokenPushTest(&data, OK); | |
| 2969 } | |
| 2970 | |
| 2971 TEST_F(SpdyNetworkTransactionTest, ServerPushNoURL) { | |
| 2972 SpdySerializedFrame stream1_syn( | |
| 2973 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 2974 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 2975 SpdySerializedFrame stream2_rst( | |
| 2976 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_PROTOCOL_ERROR)); | |
| 2977 MockWrite writes[] = { | |
| 2978 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_rst, 3), | |
| 2979 }; | |
| 2980 | |
| 2981 SpdySerializedFrame stream1_reply( | |
| 2982 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 2983 SpdyHeaderBlock incomplete_headers; | |
| 2984 incomplete_headers[spdy_util_.GetStatusKey()] = "200 OK"; | |
| 2985 incomplete_headers["hello"] = "bye"; | |
| 2986 SpdySerializedFrame stream2_syn(spdy_util_.ConstructInitialSpdyPushFrame( | |
| 2987 std::move(incomplete_headers), 2, 1)); | |
| 2988 MockRead reads[] = { | |
| 2989 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
| 2990 CreateMockRead(stream1_body, 4), | |
| 2991 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5) // Force a pause | |
| 2992 }; | |
| 2993 | |
| 2994 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 2995 RunBrokenPushTest(&data, OK); | |
| 2996 } | |
| 2997 | |
| 2998 // PUSH_PROMISE on a server-initiated stream should trigger GOAWAY. | |
| 2999 TEST_F(SpdyNetworkTransactionTest, ServerPushOnPushedStream) { | |
| 3000 SpdySerializedFrame stream1_syn( | |
| 3001 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3002 SpdySerializedFrame stream2_priority( | |
| 3003 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 3004 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
| 3005 2, ERROR_CODE_PROTOCOL_ERROR, "Push on even stream id.")); | |
| 3006 MockWrite writes[] = { | |
| 3007 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
| 3008 CreateMockWrite(goaway, 5), | |
| 3009 }; | |
| 3010 | |
| 3011 SpdySerializedFrame stream1_reply( | |
| 3012 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3013 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 3014 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 3015 SpdySerializedFrame stream3_syn(spdy_util_.ConstructSpdyPush( | |
| 3016 nullptr, 0, 4, 2, GetDefaultUrlWithPath("/bar.dat").c_str())); | |
| 3017 MockRead reads[] = { | |
| 3018 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
| 3019 CreateMockRead(stream3_syn, 4), | |
| 3020 }; | |
| 3021 | |
| 3022 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3023 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3024 NetLogWithSource(), nullptr); | |
| 3025 helper.RunToCompletion(&data); | |
| 3026 } | |
| 3027 | |
| 3028 // PUSH_PROMISE on a closed client-initiated stream should trigger RST_STREAM. | |
| 3029 TEST_F(SpdyNetworkTransactionTest, ServerPushOnClosedStream) { | |
| 3030 SpdySerializedFrame stream1_syn( | |
| 3031 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3032 SpdySerializedFrame rst( | |
| 3033 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_STREAM_CLOSED)); | |
| 3034 MockWrite writes[] = { | |
| 3035 CreateMockWrite(stream1_syn, 0), CreateMockWrite(rst, 5), | |
| 3036 }; | |
| 3037 | |
| 3038 SpdySerializedFrame stream1_reply( | |
| 3039 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3040 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 3041 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 3042 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 3043 MockRead reads[] = { | |
| 3044 CreateMockRead(stream1_reply, 1), CreateMockRead(stream1_body, 2), | |
| 3045 CreateMockRead(stream2_syn, 3), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 4), | |
| 3046 }; | |
| 3047 | |
| 3048 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3049 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3050 NetLogWithSource(), nullptr); | |
| 3051 helper.RunPreTestSetup(); | |
| 3052 helper.AddData(&data); | |
| 3053 | |
| 3054 HttpNetworkTransaction* trans = helper.trans(); | |
| 3055 | |
| 3056 TestCompletionCallback callback; | |
| 3057 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 3058 NetLogWithSource()); | |
| 3059 rv = callback.GetResult(rv); | |
| 3060 EXPECT_THAT(rv, IsOk()); | |
| 3061 | |
| 3062 // Finish async network reads/writes. | |
| 3063 base::RunLoop().RunUntilIdle(); | |
| 3064 | |
| 3065 HttpResponseInfo response = *trans->GetResponseInfo(); | |
| 3066 EXPECT_TRUE(response.headers); | |
| 3067 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 3068 | |
| 3069 EXPECT_TRUE(data.AllReadDataConsumed()); | |
| 3070 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
| 3071 VerifyStreamsClosed(helper); | |
| 3072 } | |
| 3073 | |
| 3074 // PUSH_PROMISE on a server-initiated stream should trigger GOAWAY even if | |
| 3075 // stream is closed. | |
| 3076 TEST_F(SpdyNetworkTransactionTest, ServerPushOnClosedPushedStream) { | |
| 3077 SpdySerializedFrame stream1_syn( | |
| 3078 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3079 SpdySerializedFrame stream2_priority( | |
| 3080 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 3081 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
| 3082 2, ERROR_CODE_PROTOCOL_ERROR, "Push on even stream id.")); | |
| 3083 MockWrite writes[] = { | |
| 3084 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
| 3085 CreateMockWrite(goaway, 8), | |
| 3086 }; | |
| 3087 | |
| 3088 SpdySerializedFrame stream1_reply( | |
| 3089 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3090 SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush( | |
| 3091 nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str())); | |
| 3092 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 3093 const char kPushedData[] = "pushed"; | |
| 3094 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
| 3095 2, kPushedData, strlen(kPushedData), true)); | |
| 3096 SpdySerializedFrame stream3_syn(spdy_util_.ConstructSpdyPush( | |
| 3097 nullptr, 0, 4, 2, GetDefaultUrlWithPath("/bar.dat").c_str())); | |
| 3098 | |
| 3099 MockRead reads[] = { | |
| 3100 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
| 3101 CreateMockRead(stream1_body, 4), CreateMockRead(stream2_body, 5), | |
| 3102 MockRead(ASYNC, ERR_IO_PENDING, 6), CreateMockRead(stream3_syn, 7), | |
| 3103 }; | |
| 3104 | |
| 3105 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3106 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3107 NetLogWithSource(), nullptr); | |
| 3108 helper.RunPreTestSetup(); | |
| 3109 helper.AddData(&data); | |
| 3110 | |
| 3111 HttpNetworkTransaction* trans1 = helper.trans(); | |
| 3112 TestCompletionCallback callback1; | |
| 3113 int rv = trans1->Start(&CreateGetRequest(), callback1.callback(), | |
| 3114 NetLogWithSource()); | |
| 3115 rv = callback1.GetResult(rv); | |
| 3116 EXPECT_THAT(rv, IsOk()); | |
| 3117 HttpResponseInfo response = *trans1->GetResponseInfo(); | |
| 3118 EXPECT_TRUE(response.headers); | |
| 3119 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 3120 | |
| 3121 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
| 3122 TestCompletionCallback callback2; | |
| 3123 rv = trans2.Start(&CreateGetPushRequest(), callback2.callback(), | |
| 3124 NetLogWithSource()); | |
| 3125 rv = callback2.GetResult(rv); | |
| 3126 EXPECT_THAT(rv, IsOk()); | |
| 3127 response = *trans2.GetResponseInfo(); | |
| 3128 EXPECT_TRUE(response.headers); | |
| 3129 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 3130 SpdyString result; | |
| 3131 ReadResult(&trans2, &result); | |
| 3132 EXPECT_EQ(kPushedData, result); | |
| 3133 | |
| 3134 data.Resume(); | |
| 3135 base::RunLoop().RunUntilIdle(); | |
| 3136 | |
| 3137 EXPECT_TRUE(data.AllReadDataConsumed()); | |
| 3138 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
| 3139 } | |
| 3140 | |
| 3141 // Verify that various response headers parse correctly through the HTTP layer. | |
| 3142 TEST_F(SpdyNetworkTransactionTest, ResponseHeaders) { | |
| 3143 struct ResponseHeadersTests { | |
| 3144 int num_headers; | |
| 3145 const char* extra_headers[5]; | |
| 3146 SpdyHeaderBlock expected_headers; | |
| 3147 } test_cases[] = {// This uses a multi-valued cookie header. | |
| 3148 { | |
| 3149 2, | |
| 3150 {"cookie", "val1", "cookie", | |
| 3151 "val2", // will get appended separated by nullptr | |
| 3152 nullptr}, | |
| 3153 }, | |
| 3154 // This is the minimalist set of headers. | |
| 3155 { | |
| 3156 0, {nullptr}, | |
| 3157 }, | |
| 3158 // Headers with a comma separated list. | |
| 3159 { | |
| 3160 1, {"cookie", "val1,val2", nullptr}, | |
| 3161 }}; | |
| 3162 | |
| 3163 test_cases[0].expected_headers["status"] = "200"; | |
| 3164 test_cases[1].expected_headers["status"] = "200"; | |
| 3165 test_cases[2].expected_headers["status"] = "200"; | |
| 3166 | |
| 3167 test_cases[0].expected_headers["hello"] = "bye"; | |
| 3168 test_cases[1].expected_headers["hello"] = "bye"; | |
| 3169 test_cases[2].expected_headers["hello"] = "bye"; | |
| 3170 | |
| 3171 test_cases[0].expected_headers["cookie"] = SpdyStringPiece("val1\0val2", 9); | |
| 3172 test_cases[2].expected_headers["cookie"] = "val1,val2"; | |
| 3173 | |
| 3174 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
| 3175 SpdyTestUtil spdy_test_util; | |
| 3176 SpdySerializedFrame req( | |
| 3177 spdy_test_util.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3178 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 3179 | |
| 3180 SpdySerializedFrame resp(spdy_test_util.ConstructSpdyGetReply( | |
| 3181 test_cases[i].extra_headers, test_cases[i].num_headers, 1)); | |
| 3182 SpdySerializedFrame body(spdy_test_util.ConstructSpdyDataFrame(1, true)); | |
| 3183 MockRead reads[] = { | |
| 3184 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 3185 MockRead(ASYNC, 0, 3) // EOF | |
| 3186 }; | |
| 3187 | |
| 3188 SequencedSocketData data(reads, arraysize(reads), writes, | |
| 3189 arraysize(writes)); | |
| 3190 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3191 NetLogWithSource(), nullptr); | |
| 3192 helper.RunToCompletion(&data); | |
| 3193 TransactionHelperResult out = helper.output(); | |
| 3194 | |
| 3195 EXPECT_THAT(out.rv, IsOk()); | |
| 3196 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 3197 EXPECT_EQ("hello!", out.response_data); | |
| 3198 | |
| 3199 scoped_refptr<HttpResponseHeaders> headers = out.response_info.headers; | |
| 3200 EXPECT_TRUE(headers); | |
| 3201 size_t iter = 0; | |
| 3202 SpdyString name, value; | |
| 3203 SpdyHeaderBlock header_block; | |
| 3204 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 3205 auto value_it = header_block.find(name); | |
| 3206 if (value_it == header_block.end() || value_it->second.empty()) { | |
| 3207 header_block[name] = value; | |
| 3208 } else { | |
| 3209 SpdyString joint_value = value_it->second.as_string(); | |
| 3210 joint_value.append(1, '\0'); | |
| 3211 joint_value.append(value); | |
| 3212 header_block[name] = joint_value; | |
| 3213 } | |
| 3214 } | |
| 3215 EXPECT_EQ(test_cases[i].expected_headers, header_block); | |
| 3216 } | |
| 3217 } | |
| 3218 | |
| 3219 // Verify that various response headers parse vary fields correctly through the | |
| 3220 // HTTP layer, and the response matches the request. | |
| 3221 TEST_F(SpdyNetworkTransactionTest, ResponseHeadersVary) { | |
| 3222 // Modify the following data to change/add test cases: | |
| 3223 struct ResponseTests { | |
| 3224 bool vary_matches; | |
| 3225 int num_headers[2]; | |
| 3226 const char* extra_headers[2][16]; | |
| 3227 } test_cases[] = { | |
| 3228 // Test the case of a multi-valued cookie. When the value is delimited | |
| 3229 // with NUL characters, it needs to be unfolded into multiple headers. | |
| 3230 {true, | |
| 3231 {1, 3}, | |
| 3232 {{"cookie", "val1,val2", nullptr}, | |
| 3233 {spdy_util_.GetStatusKey(), "200", spdy_util_.GetPathKey(), | |
| 3234 "/index.php", "vary", "cookie", nullptr}}}, | |
| 3235 {// Multiple vary fields. | |
| 3236 true, | |
| 3237 {2, 4}, | |
| 3238 {{"friend", "barney", "enemy", "snaggletooth", nullptr}, | |
| 3239 {spdy_util_.GetStatusKey(), "200", spdy_util_.GetPathKey(), | |
| 3240 "/index.php", "vary", "friend", "vary", "enemy", nullptr}}}, | |
| 3241 {// Test a '*' vary field. | |
| 3242 false, | |
| 3243 {1, 3}, | |
| 3244 {{"cookie", "val1,val2", nullptr}, | |
| 3245 {spdy_util_.GetStatusKey(), "200", spdy_util_.GetPathKey(), | |
| 3246 "/index.php", "vary", "*", nullptr}}}, | |
| 3247 {// Multiple comma-separated vary fields. | |
| 3248 true, | |
| 3249 {2, 3}, | |
| 3250 {{"friend", "barney", "enemy", "snaggletooth", nullptr}, | |
| 3251 {spdy_util_.GetStatusKey(), "200", spdy_util_.GetPathKey(), | |
| 3252 "/index.php", "vary", "friend,enemy", nullptr}}}}; | |
| 3253 | |
| 3254 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
| 3255 SpdyTestUtil spdy_test_util; | |
| 3256 | |
| 3257 // Construct the request. | |
| 3258 SpdySerializedFrame frame_req(spdy_test_util.ConstructSpdyGet( | |
| 3259 test_cases[i].extra_headers[0], test_cases[i].num_headers[0], 1, LOWEST, | |
| 3260 true)); | |
| 3261 | |
| 3262 MockWrite writes[] = { | |
| 3263 CreateMockWrite(frame_req, 0), | |
| 3264 }; | |
| 3265 | |
| 3266 // Construct the reply. | |
| 3267 SpdyHeaderBlock reply_headers; | |
| 3268 AppendToHeaderBlock(test_cases[i].extra_headers[1], | |
| 3269 test_cases[i].num_headers[1], | |
| 3270 &reply_headers); | |
| 3271 // Construct the expected header reply string before moving |reply_headers|. | |
| 3272 SpdyString expected_reply = | |
| 3273 spdy_test_util.ConstructSpdyReplyString(reply_headers); | |
| 3274 | |
| 3275 SpdySerializedFrame frame_reply( | |
| 3276 spdy_test_util.ConstructSpdyReply(1, std::move(reply_headers))); | |
| 3277 | |
| 3278 SpdySerializedFrame body(spdy_test_util.ConstructSpdyDataFrame(1, true)); | |
| 3279 MockRead reads[] = { | |
| 3280 CreateMockRead(frame_reply, 1), CreateMockRead(body, 2), | |
| 3281 MockRead(ASYNC, 0, 3) // EOF | |
| 3282 }; | |
| 3283 | |
| 3284 // Attach the headers to the request. | |
| 3285 int header_count = test_cases[i].num_headers[0]; | |
| 3286 | |
| 3287 HttpRequestInfo request = CreateGetRequest(); | |
| 3288 for (int ct = 0; ct < header_count; ct++) { | |
| 3289 const char* header_key = test_cases[i].extra_headers[0][ct * 2]; | |
| 3290 const char* header_value = test_cases[i].extra_headers[0][ct * 2 + 1]; | |
| 3291 request.extra_headers.SetHeader(header_key, header_value); | |
| 3292 } | |
| 3293 | |
| 3294 SequencedSocketData data(reads, arraysize(reads), writes, | |
| 3295 arraysize(writes)); | |
| 3296 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 3297 NetLogWithSource(), nullptr); | |
| 3298 helper.RunToCompletion(&data); | |
| 3299 TransactionHelperResult out = helper.output(); | |
| 3300 | |
| 3301 EXPECT_EQ(OK, out.rv) << i; | |
| 3302 EXPECT_EQ("HTTP/1.1 200", out.status_line) << i; | |
| 3303 EXPECT_EQ("hello!", out.response_data) << i; | |
| 3304 | |
| 3305 // Test the response information. | |
| 3306 EXPECT_EQ(out.response_info.vary_data.is_valid(), | |
| 3307 test_cases[i].vary_matches) << i; | |
| 3308 | |
| 3309 // Check the headers. | |
| 3310 scoped_refptr<HttpResponseHeaders> headers = out.response_info.headers; | |
| 3311 ASSERT_TRUE(headers) << i; | |
| 3312 size_t iter = 0; | |
| 3313 SpdyString name, value, lines; | |
| 3314 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 3315 lines.append(name); | |
| 3316 lines.append(": "); | |
| 3317 lines.append(value); | |
| 3318 lines.append("\n"); | |
| 3319 } | |
| 3320 | |
| 3321 EXPECT_EQ(expected_reply, lines) << i; | |
| 3322 } | |
| 3323 } | |
| 3324 | |
| 3325 // Verify that we don't crash on invalid response headers. | |
| 3326 TEST_F(SpdyNetworkTransactionTest, InvalidResponseHeaders) { | |
| 3327 struct InvalidResponseHeadersTests { | |
| 3328 int num_headers; | |
| 3329 const char* headers[10]; | |
| 3330 } test_cases[] = { | |
| 3331 // Response headers missing status header | |
| 3332 { | |
| 3333 3, | |
| 3334 {spdy_util_.GetPathKey(), "/index.php", "cookie", "val1", "cookie", | |
| 3335 "val2", nullptr}, | |
| 3336 }, | |
| 3337 // Response headers missing version header | |
| 3338 { | |
| 3339 1, {spdy_util_.GetPathKey(), "/index.php", "status", "200", nullptr}, | |
| 3340 }, | |
| 3341 // Response headers with no headers | |
| 3342 { | |
| 3343 0, {nullptr}, | |
| 3344 }, | |
| 3345 }; | |
| 3346 | |
| 3347 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
| 3348 SpdyTestUtil spdy_test_util; | |
| 3349 | |
| 3350 SpdySerializedFrame req( | |
| 3351 spdy_test_util.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3352 SpdySerializedFrame rst( | |
| 3353 spdy_test_util.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
| 3354 MockWrite writes[] = { | |
| 3355 CreateMockWrite(req, 0), CreateMockWrite(rst, 2), | |
| 3356 }; | |
| 3357 | |
| 3358 // Construct the reply. | |
| 3359 SpdyHeaderBlock reply_headers; | |
| 3360 AppendToHeaderBlock( | |
| 3361 test_cases[i].headers, test_cases[i].num_headers, &reply_headers); | |
| 3362 SpdySerializedFrame resp( | |
| 3363 spdy_test_util.ConstructSpdyReply(1, std::move(reply_headers))); | |
| 3364 MockRead reads[] = { | |
| 3365 CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3) // EOF | |
| 3366 }; | |
| 3367 | |
| 3368 SequencedSocketData data(reads, arraysize(reads), writes, | |
| 3369 arraysize(writes)); | |
| 3370 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3371 NetLogWithSource(), nullptr); | |
| 3372 helper.RunToCompletion(&data); | |
| 3373 TransactionHelperResult out = helper.output(); | |
| 3374 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
| 3375 } | |
| 3376 } | |
| 3377 | |
| 3378 TEST_F(SpdyNetworkTransactionTest, CorruptFrameSessionError) { | |
| 3379 SpdySerializedFrame req( | |
| 3380 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3381 SpdySerializedFrame goaway( | |
| 3382 spdy_util_.ConstructSpdyGoAway(0, ERROR_CODE_COMPRESSION_ERROR, | |
| 3383 "Framer error: 6 (DECOMPRESS_FAILURE).")); | |
| 3384 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(goaway, 2)}; | |
| 3385 | |
| 3386 // This is the length field that's too short. | |
| 3387 SpdySerializedFrame reply_wrong_length( | |
| 3388 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3389 size_t right_size = reply_wrong_length.size() - kFrameHeaderSize; | |
| 3390 size_t wrong_size = right_size - 4; | |
| 3391 test::SetFrameLength(&reply_wrong_length, wrong_size); | |
| 3392 | |
| 3393 MockRead reads[] = { | |
| 3394 MockRead(ASYNC, reply_wrong_length.data(), reply_wrong_length.size() - 4, | |
| 3395 1), | |
| 3396 }; | |
| 3397 | |
| 3398 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3399 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3400 NetLogWithSource(), nullptr); | |
| 3401 helper.RunToCompletion(&data); | |
| 3402 TransactionHelperResult out = helper.output(); | |
| 3403 EXPECT_THAT(out.rv, IsError(ERR_SPDY_COMPRESSION_ERROR)); | |
| 3404 } | |
| 3405 | |
| 3406 TEST_F(SpdyNetworkTransactionTest, GoAwayOnDecompressionFailure) { | |
| 3407 SpdySerializedFrame req( | |
| 3408 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3409 SpdySerializedFrame goaway( | |
| 3410 spdy_util_.ConstructSpdyGoAway(0, ERROR_CODE_COMPRESSION_ERROR, | |
| 3411 "Framer error: 6 (DECOMPRESS_FAILURE).")); | |
| 3412 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(goaway, 2)}; | |
| 3413 | |
| 3414 // Read HEADERS with corrupted payload. | |
| 3415 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3416 memset(resp.data() + 12, 0xcf, resp.size() - 12); | |
| 3417 MockRead reads[] = {CreateMockRead(resp, 1)}; | |
| 3418 | |
| 3419 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3420 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3421 NetLogWithSource(), nullptr); | |
| 3422 helper.RunToCompletion(&data); | |
| 3423 TransactionHelperResult out = helper.output(); | |
| 3424 EXPECT_THAT(out.rv, IsError(ERR_SPDY_COMPRESSION_ERROR)); | |
| 3425 } | |
| 3426 | |
| 3427 TEST_F(SpdyNetworkTransactionTest, GoAwayOnFrameSizeError) { | |
| 3428 SpdySerializedFrame req( | |
| 3429 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3430 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
| 3431 0, ERROR_CODE_FRAME_SIZE_ERROR, | |
| 3432 "Framer error: 15 (INVALID_CONTROL_FRAME_SIZE).")); | |
| 3433 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(goaway, 2)}; | |
| 3434 | |
| 3435 // Read WINDOW_UPDATE with incorrectly-sized payload. | |
| 3436 SpdySerializedFrame bad_window_update( | |
| 3437 spdy_util_.ConstructSpdyWindowUpdate(1, 1)); | |
| 3438 test::SetFrameLength(&bad_window_update, bad_window_update.size() - 1); | |
| 3439 MockRead reads[] = {CreateMockRead(bad_window_update, 1)}; | |
| 3440 | |
| 3441 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3442 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3443 NetLogWithSource(), nullptr); | |
| 3444 helper.RunToCompletion(&data); | |
| 3445 TransactionHelperResult out = helper.output(); | |
| 3446 EXPECT_THAT(out.rv, IsError(ERR_SPDY_FRAME_SIZE_ERROR)); | |
| 3447 } | |
| 3448 | |
| 3449 // Test that we shutdown correctly on write errors. | |
| 3450 TEST_F(SpdyNetworkTransactionTest, WriteError) { | |
| 3451 SpdySerializedFrame req( | |
| 3452 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3453 MockWrite writes[] = { | |
| 3454 // We'll write 10 bytes successfully | |
| 3455 MockWrite(ASYNC, req.data(), 10, 1), | |
| 3456 // Followed by ERROR! | |
| 3457 MockWrite(ASYNC, ERR_FAILED, 2), | |
| 3458 // Session drains and attempts to write a GOAWAY: Another ERROR! | |
| 3459 MockWrite(ASYNC, ERR_FAILED, 3), | |
| 3460 }; | |
| 3461 | |
| 3462 MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; | |
| 3463 | |
| 3464 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3465 | |
| 3466 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3467 NetLogWithSource(), nullptr); | |
| 3468 helper.RunPreTestSetup(); | |
| 3469 helper.AddData(&data); | |
| 3470 EXPECT_TRUE(helper.StartDefaultTest()); | |
| 3471 helper.FinishDefaultTest(); | |
| 3472 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
| 3473 EXPECT_TRUE(data.AllReadDataConsumed()); | |
| 3474 TransactionHelperResult out = helper.output(); | |
| 3475 EXPECT_THAT(out.rv, IsError(ERR_FAILED)); | |
| 3476 } | |
| 3477 | |
| 3478 // Test that partial writes work. | |
| 3479 TEST_F(SpdyNetworkTransactionTest, PartialWrite) { | |
| 3480 // Chop the HEADERS frame into 5 chunks. | |
| 3481 SpdySerializedFrame req( | |
| 3482 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3483 const int kChunks = 5; | |
| 3484 std::unique_ptr<MockWrite[]> writes(ChopWriteFrame(req, kChunks)); | |
| 3485 for (int i = 0; i < kChunks; ++i) { | |
| 3486 writes[i].sequence_number = i; | |
| 3487 } | |
| 3488 | |
| 3489 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3490 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 3491 MockRead reads[] = { | |
| 3492 CreateMockRead(resp, kChunks), CreateMockRead(body, kChunks + 1), | |
| 3493 MockRead(ASYNC, 0, kChunks + 2) // EOF | |
| 3494 }; | |
| 3495 | |
| 3496 SequencedSocketData data(reads, arraysize(reads), writes.get(), kChunks); | |
| 3497 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3498 NetLogWithSource(), nullptr); | |
| 3499 helper.RunToCompletion(&data); | |
| 3500 TransactionHelperResult out = helper.output(); | |
| 3501 EXPECT_THAT(out.rv, IsOk()); | |
| 3502 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 3503 EXPECT_EQ("hello!", out.response_data); | |
| 3504 } | |
| 3505 | |
| 3506 // Test that the NetLog contains good data for a simple GET request. | |
| 3507 TEST_F(SpdyNetworkTransactionTest, NetLog) { | |
| 3508 static const char* const kExtraHeaders[] = { | |
| 3509 "user-agent", "Chrome", | |
| 3510 }; | |
| 3511 SpdySerializedFrame req( | |
| 3512 spdy_util_.ConstructSpdyGet(kExtraHeaders, 1, 1, LOWEST, true)); | |
| 3513 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 3514 | |
| 3515 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3516 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 3517 MockRead reads[] = { | |
| 3518 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 3519 MockRead(ASYNC, 0, 3) // EOF | |
| 3520 }; | |
| 3521 | |
| 3522 BoundTestNetLog log; | |
| 3523 | |
| 3524 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3525 NormalSpdyTransactionHelper helper(CreateGetRequestWithUserAgent(), | |
| 3526 DEFAULT_PRIORITY, log.bound(), nullptr); | |
| 3527 helper.RunToCompletion(&data); | |
| 3528 TransactionHelperResult out = helper.output(); | |
| 3529 EXPECT_THAT(out.rv, IsOk()); | |
| 3530 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 3531 EXPECT_EQ("hello!", out.response_data); | |
| 3532 | |
| 3533 // Check that the NetLog was filled reasonably. | |
| 3534 // This test is intentionally non-specific about the exact ordering of the | |
| 3535 // log; instead we just check to make sure that certain events exist, and that | |
| 3536 // they are in the right order. | |
| 3537 TestNetLogEntry::List entries; | |
| 3538 log.GetEntries(&entries); | |
| 3539 | |
| 3540 EXPECT_LT(0u, entries.size()); | |
| 3541 int pos = 0; | |
| 3542 pos = ExpectLogContainsSomewhere( | |
| 3543 entries, 0, NetLogEventType::HTTP_TRANSACTION_SEND_REQUEST, | |
| 3544 NetLogEventPhase::BEGIN); | |
| 3545 pos = ExpectLogContainsSomewhere( | |
| 3546 entries, pos + 1, NetLogEventType::HTTP_TRANSACTION_SEND_REQUEST, | |
| 3547 NetLogEventPhase::END); | |
| 3548 pos = ExpectLogContainsSomewhere( | |
| 3549 entries, pos + 1, NetLogEventType::HTTP_TRANSACTION_READ_HEADERS, | |
| 3550 NetLogEventPhase::BEGIN); | |
| 3551 pos = ExpectLogContainsSomewhere( | |
| 3552 entries, pos + 1, NetLogEventType::HTTP_TRANSACTION_READ_HEADERS, | |
| 3553 NetLogEventPhase::END); | |
| 3554 pos = ExpectLogContainsSomewhere(entries, pos + 1, | |
| 3555 NetLogEventType::HTTP_TRANSACTION_READ_BODY, | |
| 3556 NetLogEventPhase::BEGIN); | |
| 3557 pos = ExpectLogContainsSomewhere(entries, pos + 1, | |
| 3558 NetLogEventType::HTTP_TRANSACTION_READ_BODY, | |
| 3559 NetLogEventPhase::END); | |
| 3560 | |
| 3561 // Check that we logged all the headers correctly | |
| 3562 pos = ExpectLogContainsSomewhere(entries, 0, | |
| 3563 NetLogEventType::HTTP2_SESSION_SEND_HEADERS, | |
| 3564 NetLogEventPhase::NONE); | |
| 3565 | |
| 3566 base::ListValue* header_list; | |
| 3567 ASSERT_TRUE(entries[pos].params.get()); | |
| 3568 ASSERT_TRUE(entries[pos].params->GetList("headers", &header_list)); | |
| 3569 | |
| 3570 std::vector<SpdyString> expected; | |
| 3571 expected.push_back(SpdyString(spdy_util_.GetHostKey()) + ": www.example.org"); | |
| 3572 expected.push_back(SpdyString(spdy_util_.GetPathKey()) + ": /"); | |
| 3573 expected.push_back(SpdyString(spdy_util_.GetSchemeKey()) + ": " + | |
| 3574 default_url_.scheme()); | |
| 3575 expected.push_back(SpdyString(spdy_util_.GetMethodKey()) + ": GET"); | |
| 3576 expected.push_back("user-agent: Chrome"); | |
| 3577 EXPECT_EQ(expected.size(), header_list->GetSize()); | |
| 3578 for (std::vector<SpdyString>::const_iterator it = expected.begin(); | |
| 3579 it != expected.end(); ++it) { | |
| 3580 base::Value header(*it); | |
| 3581 EXPECT_NE(header_list->end(), header_list->Find(header)) << | |
| 3582 "Header not found: " << *it; | |
| 3583 } | |
| 3584 } | |
| 3585 | |
| 3586 // Since we buffer the IO from the stream to the renderer, this test verifies | |
| 3587 // that when we read out the maximum amount of data (e.g. we received 50 bytes | |
| 3588 // on the network, but issued a Read for only 5 of those bytes) that the data | |
| 3589 // flow still works correctly. | |
| 3590 TEST_F(SpdyNetworkTransactionTest, BufferFull) { | |
| 3591 SpdySerializedFrame req( | |
| 3592 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3593 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 3594 | |
| 3595 // 2 data frames in a single read. | |
| 3596 SpdySerializedFrame data_frame_1( | |
| 3597 spdy_util_.ConstructSpdyDataFrame(1, "goodby", 6, /*fin=*/false)); | |
| 3598 SpdySerializedFrame data_frame_2( | |
| 3599 spdy_util_.ConstructSpdyDataFrame(1, "e worl", 6, /*fin=*/false)); | |
| 3600 const SpdySerializedFrame* data_frames[2] = { | |
| 3601 &data_frame_1, &data_frame_2, | |
| 3602 }; | |
| 3603 char combined_data_frames[100]; | |
| 3604 int combined_data_frames_len = | |
| 3605 CombineFrames(data_frames, arraysize(data_frames), | |
| 3606 combined_data_frames, arraysize(combined_data_frames)); | |
| 3607 SpdySerializedFrame last_frame( | |
| 3608 spdy_util_.ConstructSpdyDataFrame(1, "d", 1, /*fin=*/true)); | |
| 3609 | |
| 3610 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3611 MockRead reads[] = { | |
| 3612 CreateMockRead(resp, 1), | |
| 3613 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause | |
| 3614 MockRead(ASYNC, combined_data_frames, combined_data_frames_len, 3), | |
| 3615 MockRead(ASYNC, ERR_IO_PENDING, 4), // Force a pause | |
| 3616 CreateMockRead(last_frame, 5), | |
| 3617 MockRead(ASYNC, 0, 6) // EOF | |
| 3618 }; | |
| 3619 | |
| 3620 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3621 | |
| 3622 TestCompletionCallback callback; | |
| 3623 | |
| 3624 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3625 NetLogWithSource(), nullptr); | |
| 3626 helper.RunPreTestSetup(); | |
| 3627 helper.AddData(&data); | |
| 3628 HttpNetworkTransaction* trans = helper.trans(); | |
| 3629 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 3630 NetLogWithSource()); | |
| 3631 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 3632 | |
| 3633 TransactionHelperResult out = helper.output(); | |
| 3634 out.rv = callback.WaitForResult(); | |
| 3635 EXPECT_EQ(out.rv, OK); | |
| 3636 | |
| 3637 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
| 3638 EXPECT_TRUE(response->headers); | |
| 3639 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 3640 out.status_line = response->headers->GetStatusLine(); | |
| 3641 out.response_info = *response; // Make a copy so we can verify. | |
| 3642 | |
| 3643 // Read Data | |
| 3644 TestCompletionCallback read_callback; | |
| 3645 | |
| 3646 SpdyString content; | |
| 3647 do { | |
| 3648 // Read small chunks at a time. | |
| 3649 const int kSmallReadSize = 3; | |
| 3650 scoped_refptr<IOBuffer> buf(new IOBuffer(kSmallReadSize)); | |
| 3651 rv = trans->Read(buf.get(), kSmallReadSize, read_callback.callback()); | |
| 3652 if (rv == ERR_IO_PENDING) { | |
| 3653 data.Resume(); | |
| 3654 rv = read_callback.WaitForResult(); | |
| 3655 } | |
| 3656 if (rv > 0) { | |
| 3657 content.append(buf->data(), rv); | |
| 3658 } else if (rv < 0) { | |
| 3659 NOTREACHED(); | |
| 3660 } | |
| 3661 } while (rv > 0); | |
| 3662 | |
| 3663 out.response_data.swap(content); | |
| 3664 | |
| 3665 // Flush the MessageLoop while the SpdySessionDependencies (in particular, the | |
| 3666 // MockClientSocketFactory) are still alive. | |
| 3667 base::RunLoop().RunUntilIdle(); | |
| 3668 | |
| 3669 // Verify that we consumed all test data. | |
| 3670 helper.VerifyDataConsumed(); | |
| 3671 | |
| 3672 EXPECT_THAT(out.rv, IsOk()); | |
| 3673 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 3674 EXPECT_EQ("goodbye world", out.response_data); | |
| 3675 } | |
| 3676 | |
| 3677 // Verify that basic buffering works; when multiple data frames arrive | |
| 3678 // at the same time, ensure that we don't notify a read completion for | |
| 3679 // each data frame individually. | |
| 3680 TEST_F(SpdyNetworkTransactionTest, Buffering) { | |
| 3681 SpdySerializedFrame req( | |
| 3682 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3683 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 3684 | |
| 3685 // 4 data frames in a single read. | |
| 3686 SpdySerializedFrame data_frame( | |
| 3687 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/false)); | |
| 3688 SpdySerializedFrame data_frame_fin( | |
| 3689 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/true)); | |
| 3690 const SpdySerializedFrame* data_frames[4] = {&data_frame, &data_frame, | |
| 3691 &data_frame, &data_frame_fin}; | |
| 3692 char combined_data_frames[100]; | |
| 3693 int combined_data_frames_len = | |
| 3694 CombineFrames(data_frames, arraysize(data_frames), | |
| 3695 combined_data_frames, arraysize(combined_data_frames)); | |
| 3696 | |
| 3697 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3698 MockRead reads[] = { | |
| 3699 CreateMockRead(resp, 1), | |
| 3700 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause | |
| 3701 MockRead(ASYNC, combined_data_frames, combined_data_frames_len, 3), | |
| 3702 MockRead(ASYNC, 0, 4) // EOF | |
| 3703 }; | |
| 3704 | |
| 3705 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3706 | |
| 3707 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3708 NetLogWithSource(), nullptr); | |
| 3709 helper.RunPreTestSetup(); | |
| 3710 helper.AddData(&data); | |
| 3711 HttpNetworkTransaction* trans = helper.trans(); | |
| 3712 | |
| 3713 TestCompletionCallback callback; | |
| 3714 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 3715 NetLogWithSource()); | |
| 3716 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 3717 | |
| 3718 TransactionHelperResult out = helper.output(); | |
| 3719 out.rv = callback.WaitForResult(); | |
| 3720 EXPECT_EQ(out.rv, OK); | |
| 3721 | |
| 3722 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
| 3723 EXPECT_TRUE(response->headers); | |
| 3724 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 3725 out.status_line = response->headers->GetStatusLine(); | |
| 3726 out.response_info = *response; // Make a copy so we can verify. | |
| 3727 | |
| 3728 // Read Data | |
| 3729 TestCompletionCallback read_callback; | |
| 3730 | |
| 3731 SpdyString content; | |
| 3732 int reads_completed = 0; | |
| 3733 do { | |
| 3734 // Read small chunks at a time. | |
| 3735 const int kSmallReadSize = 14; | |
| 3736 scoped_refptr<IOBuffer> buf(new IOBuffer(kSmallReadSize)); | |
| 3737 rv = trans->Read(buf.get(), kSmallReadSize, read_callback.callback()); | |
| 3738 if (rv == ERR_IO_PENDING) { | |
| 3739 data.Resume(); | |
| 3740 rv = read_callback.WaitForResult(); | |
| 3741 } | |
| 3742 if (rv > 0) { | |
| 3743 EXPECT_EQ(kSmallReadSize, rv); | |
| 3744 content.append(buf->data(), rv); | |
| 3745 } else if (rv < 0) { | |
| 3746 FAIL() << "Unexpected read error: " << rv; | |
| 3747 } | |
| 3748 reads_completed++; | |
| 3749 } while (rv > 0); | |
| 3750 | |
| 3751 EXPECT_EQ(3, reads_completed); // Reads are: 14 bytes, 14 bytes, 0 bytes. | |
| 3752 | |
| 3753 out.response_data.swap(content); | |
| 3754 | |
| 3755 // Flush the MessageLoop while the SpdySessionDependencies (in particular, the | |
| 3756 // MockClientSocketFactory) are still alive. | |
| 3757 base::RunLoop().RunUntilIdle(); | |
| 3758 | |
| 3759 // Verify that we consumed all test data. | |
| 3760 helper.VerifyDataConsumed(); | |
| 3761 | |
| 3762 EXPECT_THAT(out.rv, IsOk()); | |
| 3763 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 3764 EXPECT_EQ("messagemessagemessagemessage", out.response_data); | |
| 3765 } | |
| 3766 | |
| 3767 // Verify the case where we buffer data but read it after it has been buffered. | |
| 3768 TEST_F(SpdyNetworkTransactionTest, BufferedAll) { | |
| 3769 SpdySerializedFrame req( | |
| 3770 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3771 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 3772 | |
| 3773 // 5 data frames in a single read. | |
| 3774 SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3775 SpdySerializedFrame data_frame( | |
| 3776 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/false)); | |
| 3777 SpdySerializedFrame data_frame_fin( | |
| 3778 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/true)); | |
| 3779 const SpdySerializedFrame* frames[5] = {&reply, &data_frame, &data_frame, | |
| 3780 &data_frame, &data_frame_fin}; | |
| 3781 char combined_frames[200]; | |
| 3782 int combined_frames_len = | |
| 3783 CombineFrames(frames, arraysize(frames), | |
| 3784 combined_frames, arraysize(combined_frames)); | |
| 3785 | |
| 3786 MockRead reads[] = { | |
| 3787 MockRead(ASYNC, combined_frames, combined_frames_len, 1), | |
| 3788 MockRead(ASYNC, 0, 2) // EOF | |
| 3789 }; | |
| 3790 | |
| 3791 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3792 | |
| 3793 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3794 NetLogWithSource(), nullptr); | |
| 3795 helper.RunPreTestSetup(); | |
| 3796 helper.AddData(&data); | |
| 3797 HttpNetworkTransaction* trans = helper.trans(); | |
| 3798 | |
| 3799 TestCompletionCallback callback; | |
| 3800 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 3801 NetLogWithSource()); | |
| 3802 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 3803 | |
| 3804 TransactionHelperResult out = helper.output(); | |
| 3805 out.rv = callback.WaitForResult(); | |
| 3806 EXPECT_EQ(out.rv, OK); | |
| 3807 | |
| 3808 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
| 3809 EXPECT_TRUE(response->headers); | |
| 3810 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 3811 out.status_line = response->headers->GetStatusLine(); | |
| 3812 out.response_info = *response; // Make a copy so we can verify. | |
| 3813 | |
| 3814 // Read Data | |
| 3815 TestCompletionCallback read_callback; | |
| 3816 | |
| 3817 SpdyString content; | |
| 3818 int reads_completed = 0; | |
| 3819 do { | |
| 3820 // Read small chunks at a time. | |
| 3821 const int kSmallReadSize = 14; | |
| 3822 scoped_refptr<IOBuffer> buf(new IOBuffer(kSmallReadSize)); | |
| 3823 rv = trans->Read(buf.get(), kSmallReadSize, read_callback.callback()); | |
| 3824 if (rv > 0) { | |
| 3825 EXPECT_EQ(kSmallReadSize, rv); | |
| 3826 content.append(buf->data(), rv); | |
| 3827 } else if (rv < 0) { | |
| 3828 FAIL() << "Unexpected read error: " << rv; | |
| 3829 } | |
| 3830 reads_completed++; | |
| 3831 } while (rv > 0); | |
| 3832 | |
| 3833 EXPECT_EQ(3, reads_completed); | |
| 3834 | |
| 3835 out.response_data.swap(content); | |
| 3836 | |
| 3837 // Flush the MessageLoop while the SpdySessionDependencies (in particular, the | |
| 3838 // MockClientSocketFactory) are still alive. | |
| 3839 base::RunLoop().RunUntilIdle(); | |
| 3840 | |
| 3841 // Verify that we consumed all test data. | |
| 3842 helper.VerifyDataConsumed(); | |
| 3843 | |
| 3844 EXPECT_THAT(out.rv, IsOk()); | |
| 3845 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 3846 EXPECT_EQ("messagemessagemessagemessage", out.response_data); | |
| 3847 } | |
| 3848 | |
| 3849 // Verify the case where we buffer data and close the connection. | |
| 3850 TEST_F(SpdyNetworkTransactionTest, BufferedClosed) { | |
| 3851 SpdySerializedFrame req( | |
| 3852 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3853 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 3854 | |
| 3855 // All data frames in a single read. | |
| 3856 // NOTE: We don't FIN the stream. | |
| 3857 SpdySerializedFrame data_frame( | |
| 3858 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/false)); | |
| 3859 const SpdySerializedFrame* data_frames[4] = {&data_frame, &data_frame, | |
| 3860 &data_frame, &data_frame}; | |
| 3861 char combined_data_frames[100]; | |
| 3862 int combined_data_frames_len = | |
| 3863 CombineFrames(data_frames, arraysize(data_frames), | |
| 3864 combined_data_frames, arraysize(combined_data_frames)); | |
| 3865 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3866 MockRead reads[] = { | |
| 3867 CreateMockRead(resp, 1), | |
| 3868 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a wait | |
| 3869 MockRead(ASYNC, combined_data_frames, combined_data_frames_len, 3), | |
| 3870 MockRead(ASYNC, 0, 4) // EOF | |
| 3871 }; | |
| 3872 | |
| 3873 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3874 | |
| 3875 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3876 NetLogWithSource(), nullptr); | |
| 3877 helper.RunPreTestSetup(); | |
| 3878 helper.AddData(&data); | |
| 3879 HttpNetworkTransaction* trans = helper.trans(); | |
| 3880 | |
| 3881 TestCompletionCallback callback; | |
| 3882 | |
| 3883 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 3884 NetLogWithSource()); | |
| 3885 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 3886 | |
| 3887 TransactionHelperResult out = helper.output(); | |
| 3888 out.rv = callback.WaitForResult(); | |
| 3889 EXPECT_EQ(out.rv, OK); | |
| 3890 | |
| 3891 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
| 3892 EXPECT_TRUE(response->headers); | |
| 3893 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 3894 out.status_line = response->headers->GetStatusLine(); | |
| 3895 out.response_info = *response; // Make a copy so we can verify. | |
| 3896 | |
| 3897 // Read Data | |
| 3898 TestCompletionCallback read_callback; | |
| 3899 | |
| 3900 SpdyString content; | |
| 3901 int reads_completed = 0; | |
| 3902 do { | |
| 3903 // Read small chunks at a time. | |
| 3904 const int kSmallReadSize = 14; | |
| 3905 scoped_refptr<IOBuffer> buf(new IOBuffer(kSmallReadSize)); | |
| 3906 rv = trans->Read(buf.get(), kSmallReadSize, read_callback.callback()); | |
| 3907 if (rv == ERR_IO_PENDING) { | |
| 3908 data.Resume(); | |
| 3909 rv = read_callback.WaitForResult(); | |
| 3910 } | |
| 3911 if (rv > 0) { | |
| 3912 content.append(buf->data(), rv); | |
| 3913 } else if (rv < 0) { | |
| 3914 // This test intentionally closes the connection, and will get an error. | |
| 3915 EXPECT_THAT(rv, IsError(ERR_CONNECTION_CLOSED)); | |
| 3916 break; | |
| 3917 } | |
| 3918 reads_completed++; | |
| 3919 } while (rv > 0); | |
| 3920 | |
| 3921 EXPECT_EQ(0, reads_completed); | |
| 3922 | |
| 3923 out.response_data.swap(content); | |
| 3924 | |
| 3925 // Flush the MessageLoop while the SpdySessionDependencies (in particular, the | |
| 3926 // MockClientSocketFactory) are still alive. | |
| 3927 base::RunLoop().RunUntilIdle(); | |
| 3928 | |
| 3929 // Verify that we consumed all test data. | |
| 3930 helper.VerifyDataConsumed(); | |
| 3931 } | |
| 3932 | |
| 3933 // Verify the case where we buffer data and cancel the transaction. | |
| 3934 TEST_F(SpdyNetworkTransactionTest, BufferedCancelled) { | |
| 3935 SpdySerializedFrame req( | |
| 3936 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 3937 SpdySerializedFrame rst( | |
| 3938 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_CANCEL)); | |
| 3939 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(rst, 4)}; | |
| 3940 | |
| 3941 // NOTE: We don't FIN the stream. | |
| 3942 SpdySerializedFrame data_frame( | |
| 3943 spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/false)); | |
| 3944 | |
| 3945 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 3946 MockRead reads[] = { | |
| 3947 CreateMockRead(resp, 1), | |
| 3948 MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a wait | |
| 3949 CreateMockRead(data_frame, 3), MockRead(ASYNC, 0, 5) // EOF | |
| 3950 }; | |
| 3951 | |
| 3952 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 3953 | |
| 3954 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 3955 NetLogWithSource(), nullptr); | |
| 3956 helper.RunPreTestSetup(); | |
| 3957 helper.AddData(&data); | |
| 3958 HttpNetworkTransaction* trans = helper.trans(); | |
| 3959 TestCompletionCallback callback; | |
| 3960 | |
| 3961 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 3962 NetLogWithSource()); | |
| 3963 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 3964 | |
| 3965 TransactionHelperResult out = helper.output(); | |
| 3966 out.rv = callback.WaitForResult(); | |
| 3967 EXPECT_EQ(out.rv, OK); | |
| 3968 | |
| 3969 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
| 3970 EXPECT_TRUE(response->headers); | |
| 3971 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 3972 out.status_line = response->headers->GetStatusLine(); | |
| 3973 out.response_info = *response; // Make a copy so we can verify. | |
| 3974 | |
| 3975 // Read Data | |
| 3976 TestCompletionCallback read_callback; | |
| 3977 | |
| 3978 const int kReadSize = 256; | |
| 3979 scoped_refptr<IOBuffer> buf(new IOBuffer(kReadSize)); | |
| 3980 rv = trans->Read(buf.get(), kReadSize, read_callback.callback()); | |
| 3981 ASSERT_EQ(ERR_IO_PENDING, rv) << "Unexpected read: " << rv; | |
| 3982 | |
| 3983 // Complete the read now, which causes buffering to start. | |
| 3984 data.Resume(); | |
| 3985 base::RunLoop().RunUntilIdle(); | |
| 3986 // Destroy the transaction, causing the stream to get cancelled | |
| 3987 // and orphaning the buffered IO task. | |
| 3988 helper.ResetTrans(); | |
| 3989 | |
| 3990 // Flush the MessageLoop; this will cause the buffered IO task | |
| 3991 // to run for the final time. | |
| 3992 base::RunLoop().RunUntilIdle(); | |
| 3993 | |
| 3994 // Verify that we consumed all test data. | |
| 3995 helper.VerifyDataConsumed(); | |
| 3996 } | |
| 3997 | |
| 3998 // Request should fail upon receiving a GOAWAY frame | |
| 3999 // with Last-Stream-ID lower than the stream id corresponding to the request | |
| 4000 // and with error code other than NO_ERROR. | |
| 4001 TEST_F(SpdyNetworkTransactionTest, FailOnGoAway) { | |
| 4002 SpdySerializedFrame req( | |
| 4003 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4004 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 4005 | |
| 4006 SpdySerializedFrame go_away( | |
| 4007 spdy_util_.ConstructSpdyGoAway(0, ERROR_CODE_INTERNAL_ERROR, "")); | |
| 4008 MockRead reads[] = { | |
| 4009 CreateMockRead(go_away, 1), | |
| 4010 }; | |
| 4011 | |
| 4012 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 4013 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 4014 NetLogWithSource(), nullptr); | |
| 4015 helper.RunToCompletion(&data); | |
| 4016 TransactionHelperResult out = helper.output(); | |
| 4017 EXPECT_THAT(out.rv, IsError(ERR_ABORTED)); | |
| 4018 } | |
| 4019 | |
| 4020 // Request should be retried on a new connection upon receiving a GOAWAY frame | |
| 4021 // with Last-Stream-ID lower than the stream id corresponding to the request | |
| 4022 // and with error code NO_ERROR. | |
| 4023 TEST_F(SpdyNetworkTransactionTest, RetryOnGoAway) { | |
| 4024 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 4025 NetLogWithSource(), nullptr); | |
| 4026 | |
| 4027 // First connection. | |
| 4028 SpdySerializedFrame req( | |
| 4029 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4030 MockWrite writes1[] = {CreateMockWrite(req, 0)}; | |
| 4031 SpdySerializedFrame go_away( | |
| 4032 spdy_util_.ConstructSpdyGoAway(0, ERROR_CODE_NO_ERROR, "")); | |
| 4033 MockRead reads1[] = {CreateMockRead(go_away, 1)}; | |
| 4034 SequencedSocketData data1(reads1, arraysize(reads1), writes1, | |
| 4035 arraysize(writes1)); | |
| 4036 helper.AddData(&data1); | |
| 4037 | |
| 4038 // Second connection. | |
| 4039 MockWrite writes2[] = {CreateMockWrite(req, 0)}; | |
| 4040 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4041 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 4042 MockRead reads2[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 4043 MockRead(ASYNC, 0, 3)}; | |
| 4044 SequencedSocketData data2(reads2, arraysize(reads2), writes2, | |
| 4045 arraysize(writes2)); | |
| 4046 helper.AddData(&data2); | |
| 4047 | |
| 4048 helper.RunPreTestSetup(); | |
| 4049 helper.RunDefaultTest(); | |
| 4050 | |
| 4051 TransactionHelperResult out = helper.output(); | |
| 4052 EXPECT_THAT(out.rv, IsOk()); | |
| 4053 | |
| 4054 helper.VerifyDataConsumed(); | |
| 4055 } | |
| 4056 | |
| 4057 // A server can gracefully shut down by sending a GOAWAY frame | |
| 4058 // with maximum last-stream-id value. | |
| 4059 // Transactions started before receiving such a GOAWAY frame should succeed, | |
| 4060 // but SpdySession should be unavailable for new streams. | |
| 4061 TEST_F(SpdyNetworkTransactionTest, GracefulGoaway) { | |
| 4062 SpdySerializedFrame req1( | |
| 4063 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4064 spdy_util_.UpdateWithStreamDestruction(1); | |
| 4065 SpdySerializedFrame req2( | |
| 4066 spdy_util_.ConstructSpdyGet("https://www.example.org/foo", 3, LOWEST)); | |
| 4067 MockWrite writes[] = {CreateMockWrite(req1, 0), CreateMockWrite(req2, 3)}; | |
| 4068 | |
| 4069 SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4070 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 4071 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
| 4072 0x7fffffff, ERROR_CODE_NO_ERROR, "Graceful shutdown.")); | |
| 4073 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 4074 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
| 4075 MockRead reads[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2), | |
| 4076 CreateMockRead(goaway, 4), CreateMockRead(resp2, 5), | |
| 4077 CreateMockRead(body2, 6), MockRead(ASYNC, 0, 7)}; | |
| 4078 | |
| 4079 // Run first transaction. | |
| 4080 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 4081 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 4082 NetLogWithSource(), nullptr); | |
| 4083 helper.RunPreTestSetup(); | |
| 4084 helper.AddData(&data); | |
| 4085 helper.RunDefaultTest(); | |
| 4086 | |
| 4087 // Verify first response. | |
| 4088 TransactionHelperResult out = helper.output(); | |
| 4089 EXPECT_THAT(out.rv, IsOk()); | |
| 4090 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 4091 EXPECT_EQ("hello!", out.response_data); | |
| 4092 | |
| 4093 // GOAWAY frame has not yet been received, SpdySession should be available. | |
| 4094 SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool(); | |
| 4095 SpdySessionKey key(host_port_pair_, ProxyServer::Direct(), | |
| 4096 PRIVACY_MODE_DISABLED); | |
| 4097 NetLogWithSource log; | |
| 4098 base::WeakPtr<SpdySession> spdy_session = | |
| 4099 spdy_session_pool->FindAvailableSession( | |
| 4100 key, GURL(), | |
| 4101 /* enable_ip_based_pooling = */ true, log); | |
| 4102 EXPECT_TRUE(spdy_session); | |
| 4103 | |
| 4104 // Start second transaction. | |
| 4105 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
| 4106 TestCompletionCallback callback; | |
| 4107 HttpRequestInfo request2; | |
| 4108 request2.method = "GET"; | |
| 4109 request2.url = GURL("https://www.example.org/foo"); | |
| 4110 int rv = trans2.Start(&request2, callback.callback(), log); | |
| 4111 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 4112 rv = callback.WaitForResult(); | |
| 4113 EXPECT_THAT(rv, IsOk()); | |
| 4114 | |
| 4115 // Verify second response. | |
| 4116 const HttpResponseInfo* response = trans2.GetResponseInfo(); | |
| 4117 ASSERT_TRUE(response); | |
| 4118 EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2, response->connection_info); | |
| 4119 ASSERT_TRUE(response->headers); | |
| 4120 EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); | |
| 4121 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 4122 EXPECT_TRUE(response->was_alpn_negotiated); | |
| 4123 EXPECT_EQ("127.0.0.1", response->socket_address.host()); | |
| 4124 EXPECT_EQ(443, response->socket_address.port()); | |
| 4125 SpdyString response_data; | |
| 4126 rv = ReadTransaction(&trans2, &response_data); | |
| 4127 EXPECT_THAT(rv, IsOk()); | |
| 4128 EXPECT_EQ("hello!", response_data); | |
| 4129 | |
| 4130 // Graceful GOAWAY was received, SpdySession should be unavailable. | |
| 4131 spdy_session = spdy_session_pool->FindAvailableSession( | |
| 4132 key, GURL(), | |
| 4133 /* enable_ip_based_pooling = */ true, log); | |
| 4134 EXPECT_FALSE(spdy_session); | |
| 4135 | |
| 4136 helper.VerifyDataConsumed(); | |
| 4137 } | |
| 4138 | |
| 4139 TEST_F(SpdyNetworkTransactionTest, CloseWithActiveStream) { | |
| 4140 SpdySerializedFrame req( | |
| 4141 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4142 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 4143 | |
| 4144 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4145 MockRead reads[] = { | |
| 4146 CreateMockRead(resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF | |
| 4147 }; | |
| 4148 | |
| 4149 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 4150 | |
| 4151 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 4152 NetLogWithSource(), nullptr); | |
| 4153 helper.RunPreTestSetup(); | |
| 4154 helper.AddData(&data); | |
| 4155 helper.StartDefaultTest(); | |
| 4156 EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING)); | |
| 4157 | |
| 4158 helper.WaitForCallbackToComplete(); | |
| 4159 EXPECT_THAT(helper.output().rv, IsError(ERR_CONNECTION_CLOSED)); | |
| 4160 | |
| 4161 const HttpResponseInfo* response = helper.trans()->GetResponseInfo(); | |
| 4162 EXPECT_TRUE(response->headers); | |
| 4163 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 4164 | |
| 4165 // Verify that we consumed all test data. | |
| 4166 helper.VerifyDataConsumed(); | |
| 4167 } | |
| 4168 | |
| 4169 // Retry with HTTP/1.1 when receiving HTTP_1_1_REQUIRED. Note that no actual | |
| 4170 // protocol negotiation happens, instead this test forces protocols for both | |
| 4171 // sockets. | |
| 4172 TEST_F(SpdyNetworkTransactionTest, HTTP11RequiredRetry) { | |
| 4173 HttpRequestInfo request; | |
| 4174 request.method = "GET"; | |
| 4175 request.url = default_url_; | |
| 4176 // Do not force SPDY so that second socket can negotiate HTTP/1.1. | |
| 4177 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 4178 NetLogWithSource(), nullptr); | |
| 4179 | |
| 4180 // First socket: HTTP/2 request rejected with HTTP_1_1_REQUIRED. | |
| 4181 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
| 4182 SpdySerializedFrame req( | |
| 4183 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
| 4184 MockWrite writes0[] = {CreateMockWrite(req, 0)}; | |
| 4185 SpdySerializedFrame go_away(spdy_util_.ConstructSpdyGoAway( | |
| 4186 0, ERROR_CODE_HTTP_1_1_REQUIRED, "Try again using HTTP/1.1 please.")); | |
| 4187 MockRead reads0[] = {CreateMockRead(go_away, 1)}; | |
| 4188 SequencedSocketData data0(reads0, arraysize(reads0), writes0, | |
| 4189 arraysize(writes0)); | |
| 4190 | |
| 4191 std::unique_ptr<SSLSocketDataProvider> ssl_provider0( | |
| 4192 new SSLSocketDataProvider(ASYNC, OK)); | |
| 4193 // Expect HTTP/2 protocols too in SSLConfig. | |
| 4194 ssl_provider0->next_protos_expected_in_ssl_config.push_back(kProtoHTTP2); | |
| 4195 ssl_provider0->next_protos_expected_in_ssl_config.push_back(kProtoHTTP11); | |
| 4196 // Force SPDY. | |
| 4197 ssl_provider0->next_proto = kProtoHTTP2; | |
| 4198 helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0)); | |
| 4199 | |
| 4200 // Second socket: falling back to HTTP/1.1. | |
| 4201 MockWrite writes1[] = {MockWrite(ASYNC, 0, | |
| 4202 "GET / HTTP/1.1\r\n" | |
| 4203 "Host: www.example.org\r\n" | |
| 4204 "Connection: keep-alive\r\n\r\n")}; | |
| 4205 MockRead reads1[] = {MockRead(ASYNC, 1, | |
| 4206 "HTTP/1.1 200 OK\r\n" | |
| 4207 "Content-Length: 5\r\n\r\n" | |
| 4208 "hello")}; | |
| 4209 SequencedSocketData data1(reads1, arraysize(reads1), writes1, | |
| 4210 arraysize(writes1)); | |
| 4211 | |
| 4212 std::unique_ptr<SSLSocketDataProvider> ssl_provider1( | |
| 4213 new SSLSocketDataProvider(ASYNC, OK)); | |
| 4214 // Expect only HTTP/1.1 protocol in SSLConfig. | |
| 4215 ssl_provider1->next_protos_expected_in_ssl_config.push_back(kProtoHTTP11); | |
| 4216 // Force HTTP/1.1. | |
| 4217 ssl_provider1->next_proto = kProtoHTTP11; | |
| 4218 helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1)); | |
| 4219 | |
| 4220 HttpServerProperties* http_server_properties = | |
| 4221 helper.session()->spdy_session_pool()->http_server_properties(); | |
| 4222 EXPECT_FALSE(http_server_properties->RequiresHTTP11(host_port_pair_)); | |
| 4223 | |
| 4224 helper.RunPreTestSetup(); | |
| 4225 helper.StartDefaultTest(); | |
| 4226 helper.FinishDefaultTestWithoutVerification(); | |
| 4227 helper.VerifyDataConsumed(); | |
| 4228 EXPECT_TRUE(http_server_properties->RequiresHTTP11(host_port_pair_)); | |
| 4229 | |
| 4230 const HttpResponseInfo* response = helper.trans()->GetResponseInfo(); | |
| 4231 ASSERT_TRUE(response); | |
| 4232 ASSERT_TRUE(response->headers); | |
| 4233 EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); | |
| 4234 EXPECT_FALSE(response->was_fetched_via_spdy); | |
| 4235 EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1, | |
| 4236 response->connection_info); | |
| 4237 EXPECT_TRUE(response->was_alpn_negotiated); | |
| 4238 EXPECT_TRUE(request.url.SchemeIs("https")); | |
| 4239 EXPECT_EQ("127.0.0.1", response->socket_address.host()); | |
| 4240 EXPECT_EQ(443, response->socket_address.port()); | |
| 4241 SpdyString response_data; | |
| 4242 ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk()); | |
| 4243 EXPECT_EQ("hello", response_data); | |
| 4244 } | |
| 4245 | |
| 4246 // Retry with HTTP/1.1 to the proxy when receiving HTTP_1_1_REQUIRED from the | |
| 4247 // proxy. Note that no actual protocol negotiation happens, instead this test | |
| 4248 // forces protocols for both sockets. | |
| 4249 TEST_F(SpdyNetworkTransactionTest, HTTP11RequiredProxyRetry) { | |
| 4250 HttpRequestInfo request; | |
| 4251 request.method = "GET"; | |
| 4252 request.url = default_url_; | |
| 4253 auto session_deps = base::MakeUnique<SpdySessionDependencies>( | |
| 4254 ProxyService::CreateFixedFromPacResult("HTTPS myproxy:70")); | |
| 4255 // Do not force SPDY so that second socket can negotiate HTTP/1.1. | |
| 4256 NormalSpdyTransactionHelper helper( | |
| 4257 request, DEFAULT_PRIORITY, NetLogWithSource(), std::move(session_deps)); | |
| 4258 | |
| 4259 // First socket: HTTP/2 CONNECT rejected with HTTP_1_1_REQUIRED. | |
| 4260 SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect( | |
| 4261 nullptr, 0, 1, LOWEST, HostPortPair("www.example.org", 443))); | |
| 4262 MockWrite writes0[] = {CreateMockWrite(req, 0)}; | |
| 4263 SpdySerializedFrame go_away(spdy_util_.ConstructSpdyGoAway( | |
| 4264 0, ERROR_CODE_HTTP_1_1_REQUIRED, "Try again using HTTP/1.1 please.")); | |
| 4265 MockRead reads0[] = {CreateMockRead(go_away, 1)}; | |
| 4266 SequencedSocketData data0(reads0, arraysize(reads0), writes0, | |
| 4267 arraysize(writes0)); | |
| 4268 | |
| 4269 std::unique_ptr<SSLSocketDataProvider> ssl_provider0( | |
| 4270 new SSLSocketDataProvider(ASYNC, OK)); | |
| 4271 // Expect HTTP/2 protocols too in SSLConfig. | |
| 4272 ssl_provider0->next_protos_expected_in_ssl_config.push_back(kProtoHTTP2); | |
| 4273 ssl_provider0->next_protos_expected_in_ssl_config.push_back(kProtoHTTP11); | |
| 4274 // Force SPDY. | |
| 4275 ssl_provider0->next_proto = kProtoHTTP2; | |
| 4276 helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0)); | |
| 4277 | |
| 4278 // Second socket: retry using HTTP/1.1. | |
| 4279 MockWrite writes1[] = { | |
| 4280 MockWrite(ASYNC, 0, | |
| 4281 "CONNECT www.example.org:443 HTTP/1.1\r\n" | |
| 4282 "Host: www.example.org:443\r\n" | |
| 4283 "Proxy-Connection: keep-alive\r\n\r\n"), | |
| 4284 MockWrite(ASYNC, 2, | |
| 4285 "GET / HTTP/1.1\r\n" | |
| 4286 "Host: www.example.org\r\n" | |
| 4287 "Connection: keep-alive\r\n\r\n"), | |
| 4288 }; | |
| 4289 | |
| 4290 MockRead reads1[] = { | |
| 4291 MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n\r\n"), | |
| 4292 MockRead(ASYNC, 3, | |
| 4293 "HTTP/1.1 200 OK\r\n" | |
| 4294 "Content-Length: 5\r\n\r\n" | |
| 4295 "hello"), | |
| 4296 }; | |
| 4297 SequencedSocketData data1(reads1, arraysize(reads1), writes1, | |
| 4298 arraysize(writes1)); | |
| 4299 | |
| 4300 std::unique_ptr<SSLSocketDataProvider> ssl_provider1( | |
| 4301 new SSLSocketDataProvider(ASYNC, OK)); | |
| 4302 // Expect only HTTP/1.1 protocol in SSLConfig. | |
| 4303 ssl_provider1->next_protos_expected_in_ssl_config.push_back(kProtoHTTP11); | |
| 4304 // Force HTTP/1.1. | |
| 4305 ssl_provider1->next_proto = kProtoHTTP11; | |
| 4306 helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1)); | |
| 4307 | |
| 4308 // A third socket is needed for the tunnelled connection. | |
| 4309 std::unique_ptr<SSLSocketDataProvider> ssl_provider2( | |
| 4310 new SSLSocketDataProvider(ASYNC, OK)); | |
| 4311 helper.session_deps()->socket_factory->AddSSLSocketDataProvider( | |
| 4312 ssl_provider2.get()); | |
| 4313 | |
| 4314 HttpServerProperties* http_server_properties = | |
| 4315 helper.session()->spdy_session_pool()->http_server_properties(); | |
| 4316 const HostPortPair proxy_host_port_pair = HostPortPair("myproxy", 70); | |
| 4317 EXPECT_FALSE(http_server_properties->RequiresHTTP11(proxy_host_port_pair)); | |
| 4318 | |
| 4319 helper.RunPreTestSetup(); | |
| 4320 helper.StartDefaultTest(); | |
| 4321 helper.FinishDefaultTestWithoutVerification(); | |
| 4322 helper.VerifyDataConsumed(); | |
| 4323 EXPECT_TRUE(http_server_properties->RequiresHTTP11(proxy_host_port_pair)); | |
| 4324 | |
| 4325 const HttpResponseInfo* response = helper.trans()->GetResponseInfo(); | |
| 4326 ASSERT_TRUE(response); | |
| 4327 ASSERT_TRUE(response->headers); | |
| 4328 EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); | |
| 4329 EXPECT_FALSE(response->was_fetched_via_spdy); | |
| 4330 EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1, | |
| 4331 response->connection_info); | |
| 4332 EXPECT_FALSE(response->was_alpn_negotiated); | |
| 4333 EXPECT_TRUE(request.url.SchemeIs("https")); | |
| 4334 EXPECT_EQ("127.0.0.1", response->socket_address.host()); | |
| 4335 EXPECT_EQ(70, response->socket_address.port()); | |
| 4336 SpdyString response_data; | |
| 4337 ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk()); | |
| 4338 EXPECT_EQ("hello", response_data); | |
| 4339 } | |
| 4340 | |
| 4341 // Test to make sure we can correctly connect through a proxy. | |
| 4342 TEST_F(SpdyNetworkTransactionTest, ProxyConnect) { | |
| 4343 auto session_deps = base::MakeUnique<SpdySessionDependencies>( | |
| 4344 ProxyService::CreateFixedFromPacResult("PROXY myproxy:70")); | |
| 4345 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 4346 NetLogWithSource(), | |
| 4347 std::move(session_deps)); | |
| 4348 helper.RunPreTestSetup(); | |
| 4349 HttpNetworkTransaction* trans = helper.trans(); | |
| 4350 | |
| 4351 const char kConnect443[] = { | |
| 4352 "CONNECT www.example.org:443 HTTP/1.1\r\n" | |
| 4353 "Host: www.example.org:443\r\n" | |
| 4354 "Proxy-Connection: keep-alive\r\n\r\n"}; | |
| 4355 const char kHTTP200[] = {"HTTP/1.1 200 OK\r\n\r\n"}; | |
| 4356 SpdySerializedFrame req( | |
| 4357 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4358 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4359 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 4360 | |
| 4361 MockWrite writes[] = { | |
| 4362 MockWrite(SYNCHRONOUS, kConnect443, arraysize(kConnect443) - 1, 0), | |
| 4363 CreateMockWrite(req, 2), | |
| 4364 }; | |
| 4365 MockRead reads[] = { | |
| 4366 MockRead(SYNCHRONOUS, kHTTP200, arraysize(kHTTP200) - 1, 1), | |
| 4367 CreateMockRead(resp, 3), CreateMockRead(body, 4), | |
| 4368 MockRead(ASYNC, 0, 0, 5), | |
| 4369 }; | |
| 4370 std::unique_ptr<SequencedSocketData> data(new SequencedSocketData( | |
| 4371 reads, arraysize(reads), writes, arraysize(writes))); | |
| 4372 | |
| 4373 helper.AddData(data.get()); | |
| 4374 TestCompletionCallback callback; | |
| 4375 | |
| 4376 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 4377 NetLogWithSource()); | |
| 4378 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 4379 | |
| 4380 rv = callback.WaitForResult(); | |
| 4381 EXPECT_EQ(0, rv); | |
| 4382 | |
| 4383 // Verify the response headers. | |
| 4384 HttpResponseInfo response = *trans->GetResponseInfo(); | |
| 4385 ASSERT_TRUE(response.headers); | |
| 4386 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 4387 | |
| 4388 SpdyString response_data; | |
| 4389 ASSERT_THAT(ReadTransaction(trans, &response_data), IsOk()); | |
| 4390 EXPECT_EQ("hello!", response_data); | |
| 4391 helper.VerifyDataConsumed(); | |
| 4392 } | |
| 4393 | |
| 4394 // Test to make sure we can correctly connect through a proxy to | |
| 4395 // www.example.org, if there already exists a direct spdy connection to | |
| 4396 // www.example.org. See https://crbug.com/49874. | |
| 4397 TEST_F(SpdyNetworkTransactionTest, DirectConnectProxyReconnect) { | |
| 4398 // Use a proxy service which returns a proxy fallback list from DIRECT to | |
| 4399 // myproxy:70. For this test there will be no fallback, so it is equivalent | |
| 4400 // to simply DIRECT. The reason for appending the second proxy is to verify | |
| 4401 // that the session pool key used does is just "DIRECT". | |
| 4402 auto session_deps = base::MakeUnique<SpdySessionDependencies>( | |
| 4403 ProxyService::CreateFixedFromPacResult("DIRECT; PROXY myproxy:70")); | |
| 4404 // When setting up the first transaction, we store the SpdySessionPool so that | |
| 4405 // we can use the same pool in the second transaction. | |
| 4406 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 4407 NetLogWithSource(), | |
| 4408 std::move(session_deps)); | |
| 4409 | |
| 4410 SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool(); | |
| 4411 helper.RunPreTestSetup(); | |
| 4412 | |
| 4413 // Construct and send a simple GET request. | |
| 4414 SpdySerializedFrame req( | |
| 4415 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4416 MockWrite writes[] = { | |
| 4417 CreateMockWrite(req, 0), | |
| 4418 }; | |
| 4419 | |
| 4420 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4421 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 4422 MockRead reads[] = { | |
| 4423 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 4424 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3), // Force a pause | |
| 4425 }; | |
| 4426 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 4427 helper.AddData(&data); | |
| 4428 HttpNetworkTransaction* trans = helper.trans(); | |
| 4429 | |
| 4430 TestCompletionCallback callback; | |
| 4431 TransactionHelperResult out; | |
| 4432 out.rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 4433 NetLogWithSource()); | |
| 4434 | |
| 4435 EXPECT_EQ(out.rv, ERR_IO_PENDING); | |
| 4436 out.rv = callback.WaitForResult(); | |
| 4437 EXPECT_EQ(out.rv, OK); | |
| 4438 | |
| 4439 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
| 4440 EXPECT_TRUE(response->headers); | |
| 4441 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 4442 out.rv = ReadTransaction(trans, &out.response_data); | |
| 4443 EXPECT_THAT(out.rv, IsOk()); | |
| 4444 out.status_line = response->headers->GetStatusLine(); | |
| 4445 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 4446 EXPECT_EQ("hello!", out.response_data); | |
| 4447 | |
| 4448 // Check that the SpdySession is still in the SpdySessionPool. | |
| 4449 SpdySessionKey session_pool_key_direct(host_port_pair_, ProxyServer::Direct(), | |
| 4450 PRIVACY_MODE_DISABLED); | |
| 4451 EXPECT_TRUE(HasSpdySession(spdy_session_pool, session_pool_key_direct)); | |
| 4452 SpdySessionKey session_pool_key_proxy( | |
| 4453 host_port_pair_, | |
| 4454 ProxyServer::FromURI("www.foo.com", ProxyServer::SCHEME_HTTP), | |
| 4455 PRIVACY_MODE_DISABLED); | |
| 4456 EXPECT_FALSE(HasSpdySession(spdy_session_pool, session_pool_key_proxy)); | |
| 4457 | |
| 4458 // New SpdyTestUtil instance for the session that will be used for the | |
| 4459 // proxy connection. | |
| 4460 SpdyTestUtil spdy_util_2; | |
| 4461 | |
| 4462 // Set up data for the proxy connection. | |
| 4463 const char kConnect443[] = { | |
| 4464 "CONNECT www.example.org:443 HTTP/1.1\r\n" | |
| 4465 "Host: www.example.org:443\r\n" | |
| 4466 "Proxy-Connection: keep-alive\r\n\r\n"}; | |
| 4467 const char kHTTP200[] = {"HTTP/1.1 200 OK\r\n\r\n"}; | |
| 4468 SpdySerializedFrame req2(spdy_util_2.ConstructSpdyGet( | |
| 4469 GetDefaultUrlWithPath("/foo.dat").c_str(), 1, LOWEST)); | |
| 4470 SpdySerializedFrame resp2(spdy_util_2.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4471 SpdySerializedFrame body2(spdy_util_2.ConstructSpdyDataFrame(1, true)); | |
| 4472 | |
| 4473 MockWrite writes2[] = { | |
| 4474 MockWrite(SYNCHRONOUS, kConnect443, arraysize(kConnect443) - 1, 0), | |
| 4475 CreateMockWrite(req2, 2), | |
| 4476 }; | |
| 4477 MockRead reads2[] = { | |
| 4478 MockRead(SYNCHRONOUS, kHTTP200, arraysize(kHTTP200) - 1, 1), | |
| 4479 CreateMockRead(resp2, 3), CreateMockRead(body2, 4), | |
| 4480 MockRead(ASYNC, 0, 5) // EOF | |
| 4481 }; | |
| 4482 | |
| 4483 std::unique_ptr<SequencedSocketData> data_proxy(new SequencedSocketData( | |
| 4484 reads2, arraysize(reads2), writes2, arraysize(writes2))); | |
| 4485 | |
| 4486 // Create another request to www.example.org, but this time through a proxy. | |
| 4487 HttpRequestInfo request_proxy; | |
| 4488 request_proxy.method = "GET"; | |
| 4489 request_proxy.url = GURL(GetDefaultUrlWithPath("/foo.dat")); | |
| 4490 request_proxy.load_flags = 0; | |
| 4491 auto session_deps_proxy = base::MakeUnique<SpdySessionDependencies>( | |
| 4492 ProxyService::CreateFixedFromPacResult("PROXY myproxy:70")); | |
| 4493 NormalSpdyTransactionHelper helper_proxy(request_proxy, DEFAULT_PRIORITY, | |
| 4494 NetLogWithSource(), | |
| 4495 std::move(session_deps_proxy)); | |
| 4496 helper_proxy.RunPreTestSetup(); | |
| 4497 helper_proxy.AddData(data_proxy.get()); | |
| 4498 | |
| 4499 HttpNetworkTransaction* trans_proxy = helper_proxy.trans(); | |
| 4500 TestCompletionCallback callback_proxy; | |
| 4501 int rv = trans_proxy->Start(&request_proxy, callback_proxy.callback(), | |
| 4502 NetLogWithSource()); | |
| 4503 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 4504 rv = callback_proxy.WaitForResult(); | |
| 4505 EXPECT_EQ(0, rv); | |
| 4506 | |
| 4507 HttpResponseInfo response_proxy = *trans_proxy->GetResponseInfo(); | |
| 4508 ASSERT_TRUE(response_proxy.headers); | |
| 4509 EXPECT_EQ("HTTP/1.1 200", response_proxy.headers->GetStatusLine()); | |
| 4510 | |
| 4511 SpdyString response_data; | |
| 4512 ASSERT_THAT(ReadTransaction(trans_proxy, &response_data), IsOk()); | |
| 4513 EXPECT_EQ("hello!", response_data); | |
| 4514 | |
| 4515 helper_proxy.VerifyDataConsumed(); | |
| 4516 } | |
| 4517 | |
| 4518 // When we get a TCP-level RST, we need to retry a HttpNetworkTransaction | |
| 4519 // on a new connection, if the connection was previously known to be good. | |
| 4520 // This can happen when a server reboots without saying goodbye, or when | |
| 4521 // we're behind a NAT that masked the RST. | |
| 4522 TEST_F(SpdyNetworkTransactionTest, VerifyRetryOnConnectionReset) { | |
| 4523 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4524 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 4525 MockRead reads[] = { | |
| 4526 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 4527 MockRead(ASYNC, ERR_IO_PENDING, 3), | |
| 4528 MockRead(ASYNC, ERR_CONNECTION_RESET, 4), | |
| 4529 }; | |
| 4530 | |
| 4531 MockRead reads2[] = { | |
| 4532 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 4533 MockRead(ASYNC, 0, 3) // EOF | |
| 4534 }; | |
| 4535 | |
| 4536 SpdySerializedFrame req( | |
| 4537 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4538 // In all cases the connection will be reset before req3 can be | |
| 4539 // dispatched, destroying both streams. | |
| 4540 spdy_util_.UpdateWithStreamDestruction(1); | |
| 4541 SpdySerializedFrame req3( | |
| 4542 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
| 4543 MockWrite writes1[] = {CreateMockWrite(req, 0), CreateMockWrite(req3, 5)}; | |
| 4544 MockWrite writes2[] = {CreateMockWrite(req, 0)}; | |
| 4545 | |
| 4546 // This test has a couple of variants. | |
| 4547 enum { | |
| 4548 // Induce the RST while waiting for our transaction to send. | |
| 4549 VARIANT_RST_DURING_SEND_COMPLETION = 0, | |
| 4550 // Induce the RST while waiting for our transaction to read. | |
| 4551 // In this case, the send completed - everything copied into the SNDBUF. | |
| 4552 VARIANT_RST_DURING_READ_COMPLETION = 1 | |
| 4553 }; | |
| 4554 | |
| 4555 for (int variant = VARIANT_RST_DURING_SEND_COMPLETION; | |
| 4556 variant <= VARIANT_RST_DURING_READ_COMPLETION; | |
| 4557 ++variant) { | |
| 4558 SequencedSocketData data1(reads, arraysize(reads), writes1, 1 + variant); | |
| 4559 | |
| 4560 SequencedSocketData data2(reads2, arraysize(reads2), writes2, | |
| 4561 arraysize(writes2)); | |
| 4562 | |
| 4563 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 4564 NetLogWithSource(), nullptr); | |
| 4565 helper.AddData(&data1); | |
| 4566 helper.AddData(&data2); | |
| 4567 helper.RunPreTestSetup(); | |
| 4568 | |
| 4569 for (int i = 0; i < 2; ++i) { | |
| 4570 HttpNetworkTransaction trans(DEFAULT_PRIORITY, helper.session()); | |
| 4571 | |
| 4572 TestCompletionCallback callback; | |
| 4573 int rv = trans.Start(&helper.request(), callback.callback(), | |
| 4574 NetLogWithSource()); | |
| 4575 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 4576 // On the second transaction, we trigger the RST. | |
| 4577 if (i == 1) { | |
| 4578 if (variant == VARIANT_RST_DURING_READ_COMPLETION) { | |
| 4579 // Writes to the socket complete asynchronously on SPDY by running | |
| 4580 // through the message loop. Complete the write here. | |
| 4581 base::RunLoop().RunUntilIdle(); | |
| 4582 } | |
| 4583 | |
| 4584 // Now schedule the ERR_CONNECTION_RESET. | |
| 4585 data1.Resume(); | |
| 4586 } | |
| 4587 rv = callback.WaitForResult(); | |
| 4588 EXPECT_THAT(rv, IsOk()); | |
| 4589 | |
| 4590 const HttpResponseInfo* response = trans.GetResponseInfo(); | |
| 4591 ASSERT_TRUE(response); | |
| 4592 EXPECT_TRUE(response->headers); | |
| 4593 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 4594 SpdyString response_data; | |
| 4595 rv = ReadTransaction(&trans, &response_data); | |
| 4596 EXPECT_THAT(rv, IsOk()); | |
| 4597 EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); | |
| 4598 EXPECT_EQ("hello!", response_data); | |
| 4599 base::RunLoop().RunUntilIdle(); | |
| 4600 } | |
| 4601 | |
| 4602 helper.VerifyDataConsumed(); | |
| 4603 base::RunLoop().RunUntilIdle(); | |
| 4604 } | |
| 4605 } | |
| 4606 | |
| 4607 // Tests that Basic authentication works over SPDY | |
| 4608 TEST_F(SpdyNetworkTransactionTest, SpdyBasicAuth) { | |
| 4609 // The first request will be a bare GET, the second request will be a | |
| 4610 // GET with an Authorization header. | |
| 4611 SpdySerializedFrame req_get( | |
| 4612 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4613 // Will be refused for lack of auth. | |
| 4614 spdy_util_.UpdateWithStreamDestruction(1); | |
| 4615 const char* const kExtraAuthorizationHeaders[] = { | |
| 4616 "authorization", "Basic Zm9vOmJhcg==" | |
| 4617 }; | |
| 4618 SpdySerializedFrame req_get_authorization(spdy_util_.ConstructSpdyGet( | |
| 4619 kExtraAuthorizationHeaders, arraysize(kExtraAuthorizationHeaders) / 2, 3, | |
| 4620 LOWEST, true)); | |
| 4621 MockWrite spdy_writes[] = { | |
| 4622 CreateMockWrite(req_get, 0), CreateMockWrite(req_get_authorization, 3), | |
| 4623 }; | |
| 4624 | |
| 4625 // The first response is a 401 authentication challenge, and the second | |
| 4626 // response will be a 200 response since the second request includes a valid | |
| 4627 // Authorization header. | |
| 4628 const char* const kExtraAuthenticationHeaders[] = { | |
| 4629 "www-authenticate", | |
| 4630 "Basic realm=\"MyRealm\"" | |
| 4631 }; | |
| 4632 SpdySerializedFrame resp_authentication(spdy_util_.ConstructSpdyReplyError( | |
| 4633 "401", kExtraAuthenticationHeaders, | |
| 4634 arraysize(kExtraAuthenticationHeaders) / 2, 1)); | |
| 4635 SpdySerializedFrame body_authentication( | |
| 4636 spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 4637 SpdySerializedFrame resp_data( | |
| 4638 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 4639 SpdySerializedFrame body_data(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
| 4640 | |
| 4641 MockRead spdy_reads[] = { | |
| 4642 CreateMockRead(resp_authentication, 1), | |
| 4643 CreateMockRead(body_authentication, 2, SYNCHRONOUS), | |
| 4644 CreateMockRead(resp_data, 4), | |
| 4645 CreateMockRead(body_data, 5), | |
| 4646 MockRead(ASYNC, 0, 6), | |
| 4647 }; | |
| 4648 | |
| 4649 SequencedSocketData data(spdy_reads, arraysize(spdy_reads), spdy_writes, | |
| 4650 arraysize(spdy_writes)); | |
| 4651 HttpRequestInfo request(CreateGetRequest()); | |
| 4652 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 4653 NetLogWithSource(), nullptr); | |
| 4654 | |
| 4655 helper.RunPreTestSetup(); | |
| 4656 helper.AddData(&data); | |
| 4657 helper.StartDefaultTest(); | |
| 4658 EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING)); | |
| 4659 | |
| 4660 helper.WaitForCallbackToComplete(); | |
| 4661 EXPECT_THAT(helper.output().rv, IsOk()); | |
| 4662 | |
| 4663 // Make sure the response has an auth challenge. | |
| 4664 HttpNetworkTransaction* trans = helper.trans(); | |
| 4665 const HttpResponseInfo* const response_start = trans->GetResponseInfo(); | |
| 4666 ASSERT_TRUE(response_start); | |
| 4667 ASSERT_TRUE(response_start->headers); | |
| 4668 EXPECT_EQ(401, response_start->headers->response_code()); | |
| 4669 EXPECT_TRUE(response_start->was_fetched_via_spdy); | |
| 4670 AuthChallengeInfo* auth_challenge = response_start->auth_challenge.get(); | |
| 4671 ASSERT_TRUE(auth_challenge); | |
| 4672 EXPECT_FALSE(auth_challenge->is_proxy); | |
| 4673 EXPECT_EQ(kBasicAuthScheme, auth_challenge->scheme); | |
| 4674 EXPECT_EQ("MyRealm", auth_challenge->realm); | |
| 4675 | |
| 4676 // Restart with a username/password. | |
| 4677 AuthCredentials credentials(base::ASCIIToUTF16("foo"), | |
| 4678 base::ASCIIToUTF16("bar")); | |
| 4679 TestCompletionCallback callback_restart; | |
| 4680 const int rv_restart = trans->RestartWithAuth( | |
| 4681 credentials, callback_restart.callback()); | |
| 4682 EXPECT_THAT(rv_restart, IsError(ERR_IO_PENDING)); | |
| 4683 const int rv_restart_complete = callback_restart.WaitForResult(); | |
| 4684 EXPECT_THAT(rv_restart_complete, IsOk()); | |
| 4685 // TODO(cbentzel): This is actually the same response object as before, but | |
| 4686 // data has changed. | |
| 4687 const HttpResponseInfo* const response_restart = trans->GetResponseInfo(); | |
| 4688 ASSERT_TRUE(response_restart); | |
| 4689 ASSERT_TRUE(response_restart->headers); | |
| 4690 EXPECT_EQ(200, response_restart->headers->response_code()); | |
| 4691 EXPECT_TRUE(response_restart->auth_challenge.get() == nullptr); | |
| 4692 } | |
| 4693 | |
| 4694 TEST_F(SpdyNetworkTransactionTest, ServerPushWithHeaders) { | |
| 4695 SpdySerializedFrame stream1_syn( | |
| 4696 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4697 SpdySerializedFrame stream2_priority( | |
| 4698 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 4699 MockWrite writes[] = { | |
| 4700 CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3), | |
| 4701 }; | |
| 4702 | |
| 4703 SpdySerializedFrame stream1_reply( | |
| 4704 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4705 | |
| 4706 SpdyHeaderBlock initial_headers; | |
| 4707 spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat"), | |
| 4708 &initial_headers); | |
| 4709 SpdySerializedFrame stream2_syn(spdy_util_.ConstructInitialSpdyPushFrame( | |
| 4710 std::move(initial_headers), 2, 1)); | |
| 4711 | |
| 4712 SpdyHeaderBlock late_headers; | |
| 4713 late_headers[spdy_util_.GetStatusKey()] = "200"; | |
| 4714 late_headers["hello"] = "bye"; | |
| 4715 SpdySerializedFrame stream2_headers(spdy_util_.ConstructSpdyResponseHeaders( | |
| 4716 2, std::move(late_headers), false)); | |
| 4717 | |
| 4718 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 4719 | |
| 4720 const char kPushedData[] = "pushed"; | |
| 4721 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
| 4722 2, kPushedData, strlen(kPushedData), true)); | |
| 4723 | |
| 4724 MockRead reads[] = { | |
| 4725 CreateMockRead(stream1_reply, 1), | |
| 4726 CreateMockRead(stream2_syn, 2), | |
| 4727 CreateMockRead(stream2_headers, 4), | |
| 4728 CreateMockRead(stream1_body, 5, SYNCHRONOUS), | |
| 4729 CreateMockRead(stream2_body, 6), | |
| 4730 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 7), // Force a pause | |
| 4731 }; | |
| 4732 | |
| 4733 HttpResponseInfo response; | |
| 4734 HttpResponseInfo response2; | |
| 4735 SpdyString expected_push_result("pushed"); | |
| 4736 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 4737 RunServerPushTest(&data, | |
| 4738 &response, | |
| 4739 &response2, | |
| 4740 expected_push_result); | |
| 4741 | |
| 4742 // Verify the response headers. | |
| 4743 EXPECT_TRUE(response.headers); | |
| 4744 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 4745 | |
| 4746 // Verify the pushed stream. | |
| 4747 EXPECT_TRUE(response2.headers); | |
| 4748 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
| 4749 } | |
| 4750 | |
| 4751 TEST_F(SpdyNetworkTransactionTest, ServerPushClaimBeforeHeaders) { | |
| 4752 // We push a stream and attempt to claim it before the headers come down. | |
| 4753 SpdySerializedFrame stream1_syn( | |
| 4754 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4755 SpdySerializedFrame stream2_priority( | |
| 4756 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 4757 MockWrite writes[] = { | |
| 4758 CreateMockWrite(stream1_syn, 0, SYNCHRONOUS), | |
| 4759 CreateMockWrite(stream2_priority, 3), | |
| 4760 }; | |
| 4761 | |
| 4762 SpdySerializedFrame stream1_reply( | |
| 4763 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4764 SpdyHeaderBlock initial_headers; | |
| 4765 spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat"), | |
| 4766 &initial_headers); | |
| 4767 SpdySerializedFrame stream2_syn(spdy_util_.ConstructInitialSpdyPushFrame( | |
| 4768 std::move(initial_headers), 2, 1)); | |
| 4769 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 4770 SpdyHeaderBlock late_headers; | |
| 4771 late_headers[spdy_util_.GetStatusKey()] = "200"; | |
| 4772 late_headers["hello"] = "bye"; | |
| 4773 SpdySerializedFrame stream2_headers(spdy_util_.ConstructSpdyResponseHeaders( | |
| 4774 2, std::move(late_headers), false)); | |
| 4775 const char kPushedData[] = "pushed"; | |
| 4776 SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame( | |
| 4777 2, kPushedData, strlen(kPushedData), true)); | |
| 4778 MockRead reads[] = { | |
| 4779 CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2), | |
| 4780 CreateMockRead(stream1_body, 4), MockRead(ASYNC, ERR_IO_PENDING, 5), | |
| 4781 CreateMockRead(stream2_headers, 6), CreateMockRead(stream2_body, 7), | |
| 4782 MockRead(ASYNC, ERR_IO_PENDING, 8), MockRead(ASYNC, 0, 9), // EOF | |
| 4783 }; | |
| 4784 | |
| 4785 HttpResponseInfo response; | |
| 4786 HttpResponseInfo response2; | |
| 4787 SpdyString expected_push_result("pushed"); | |
| 4788 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 4789 | |
| 4790 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 4791 NetLogWithSource(), nullptr); | |
| 4792 helper.AddData(&data); | |
| 4793 helper.RunPreTestSetup(); | |
| 4794 | |
| 4795 HttpNetworkTransaction* trans = helper.trans(); | |
| 4796 | |
| 4797 // Start the transaction. | |
| 4798 TestCompletionCallback callback; | |
| 4799 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 4800 NetLogWithSource()); | |
| 4801 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 4802 // Run until we've received the primary HEADERS, the pushed HEADERS, | |
| 4803 // and the body of the primary stream, but before we've received the HEADERS | |
| 4804 // for the pushed stream. | |
| 4805 data.RunUntilPaused(); | |
| 4806 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
| 4807 | |
| 4808 // Request the pushed path. At this point, we've received the push, but the | |
| 4809 // headers are not yet complete. | |
| 4810 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
| 4811 rv = trans2.Start(&CreateGetPushRequest(), callback.callback(), | |
| 4812 NetLogWithSource()); | |
| 4813 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 4814 data.Resume(); | |
| 4815 data.RunUntilPaused(); | |
| 4816 base::RunLoop().RunUntilIdle(); | |
| 4817 | |
| 4818 // Read the server push body. | |
| 4819 SpdyString result2; | |
| 4820 ReadResult(&trans2, &result2); | |
| 4821 // Read the response body. | |
| 4822 SpdyString result; | |
| 4823 ReadResult(trans, &result); | |
| 4824 | |
| 4825 // Verify that the received push data is same as the expected push data. | |
| 4826 EXPECT_EQ(result2.compare(expected_push_result), 0) | |
| 4827 << "Received data: " | |
| 4828 << result2 | |
| 4829 << "||||| Expected data: " | |
| 4830 << expected_push_result; | |
| 4831 | |
| 4832 // Verify the response headers. | |
| 4833 // Copy the response info, because trans goes away. | |
| 4834 response = *trans->GetResponseInfo(); | |
| 4835 response2 = *trans2.GetResponseInfo(); | |
| 4836 | |
| 4837 VerifyStreamsClosed(helper); | |
| 4838 | |
| 4839 // Verify the response headers. | |
| 4840 EXPECT_TRUE(response.headers); | |
| 4841 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 4842 | |
| 4843 // Verify the pushed stream. | |
| 4844 EXPECT_TRUE(response2.headers); | |
| 4845 EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine()); | |
| 4846 | |
| 4847 // Read the final EOF (which will close the session) | |
| 4848 data.Resume(); | |
| 4849 base::RunLoop().RunUntilIdle(); | |
| 4850 | |
| 4851 // Verify that we consumed all test data. | |
| 4852 EXPECT_TRUE(data.AllReadDataConsumed()); | |
| 4853 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
| 4854 } | |
| 4855 | |
| 4856 TEST_F(SpdyNetworkTransactionTest, ResponseHeadersTwice) { | |
| 4857 SpdySerializedFrame req( | |
| 4858 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4859 SpdySerializedFrame rst( | |
| 4860 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
| 4861 MockWrite writes[] = { | |
| 4862 CreateMockWrite(req, 0), CreateMockWrite(rst, 4), | |
| 4863 }; | |
| 4864 | |
| 4865 SpdySerializedFrame stream1_reply( | |
| 4866 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4867 | |
| 4868 SpdyHeaderBlock late_headers; | |
| 4869 late_headers["hello"] = "bye"; | |
| 4870 SpdySerializedFrame stream1_headers(spdy_util_.ConstructSpdyResponseHeaders( | |
| 4871 1, std::move(late_headers), false)); | |
| 4872 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 4873 MockRead reads[] = { | |
| 4874 CreateMockRead(stream1_reply, 1), CreateMockRead(stream1_headers, 2), | |
| 4875 CreateMockRead(stream1_body, 3), MockRead(ASYNC, 0, 5) // EOF | |
| 4876 }; | |
| 4877 | |
| 4878 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 4879 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 4880 NetLogWithSource(), nullptr); | |
| 4881 helper.RunToCompletion(&data); | |
| 4882 TransactionHelperResult out = helper.output(); | |
| 4883 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
| 4884 } | |
| 4885 | |
| 4886 // Tests that receiving HEADERS, DATA, HEADERS, and DATA in that sequence will | |
| 4887 // trigger a ERR_SPDY_PROTOCOL_ERROR because trailing HEADERS must not be | |
| 4888 // followed by any DATA frames. | |
| 4889 TEST_F(SpdyNetworkTransactionTest, SyncReplyDataAfterTrailers) { | |
| 4890 SpdySerializedFrame req( | |
| 4891 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 4892 SpdySerializedFrame rst( | |
| 4893 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
| 4894 MockWrite writes[] = { | |
| 4895 CreateMockWrite(req, 0), CreateMockWrite(rst, 5), | |
| 4896 }; | |
| 4897 | |
| 4898 SpdySerializedFrame stream1_reply( | |
| 4899 spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4900 SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, false)); | |
| 4901 | |
| 4902 SpdyHeaderBlock late_headers; | |
| 4903 late_headers["hello"] = "bye"; | |
| 4904 SpdySerializedFrame stream1_headers(spdy_util_.ConstructSpdyResponseHeaders( | |
| 4905 1, std::move(late_headers), false)); | |
| 4906 SpdySerializedFrame stream1_body2(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 4907 MockRead reads[] = { | |
| 4908 CreateMockRead(stream1_reply, 1), CreateMockRead(stream1_body, 2), | |
| 4909 CreateMockRead(stream1_headers, 3), CreateMockRead(stream1_body2, 4), | |
| 4910 MockRead(ASYNC, 0, 6) // EOF | |
| 4911 }; | |
| 4912 | |
| 4913 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 4914 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 4915 NetLogWithSource(), nullptr); | |
| 4916 helper.RunToCompletion(&data); | |
| 4917 TransactionHelperResult out = helper.output(); | |
| 4918 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
| 4919 } | |
| 4920 | |
| 4921 TEST_F(SpdyNetworkTransactionTest, ServerPushCrossOriginCorrectness) { | |
| 4922 // In this test we want to verify that we can't accidentally push content | |
| 4923 // which can't be pushed by this content server. | |
| 4924 // This test assumes that: | |
| 4925 // - if we're requesting http://www.foo.com/barbaz | |
| 4926 // - the browser has made a connection to "www.foo.com". | |
| 4927 | |
| 4928 // A list of the URL to fetch, followed by the URL being pushed. | |
| 4929 static const char* const kTestCases[] = { | |
| 4930 "https://www.example.org/foo.html", | |
| 4931 "http://www.example.org/foo.js", // Bad protocol | |
| 4932 | |
| 4933 "https://www.example.org/foo.html", | |
| 4934 "ftp://www.example.org/foo.js", // Invalid Protocol | |
| 4935 | |
| 4936 "https://www.example.org/foo.html", | |
| 4937 "https://blat.www.example.org/foo.js", // Cross subdomain | |
| 4938 | |
| 4939 "https://www.example.org/foo.html", | |
| 4940 "https://www.foo.com/foo.js", // Cross domain | |
| 4941 }; | |
| 4942 | |
| 4943 for (size_t index = 0; index < arraysize(kTestCases); index += 2) { | |
| 4944 const char* url_to_fetch = kTestCases[index]; | |
| 4945 const char* url_to_push = kTestCases[index + 1]; | |
| 4946 | |
| 4947 SpdyTestUtil spdy_test_util; | |
| 4948 SpdySerializedFrame stream1_syn( | |
| 4949 spdy_test_util.ConstructSpdyGet(url_to_fetch, 1, LOWEST)); | |
| 4950 SpdySerializedFrame stream1_body( | |
| 4951 spdy_test_util.ConstructSpdyDataFrame(1, true)); | |
| 4952 SpdySerializedFrame push_rst( | |
| 4953 spdy_test_util.ConstructSpdyRstStream(2, ERROR_CODE_REFUSED_STREAM)); | |
| 4954 MockWrite writes[] = { | |
| 4955 CreateMockWrite(stream1_syn, 0), CreateMockWrite(push_rst, 3), | |
| 4956 }; | |
| 4957 | |
| 4958 SpdySerializedFrame stream1_reply( | |
| 4959 spdy_test_util.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 4960 SpdySerializedFrame stream2_syn( | |
| 4961 spdy_test_util.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push)); | |
| 4962 const char kPushedData[] = "pushed"; | |
| 4963 SpdySerializedFrame stream2_body(spdy_test_util.ConstructSpdyDataFrame( | |
| 4964 2, kPushedData, strlen(kPushedData), true)); | |
| 4965 SpdySerializedFrame rst( | |
| 4966 spdy_test_util.ConstructSpdyRstStream(2, ERROR_CODE_CANCEL)); | |
| 4967 | |
| 4968 MockRead reads[] = { | |
| 4969 CreateMockRead(stream1_reply, 1), | |
| 4970 CreateMockRead(stream2_syn, 2), | |
| 4971 CreateMockRead(stream1_body, 4), | |
| 4972 CreateMockRead(stream2_body, 5), | |
| 4973 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause | |
| 4974 }; | |
| 4975 | |
| 4976 HttpResponseInfo response; | |
| 4977 SequencedSocketData data(reads, arraysize(reads), writes, | |
| 4978 arraysize(writes)); | |
| 4979 | |
| 4980 HttpRequestInfo request; | |
| 4981 request.method = "GET"; | |
| 4982 request.url = GURL(url_to_fetch); | |
| 4983 request.load_flags = 0; | |
| 4984 | |
| 4985 // Enable cross-origin push. Since we are not using a proxy, this should | |
| 4986 // not actually enable cross-origin SPDY push. | |
| 4987 auto session_deps = base::MakeUnique<SpdySessionDependencies>(); | |
| 4988 std::unique_ptr<TestProxyDelegate> proxy_delegate(new TestProxyDelegate()); | |
| 4989 proxy_delegate->set_trusted_spdy_proxy(net::ProxyServer::FromURI( | |
| 4990 "https://123.45.67.89:443", net::ProxyServer::SCHEME_HTTP)); | |
| 4991 session_deps->proxy_delegate = std::move(proxy_delegate); | |
| 4992 NormalSpdyTransactionHelper helper( | |
| 4993 request, DEFAULT_PRIORITY, NetLogWithSource(), std::move(session_deps)); | |
| 4994 helper.RunPreTestSetup(); | |
| 4995 helper.AddData(&data); | |
| 4996 | |
| 4997 HttpNetworkTransaction* trans = helper.trans(); | |
| 4998 | |
| 4999 // Start the transaction with basic parameters. | |
| 5000 TestCompletionCallback callback; | |
| 5001 | |
| 5002 int rv = trans->Start(&request, callback.callback(), NetLogWithSource()); | |
| 5003 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 5004 rv = callback.WaitForResult(); | |
| 5005 | |
| 5006 // Finish async network reads/writes. | |
| 5007 base::RunLoop().RunUntilIdle(); | |
| 5008 | |
| 5009 // Read the response body. | |
| 5010 SpdyString result; | |
| 5011 ReadResult(trans, &result); | |
| 5012 | |
| 5013 // Verify that we consumed all test data. | |
| 5014 EXPECT_TRUE(data.AllReadDataConsumed()); | |
| 5015 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
| 5016 | |
| 5017 // Verify the response headers. | |
| 5018 // Copy the response info, because trans goes away. | |
| 5019 response = *trans->GetResponseInfo(); | |
| 5020 | |
| 5021 VerifyStreamsClosed(helper); | |
| 5022 | |
| 5023 // Verify the response headers. | |
| 5024 EXPECT_TRUE(response.headers); | |
| 5025 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 5026 } | |
| 5027 } | |
| 5028 | |
| 5029 // Verify that push works cross origin as long as the certificate is valid for | |
| 5030 // the pushed authority. | |
| 5031 TEST_F(SpdyNetworkTransactionTest, ServerPushValidCrossOrigin) { | |
| 5032 // "spdy_pooling.pem" is valid for both www.example.org and mail.example.org. | |
| 5033 const char* url_to_fetch = "https://www.example.org"; | |
| 5034 const char* url_to_push = "https://mail.example.org"; | |
| 5035 | |
| 5036 SpdySerializedFrame headers( | |
| 5037 spdy_util_.ConstructSpdyGet(url_to_fetch, 1, LOWEST)); | |
| 5038 SpdySerializedFrame push_priority( | |
| 5039 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 5040 MockWrite writes[] = { | |
| 5041 CreateMockWrite(headers, 0), CreateMockWrite(push_priority, 3), | |
| 5042 }; | |
| 5043 | |
| 5044 SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 5045 SpdySerializedFrame push( | |
| 5046 spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push)); | |
| 5047 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 5048 const char kPushedData[] = "pushed"; | |
| 5049 SpdySerializedFrame pushed_body(spdy_util_.ConstructSpdyDataFrame( | |
| 5050 2, kPushedData, strlen(kPushedData), true)); | |
| 5051 MockRead reads[] = { | |
| 5052 CreateMockRead(reply, 1), | |
| 5053 CreateMockRead(push, 2, SYNCHRONOUS), | |
| 5054 CreateMockRead(body, 4), | |
| 5055 CreateMockRead(pushed_body, 5, SYNCHRONOUS), | |
| 5056 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), | |
| 5057 }; | |
| 5058 | |
| 5059 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 5060 | |
| 5061 HttpRequestInfo request; | |
| 5062 request.method = "GET"; | |
| 5063 request.url = GURL(url_to_fetch); | |
| 5064 request.load_flags = 0; | |
| 5065 | |
| 5066 NetLogWithSource log; | |
| 5067 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, log, nullptr); | |
| 5068 helper.RunPreTestSetup(); | |
| 5069 helper.AddData(&data); | |
| 5070 | |
| 5071 HttpNetworkTransaction* trans0 = helper.trans(); | |
| 5072 TestCompletionCallback callback0; | |
| 5073 int rv = trans0->Start(&request, callback0.callback(), log); | |
| 5074 rv = callback0.GetResult(rv); | |
| 5075 EXPECT_THAT(rv, IsOk()); | |
| 5076 | |
| 5077 SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool(); | |
| 5078 SpdySessionKey key(host_port_pair_, ProxyServer::Direct(), | |
| 5079 PRIVACY_MODE_DISABLED); | |
| 5080 base::WeakPtr<SpdySession> spdy_session = | |
| 5081 spdy_session_pool->FindAvailableSession( | |
| 5082 key, GURL(), | |
| 5083 /* enable_ip_based_pooling = */ true, log); | |
| 5084 | |
| 5085 EXPECT_FALSE(spdy_session->unclaimed_pushed_streams_.empty()); | |
| 5086 EXPECT_EQ(1u, spdy_session->unclaimed_pushed_streams_.size()); | |
| 5087 EXPECT_EQ(1u, | |
| 5088 spdy_session->unclaimed_pushed_streams_.count(GURL(url_to_push))); | |
| 5089 | |
| 5090 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
| 5091 HttpRequestInfo push_request; | |
| 5092 push_request.method = "GET"; | |
| 5093 push_request.url = GURL(url_to_push); | |
| 5094 push_request.load_flags = 0; | |
| 5095 TestCompletionCallback callback1; | |
| 5096 rv = trans1.Start(&push_request, callback1.callback(), log); | |
| 5097 rv = callback1.GetResult(rv); | |
| 5098 EXPECT_THAT(rv, IsOk()); | |
| 5099 | |
| 5100 EXPECT_TRUE(spdy_session->unclaimed_pushed_streams_.empty()); | |
| 5101 EXPECT_EQ(0u, spdy_session->unclaimed_pushed_streams_.size()); | |
| 5102 | |
| 5103 HttpResponseInfo response = *trans0->GetResponseInfo(); | |
| 5104 EXPECT_TRUE(response.headers); | |
| 5105 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 5106 | |
| 5107 SpdyString result0; | |
| 5108 ReadResult(trans0, &result0); | |
| 5109 EXPECT_EQ("hello!", result0); | |
| 5110 | |
| 5111 HttpResponseInfo push_response = *trans1.GetResponseInfo(); | |
| 5112 EXPECT_TRUE(push_response.headers); | |
| 5113 EXPECT_EQ("HTTP/1.1 200", push_response.headers->GetStatusLine()); | |
| 5114 | |
| 5115 SpdyString result1; | |
| 5116 ReadResult(&trans1, &result1); | |
| 5117 EXPECT_EQ(kPushedData, result1); | |
| 5118 | |
| 5119 base::RunLoop().RunUntilIdle(); | |
| 5120 helper.VerifyDataConsumed(); | |
| 5121 VerifyStreamsClosed(helper); | |
| 5122 } | |
| 5123 | |
| 5124 // Verify that push works cross origin, even if there is already a connection | |
| 5125 // open to origin of pushed resource. | |
| 5126 TEST_F(SpdyNetworkTransactionTest, ServerPushValidCrossOriginWithOpenSession) { | |
| 5127 const char* url_to_fetch0 = "https://mail.example.org/foo"; | |
| 5128 const char* url_to_fetch1 = "https://docs.example.org"; | |
| 5129 const char* url_to_push = "https://mail.example.org/bar"; | |
| 5130 | |
| 5131 SpdyTestUtil spdy_util_0; | |
| 5132 | |
| 5133 SpdySerializedFrame headers0( | |
| 5134 spdy_util_0.ConstructSpdyGet(url_to_fetch0, 1, LOWEST)); | |
| 5135 MockWrite writes0[] = { | |
| 5136 CreateMockWrite(headers0, 0), | |
| 5137 }; | |
| 5138 | |
| 5139 SpdySerializedFrame reply0(spdy_util_0.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 5140 const char kData0[] = "first"; | |
| 5141 SpdySerializedFrame body0( | |
| 5142 spdy_util_0.ConstructSpdyDataFrame(1, kData0, strlen(kData0), true)); | |
| 5143 MockRead reads0[] = {CreateMockRead(reply0, 1), CreateMockRead(body0, 2), | |
| 5144 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)}; | |
| 5145 | |
| 5146 SequencedSocketData data0(reads0, arraysize(reads0), writes0, | |
| 5147 arraysize(writes0)); | |
| 5148 | |
| 5149 SpdyTestUtil spdy_util_1; | |
| 5150 | |
| 5151 SpdySerializedFrame headers1( | |
| 5152 spdy_util_1.ConstructSpdyGet(url_to_fetch1, 1, LOWEST)); | |
| 5153 SpdySerializedFrame push_priority( | |
| 5154 spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); | |
| 5155 MockWrite writes1[] = { | |
| 5156 CreateMockWrite(headers1, 0), | |
| 5157 CreateMockWrite(push_priority, 3, SYNCHRONOUS), | |
| 5158 }; | |
| 5159 | |
| 5160 SpdySerializedFrame reply1(spdy_util_1.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 5161 SpdySerializedFrame push( | |
| 5162 spdy_util_1.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push)); | |
| 5163 const char kData1[] = "second"; | |
| 5164 SpdySerializedFrame body1( | |
| 5165 spdy_util_1.ConstructSpdyDataFrame(1, kData1, strlen(kData1), true)); | |
| 5166 const char kPushedData[] = "pushed"; | |
| 5167 SpdySerializedFrame pushed_body(spdy_util_1.ConstructSpdyDataFrame( | |
| 5168 2, kPushedData, strlen(kPushedData), true)); | |
| 5169 | |
| 5170 MockRead reads1[] = { | |
| 5171 CreateMockRead(reply1, 1), | |
| 5172 CreateMockRead(push, 2, SYNCHRONOUS), | |
| 5173 CreateMockRead(body1, 4), | |
| 5174 CreateMockRead(pushed_body, 5, SYNCHRONOUS), | |
| 5175 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), | |
| 5176 }; | |
| 5177 | |
| 5178 SequencedSocketData data1(reads1, arraysize(reads1), writes1, | |
| 5179 arraysize(writes1)); | |
| 5180 | |
| 5181 // Request |url_to_fetch0| to open connection to mail.example.org. | |
| 5182 HttpRequestInfo request0; | |
| 5183 request0.method = "GET"; | |
| 5184 request0.url = GURL(url_to_fetch0); | |
| 5185 request0.load_flags = 0; | |
| 5186 | |
| 5187 NetLogWithSource log; | |
| 5188 NormalSpdyTransactionHelper helper(request0, DEFAULT_PRIORITY, log, nullptr); | |
| 5189 helper.RunPreTestSetup(); | |
| 5190 | |
| 5191 // "spdy_pooling.pem" is valid for www.example.org, but not for | |
| 5192 // docs.example.org. | |
| 5193 std::unique_ptr<SSLSocketDataProvider> ssl_provider0( | |
| 5194 new SSLSocketDataProvider(ASYNC, OK)); | |
| 5195 ssl_provider0->cert = | |
| 5196 ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); | |
| 5197 helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0)); | |
| 5198 | |
| 5199 // "wildcard.pem" is valid for both www.example.org and docs.example.org. | |
| 5200 std::unique_ptr<SSLSocketDataProvider> ssl_provider1( | |
| 5201 new SSLSocketDataProvider(ASYNC, OK)); | |
| 5202 ssl_provider1->cert = | |
| 5203 ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"); | |
| 5204 helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1)); | |
| 5205 | |
| 5206 HttpNetworkTransaction* trans0 = helper.trans(); | |
| 5207 TestCompletionCallback callback0; | |
| 5208 int rv = trans0->Start(&request0, callback0.callback(), log); | |
| 5209 rv = callback0.GetResult(rv); | |
| 5210 EXPECT_THAT(rv, IsOk()); | |
| 5211 | |
| 5212 // Request |url_to_fetch1|, during which docs.example.org pushes | |
| 5213 // |url_to_push|, which happens to be for www.example.org, to which there is | |
| 5214 // already an open connection. | |
| 5215 HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session()); | |
| 5216 HttpRequestInfo request1; | |
| 5217 request1.method = "GET"; | |
| 5218 request1.url = GURL(url_to_fetch1); | |
| 5219 request1.load_flags = 0; | |
| 5220 TestCompletionCallback callback1; | |
| 5221 rv = trans1.Start(&request1, callback1.callback(), log); | |
| 5222 rv = callback1.GetResult(rv); | |
| 5223 EXPECT_THAT(rv, IsOk()); | |
| 5224 | |
| 5225 SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool(); | |
| 5226 HostPortPair host_port_pair0("mail.example.org", 443); | |
| 5227 SpdySessionKey key0(host_port_pair0, ProxyServer::Direct(), | |
| 5228 PRIVACY_MODE_DISABLED); | |
| 5229 base::WeakPtr<SpdySession> spdy_session0 = | |
| 5230 spdy_session_pool->FindAvailableSession( | |
| 5231 key0, GURL(), | |
| 5232 /* enable_ip_based_pooling = */ true, log); | |
| 5233 | |
| 5234 EXPECT_TRUE(spdy_session0->unclaimed_pushed_streams_.empty()); | |
| 5235 EXPECT_EQ(0u, spdy_session0->unclaimed_pushed_streams_.size()); | |
| 5236 | |
| 5237 HostPortPair host_port_pair1("docs.example.org", 443); | |
| 5238 SpdySessionKey key1(host_port_pair1, ProxyServer::Direct(), | |
| 5239 PRIVACY_MODE_DISABLED); | |
| 5240 base::WeakPtr<SpdySession> spdy_session1 = | |
| 5241 spdy_session_pool->FindAvailableSession( | |
| 5242 key1, GURL(), | |
| 5243 /* enable_ip_based_pooling = */ true, log); | |
| 5244 | |
| 5245 EXPECT_FALSE(spdy_session1->unclaimed_pushed_streams_.empty()); | |
| 5246 EXPECT_EQ(1u, spdy_session1->unclaimed_pushed_streams_.size()); | |
| 5247 EXPECT_EQ(1u, | |
| 5248 spdy_session1->unclaimed_pushed_streams_.count(GURL(url_to_push))); | |
| 5249 | |
| 5250 // Request |url_to_push|, which should be served from the pushed resource. | |
| 5251 HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session()); | |
| 5252 HttpRequestInfo push_request; | |
| 5253 push_request.method = "GET"; | |
| 5254 push_request.url = GURL(url_to_push); | |
| 5255 push_request.load_flags = 0; | |
| 5256 TestCompletionCallback callback2; | |
| 5257 rv = trans2.Start(&push_request, callback2.callback(), log); | |
| 5258 rv = callback2.GetResult(rv); | |
| 5259 EXPECT_THAT(rv, IsOk()); | |
| 5260 | |
| 5261 EXPECT_TRUE(spdy_session0->unclaimed_pushed_streams_.empty()); | |
| 5262 EXPECT_EQ(0u, spdy_session0->unclaimed_pushed_streams_.size()); | |
| 5263 | |
| 5264 EXPECT_TRUE(spdy_session1->unclaimed_pushed_streams_.empty()); | |
| 5265 EXPECT_EQ(0u, spdy_session1->unclaimed_pushed_streams_.size()); | |
| 5266 | |
| 5267 HttpResponseInfo response0 = *trans0->GetResponseInfo(); | |
| 5268 EXPECT_TRUE(response0.headers); | |
| 5269 EXPECT_EQ("HTTP/1.1 200", response0.headers->GetStatusLine()); | |
| 5270 | |
| 5271 SpdyString result0; | |
| 5272 ReadResult(trans0, &result0); | |
| 5273 EXPECT_EQ(kData0, result0); | |
| 5274 | |
| 5275 HttpResponseInfo response1 = *trans1.GetResponseInfo(); | |
| 5276 EXPECT_TRUE(response1.headers); | |
| 5277 EXPECT_EQ("HTTP/1.1 200", response1.headers->GetStatusLine()); | |
| 5278 | |
| 5279 SpdyString result1; | |
| 5280 ReadResult(&trans1, &result1); | |
| 5281 EXPECT_EQ(kData1, result1); | |
| 5282 | |
| 5283 HttpResponseInfo push_response = *trans2.GetResponseInfo(); | |
| 5284 EXPECT_TRUE(push_response.headers); | |
| 5285 EXPECT_EQ("HTTP/1.1 200", push_response.headers->GetStatusLine()); | |
| 5286 | |
| 5287 SpdyString result2; | |
| 5288 ReadResult(&trans2, &result2); | |
| 5289 EXPECT_EQ(kPushedData, result2); | |
| 5290 | |
| 5291 base::RunLoop().RunUntilIdle(); | |
| 5292 helper.VerifyDataConsumed(); | |
| 5293 VerifyStreamsClosed(helper); | |
| 5294 } | |
| 5295 | |
| 5296 TEST_F(SpdyNetworkTransactionTest, ServerPushInvalidCrossOrigin) { | |
| 5297 // "spdy_pooling.pem" is valid for www.example.org, | |
| 5298 // but not for invalid.example.org. | |
| 5299 const char* url_to_fetch = "https://www.example.org"; | |
| 5300 const char* url_to_push = "https://invalid.example.org"; | |
| 5301 | |
| 5302 SpdySerializedFrame headers( | |
| 5303 spdy_util_.ConstructSpdyGet(url_to_fetch, 1, LOWEST)); | |
| 5304 SpdySerializedFrame rst( | |
| 5305 spdy_util_.ConstructSpdyRstStream(2, ERROR_CODE_REFUSED_STREAM)); | |
| 5306 MockWrite writes[] = { | |
| 5307 CreateMockWrite(headers, 0), CreateMockWrite(rst, 3), | |
| 5308 }; | |
| 5309 | |
| 5310 SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 5311 SpdySerializedFrame push( | |
| 5312 spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push)); | |
| 5313 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 5314 const char kPushedData[] = "pushed"; | |
| 5315 SpdySerializedFrame pushed_body(spdy_util_.ConstructSpdyDataFrame( | |
| 5316 2, kPushedData, strlen(kPushedData), true)); | |
| 5317 MockRead reads[] = { | |
| 5318 CreateMockRead(reply, 1), | |
| 5319 CreateMockRead(push, 2, SYNCHRONOUS), | |
| 5320 CreateMockRead(body, 4), | |
| 5321 CreateMockRead(pushed_body, 5, SYNCHRONOUS), | |
| 5322 MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), | |
| 5323 }; | |
| 5324 | |
| 5325 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 5326 | |
| 5327 HttpRequestInfo request; | |
| 5328 request.method = "GET"; | |
| 5329 request.url = GURL(url_to_fetch); | |
| 5330 request.load_flags = 0; | |
| 5331 | |
| 5332 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 5333 NetLogWithSource(), nullptr); | |
| 5334 helper.RunToCompletion(&data); | |
| 5335 TransactionHelperResult out = helper.output(); | |
| 5336 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 5337 EXPECT_EQ("hello!", out.response_data); | |
| 5338 } | |
| 5339 | |
| 5340 TEST_F(SpdyNetworkTransactionTest, RetryAfterRefused) { | |
| 5341 // Construct the request. | |
| 5342 SpdySerializedFrame req( | |
| 5343 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 5344 // Will be destroyed by the RST before stream 3 starts. | |
| 5345 spdy_util_.UpdateWithStreamDestruction(1); | |
| 5346 SpdySerializedFrame req2( | |
| 5347 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true)); | |
| 5348 MockWrite writes[] = { | |
| 5349 CreateMockWrite(req, 0), CreateMockWrite(req2, 2), | |
| 5350 }; | |
| 5351 | |
| 5352 SpdySerializedFrame refused( | |
| 5353 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_REFUSED_STREAM)); | |
| 5354 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 5355 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
| 5356 MockRead reads[] = { | |
| 5357 CreateMockRead(refused, 1), CreateMockRead(resp, 3), | |
| 5358 CreateMockRead(body, 4), MockRead(ASYNC, 0, 5) // EOF | |
| 5359 }; | |
| 5360 | |
| 5361 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 5362 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 5363 NetLogWithSource(), nullptr); | |
| 5364 | |
| 5365 helper.RunPreTestSetup(); | |
| 5366 helper.AddData(&data); | |
| 5367 | |
| 5368 HttpNetworkTransaction* trans = helper.trans(); | |
| 5369 | |
| 5370 // Start the transaction with basic parameters. | |
| 5371 TestCompletionCallback callback; | |
| 5372 int rv = trans->Start(&CreateGetRequest(), callback.callback(), | |
| 5373 NetLogWithSource()); | |
| 5374 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 5375 rv = callback.WaitForResult(); | |
| 5376 EXPECT_THAT(rv, IsOk()); | |
| 5377 | |
| 5378 // Finish async network reads. | |
| 5379 base::RunLoop().RunUntilIdle(); | |
| 5380 | |
| 5381 // Verify that we consumed all test data. | |
| 5382 EXPECT_TRUE(data.AllReadDataConsumed()); | |
| 5383 EXPECT_TRUE(data.AllWriteDataConsumed()); | |
| 5384 | |
| 5385 // Verify the response headers. | |
| 5386 HttpResponseInfo response = *trans->GetResponseInfo(); | |
| 5387 EXPECT_TRUE(response.headers); | |
| 5388 EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine()); | |
| 5389 } | |
| 5390 | |
| 5391 TEST_F(SpdyNetworkTransactionTest, OutOfOrderHeaders) { | |
| 5392 // This first request will start to establish the SpdySession. | |
| 5393 // Then we will start the second (MEDIUM priority) and then third | |
| 5394 // (HIGHEST priority) request in such a way that the third will actually | |
| 5395 // start before the second, causing the second to be numbered differently | |
| 5396 // than the order they were created. | |
| 5397 // | |
| 5398 // Note that the requests and responses created below are expectations | |
| 5399 // of what the above will produce on the wire, and hence are in the | |
| 5400 // initial->HIGHEST->LOWEST priority. | |
| 5401 // | |
| 5402 // Frames are created by SpdySession just before the write associated | |
| 5403 // with the frame is attempted, so stream dependencies will be based | |
| 5404 // on the streams alive at the point of the request write attempt. Thus | |
| 5405 // req1 is alive when req2 is attempted (during but not after the | |
| 5406 // |data.RunFor(2);| statement below) but not when req3 is attempted. | |
| 5407 // The call to spdy_util_.UpdateWithStreamDestruction() reflects this. | |
| 5408 SpdySerializedFrame req1( | |
| 5409 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 5410 SpdySerializedFrame req2( | |
| 5411 spdy_util_.ConstructSpdyGet(nullptr, 0, 3, HIGHEST, true)); | |
| 5412 spdy_util_.UpdateWithStreamDestruction(1); | |
| 5413 SpdySerializedFrame req3( | |
| 5414 spdy_util_.ConstructSpdyGet(nullptr, 0, 5, MEDIUM, true)); | |
| 5415 MockWrite writes[] = { | |
| 5416 MockWrite(ASYNC, ERR_IO_PENDING, 0), CreateMockWrite(req1, 1), | |
| 5417 CreateMockWrite(req2, 5), CreateMockWrite(req3, 6), | |
| 5418 }; | |
| 5419 | |
| 5420 SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 5421 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 5422 SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); | |
| 5423 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true)); | |
| 5424 SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5)); | |
| 5425 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, true)); | |
| 5426 MockRead reads[] = { | |
| 5427 CreateMockRead(resp1, 2), MockRead(ASYNC, ERR_IO_PENDING, 3), | |
| 5428 CreateMockRead(body1, 4), CreateMockRead(resp2, 7), | |
| 5429 CreateMockRead(body2, 8), CreateMockRead(resp3, 9), | |
| 5430 CreateMockRead(body3, 10), MockRead(ASYNC, 0, 11) // EOF | |
| 5431 }; | |
| 5432 | |
| 5433 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 5434 NormalSpdyTransactionHelper helper(CreateGetRequest(), LOWEST, | |
| 5435 NetLogWithSource(), nullptr); | |
| 5436 helper.RunPreTestSetup(); | |
| 5437 helper.AddData(&data); | |
| 5438 | |
| 5439 // Start the first transaction to set up the SpdySession | |
| 5440 HttpNetworkTransaction* trans = helper.trans(); | |
| 5441 TestCompletionCallback callback; | |
| 5442 HttpRequestInfo info1 = CreateGetRequest(); | |
| 5443 int rv = trans->Start(&info1, callback.callback(), NetLogWithSource()); | |
| 5444 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 5445 | |
| 5446 // Run the message loop, but do not allow the write to complete. | |
| 5447 // This leaves the SpdySession with a write pending, which prevents | |
| 5448 // SpdySession from attempting subsequent writes until this write completes. | |
| 5449 base::RunLoop().RunUntilIdle(); | |
| 5450 | |
| 5451 // Now, start both new transactions | |
| 5452 HttpRequestInfo info2 = CreateGetRequest(); | |
| 5453 TestCompletionCallback callback2; | |
| 5454 HttpNetworkTransaction trans2(MEDIUM, helper.session()); | |
| 5455 rv = trans2.Start(&info2, callback2.callback(), NetLogWithSource()); | |
| 5456 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 5457 base::RunLoop().RunUntilIdle(); | |
| 5458 | |
| 5459 HttpRequestInfo info3 = CreateGetRequest(); | |
| 5460 TestCompletionCallback callback3; | |
| 5461 HttpNetworkTransaction trans3(HIGHEST, helper.session()); | |
| 5462 rv = trans3.Start(&info3, callback3.callback(), NetLogWithSource()); | |
| 5463 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 5464 base::RunLoop().RunUntilIdle(); | |
| 5465 | |
| 5466 // We now have two HEADERS frames queued up which will be | |
| 5467 // dequeued only once the first write completes, which we | |
| 5468 // now allow to happen. | |
| 5469 ASSERT_TRUE(data.IsPaused()); | |
| 5470 data.Resume(); | |
| 5471 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
| 5472 | |
| 5473 // And now we can allow everything else to run to completion. | |
| 5474 data.Resume(); | |
| 5475 base::RunLoop().RunUntilIdle(); | |
| 5476 EXPECT_THAT(callback2.WaitForResult(), IsOk()); | |
| 5477 EXPECT_THAT(callback3.WaitForResult(), IsOk()); | |
| 5478 | |
| 5479 helper.VerifyDataConsumed(); | |
| 5480 | |
| 5481 // At this point the test is completed and we need to safely destroy | |
| 5482 // all allocated structures. Helper stores a transaction that has a | |
| 5483 // reference to a stack allocated request, which has a short lifetime, | |
| 5484 // and is accessed during the transaction destruction. We need to delete | |
| 5485 // the transaction while the request is still a valid object. | |
| 5486 helper.ResetTrans(); | |
| 5487 } | |
| 5488 | |
| 5489 // Test that sent data frames and received WINDOW_UPDATE frames change | |
| 5490 // the send_window_size_ correctly. | |
| 5491 | |
| 5492 // WINDOW_UPDATE is different than most other frames in that it can arrive | |
| 5493 // while the client is still sending the request body. In order to enforce | |
| 5494 // this scenario, we feed a couple of dummy frames and give a delay of 0 to | |
| 5495 // socket data provider, so that initial read that is done as soon as the | |
| 5496 // stream is created, succeeds and schedules another read. This way reads | |
| 5497 // and writes are interleaved; after doing a full frame write, SpdyStream | |
| 5498 // will break out of DoLoop and will read and process a WINDOW_UPDATE. | |
| 5499 // Once our WINDOW_UPDATE is read, we cannot send HEADERS right away | |
| 5500 // since request has not been completely written, therefore we feed | |
| 5501 // enough number of WINDOW_UPDATEs to finish the first read and cause a | |
| 5502 // write, leading to a complete write of request body; after that we send | |
| 5503 // a reply with a body, to cause a graceful shutdown. | |
| 5504 | |
| 5505 // TODO(agayev): develop a socket data provider where both, reads and | |
| 5506 // writes are ordered so that writing tests like these are easy and rewrite | |
| 5507 // all these tests using it. Right now we are working around the | |
| 5508 // limitations as described above and it's not deterministic, tests may | |
| 5509 // fail under specific circumstances. | |
| 5510 TEST_F(SpdyNetworkTransactionTest, WindowUpdateReceived) { | |
| 5511 static int kFrameCount = 2; | |
| 5512 std::unique_ptr<SpdyString> content( | |
| 5513 new SpdyString(kMaxSpdyFrameChunkSize, 'a')); | |
| 5514 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
| 5515 kDefaultUrl, 1, kMaxSpdyFrameChunkSize * kFrameCount, LOWEST, nullptr, | |
| 5516 0)); | |
| 5517 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame( | |
| 5518 1, content->c_str(), content->size(), false)); | |
| 5519 SpdySerializedFrame body_end(spdy_util_.ConstructSpdyDataFrame( | |
| 5520 1, content->c_str(), content->size(), true)); | |
| 5521 | |
| 5522 MockWrite writes[] = { | |
| 5523 CreateMockWrite(req, 0), CreateMockWrite(body, 1), | |
| 5524 CreateMockWrite(body_end, 2), | |
| 5525 }; | |
| 5526 | |
| 5527 static const int32_t kDeltaWindowSize = 0xff; | |
| 5528 static const int kDeltaCount = 4; | |
| 5529 SpdySerializedFrame window_update( | |
| 5530 spdy_util_.ConstructSpdyWindowUpdate(1, kDeltaWindowSize)); | |
| 5531 SpdySerializedFrame window_update_dummy( | |
| 5532 spdy_util_.ConstructSpdyWindowUpdate(2, kDeltaWindowSize)); | |
| 5533 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 5534 MockRead reads[] = { | |
| 5535 CreateMockRead(window_update_dummy, 3), | |
| 5536 CreateMockRead(window_update_dummy, 4), | |
| 5537 CreateMockRead(window_update_dummy, 5), | |
| 5538 CreateMockRead(window_update, 6), // Four updates, therefore window | |
| 5539 CreateMockRead(window_update, 7), // size should increase by | |
| 5540 CreateMockRead(window_update, 8), // kDeltaWindowSize * 4 | |
| 5541 CreateMockRead(window_update, 9), | |
| 5542 CreateMockRead(resp, 10), | |
| 5543 MockRead(ASYNC, ERR_IO_PENDING, 11), | |
| 5544 CreateMockRead(body_end, 12), | |
| 5545 MockRead(ASYNC, 0, 13) // EOF | |
| 5546 }; | |
| 5547 | |
| 5548 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 5549 | |
| 5550 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
| 5551 for (int i = 0; i < kFrameCount; ++i) { | |
| 5552 element_readers.push_back(base::WrapUnique( | |
| 5553 new UploadBytesElementReader(content->c_str(), content->size()))); | |
| 5554 } | |
| 5555 ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0); | |
| 5556 | |
| 5557 // Setup the request | |
| 5558 HttpRequestInfo request; | |
| 5559 request.method = "POST"; | |
| 5560 request.url = default_url_; | |
| 5561 request.upload_data_stream = &upload_data_stream; | |
| 5562 | |
| 5563 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 5564 NetLogWithSource(), nullptr); | |
| 5565 helper.AddData(&data); | |
| 5566 helper.RunPreTestSetup(); | |
| 5567 | |
| 5568 HttpNetworkTransaction* trans = helper.trans(); | |
| 5569 | |
| 5570 TestCompletionCallback callback; | |
| 5571 int rv = | |
| 5572 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
| 5573 | |
| 5574 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 5575 | |
| 5576 data.RunUntilPaused(); | |
| 5577 base::RunLoop().RunUntilIdle(); | |
| 5578 | |
| 5579 SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); | |
| 5580 ASSERT_TRUE(stream); | |
| 5581 ASSERT_TRUE(stream->stream()); | |
| 5582 EXPECT_EQ(static_cast<int>(kDefaultInitialWindowSize) + | |
| 5583 kDeltaWindowSize * kDeltaCount - | |
| 5584 kMaxSpdyFrameChunkSize * kFrameCount, | |
| 5585 stream->stream()->send_window_size()); | |
| 5586 | |
| 5587 data.Resume(); | |
| 5588 base::RunLoop().RunUntilIdle(); | |
| 5589 | |
| 5590 rv = callback.WaitForResult(); | |
| 5591 EXPECT_THAT(rv, IsOk()); | |
| 5592 | |
| 5593 helper.VerifyDataConsumed(); | |
| 5594 } | |
| 5595 | |
| 5596 // Test that received data frames and sent WINDOW_UPDATE frames change | |
| 5597 // the recv_window_size_ correctly. | |
| 5598 TEST_F(SpdyNetworkTransactionTest, WindowUpdateSent) { | |
| 5599 // Session level maximum window size that is more than twice the default | |
| 5600 // initial window size so that an initial window update is sent. | |
| 5601 const int32_t session_max_recv_window_size = 5 * 64 * 1024; | |
| 5602 ASSERT_LT(2 * kDefaultInitialWindowSize, session_max_recv_window_size); | |
| 5603 // Stream level maximum window size that is less than the session level | |
| 5604 // maximum window size so that we test for confusion between the two. | |
| 5605 const int32_t stream_max_recv_window_size = 4 * 64 * 1024; | |
| 5606 ASSERT_GT(session_max_recv_window_size, stream_max_recv_window_size); | |
| 5607 // Size of body to be sent. Has to be less than or equal to both window sizes | |
| 5608 // so that we do not run out of receiving window. Also has to be greater than | |
| 5609 // half of them so that it triggers both a session level and a stream level | |
| 5610 // window update frame. | |
| 5611 const int32_t kTargetSize = 3 * 64 * 1024; | |
| 5612 ASSERT_GE(session_max_recv_window_size, kTargetSize); | |
| 5613 ASSERT_GE(stream_max_recv_window_size, kTargetSize); | |
| 5614 ASSERT_LT(session_max_recv_window_size / 2, kTargetSize); | |
| 5615 ASSERT_LT(stream_max_recv_window_size / 2, kTargetSize); | |
| 5616 // Size of each DATA frame. | |
| 5617 const int32_t kChunkSize = 4096; | |
| 5618 // Size of window updates. | |
| 5619 ASSERT_EQ(0, session_max_recv_window_size / 2 % kChunkSize); | |
| 5620 const int32_t session_window_update_delta = | |
| 5621 session_max_recv_window_size / 2 + kChunkSize; | |
| 5622 ASSERT_EQ(0, stream_max_recv_window_size / 2 % kChunkSize); | |
| 5623 const int32_t stream_window_update_delta = | |
| 5624 stream_max_recv_window_size / 2 + kChunkSize; | |
| 5625 | |
| 5626 SettingsMap initial_settings; | |
| 5627 initial_settings[SETTINGS_HEADER_TABLE_SIZE] = kSpdyMaxHeaderTableSize; | |
| 5628 initial_settings[SETTINGS_MAX_CONCURRENT_STREAMS] = | |
| 5629 kSpdyMaxConcurrentPushedStreams; | |
| 5630 initial_settings[SETTINGS_INITIAL_WINDOW_SIZE] = stream_max_recv_window_size; | |
| 5631 SpdySerializedFrame initial_settings_frame( | |
| 5632 spdy_util_.ConstructSpdySettings(initial_settings)); | |
| 5633 SpdySerializedFrame initial_window_update( | |
| 5634 spdy_util_.ConstructSpdyWindowUpdate( | |
| 5635 kSessionFlowControlStreamId, | |
| 5636 session_max_recv_window_size - kDefaultInitialWindowSize)); | |
| 5637 SpdySerializedFrame req( | |
| 5638 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 5639 SpdySerializedFrame session_window_update( | |
| 5640 spdy_util_.ConstructSpdyWindowUpdate(0, session_window_update_delta)); | |
| 5641 SpdySerializedFrame stream_window_update( | |
| 5642 spdy_util_.ConstructSpdyWindowUpdate(1, stream_window_update_delta)); | |
| 5643 | |
| 5644 std::vector<MockWrite> writes; | |
| 5645 writes.push_back(MockWrite(ASYNC, kHttp2ConnectionHeaderPrefix, | |
| 5646 kHttp2ConnectionHeaderPrefixSize, 0)); | |
| 5647 writes.push_back(CreateMockWrite(initial_settings_frame, writes.size())); | |
| 5648 writes.push_back(CreateMockWrite(initial_window_update, writes.size())); | |
| 5649 writes.push_back(CreateMockWrite(req, writes.size())); | |
| 5650 | |
| 5651 std::vector<MockRead> reads; | |
| 5652 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 5653 reads.push_back(CreateMockRead(resp, writes.size() + reads.size())); | |
| 5654 | |
| 5655 std::vector<SpdySerializedFrame> body_frames; | |
| 5656 const SpdyString body_data(kChunkSize, 'x'); | |
| 5657 for (size_t remaining = kTargetSize; remaining != 0;) { | |
| 5658 size_t frame_size = std::min(remaining, body_data.size()); | |
| 5659 body_frames.push_back(spdy_util_.ConstructSpdyDataFrame(1, body_data.data(), | |
| 5660 frame_size, false)); | |
| 5661 reads.push_back( | |
| 5662 CreateMockRead(body_frames.back(), writes.size() + reads.size())); | |
| 5663 remaining -= frame_size; | |
| 5664 } | |
| 5665 // Yield. | |
| 5666 reads.push_back( | |
| 5667 MockRead(SYNCHRONOUS, ERR_IO_PENDING, writes.size() + reads.size())); | |
| 5668 | |
| 5669 writes.push_back( | |
| 5670 CreateMockWrite(session_window_update, writes.size() + reads.size())); | |
| 5671 writes.push_back( | |
| 5672 CreateMockWrite(stream_window_update, writes.size() + reads.size())); | |
| 5673 | |
| 5674 SequencedSocketData data(reads.data(), reads.size(), writes.data(), | |
| 5675 writes.size()); | |
| 5676 | |
| 5677 auto session_deps = base::MakeUnique<SpdySessionDependencies>(); | |
| 5678 session_deps->session_max_recv_window_size = session_max_recv_window_size; | |
| 5679 session_deps->http2_settings[SETTINGS_INITIAL_WINDOW_SIZE] = | |
| 5680 stream_max_recv_window_size; | |
| 5681 | |
| 5682 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 5683 NetLogWithSource(), | |
| 5684 std::move(session_deps)); | |
| 5685 helper.AddData(&data); | |
| 5686 helper.RunPreTestSetup(); | |
| 5687 | |
| 5688 SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool(); | |
| 5689 SpdySessionPoolPeer pool_peer(spdy_session_pool); | |
| 5690 pool_peer.SetEnableSendingInitialData(true); | |
| 5691 | |
| 5692 HttpNetworkTransaction* trans = helper.trans(); | |
| 5693 TestCompletionCallback callback; | |
| 5694 int rv = | |
| 5695 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
| 5696 | |
| 5697 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 5698 rv = callback.WaitForResult(); | |
| 5699 EXPECT_THAT(rv, IsOk()); | |
| 5700 | |
| 5701 // Finish async network reads. | |
| 5702 base::RunLoop().RunUntilIdle(); | |
| 5703 | |
| 5704 SpdyHttpStream* stream = | |
| 5705 static_cast<SpdyHttpStream*>(trans->stream_.get()); | |
| 5706 ASSERT_TRUE(stream); | |
| 5707 ASSERT_TRUE(stream->stream()); | |
| 5708 | |
| 5709 // All data has been read, but not consumed. The window reflects this. | |
| 5710 EXPECT_EQ(static_cast<int>(stream_max_recv_window_size - kTargetSize), | |
| 5711 stream->stream()->recv_window_size()); | |
| 5712 | |
| 5713 const HttpResponseInfo* response = trans->GetResponseInfo(); | |
| 5714 ASSERT_TRUE(response); | |
| 5715 ASSERT_TRUE(response->headers); | |
| 5716 EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); | |
| 5717 EXPECT_TRUE(response->was_fetched_via_spdy); | |
| 5718 | |
| 5719 // Issue a read which will cause a WINDOW_UPDATE to be sent and window | |
| 5720 // size increased to default. | |
| 5721 scoped_refptr<IOBuffer> buf(new IOBuffer(kTargetSize)); | |
| 5722 EXPECT_EQ(static_cast<int>(kTargetSize), | |
| 5723 trans->Read(buf.get(), kTargetSize, CompletionCallback())); | |
| 5724 EXPECT_EQ(static_cast<int>(stream_max_recv_window_size), | |
| 5725 stream->stream()->recv_window_size()); | |
| 5726 EXPECT_THAT(SpdyStringPiece(buf->data(), kTargetSize), Each(Eq('x'))); | |
| 5727 | |
| 5728 // Allow scheduled WINDOW_UPDATE frames to write. | |
| 5729 base::RunLoop().RunUntilIdle(); | |
| 5730 helper.VerifyDataConsumed(); | |
| 5731 } | |
| 5732 | |
| 5733 // Test that WINDOW_UPDATE frame causing overflow is handled correctly. | |
| 5734 TEST_F(SpdyNetworkTransactionTest, WindowUpdateOverflow) { | |
| 5735 // Number of full frames we hope to write (but will not, used to | |
| 5736 // set content-length header correctly) | |
| 5737 static int kFrameCount = 3; | |
| 5738 | |
| 5739 std::unique_ptr<SpdyString> content( | |
| 5740 new SpdyString(kMaxSpdyFrameChunkSize, 'a')); | |
| 5741 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
| 5742 kDefaultUrl, 1, kMaxSpdyFrameChunkSize * kFrameCount, LOWEST, nullptr, | |
| 5743 0)); | |
| 5744 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame( | |
| 5745 1, content->c_str(), content->size(), false)); | |
| 5746 SpdySerializedFrame rst( | |
| 5747 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_FLOW_CONTROL_ERROR)); | |
| 5748 | |
| 5749 // We're not going to write a data frame with FIN, we'll receive a bad | |
| 5750 // WINDOW_UPDATE while sending a request and will send a RST_STREAM frame. | |
| 5751 MockWrite writes[] = { | |
| 5752 CreateMockWrite(req, 0), CreateMockWrite(body, 2), | |
| 5753 CreateMockWrite(rst, 3), | |
| 5754 }; | |
| 5755 | |
| 5756 static const int32_t kDeltaWindowSize = 0x7fffffff; // cause an overflow | |
| 5757 SpdySerializedFrame window_update( | |
| 5758 spdy_util_.ConstructSpdyWindowUpdate(1, kDeltaWindowSize)); | |
| 5759 MockRead reads[] = { | |
| 5760 CreateMockRead(window_update, 1), MockRead(ASYNC, 0, 4) // EOF | |
| 5761 }; | |
| 5762 | |
| 5763 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 5764 | |
| 5765 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
| 5766 for (int i = 0; i < kFrameCount; ++i) { | |
| 5767 element_readers.push_back(base::WrapUnique( | |
| 5768 new UploadBytesElementReader(content->c_str(), content->size()))); | |
| 5769 } | |
| 5770 ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0); | |
| 5771 | |
| 5772 // Setup the request | |
| 5773 HttpRequestInfo request; | |
| 5774 request.method = "POST"; | |
| 5775 request.url = default_url_; | |
| 5776 request.upload_data_stream = &upload_data_stream; | |
| 5777 | |
| 5778 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 5779 NetLogWithSource(), nullptr); | |
| 5780 helper.RunPreTestSetup(); | |
| 5781 helper.AddData(&data); | |
| 5782 HttpNetworkTransaction* trans = helper.trans(); | |
| 5783 | |
| 5784 TestCompletionCallback callback; | |
| 5785 int rv = | |
| 5786 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
| 5787 ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 5788 | |
| 5789 base::RunLoop().RunUntilIdle(); | |
| 5790 ASSERT_TRUE(callback.have_result()); | |
| 5791 EXPECT_THAT(callback.WaitForResult(), IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
| 5792 helper.VerifyDataConsumed(); | |
| 5793 } | |
| 5794 | |
| 5795 // Test that after hitting a send window size of 0, the write process | |
| 5796 // stalls and upon receiving WINDOW_UPDATE frame write resumes. | |
| 5797 | |
| 5798 // This test constructs a POST request followed by enough data frames | |
| 5799 // containing 'a' that would make the window size 0, followed by another | |
| 5800 // data frame containing default content (which is "hello!") and this frame | |
| 5801 // also contains a FIN flag. SequencedSocketData is used to enforce all | |
| 5802 // writes, save the last, go through before a read could happen. The last frame | |
| 5803 // ("hello!") is not permitted to go through since by the time its turn | |
| 5804 // arrives, window size is 0. At this point MessageLoop::Run() called via | |
| 5805 // callback would block. Therefore we call MessageLoop::RunUntilIdle() | |
| 5806 // which returns after performing all possible writes. We use DCHECKS to | |
| 5807 // ensure that last data frame is still there and stream has stalled. | |
| 5808 // After that, next read is artifically enforced, which causes a | |
| 5809 // WINDOW_UPDATE to be read and I/O process resumes. | |
| 5810 TEST_F(SpdyNetworkTransactionTest, FlowControlStallResume) { | |
| 5811 const int32_t initial_window_size = kDefaultInitialWindowSize; | |
| 5812 // Number of upload data buffers we need to send to zero out the window size | |
| 5813 // is the minimal number of upload buffers takes to be bigger than | |
| 5814 // |initial_window_size|. | |
| 5815 size_t num_upload_buffers = | |
| 5816 ceil(static_cast<double>(initial_window_size) / kBufferSize); | |
| 5817 // Each upload data buffer consists of |num_frames_in_one_upload_buffer| | |
| 5818 // frames, each with |kMaxSpdyFrameChunkSize| bytes except the last frame, | |
| 5819 // which has kBufferSize % kMaxSpdyChunkSize bytes. | |
| 5820 size_t num_frames_in_one_upload_buffer = | |
| 5821 ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize); | |
| 5822 | |
| 5823 // Construct content for a data frame of maximum size. | |
| 5824 SpdyString content(kMaxSpdyFrameChunkSize, 'a'); | |
| 5825 | |
| 5826 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
| 5827 kDefaultUrl, 1, | |
| 5828 /*content_length=*/kBufferSize * num_upload_buffers + kUploadDataSize, | |
| 5829 LOWEST, nullptr, 0)); | |
| 5830 | |
| 5831 // Full frames. | |
| 5832 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame( | |
| 5833 1, content.c_str(), content.size(), false)); | |
| 5834 | |
| 5835 // Last frame in each upload data buffer. | |
| 5836 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame( | |
| 5837 1, content.c_str(), kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
| 5838 | |
| 5839 // The very last frame before the stalled frames. | |
| 5840 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame( | |
| 5841 1, content.c_str(), | |
| 5842 initial_window_size % kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
| 5843 | |
| 5844 // Data frames to be sent once WINDOW_UPDATE frame is received. | |
| 5845 | |
| 5846 // If kBufferSize * num_upload_buffers > initial_window_size, | |
| 5847 // we need one additional frame to send the rest of 'a'. | |
| 5848 SpdyString last_body(kBufferSize * num_upload_buffers - initial_window_size, | |
| 5849 'a'); | |
| 5850 SpdySerializedFrame body4(spdy_util_.ConstructSpdyDataFrame( | |
| 5851 1, last_body.c_str(), last_body.size(), false)); | |
| 5852 | |
| 5853 // Also send a "hello!" after WINDOW_UPDATE. | |
| 5854 SpdySerializedFrame body5(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 5855 | |
| 5856 // Fill in mock writes. | |
| 5857 size_t i = 0; | |
| 5858 std::vector<MockWrite> writes; | |
| 5859 writes.push_back(CreateMockWrite(req, i++)); | |
| 5860 for (size_t j = 0; j < num_upload_buffers; j++) { | |
| 5861 for (size_t k = 0; k < num_frames_in_one_upload_buffer; k++) { | |
| 5862 if (k == num_frames_in_one_upload_buffer - 1 && | |
| 5863 kBufferSize % kMaxSpdyFrameChunkSize != 0) { | |
| 5864 if (j == num_upload_buffers - 1 && | |
| 5865 (initial_window_size % kBufferSize != 0)) { | |
| 5866 writes.push_back(CreateMockWrite(body3, i++)); | |
| 5867 } else { | |
| 5868 writes.push_back(CreateMockWrite(body2, i++)); | |
| 5869 } | |
| 5870 } else { | |
| 5871 writes.push_back(CreateMockWrite(body1, i++)); | |
| 5872 } | |
| 5873 } | |
| 5874 } | |
| 5875 | |
| 5876 // Fill in mock reads. | |
| 5877 std::vector<MockRead> reads; | |
| 5878 // Force a pause. | |
| 5879 reads.push_back(MockRead(ASYNC, ERR_IO_PENDING, i++)); | |
| 5880 // Construct read frame for window updates that gives enough space to upload | |
| 5881 // the rest of the data. | |
| 5882 SpdySerializedFrame session_window_update( | |
| 5883 spdy_util_.ConstructSpdyWindowUpdate(0, | |
| 5884 kUploadDataSize + last_body.size())); | |
| 5885 SpdySerializedFrame window_update(spdy_util_.ConstructSpdyWindowUpdate( | |
| 5886 1, kUploadDataSize + last_body.size())); | |
| 5887 | |
| 5888 reads.push_back(CreateMockRead(session_window_update, i++)); | |
| 5889 reads.push_back(CreateMockRead(window_update, i++)); | |
| 5890 | |
| 5891 // Stalled frames which can be sent after receiving window updates. | |
| 5892 if (last_body.size() > 0) | |
| 5893 writes.push_back(CreateMockWrite(body4, i++)); | |
| 5894 writes.push_back(CreateMockWrite(body5, i++)); | |
| 5895 | |
| 5896 SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 5897 reads.push_back(CreateMockRead(reply, i++)); | |
| 5898 reads.push_back(CreateMockRead(body2, i++)); | |
| 5899 reads.push_back(CreateMockRead(body5, i++)); | |
| 5900 reads.push_back(MockRead(ASYNC, 0, i++)); // EOF | |
| 5901 | |
| 5902 SequencedSocketData data(reads.data(), reads.size(), writes.data(), | |
| 5903 writes.size()); | |
| 5904 | |
| 5905 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
| 5906 SpdyString upload_data_string(kBufferSize * num_upload_buffers, 'a'); | |
| 5907 upload_data_string.append(kUploadData, kUploadDataSize); | |
| 5908 element_readers.push_back(base::WrapUnique(new UploadBytesElementReader( | |
| 5909 upload_data_string.c_str(), upload_data_string.size()))); | |
| 5910 ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0); | |
| 5911 | |
| 5912 HttpRequestInfo request; | |
| 5913 request.method = "POST"; | |
| 5914 request.url = default_url_; | |
| 5915 request.upload_data_stream = &upload_data_stream; | |
| 5916 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 5917 NetLogWithSource(), nullptr); | |
| 5918 helper.AddData(&data); | |
| 5919 helper.RunPreTestSetup(); | |
| 5920 | |
| 5921 HttpNetworkTransaction* trans = helper.trans(); | |
| 5922 | |
| 5923 TestCompletionCallback callback; | |
| 5924 int rv = | |
| 5925 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
| 5926 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 5927 | |
| 5928 base::RunLoop().RunUntilIdle(); // Write as much as we can. | |
| 5929 | |
| 5930 SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); | |
| 5931 ASSERT_TRUE(stream); | |
| 5932 ASSERT_TRUE(stream->stream()); | |
| 5933 EXPECT_EQ(0, stream->stream()->send_window_size()); | |
| 5934 if (initial_window_size % kBufferSize != 0) { | |
| 5935 // If it does not take whole number of full upload buffer to zero out | |
| 5936 // initial window size, then the upload data is not at EOF, because the | |
| 5937 // last read must be stalled. | |
| 5938 EXPECT_FALSE(upload_data_stream.IsEOF()); | |
| 5939 } else { | |
| 5940 // All the body data should have been read. | |
| 5941 // TODO(satorux): This is because of the weirdness in reading the request | |
| 5942 // body in OnSendBodyComplete(). See crbug.com/113107. | |
| 5943 EXPECT_TRUE(upload_data_stream.IsEOF()); | |
| 5944 } | |
| 5945 // But the body is not yet fully sent (kUploadData is not yet sent) | |
| 5946 // since we're send-stalled. | |
| 5947 EXPECT_TRUE(stream->stream()->send_stalled_by_flow_control()); | |
| 5948 | |
| 5949 data.Resume(); // Read in WINDOW_UPDATE frame. | |
| 5950 rv = callback.WaitForResult(); | |
| 5951 EXPECT_THAT(rv, IsOk()); | |
| 5952 | |
| 5953 // Finish async network reads. | |
| 5954 base::RunLoop().RunUntilIdle(); | |
| 5955 helper.VerifyDataConsumed(); | |
| 5956 } | |
| 5957 | |
| 5958 // Test we correctly handle the case where the SETTINGS frame results in | |
| 5959 // unstalling the send window. | |
| 5960 TEST_F(SpdyNetworkTransactionTest, FlowControlStallResumeAfterSettings) { | |
| 5961 const int32_t initial_window_size = kDefaultInitialWindowSize; | |
| 5962 // Number of upload data buffers we need to send to zero out the window size | |
| 5963 // is the minimal number of upload buffers takes to be bigger than | |
| 5964 // |initial_window_size|. | |
| 5965 size_t num_upload_buffers = | |
| 5966 ceil(static_cast<double>(initial_window_size) / kBufferSize); | |
| 5967 // Each upload data buffer consists of |num_frames_in_one_upload_buffer| | |
| 5968 // frames, each with |kMaxSpdyFrameChunkSize| bytes except the last frame, | |
| 5969 // which has kBufferSize % kMaxSpdyChunkSize bytes. | |
| 5970 size_t num_frames_in_one_upload_buffer = | |
| 5971 ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize); | |
| 5972 | |
| 5973 // Construct content for a data frame of maximum size. | |
| 5974 SpdyString content(kMaxSpdyFrameChunkSize, 'a'); | |
| 5975 | |
| 5976 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
| 5977 kDefaultUrl, 1, | |
| 5978 /*content_length=*/kBufferSize * num_upload_buffers + kUploadDataSize, | |
| 5979 LOWEST, nullptr, 0)); | |
| 5980 | |
| 5981 // Full frames. | |
| 5982 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame( | |
| 5983 1, content.c_str(), content.size(), false)); | |
| 5984 | |
| 5985 // Last frame in each upload data buffer. | |
| 5986 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame( | |
| 5987 1, content.c_str(), kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
| 5988 | |
| 5989 // The very last frame before the stalled frames. | |
| 5990 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame( | |
| 5991 1, content.c_str(), | |
| 5992 initial_window_size % kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
| 5993 | |
| 5994 // Data frames to be sent once WINDOW_UPDATE frame is received. | |
| 5995 | |
| 5996 // If kBufferSize * num_upload_buffers > initial_window_size, | |
| 5997 // we need one additional frame to send the rest of 'a'. | |
| 5998 SpdyString last_body(kBufferSize * num_upload_buffers - initial_window_size, | |
| 5999 'a'); | |
| 6000 SpdySerializedFrame body4(spdy_util_.ConstructSpdyDataFrame( | |
| 6001 1, last_body.c_str(), last_body.size(), false)); | |
| 6002 | |
| 6003 // Also send a "hello!" after WINDOW_UPDATE. | |
| 6004 SpdySerializedFrame body5(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 6005 | |
| 6006 // Fill in mock writes. | |
| 6007 size_t i = 0; | |
| 6008 std::vector<MockWrite> writes; | |
| 6009 writes.push_back(CreateMockWrite(req, i++)); | |
| 6010 for (size_t j = 0; j < num_upload_buffers; j++) { | |
| 6011 for (size_t k = 0; k < num_frames_in_one_upload_buffer; k++) { | |
| 6012 if (k == num_frames_in_one_upload_buffer - 1 && | |
| 6013 kBufferSize % kMaxSpdyFrameChunkSize != 0) { | |
| 6014 if (j == num_upload_buffers - 1 && | |
| 6015 (initial_window_size % kBufferSize != 0)) { | |
| 6016 writes.push_back(CreateMockWrite(body3, i++)); | |
| 6017 } else { | |
| 6018 writes.push_back(CreateMockWrite(body2, i++)); | |
| 6019 } | |
| 6020 } else { | |
| 6021 writes.push_back(CreateMockWrite(body1, i++)); | |
| 6022 } | |
| 6023 } | |
| 6024 } | |
| 6025 | |
| 6026 // Fill in mock reads. | |
| 6027 std::vector<MockRead> reads; | |
| 6028 // Force a pause. | |
| 6029 reads.push_back(MockRead(ASYNC, ERR_IO_PENDING, i++)); | |
| 6030 | |
| 6031 // Construct read frame for SETTINGS that gives enough space to upload the | |
| 6032 // rest of the data. | |
| 6033 SettingsMap settings; | |
| 6034 settings[SETTINGS_INITIAL_WINDOW_SIZE] = initial_window_size * 2; | |
| 6035 SpdySerializedFrame settings_frame_large( | |
| 6036 spdy_util_.ConstructSpdySettings(settings)); | |
| 6037 | |
| 6038 reads.push_back(CreateMockRead(settings_frame_large, i++)); | |
| 6039 | |
| 6040 SpdySerializedFrame session_window_update( | |
| 6041 spdy_util_.ConstructSpdyWindowUpdate(0, | |
| 6042 last_body.size() + kUploadDataSize)); | |
| 6043 reads.push_back(CreateMockRead(session_window_update, i++)); | |
| 6044 | |
| 6045 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
| 6046 writes.push_back(CreateMockWrite(settings_ack, i++)); | |
| 6047 | |
| 6048 // Stalled frames which can be sent after |settings_ack|. | |
| 6049 if (last_body.size() > 0) | |
| 6050 writes.push_back(CreateMockWrite(body4, i++)); | |
| 6051 writes.push_back(CreateMockWrite(body5, i++)); | |
| 6052 | |
| 6053 SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 6054 reads.push_back(CreateMockRead(reply, i++)); | |
| 6055 reads.push_back(CreateMockRead(body2, i++)); | |
| 6056 reads.push_back(CreateMockRead(body5, i++)); | |
| 6057 reads.push_back(MockRead(ASYNC, 0, i++)); // EOF | |
| 6058 | |
| 6059 // Force all writes to happen before any read, last write will not | |
| 6060 // actually queue a frame, due to window size being 0. | |
| 6061 SequencedSocketData data(reads.data(), reads.size(), writes.data(), | |
| 6062 writes.size()); | |
| 6063 | |
| 6064 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
| 6065 SpdyString upload_data_string(kBufferSize * num_upload_buffers, 'a'); | |
| 6066 upload_data_string.append(kUploadData, kUploadDataSize); | |
| 6067 element_readers.push_back(base::WrapUnique(new UploadBytesElementReader( | |
| 6068 upload_data_string.c_str(), upload_data_string.size()))); | |
| 6069 ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0); | |
| 6070 | |
| 6071 HttpRequestInfo request; | |
| 6072 request.method = "POST"; | |
| 6073 request.url = default_url_; | |
| 6074 request.upload_data_stream = &upload_data_stream; | |
| 6075 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 6076 NetLogWithSource(), nullptr); | |
| 6077 helper.RunPreTestSetup(); | |
| 6078 helper.AddData(&data); | |
| 6079 | |
| 6080 HttpNetworkTransaction* trans = helper.trans(); | |
| 6081 | |
| 6082 TestCompletionCallback callback; | |
| 6083 int rv = | |
| 6084 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
| 6085 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 6086 | |
| 6087 data.RunUntilPaused(); // Write as much as we can. | |
| 6088 base::RunLoop().RunUntilIdle(); | |
| 6089 | |
| 6090 SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); | |
| 6091 ASSERT_TRUE(stream); | |
| 6092 ASSERT_TRUE(stream->stream()); | |
| 6093 EXPECT_EQ(0, stream->stream()->send_window_size()); | |
| 6094 | |
| 6095 if (initial_window_size % kBufferSize != 0) { | |
| 6096 // If it does not take whole number of full upload buffer to zero out | |
| 6097 // initial window size, then the upload data is not at EOF, because the | |
| 6098 // last read must be stalled. | |
| 6099 EXPECT_FALSE(upload_data_stream.IsEOF()); | |
| 6100 } else { | |
| 6101 // All the body data should have been read. | |
| 6102 // TODO(satorux): This is because of the weirdness in reading the request | |
| 6103 // body in OnSendBodyComplete(). See crbug.com/113107. | |
| 6104 EXPECT_TRUE(upload_data_stream.IsEOF()); | |
| 6105 } | |
| 6106 // But the body is not yet fully sent (kUploadData is not yet sent) | |
| 6107 // since we're send-stalled. | |
| 6108 EXPECT_TRUE(stream->stream()->send_stalled_by_flow_control()); | |
| 6109 | |
| 6110 // Read in SETTINGS frame to unstall. | |
| 6111 data.Resume(); | |
| 6112 base::RunLoop().RunUntilIdle(); | |
| 6113 | |
| 6114 rv = callback.WaitForResult(); | |
| 6115 helper.VerifyDataConsumed(); | |
| 6116 // If stream is nullptr, that means it was unstalled and closed. | |
| 6117 EXPECT_TRUE(stream->stream() == nullptr); | |
| 6118 } | |
| 6119 | |
| 6120 // Test we correctly handle the case where the SETTINGS frame results in a | |
| 6121 // negative send window size. | |
| 6122 TEST_F(SpdyNetworkTransactionTest, FlowControlNegativeSendWindowSize) { | |
| 6123 const int32_t initial_window_size = kDefaultInitialWindowSize; | |
| 6124 // Number of upload data buffers we need to send to zero out the window size | |
| 6125 // is the minimal number of upload buffers takes to be bigger than | |
| 6126 // |initial_window_size|. | |
| 6127 size_t num_upload_buffers = | |
| 6128 ceil(static_cast<double>(initial_window_size) / kBufferSize); | |
| 6129 // Each upload data buffer consists of |num_frames_in_one_upload_buffer| | |
| 6130 // frames, each with |kMaxSpdyFrameChunkSize| bytes except the last frame, | |
| 6131 // which has kBufferSize % kMaxSpdyChunkSize bytes. | |
| 6132 size_t num_frames_in_one_upload_buffer = | |
| 6133 ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize); | |
| 6134 | |
| 6135 // Construct content for a data frame of maximum size. | |
| 6136 SpdyString content(kMaxSpdyFrameChunkSize, 'a'); | |
| 6137 | |
| 6138 SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( | |
| 6139 kDefaultUrl, 1, | |
| 6140 /*content_length=*/kBufferSize * num_upload_buffers + kUploadDataSize, | |
| 6141 LOWEST, nullptr, 0)); | |
| 6142 | |
| 6143 // Full frames. | |
| 6144 SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame( | |
| 6145 1, content.c_str(), content.size(), false)); | |
| 6146 | |
| 6147 // Last frame in each upload data buffer. | |
| 6148 SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame( | |
| 6149 1, content.c_str(), kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
| 6150 | |
| 6151 // The very last frame before the stalled frames. | |
| 6152 SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame( | |
| 6153 1, content.c_str(), | |
| 6154 initial_window_size % kBufferSize % kMaxSpdyFrameChunkSize, false)); | |
| 6155 | |
| 6156 // Data frames to be sent once WINDOW_UPDATE frame is received. | |
| 6157 | |
| 6158 // If kBufferSize * num_upload_buffers > initial_window_size, | |
| 6159 // we need one additional frame to send the rest of 'a'. | |
| 6160 SpdyString last_body(kBufferSize * num_upload_buffers - initial_window_size, | |
| 6161 'a'); | |
| 6162 SpdySerializedFrame body4(spdy_util_.ConstructSpdyDataFrame( | |
| 6163 1, last_body.c_str(), last_body.size(), false)); | |
| 6164 | |
| 6165 // Also send a "hello!" after WINDOW_UPDATE. | |
| 6166 SpdySerializedFrame body5(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 6167 | |
| 6168 // Fill in mock writes. | |
| 6169 size_t i = 0; | |
| 6170 std::vector<MockWrite> writes; | |
| 6171 writes.push_back(CreateMockWrite(req, i++)); | |
| 6172 for (size_t j = 0; j < num_upload_buffers; j++) { | |
| 6173 for (size_t k = 0; k < num_frames_in_one_upload_buffer; k++) { | |
| 6174 if (k == num_frames_in_one_upload_buffer - 1 && | |
| 6175 kBufferSize % kMaxSpdyFrameChunkSize != 0) { | |
| 6176 if (j == num_upload_buffers - 1 && | |
| 6177 (initial_window_size % kBufferSize != 0)) { | |
| 6178 writes.push_back(CreateMockWrite(body3, i++)); | |
| 6179 } else { | |
| 6180 writes.push_back(CreateMockWrite(body2, i++)); | |
| 6181 } | |
| 6182 } else { | |
| 6183 writes.push_back(CreateMockWrite(body1, i++)); | |
| 6184 } | |
| 6185 } | |
| 6186 } | |
| 6187 | |
| 6188 // Fill in mock reads. | |
| 6189 std::vector<MockRead> reads; | |
| 6190 // Force a pause. | |
| 6191 reads.push_back(MockRead(ASYNC, ERR_IO_PENDING, i++)); | |
| 6192 // Construct read frame for SETTINGS that makes the send_window_size | |
| 6193 // negative. | |
| 6194 SettingsMap new_settings; | |
| 6195 new_settings[SETTINGS_INITIAL_WINDOW_SIZE] = initial_window_size / 2; | |
| 6196 SpdySerializedFrame settings_frame_small( | |
| 6197 spdy_util_.ConstructSpdySettings(new_settings)); | |
| 6198 // Construct read frames for WINDOW_UPDATE that makes the send_window_size | |
| 6199 // positive. | |
| 6200 SpdySerializedFrame session_window_update_init_size( | |
| 6201 spdy_util_.ConstructSpdyWindowUpdate(0, initial_window_size)); | |
| 6202 SpdySerializedFrame window_update_init_size( | |
| 6203 spdy_util_.ConstructSpdyWindowUpdate(1, initial_window_size)); | |
| 6204 | |
| 6205 reads.push_back(CreateMockRead(settings_frame_small, i++)); | |
| 6206 reads.push_back(CreateMockRead(session_window_update_init_size, i++)); | |
| 6207 reads.push_back(CreateMockRead(window_update_init_size, i++)); | |
| 6208 | |
| 6209 SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); | |
| 6210 writes.push_back(CreateMockWrite(settings_ack, i++)); | |
| 6211 | |
| 6212 // Stalled frames which can be sent after |settings_ack|. | |
| 6213 if (last_body.size() > 0) | |
| 6214 writes.push_back(CreateMockWrite(body4, i++)); | |
| 6215 writes.push_back(CreateMockWrite(body5, i++)); | |
| 6216 | |
| 6217 SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 6218 reads.push_back(CreateMockRead(reply, i++)); | |
| 6219 reads.push_back(CreateMockRead(body2, i++)); | |
| 6220 reads.push_back(CreateMockRead(body5, i++)); | |
| 6221 reads.push_back(MockRead(ASYNC, 0, i++)); // EOF | |
| 6222 | |
| 6223 // Force all writes to happen before any read, last write will not | |
| 6224 // actually queue a frame, due to window size being 0. | |
| 6225 SequencedSocketData data(reads.data(), reads.size(), writes.data(), | |
| 6226 writes.size()); | |
| 6227 | |
| 6228 std::vector<std::unique_ptr<UploadElementReader>> element_readers; | |
| 6229 SpdyString upload_data_string(kBufferSize * num_upload_buffers, 'a'); | |
| 6230 upload_data_string.append(kUploadData, kUploadDataSize); | |
| 6231 element_readers.push_back(base::WrapUnique(new UploadBytesElementReader( | |
| 6232 upload_data_string.c_str(), upload_data_string.size()))); | |
| 6233 ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0); | |
| 6234 | |
| 6235 HttpRequestInfo request; | |
| 6236 request.method = "POST"; | |
| 6237 request.url = default_url_; | |
| 6238 request.upload_data_stream = &upload_data_stream; | |
| 6239 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 6240 NetLogWithSource(), nullptr); | |
| 6241 helper.RunPreTestSetup(); | |
| 6242 helper.AddData(&data); | |
| 6243 | |
| 6244 HttpNetworkTransaction* trans = helper.trans(); | |
| 6245 | |
| 6246 TestCompletionCallback callback; | |
| 6247 int rv = | |
| 6248 trans->Start(&helper.request(), callback.callback(), NetLogWithSource()); | |
| 6249 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
| 6250 | |
| 6251 data.RunUntilPaused(); // Write as much as we can. | |
| 6252 base::RunLoop().RunUntilIdle(); | |
| 6253 | |
| 6254 SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); | |
| 6255 ASSERT_TRUE(stream); | |
| 6256 ASSERT_TRUE(stream->stream()); | |
| 6257 EXPECT_EQ(0, stream->stream()->send_window_size()); | |
| 6258 | |
| 6259 if (initial_window_size % kBufferSize != 0) { | |
| 6260 // If it does not take whole number of full upload buffer to zero out | |
| 6261 // initial window size, then the upload data is not at EOF, because the | |
| 6262 // last read must be stalled. | |
| 6263 EXPECT_FALSE(upload_data_stream.IsEOF()); | |
| 6264 } else { | |
| 6265 // All the body data should have been read. | |
| 6266 // TODO(satorux): This is because of the weirdness in reading the request | |
| 6267 // body in OnSendBodyComplete(). See crbug.com/113107. | |
| 6268 EXPECT_TRUE(upload_data_stream.IsEOF()); | |
| 6269 } | |
| 6270 | |
| 6271 // Read in WINDOW_UPDATE or SETTINGS frame. | |
| 6272 data.Resume(); | |
| 6273 base::RunLoop().RunUntilIdle(); | |
| 6274 rv = callback.WaitForResult(); | |
| 6275 helper.VerifyDataConsumed(); | |
| 6276 } | |
| 6277 | |
| 6278 TEST_F(SpdyNetworkTransactionTest, GoAwayOnOddPushStreamId) { | |
| 6279 SpdyHeaderBlock push_headers; | |
| 6280 spdy_util_.AddUrlToHeaderBlock("http://www.example.org/a.dat", &push_headers); | |
| 6281 SpdySerializedFrame push( | |
| 6282 spdy_util_.ConstructInitialSpdyPushFrame(std::move(push_headers), 3, 1)); | |
| 6283 MockRead reads[] = {CreateMockRead(push, 1)}; | |
| 6284 | |
| 6285 SpdySerializedFrame req( | |
| 6286 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 6287 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
| 6288 0, ERROR_CODE_PROTOCOL_ERROR, "Odd push stream id.")); | |
| 6289 MockWrite writes[] = { | |
| 6290 CreateMockWrite(req, 0), CreateMockWrite(goaway, 2), | |
| 6291 }; | |
| 6292 | |
| 6293 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 6294 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 6295 NetLogWithSource(), nullptr); | |
| 6296 helper.RunToCompletion(&data); | |
| 6297 TransactionHelperResult out = helper.output(); | |
| 6298 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
| 6299 } | |
| 6300 | |
| 6301 TEST_F(SpdyNetworkTransactionTest, | |
| 6302 GoAwayOnPushStreamIdLesserOrEqualThanLastAccepted) { | |
| 6303 SpdySerializedFrame push_a(spdy_util_.ConstructSpdyPush( | |
| 6304 nullptr, 0, 4, 1, GetDefaultUrlWithPath("/a.dat").c_str())); | |
| 6305 SpdyHeaderBlock push_b_headers; | |
| 6306 spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/b.dat"), | |
| 6307 &push_b_headers); | |
| 6308 SpdySerializedFrame push_b(spdy_util_.ConstructInitialSpdyPushFrame( | |
| 6309 std::move(push_b_headers), 2, 1)); | |
| 6310 MockRead reads[] = { | |
| 6311 CreateMockRead(push_a, 1), CreateMockRead(push_b, 3), | |
| 6312 }; | |
| 6313 | |
| 6314 SpdySerializedFrame req( | |
| 6315 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 6316 SpdySerializedFrame priority_a( | |
| 6317 spdy_util_.ConstructSpdyPriority(4, 1, IDLE, true)); | |
| 6318 SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway( | |
| 6319 4, ERROR_CODE_PROTOCOL_ERROR, | |
| 6320 "New push stream id must be greater than the last accepted.")); | |
| 6321 MockWrite writes[] = { | |
| 6322 CreateMockWrite(req, 0), CreateMockWrite(priority_a, 2), | |
| 6323 CreateMockWrite(goaway, 4), | |
| 6324 }; | |
| 6325 | |
| 6326 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 6327 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 6328 NetLogWithSource(), nullptr); | |
| 6329 helper.RunToCompletion(&data); | |
| 6330 TransactionHelperResult out = helper.output(); | |
| 6331 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
| 6332 } | |
| 6333 | |
| 6334 // Regression test for https://crbug.com/493348: request header exceeds 16 kB | |
| 6335 // and thus sent in multiple frames when using HTTP/2. | |
| 6336 TEST_F(SpdyNetworkTransactionTest, LargeRequest) { | |
| 6337 const SpdyString kKey("foo"); | |
| 6338 const SpdyString kValue(1 << 15, 'z'); | |
| 6339 | |
| 6340 HttpRequestInfo request; | |
| 6341 request.method = "GET"; | |
| 6342 request.url = default_url_; | |
| 6343 request.extra_headers.SetHeader(kKey, kValue); | |
| 6344 | |
| 6345 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
| 6346 headers[kKey] = kValue; | |
| 6347 SpdySerializedFrame req( | |
| 6348 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
| 6349 MockWrite writes[] = { | |
| 6350 CreateMockWrite(req, 0), | |
| 6351 }; | |
| 6352 | |
| 6353 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 6354 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 6355 MockRead reads[] = { | |
| 6356 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 6357 MockRead(ASYNC, 0, 3) // EOF | |
| 6358 }; | |
| 6359 | |
| 6360 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 6361 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 6362 NetLogWithSource(), nullptr); | |
| 6363 helper.RunToCompletion(&data); | |
| 6364 TransactionHelperResult out = helper.output(); | |
| 6365 | |
| 6366 EXPECT_THAT(out.rv, IsOk()); | |
| 6367 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 6368 EXPECT_EQ("hello!", out.response_data); | |
| 6369 } | |
| 6370 | |
| 6371 // Regression test for https://crbug.com/535629: response header exceeds 16 kB. | |
| 6372 TEST_F(SpdyNetworkTransactionTest, LargeResponseHeader) { | |
| 6373 SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); | |
| 6374 SpdySerializedFrame req( | |
| 6375 spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true)); | |
| 6376 MockWrite writes[] = { | |
| 6377 CreateMockWrite(req, 0), | |
| 6378 }; | |
| 6379 | |
| 6380 // HPACK decoder implementation limits string literal length to 16 kB. | |
| 6381 const char* response_headers[2]; | |
| 6382 const SpdyString kKey(16 * 1024, 'a'); | |
| 6383 response_headers[0] = kKey.data(); | |
| 6384 const SpdyString kValue(16 * 1024, 'b'); | |
| 6385 response_headers[1] = kValue.data(); | |
| 6386 | |
| 6387 SpdySerializedFrame resp( | |
| 6388 spdy_util_.ConstructSpdyGetReply(response_headers, 1, 1)); | |
| 6389 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 6390 MockRead reads[] = { | |
| 6391 CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 6392 MockRead(ASYNC, 0, 3) // EOF | |
| 6393 }; | |
| 6394 | |
| 6395 HttpRequestInfo request; | |
| 6396 request.method = "GET"; | |
| 6397 request.url = default_url_; | |
| 6398 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 6399 NetLogWithSource(), nullptr); | |
| 6400 | |
| 6401 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 6402 helper.RunToCompletion(&data); | |
| 6403 TransactionHelperResult out = helper.output(); | |
| 6404 | |
| 6405 EXPECT_THAT(out.rv, IsOk()); | |
| 6406 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 6407 EXPECT_EQ("hello!", out.response_data); | |
| 6408 ASSERT_TRUE(out.response_info.headers->HasHeaderValue(kKey, kValue)); | |
| 6409 } | |
| 6410 | |
| 6411 // End of line delimiter is forbidden according to RFC 7230 Section 3.2. | |
| 6412 TEST_F(SpdyNetworkTransactionTest, CRLFInHeaderValue) { | |
| 6413 SpdySerializedFrame req( | |
| 6414 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 6415 SpdySerializedFrame rst( | |
| 6416 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_PROTOCOL_ERROR)); | |
| 6417 MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(rst, 2)}; | |
| 6418 | |
| 6419 const char* response_headers[] = {"folded", "foo\r\nbar"}; | |
| 6420 SpdySerializedFrame resp( | |
| 6421 spdy_util_.ConstructSpdyGetReply(response_headers, 1, 1)); | |
| 6422 MockRead reads[] = {CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3)}; | |
| 6423 | |
| 6424 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 6425 | |
| 6426 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 6427 NetLogWithSource(), nullptr); | |
| 6428 helper.RunToCompletion(&data); | |
| 6429 TransactionHelperResult out = helper.output(); | |
| 6430 | |
| 6431 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
| 6432 } | |
| 6433 | |
| 6434 // Regression test for https://crbug.com/603182. | |
| 6435 // No response headers received before RST_STREAM: error. | |
| 6436 TEST_F(SpdyNetworkTransactionTest, RstStreamNoError) { | |
| 6437 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 6438 MockWrite writes[] = {CreateMockWrite(req, 0, ASYNC)}; | |
| 6439 | |
| 6440 SpdySerializedFrame rst( | |
| 6441 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_NO_ERROR)); | |
| 6442 MockRead reads[] = {CreateMockRead(rst, 1), MockRead(ASYNC, 0, 2)}; | |
| 6443 | |
| 6444 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 6445 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
| 6446 DEFAULT_PRIORITY, NetLogWithSource(), | |
| 6447 nullptr); | |
| 6448 helper.RunToCompletion(&data); | |
| 6449 TransactionHelperResult out = helper.output(); | |
| 6450 EXPECT_THAT(out.rv, IsError(ERR_SPDY_PROTOCOL_ERROR)); | |
| 6451 } | |
| 6452 | |
| 6453 // Regression test for https://crbug.com/603182. | |
| 6454 // Response headers and data, then RST_STREAM received, | |
| 6455 // before request body is sent: success. | |
| 6456 TEST_F(SpdyNetworkTransactionTest, RstStreamNoErrorAfterResponse) { | |
| 6457 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 6458 MockWrite writes[] = {CreateMockWrite(req, 0, ASYNC)}; | |
| 6459 | |
| 6460 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 6461 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 6462 SpdySerializedFrame rst( | |
| 6463 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_NO_ERROR)); | |
| 6464 MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 6465 CreateMockRead(rst, 3), MockRead(ASYNC, 0, 4)}; | |
| 6466 | |
| 6467 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 6468 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
| 6469 DEFAULT_PRIORITY, NetLogWithSource(), | |
| 6470 nullptr); | |
| 6471 helper.RunToCompletion(&data); | |
| 6472 TransactionHelperResult out = helper.output(); | |
| 6473 EXPECT_THAT(out.rv, IsOk()); | |
| 6474 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 6475 EXPECT_EQ("hello!", out.response_data); | |
| 6476 } | |
| 6477 | |
| 6478 TEST_F(SpdyNetworkTransactionTest, 100Continue) { | |
| 6479 SpdySerializedFrame req( | |
| 6480 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 6481 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 6482 | |
| 6483 SpdyHeaderBlock informational_headers; | |
| 6484 informational_headers[spdy_util_.GetStatusKey()] = "100"; | |
| 6485 SpdySerializedFrame informational_response( | |
| 6486 spdy_util_.ConstructSpdyReply(1, std::move(informational_headers))); | |
| 6487 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 6488 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 6489 MockRead reads[] = { | |
| 6490 CreateMockRead(informational_response, 1), CreateMockRead(resp, 2), | |
| 6491 CreateMockRead(body, 3), MockRead(ASYNC, 0, 4) // EOF | |
| 6492 }; | |
| 6493 | |
| 6494 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 6495 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 6496 NetLogWithSource(), nullptr); | |
| 6497 helper.RunToCompletion(&data); | |
| 6498 TransactionHelperResult out = helper.output(); | |
| 6499 EXPECT_THAT(out.rv, IsOk()); | |
| 6500 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 6501 EXPECT_EQ("hello!", out.response_data); | |
| 6502 } | |
| 6503 | |
| 6504 // "A server can send a complete response prior to the client sending an entire | |
| 6505 // request if the response does not depend on any portion of the request that | |
| 6506 // has not been sent and received." (RFC7540 Section 8.1) | |
| 6507 // Regression test for https://crbug.com/606990. Server responds before POST | |
| 6508 // data are sent and closes connection: this must result in | |
| 6509 // ERR_CONNECTION_CLOSED (as opposed to ERR_SPDY_PROTOCOL_ERROR). | |
| 6510 TEST_F(SpdyNetworkTransactionTest, ResponseBeforePostDataSent) { | |
| 6511 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 6512 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 6513 | |
| 6514 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 6515 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 6516 MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 6517 MockRead(ASYNC, 0, 3)}; | |
| 6518 | |
| 6519 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 6520 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
| 6521 DEFAULT_PRIORITY, NetLogWithSource(), | |
| 6522 nullptr); | |
| 6523 | |
| 6524 helper.RunPreTestSetup(); | |
| 6525 helper.AddData(&data); | |
| 6526 helper.StartDefaultTest(); | |
| 6527 EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING)); | |
| 6528 helper.WaitForCallbackToComplete(); | |
| 6529 EXPECT_THAT(helper.output().rv, IsError(ERR_CONNECTION_CLOSED)); | |
| 6530 } | |
| 6531 | |
| 6532 // Regression test for https://crbug.com/606990. | |
| 6533 // Server responds before POST data are sent and resets stream with NO_ERROR. | |
| 6534 TEST_F(SpdyNetworkTransactionTest, ResponseAndRstStreamBeforePostDataSent) { | |
| 6535 SpdySerializedFrame req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); | |
| 6536 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 6537 | |
| 6538 SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); | |
| 6539 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 6540 SpdySerializedFrame rst( | |
| 6541 spdy_util_.ConstructSpdyRstStream(1, ERROR_CODE_NO_ERROR)); | |
| 6542 MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2), | |
| 6543 CreateMockRead(rst, 3), MockRead(ASYNC, 0, 4)}; | |
| 6544 | |
| 6545 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 6546 NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(), | |
| 6547 DEFAULT_PRIORITY, NetLogWithSource(), | |
| 6548 nullptr); | |
| 6549 | |
| 6550 helper.RunToCompletion(&data); | |
| 6551 | |
| 6552 TransactionHelperResult out = helper.output(); | |
| 6553 EXPECT_THAT(out.rv, IsOk()); | |
| 6554 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 6555 EXPECT_EQ("hello!", out.response_data); | |
| 6556 } | |
| 6557 | |
| 6558 // Unsupported frames must be ignored. This is especially important for frame | |
| 6559 // type 0xb, which used to be the BLOCKED frame in previous versions of SPDY, | |
| 6560 // but is going to be used for the ORIGIN frame. | |
| 6561 // TODO(bnc): Implement ORIGIN frame support. https://crbug.com/697333 | |
| 6562 TEST_F(SpdyNetworkTransactionTest, IgnoreUnsupportedOriginFrame) { | |
| 6563 SpdySerializedFrame req( | |
| 6564 spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true)); | |
| 6565 MockWrite writes[] = {CreateMockWrite(req, 0)}; | |
| 6566 | |
| 6567 const char origin_frame_on_stream_zero[] = { | |
| 6568 0x00, 0x00, 0x05, // Length | |
| 6569 0x0b, // Type | |
| 6570 0x00, // Flags | |
| 6571 0x00, 0x00, 0x00, 0x00, // Stream ID | |
| 6572 0x00, 0x03, // Origin-Len | |
| 6573 'f', 'o', 'o' // ASCII-Origin | |
| 6574 }; | |
| 6575 | |
| 6576 const char origin_frame_on_stream_one[] = { | |
| 6577 0x00, 0x00, 0x05, // Length | |
| 6578 0x0b, // Type | |
| 6579 0x00, // Flags | |
| 6580 0x00, 0x00, 0x00, 0x01, // Stream ID | |
| 6581 0x00, 0x03, // Origin-Len | |
| 6582 'b', 'a', 'r' // ASCII-Origin | |
| 6583 }; | |
| 6584 | |
| 6585 SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); | |
| 6586 SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); | |
| 6587 MockRead reads[] = {MockRead(ASYNC, origin_frame_on_stream_zero, | |
| 6588 arraysize(origin_frame_on_stream_zero), 1), | |
| 6589 CreateMockRead(resp, 2), | |
| 6590 MockRead(ASYNC, origin_frame_on_stream_one, | |
| 6591 arraysize(origin_frame_on_stream_one), 3), | |
| 6592 CreateMockRead(body, 4), MockRead(ASYNC, 0, 5)}; | |
| 6593 | |
| 6594 SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes)); | |
| 6595 NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, | |
| 6596 NetLogWithSource(), nullptr); | |
| 6597 helper.RunToCompletion(&data); | |
| 6598 TransactionHelperResult out = helper.output(); | |
| 6599 EXPECT_THAT(out.rv, IsOk()); | |
| 6600 EXPECT_EQ("HTTP/1.1 200", out.status_line); | |
| 6601 EXPECT_EQ("hello!", out.response_data); | |
| 6602 } | |
| 6603 | |
| 6604 class SpdyNetworkTransactionTLSUsageCheckTest | |
| 6605 : public SpdyNetworkTransactionTest { | |
| 6606 protected: | |
| 6607 void RunTLSUsageCheckTest( | |
| 6608 std::unique_ptr<SSLSocketDataProvider> ssl_provider) { | |
| 6609 SpdySerializedFrame goaway( | |
| 6610 spdy_util_.ConstructSpdyGoAway(0, ERROR_CODE_INADEQUATE_SECURITY, "")); | |
| 6611 MockWrite writes[] = {CreateMockWrite(goaway)}; | |
| 6612 | |
| 6613 StaticSocketDataProvider data(nullptr, 0, writes, arraysize(writes)); | |
| 6614 HttpRequestInfo request; | |
| 6615 request.method = "GET"; | |
| 6616 request.url = default_url_; | |
| 6617 NormalSpdyTransactionHelper helper(request, DEFAULT_PRIORITY, | |
| 6618 NetLogWithSource(), nullptr); | |
| 6619 helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider)); | |
| 6620 TransactionHelperResult out = helper.output(); | |
| 6621 EXPECT_THAT(out.rv, IsError(ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY)); | |
| 6622 } | |
| 6623 }; | |
| 6624 | |
| 6625 TEST_F(SpdyNetworkTransactionTLSUsageCheckTest, TLSVersionTooOld) { | |
| 6626 std::unique_ptr<SSLSocketDataProvider> ssl_provider( | |
| 6627 new SSLSocketDataProvider(ASYNC, OK)); | |
| 6628 SSLConnectionStatusSetVersion(SSL_CONNECTION_VERSION_SSL3, | |
| 6629 &ssl_provider->connection_status); | |
| 6630 | |
| 6631 RunTLSUsageCheckTest(std::move(ssl_provider)); | |
| 6632 } | |
| 6633 | |
| 6634 TEST_F(SpdyNetworkTransactionTLSUsageCheckTest, TLSCipherSuiteSucky) { | |
| 6635 std::unique_ptr<SSLSocketDataProvider> ssl_provider( | |
| 6636 new SSLSocketDataProvider(ASYNC, OK)); | |
| 6637 // Set to TLS_RSA_WITH_NULL_MD5 | |
| 6638 SSLConnectionStatusSetCipherSuite(0x1, &ssl_provider->connection_status); | |
| 6639 | |
| 6640 RunTLSUsageCheckTest(std::move(ssl_provider)); | |
| 6641 } | |
| 6642 | |
| 6643 } // namespace net | |
| OLD | NEW |