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 "net/spdy/spdy_http_stream.h" 6 7 #include <vector> 8 9 #include "base/memory/scoped_ptr.h" 10 #include "base/message_loop/message_loop_proxy.h" 11 #include "base/run_loop.h" 12 #include "base/stl_util.h" 13 #include "crypto/ec_private_key.h" 14 #include "crypto/ec_signature_creator.h" 15 #include "crypto/signature_creator.h" 16 #include "net/base/capturing_net_log.h" 17 #include "net/base/load_timing_info.h" 18 #include "net/base/load_timing_info_test_util.h" 19 #include "net/base/upload_data_stream.h" 20 #include "net/base/upload_element_reader.h" 21 #include "net/cert/asn1_util.h" 22 #include "net/http/http_request_info.h" 23 #include "net/http/http_response_headers.h" 24 #include "net/http/http_response_info.h" 25 #include "net/socket/next_proto.h" 26 #include "net/socket/socket_test_util.h" 27 #include "net/spdy/spdy_http_utils.h" 28 #include "net/spdy/spdy_session.h" 29 #include "net/spdy/spdy_test_util_common.h" 30 #include "net/ssl/default_server_bound_cert_store.h" 31 #include "testing/gtest/include/gtest/gtest.h" 32 33 namespace net { 34 35 namespace { 36 37 // Tests the load timing of a stream that's connected and is not the first 38 // request sent on a connection. 39 void TestLoadTimingReused(const HttpStream& stream) { 40 LoadTimingInfo load_timing_info; 41 EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info)); 42 43 EXPECT_TRUE(load_timing_info.socket_reused); 44 EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); 45 46 ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); 47 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info); 48 } 49 50 // Tests the load timing of a stream that's connected and using a fresh 51 // connection. 52 void TestLoadTimingNotReused(const HttpStream& stream) { 53 LoadTimingInfo load_timing_info; 54 EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info)); 55 56 EXPECT_FALSE(load_timing_info.socket_reused); 57 EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); 58 59 ExpectConnectTimingHasTimes(load_timing_info.connect_timing, 60 CONNECT_TIMING_HAS_DNS_TIMES); 61 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info); 62 } 63 64 } // namespace 65 66 class SpdyHttpStreamTest : public testing::Test, 67 public testing::WithParamInterface<NextProto> { 68 public: 69 SpdyHttpStreamTest() 70 : spdy_util_(GetParam()), 71 session_deps_(GetParam()) { 72 session_deps_.net_log = &net_log_; 73 } 74 75 DeterministicSocketData* deterministic_data() { 76 return deterministic_data_.get(); 77 } 78 79 OrderedSocketData* data() { return data_.get(); } 80 81 protected: 82 virtual void TearDown() OVERRIDE { 83 crypto::ECSignatureCreator::SetFactoryForTesting(NULL); 84 base::MessageLoop::current()->RunUntilIdle(); 85 } 86 87 // Initializes the session using DeterministicSocketData. It's advisable 88 // to use this function rather than the OrderedSocketData, since the 89 // DeterministicSocketData behaves in a reasonable manner. 90 void InitSessionDeterministic(MockRead* reads, size_t reads_count, 91 MockWrite* writes, size_t writes_count, 92 const SpdySessionKey& key) { 93 deterministic_data_.reset( 94 new DeterministicSocketData(reads, reads_count, writes, writes_count)); 95 session_deps_.deterministic_socket_factory->AddSocketDataProvider( 96 deterministic_data_.get()); 97 http_session_ = 98 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_); 99 session_ = CreateInsecureSpdySession(http_session_, key, BoundNetLog()); 100 } 101 102 // Initializes the session using the finicky OrderedSocketData class. 103 void InitSession(MockRead* reads, size_t reads_count, 104 MockWrite* writes, size_t writes_count, 105 const SpdySessionKey& key) { 106 data_.reset(new OrderedSocketData(reads, reads_count, 107 writes, writes_count)); 108 session_deps_.socket_factory->AddSocketDataProvider(data_.get()); 109 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); 110 session_ = CreateInsecureSpdySession(http_session_, key, BoundNetLog()); 111 } 112 113 void TestSendCredentials( 114 ServerBoundCertService* server_bound_cert_service, 115 const std::string& cert, 116 const std::string& proof); 117 118 SpdyTestUtil spdy_util_; 119 CapturingNetLog net_log_; 120 SpdySessionDependencies session_deps_; 121 scoped_ptr<OrderedSocketData> data_; 122 scoped_ptr<DeterministicSocketData> deterministic_data_; 123 scoped_refptr<HttpNetworkSession> http_session_; 124 base::WeakPtr<SpdySession> session_; 125 126 private: 127 MockECSignatureCreatorFactory ec_signature_creator_factory_; 128 }; 129 130 INSTANTIATE_TEST_CASE_P( 131 NextProto, 132 SpdyHttpStreamTest, 133 testing::Values(kProtoDeprecatedSPDY2, 134 kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); 135 136 // SpdyHttpStream::GetUploadProgress() should still work even before the 137 // stream is initialized. 138 TEST_P(SpdyHttpStreamTest, GetUploadProgressBeforeInitialization) { 139 MockRead reads[] = { 140 MockRead(ASYNC, 0, 0) // EOF 141 }; 142 143 HostPortPair host_port_pair("www.google.com", 80); 144 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 145 PRIVACY_MODE_DISABLED); 146 InitSession(reads, arraysize(reads), NULL, 0, key); 147 148 SpdyHttpStream stream(session_, false); 149 UploadProgress progress = stream.GetUploadProgress(); 150 EXPECT_EQ(0u, progress.size()); 151 EXPECT_EQ(0u, progress.position()); 152 153 // Pump the event loop so |reads| is consumed before the function returns. 154 base::RunLoop().RunUntilIdle(); 155 } 156 157 TEST_P(SpdyHttpStreamTest, SendRequest) { 158 scoped_ptr<SpdyFrame> req( 159 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true)); 160 MockWrite writes[] = { 161 CreateMockWrite(*req.get(), 1), 162 }; 163 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); 164 MockRead reads[] = { 165 CreateMockRead(*resp, 2), 166 MockRead(SYNCHRONOUS, 0, 3) // EOF 167 }; 168 169 HostPortPair host_port_pair("www.google.com", 80); 170 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 171 PRIVACY_MODE_DISABLED); 172 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); 173 174 HttpRequestInfo request; 175 request.method = "GET"; 176 request.url = GURL("http://www.google.com/"); 177 TestCompletionCallback callback; 178 HttpResponseInfo response; 179 HttpRequestHeaders headers; 180 BoundNetLog net_log; 181 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 182 // Make sure getting load timing information the stream early does not crash. 183 LoadTimingInfo load_timing_info; 184 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); 185 186 ASSERT_EQ( 187 OK, 188 http_stream->InitializeStream(&request, DEFAULT_PRIORITY, 189 net_log, CompletionCallback())); 190 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); 191 192 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 193 callback.callback())); 194 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 195 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); 196 197 // This triggers the MockWrite and read 2 198 callback.WaitForResult(); 199 200 // Can get timing information once the stream connects. 201 TestLoadTimingNotReused(*http_stream); 202 203 // This triggers read 3. The empty read causes the session to shut down. 204 data()->CompleteRead(); 205 206 // Because we abandoned the stream, we don't expect to find a session in the 207 // pool anymore. 208 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key)); 209 EXPECT_TRUE(data()->at_read_eof()); 210 EXPECT_TRUE(data()->at_write_eof()); 211 212 TestLoadTimingNotReused(*http_stream); 213 http_stream->Close(true); 214 // Test that there's no crash when trying to get the load timing after the 215 // stream has been closed. 216 TestLoadTimingNotReused(*http_stream); 217 } 218 219 TEST_P(SpdyHttpStreamTest, LoadTimingTwoRequests) { 220 scoped_ptr<SpdyFrame> req1( 221 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true)); 222 scoped_ptr<SpdyFrame> req2( 223 spdy_util_.ConstructSpdyGet(NULL, 0, false, 3, LOWEST, true)); 224 MockWrite writes[] = { 225 CreateMockWrite(*req1, 0), 226 CreateMockWrite(*req2, 1), 227 }; 228 scoped_ptr<SpdyFrame> resp1( 229 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); 230 scoped_ptr<SpdyFrame> body1( 231 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true)); 232 scoped_ptr<SpdyFrame> resp2( 233 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3)); 234 scoped_ptr<SpdyFrame> body2( 235 spdy_util_.ConstructSpdyBodyFrame(3, "", 0, true)); 236 MockRead reads[] = { 237 CreateMockRead(*resp1, 2), 238 CreateMockRead(*body1, 3), 239 CreateMockRead(*resp2, 4), 240 CreateMockRead(*body2, 5), 241 MockRead(ASYNC, 0, 6) // EOF 242 }; 243 244 HostPortPair host_port_pair("www.google.com", 80); 245 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 246 PRIVACY_MODE_DISABLED); 247 InitSessionDeterministic(reads, arraysize(reads), 248 writes, arraysize(writes), 249 key); 250 251 HttpRequestInfo request1; 252 request1.method = "GET"; 253 request1.url = GURL("http://www.google.com/"); 254 TestCompletionCallback callback1; 255 HttpResponseInfo response1; 256 HttpRequestHeaders headers1; 257 scoped_ptr<SpdyHttpStream> http_stream1(new SpdyHttpStream(session_, true)); 258 259 HttpRequestInfo request2; 260 request2.method = "GET"; 261 request2.url = GURL("http://www.google.com/"); 262 TestCompletionCallback callback2; 263 HttpResponseInfo response2; 264 HttpRequestHeaders headers2; 265 scoped_ptr<SpdyHttpStream> http_stream2(new SpdyHttpStream(session_, true)); 266 267 // First write. 268 ASSERT_EQ(OK, 269 http_stream1->InitializeStream(&request1, DEFAULT_PRIORITY, 270 BoundNetLog(), 271 CompletionCallback())); 272 EXPECT_EQ(ERR_IO_PENDING, http_stream1->SendRequest(headers1, &response1, 273 callback1.callback())); 274 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 275 276 deterministic_data()->RunFor(1); 277 EXPECT_LE(0, callback1.WaitForResult()); 278 279 TestLoadTimingNotReused(*http_stream1); 280 LoadTimingInfo load_timing_info1; 281 LoadTimingInfo load_timing_info2; 282 EXPECT_TRUE(http_stream1->GetLoadTimingInfo(&load_timing_info1)); 283 EXPECT_FALSE(http_stream2->GetLoadTimingInfo(&load_timing_info2)); 284 285 // Second write. 286 ASSERT_EQ(OK, 287 http_stream2->InitializeStream(&request2, DEFAULT_PRIORITY, 288 BoundNetLog(), 289 CompletionCallback())); 290 EXPECT_EQ(ERR_IO_PENDING, http_stream2->SendRequest(headers2, &response2, 291 callback2.callback())); 292 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 293 294 deterministic_data()->RunFor(1); 295 EXPECT_LE(0, callback2.WaitForResult()); 296 TestLoadTimingReused(*http_stream2); 297 EXPECT_TRUE(http_stream2->GetLoadTimingInfo(&load_timing_info2)); 298 EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id); 299 300 // All the reads. 301 deterministic_data()->RunFor(6); 302 303 // Read stream 1 to completion, before making sure we can still read load 304 // timing from both streams. 305 scoped_refptr<IOBuffer> buf1(new IOBuffer(1)); 306 ASSERT_EQ( 307 0, http_stream1->ReadResponseBody(buf1.get(), 1, callback1.callback())); 308 309 // Stream 1 has been read to completion. 310 TestLoadTimingNotReused(*http_stream1); 311 // Stream 2 still has queued body data. 312 TestLoadTimingReused(*http_stream2); 313 } 314 315 TEST_P(SpdyHttpStreamTest, SendChunkedPost) { 316 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false); 317 318 scoped_ptr<SpdyFrame> req( 319 spdy_util_.ConstructChunkedSpdyPost(NULL, 0)); 320 scoped_ptr<SpdyFrame> body( 321 framer.CreateDataFrame(1, kUploadData, kUploadDataSize, DATA_FLAG_FIN)); 322 std::vector<MockWrite> writes; 323 int seq = 0; 324 writes.push_back(CreateMockWrite(*req, seq++)); 325 writes.push_back(CreateMockWrite(*body, seq++)); // POST upload frame 326 327 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0)); 328 std::vector<MockRead> reads; 329 reads.push_back(CreateMockRead(*resp, seq++)); 330 reads.push_back(CreateMockRead(*body, seq++)); 331 reads.push_back(MockRead(SYNCHRONOUS, 0, seq++)); // EOF 332 333 HostPortPair host_port_pair("www.google.com", 80); 334 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 335 PRIVACY_MODE_DISABLED); 336 InitSession(vector_as_array(&reads), reads.size(), 337 vector_as_array(&writes), writes.size(), 338 key); 339 EXPECT_EQ(spdy_util_.spdy_version(), session_->GetProtocolVersion()); 340 341 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0); 342 const int kFirstChunkSize = kUploadDataSize/2; 343 upload_stream.AppendChunk(kUploadData, kFirstChunkSize, false); 344 upload_stream.AppendChunk(kUploadData + kFirstChunkSize, 345 kUploadDataSize - kFirstChunkSize, true); 346 347 HttpRequestInfo request; 348 request.method = "POST"; 349 request.url = GURL("http://www.google.com/"); 350 request.upload_data_stream = &upload_stream; 351 352 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback())); 353 354 TestCompletionCallback callback; 355 HttpResponseInfo response; 356 HttpRequestHeaders headers; 357 BoundNetLog net_log; 358 SpdyHttpStream http_stream(session_, true); 359 ASSERT_EQ( 360 OK, 361 http_stream.InitializeStream(&request, DEFAULT_PRIORITY, 362 net_log, CompletionCallback())); 363 364 EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest( 365 headers, &response, callback.callback())); 366 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 367 368 // This results in writing the post body and reading the response headers. 369 callback.WaitForResult(); 370 371 // This triggers reading the body and the EOF, causing the session to shut 372 // down. 373 data()->CompleteRead(); 374 base::MessageLoop::current()->RunUntilIdle(); 375 376 // Because we abandoned the stream, we don't expect to find a session in the 377 // pool anymore. 378 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key)); 379 EXPECT_TRUE(data()->at_read_eof()); 380 EXPECT_TRUE(data()->at_write_eof()); 381 } 382 383 // Test to ensure the SpdyStream state machine does not get confused when a 384 // chunk becomes available while a write is pending. 385 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPost) { 386 const char kUploadData1[] = "12345678"; 387 const int kUploadData1Size = arraysize(kUploadData1)-1; 388 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0)); 389 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false)); 390 scoped_ptr<SpdyFrame> chunk2( 391 spdy_util_.ConstructSpdyBodyFrame( 392 1, kUploadData1, kUploadData1Size, false)); 393 scoped_ptr<SpdyFrame> chunk3(spdy_util_.ConstructSpdyBodyFrame(1, true)); 394 MockWrite writes[] = { 395 CreateMockWrite(*req.get(), 0), 396 CreateMockWrite(*chunk1, 1), // POST upload frames 397 CreateMockWrite(*chunk2, 2), 398 CreateMockWrite(*chunk3, 3), 399 }; 400 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0)); 401 MockRead reads[] = { 402 CreateMockRead(*resp, 4), 403 CreateMockRead(*chunk1, 5), 404 CreateMockRead(*chunk2, 6), 405 CreateMockRead(*chunk3, 7), 406 MockRead(ASYNC, 0, 8) // EOF 407 }; 408 409 HostPortPair host_port_pair("www.google.com", 80); 410 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 411 PRIVACY_MODE_DISABLED); 412 InitSessionDeterministic(reads, arraysize(reads), 413 writes, arraysize(writes), 414 key); 415 416 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0); 417 418 HttpRequestInfo request; 419 request.method = "POST"; 420 request.url = GURL("http://www.google.com/"); 421 request.upload_data_stream = &upload_stream; 422 423 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback())); 424 upload_stream.AppendChunk(kUploadData, kUploadDataSize, false); 425 426 BoundNetLog net_log; 427 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 428 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY, 429 net_log, CompletionCallback())); 430 431 TestCompletionCallback callback; 432 HttpRequestHeaders headers; 433 HttpResponseInfo response; 434 // This will attempt to Write() the initial request and headers, which will 435 // complete asynchronously. 436 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 437 callback.callback())); 438 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 439 440 // Complete the initial request write and the first chunk. 441 deterministic_data()->RunFor(2); 442 ASSERT_TRUE(callback.have_result()); 443 EXPECT_EQ(OK, callback.WaitForResult()); 444 445 // Now append the final two chunks which will enqueue two more writes. 446 upload_stream.AppendChunk(kUploadData1, kUploadData1Size, false); 447 upload_stream.AppendChunk(kUploadData, kUploadDataSize, true); 448 449 // Finish writing all the chunks. 450 deterministic_data()->RunFor(2); 451 452 // Read response headers. 453 deterministic_data()->RunFor(1); 454 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback())); 455 456 // Read and check |chunk1| response. 457 deterministic_data()->RunFor(1); 458 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); 459 ASSERT_EQ(kUploadDataSize, 460 http_stream->ReadResponseBody( 461 buf1.get(), kUploadDataSize, callback.callback())); 462 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize)); 463 464 // Read and check |chunk2| response. 465 deterministic_data()->RunFor(1); 466 scoped_refptr<IOBuffer> buf2(new IOBuffer(kUploadData1Size)); 467 ASSERT_EQ(kUploadData1Size, 468 http_stream->ReadResponseBody( 469 buf2.get(), kUploadData1Size, callback.callback())); 470 EXPECT_EQ(kUploadData1, std::string(buf2->data(), kUploadData1Size)); 471 472 // Read and check |chunk3| response. 473 deterministic_data()->RunFor(1); 474 scoped_refptr<IOBuffer> buf3(new IOBuffer(kUploadDataSize)); 475 ASSERT_EQ(kUploadDataSize, 476 http_stream->ReadResponseBody( 477 buf3.get(), kUploadDataSize, callback.callback())); 478 EXPECT_EQ(kUploadData, std::string(buf3->data(), kUploadDataSize)); 479 480 // Finish reading the |EOF|. 481 deterministic_data()->RunFor(1); 482 ASSERT_TRUE(response.headers.get()); 483 ASSERT_EQ(200, response.headers->response_code()); 484 EXPECT_TRUE(deterministic_data()->at_read_eof()); 485 EXPECT_TRUE(deterministic_data()->at_write_eof()); 486 } 487 488 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058 489 TEST_P(SpdyHttpStreamTest, SpdyURLTest) { 490 const char * const full_url = "http://www.google.com/foo?query=what#anchor"; 491 const char * const base_url = "http://www.google.com/foo?query=what"; 492 scoped_ptr<SpdyFrame> req( 493 spdy_util_.ConstructSpdyGet(base_url, false, 1, LOWEST)); 494 MockWrite writes[] = { 495 CreateMockWrite(*req.get(), 1), 496 }; 497 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); 498 MockRead reads[] = { 499 CreateMockRead(*resp, 2), 500 MockRead(SYNCHRONOUS, 0, 3) // EOF 501 }; 502 503 HostPortPair host_port_pair("www.google.com", 80); 504 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 505 PRIVACY_MODE_DISABLED); 506 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); 507 508 HttpRequestInfo request; 509 request.method = "GET"; 510 request.url = GURL(full_url); 511 TestCompletionCallback callback; 512 HttpResponseInfo response; 513 HttpRequestHeaders headers; 514 BoundNetLog net_log; 515 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 516 ASSERT_EQ(OK, 517 http_stream->InitializeStream( 518 &request, DEFAULT_PRIORITY, net_log, CompletionCallback())); 519 520 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 521 callback.callback())); 522 523 EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec()); 524 525 // This triggers the MockWrite and read 2 526 callback.WaitForResult(); 527 528 // This triggers read 3. The empty read causes the session to shut down. 529 data()->CompleteRead(); 530 531 // Because we abandoned the stream, we don't expect to find a session in the 532 // pool anymore. 533 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key)); 534 EXPECT_TRUE(data()->at_read_eof()); 535 EXPECT_TRUE(data()->at_write_eof()); 536 } 537 538 // The tests below are only for SPDY/3 and above. 539 540 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be 541 // made available is handled correctly. 542 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) { 543 if (GetParam() < kProtoSPDY3) 544 return; 545 546 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0)); 547 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, true)); 548 MockWrite writes[] = { 549 CreateMockWrite(*req.get(), 0), 550 CreateMockWrite(*chunk1, 1), 551 }; 552 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0)); 553 scoped_ptr<SpdyFrame> window_update( 554 spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize)); 555 MockRead reads[] = { 556 CreateMockRead(*window_update, 2), 557 CreateMockRead(*resp, 3), 558 CreateMockRead(*chunk1, 4), 559 MockRead(ASYNC, 0, 5) // EOF 560 }; 561 562 HostPortPair host_port_pair("www.google.com", 80); 563 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 564 PRIVACY_MODE_DISABLED); 565 566 InitSessionDeterministic(reads, arraysize(reads), 567 writes, arraysize(writes), 568 key); 569 570 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0); 571 572 HttpRequestInfo request; 573 request.method = "POST"; 574 request.url = GURL("http://www.google.com/"); 575 request.upload_data_stream = &upload_stream; 576 577 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback())); 578 upload_stream.AppendChunk(kUploadData, kUploadDataSize, true); 579 580 BoundNetLog net_log; 581 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 582 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY, 583 net_log, CompletionCallback())); 584 585 HttpRequestHeaders headers; 586 HttpResponseInfo response; 587 // This will attempt to Write() the initial request and headers, which will 588 // complete asynchronously. 589 TestCompletionCallback callback; 590 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 591 callback.callback())); 592 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 593 594 // Complete the initial request write and first chunk. 595 deterministic_data_->RunFor(2); 596 ASSERT_TRUE(callback.have_result()); 597 EXPECT_EQ(OK, callback.WaitForResult()); 598 599 // Verify that the window size has decreased. 600 ASSERT_TRUE(http_stream->stream() != NULL); 601 EXPECT_NE(static_cast<int>(kSpdyStreamInitialWindowSize), 602 http_stream->stream()->send_window_size()); 603 604 // Read window update. 605 deterministic_data_->RunFor(1); 606 607 // Verify the window update. 608 ASSERT_TRUE(http_stream->stream() != NULL); 609 EXPECT_EQ(static_cast<int>(kSpdyStreamInitialWindowSize), 610 http_stream->stream()->send_window_size()); 611 612 // Read response headers. 613 deterministic_data_->RunFor(1); 614 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback())); 615 616 // Read and check |chunk1| response. 617 deterministic_data_->RunFor(1); 618 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); 619 ASSERT_EQ(kUploadDataSize, 620 http_stream->ReadResponseBody( 621 buf1.get(), kUploadDataSize, callback.callback())); 622 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize)); 623 624 // Finish reading the |EOF|. 625 deterministic_data_->RunFor(1); 626 ASSERT_TRUE(response.headers.get()); 627 ASSERT_EQ(200, response.headers->response_code()); 628 EXPECT_TRUE(deterministic_data_->at_read_eof()); 629 EXPECT_TRUE(deterministic_data_->at_write_eof()); 630 } 631 632 // TODO(willchan): Write a longer test for SpdyStream that exercises all 633 // methods. 634 635 } // namespace net 636