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/stl_util.h" 11 #include "base/threading/sequenced_worker_pool.h" 12 #include "crypto/ec_private_key.h" 13 #include "crypto/ec_signature_creator.h" 14 #include "crypto/signature_creator.h" 15 #include "net/base/capturing_net_log.h" 16 #include "net/base/load_timing_info.h" 17 #include "net/base/load_timing_info_test_util.h" 18 #include "net/base/upload_data_stream.h" 19 #include "net/base/upload_element_reader.h" 20 #include "net/cert/asn1_util.h" 21 #include "net/http/http_request_info.h" 22 #include "net/http/http_response_headers.h" 23 #include "net/http/http_response_info.h" 24 #include "net/socket/next_proto.h" 25 #include "net/socket/socket_test_util.h" 26 #include "net/spdy/spdy_credential_builder.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(kProtoSPDY2, kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2, 134 kProtoHTTP2Draft04)); 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 kPrivacyModeDisabled); 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 154 TEST_P(SpdyHttpStreamTest, SendRequest) { 155 scoped_ptr<SpdyFrame> req( 156 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true)); 157 MockWrite writes[] = { 158 CreateMockWrite(*req.get(), 1), 159 }; 160 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); 161 MockRead reads[] = { 162 CreateMockRead(*resp, 2), 163 MockRead(SYNCHRONOUS, 0, 3) // EOF 164 }; 165 166 HostPortPair host_port_pair("www.google.com", 80); 167 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 168 kPrivacyModeDisabled); 169 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); 170 171 HttpRequestInfo request; 172 request.method = "GET"; 173 request.url = GURL("http://www.google.com/"); 174 TestCompletionCallback callback; 175 HttpResponseInfo response; 176 HttpRequestHeaders headers; 177 BoundNetLog net_log; 178 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 179 // Make sure getting load timing information the stream early does not crash. 180 LoadTimingInfo load_timing_info; 181 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); 182 183 ASSERT_EQ( 184 OK, 185 http_stream->InitializeStream(&request, DEFAULT_PRIORITY, 186 net_log, CompletionCallback())); 187 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); 188 189 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 190 callback.callback())); 191 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 192 EXPECT_FALSE(http_stream->GetLoadTimingInfo(&load_timing_info)); 193 194 // This triggers the MockWrite and read 2 195 callback.WaitForResult(); 196 197 // Can get timing information once the stream connects. 198 TestLoadTimingNotReused(*http_stream); 199 200 // This triggers read 3. The empty read causes the session to shut down. 201 data()->CompleteRead(); 202 203 // Because we abandoned the stream, we don't expect to find a session in the 204 // pool anymore. 205 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key)); 206 EXPECT_TRUE(data()->at_read_eof()); 207 EXPECT_TRUE(data()->at_write_eof()); 208 209 TestLoadTimingNotReused(*http_stream); 210 http_stream->Close(true); 211 // Test that there's no crash when trying to get the load timing after the 212 // stream has been closed. 213 TestLoadTimingNotReused(*http_stream); 214 } 215 216 TEST_P(SpdyHttpStreamTest, LoadTimingTwoRequests) { 217 scoped_ptr<SpdyFrame> req1( 218 spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true)); 219 scoped_ptr<SpdyFrame> req2( 220 spdy_util_.ConstructSpdyGet(NULL, 0, false, 3, LOWEST, true)); 221 MockWrite writes[] = { 222 CreateMockWrite(*req1, 0), 223 CreateMockWrite(*req2, 1), 224 }; 225 scoped_ptr<SpdyFrame> resp1( 226 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); 227 scoped_ptr<SpdyFrame> body1( 228 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true)); 229 scoped_ptr<SpdyFrame> resp2( 230 spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3)); 231 scoped_ptr<SpdyFrame> body2( 232 spdy_util_.ConstructSpdyBodyFrame(3, "", 0, true)); 233 MockRead reads[] = { 234 CreateMockRead(*resp1, 2), 235 CreateMockRead(*body1, 3), 236 CreateMockRead(*resp2, 4), 237 CreateMockRead(*body2, 5), 238 MockRead(ASYNC, 0, 6) // EOF 239 }; 240 241 HostPortPair host_port_pair("www.google.com", 80); 242 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 243 kPrivacyModeDisabled); 244 InitSessionDeterministic(reads, arraysize(reads), 245 writes, arraysize(writes), 246 key); 247 248 HttpRequestInfo request1; 249 request1.method = "GET"; 250 request1.url = GURL("http://www.google.com/"); 251 TestCompletionCallback callback1; 252 HttpResponseInfo response1; 253 HttpRequestHeaders headers1; 254 scoped_ptr<SpdyHttpStream> http_stream1(new SpdyHttpStream(session_, true)); 255 256 HttpRequestInfo request2; 257 request2.method = "GET"; 258 request2.url = GURL("http://www.google.com/"); 259 TestCompletionCallback callback2; 260 HttpResponseInfo response2; 261 HttpRequestHeaders headers2; 262 scoped_ptr<SpdyHttpStream> http_stream2(new SpdyHttpStream(session_, true)); 263 264 // First write. 265 ASSERT_EQ(OK, 266 http_stream1->InitializeStream(&request1, DEFAULT_PRIORITY, 267 BoundNetLog(), 268 CompletionCallback())); 269 EXPECT_EQ(ERR_IO_PENDING, http_stream1->SendRequest(headers1, &response1, 270 callback1.callback())); 271 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 272 273 deterministic_data()->RunFor(1); 274 EXPECT_LE(0, callback1.WaitForResult()); 275 276 TestLoadTimingNotReused(*http_stream1); 277 LoadTimingInfo load_timing_info1; 278 LoadTimingInfo load_timing_info2; 279 EXPECT_TRUE(http_stream1->GetLoadTimingInfo(&load_timing_info1)); 280 EXPECT_FALSE(http_stream2->GetLoadTimingInfo(&load_timing_info2)); 281 282 // Second write. 283 ASSERT_EQ(OK, 284 http_stream2->InitializeStream(&request2, DEFAULT_PRIORITY, 285 BoundNetLog(), 286 CompletionCallback())); 287 EXPECT_EQ(ERR_IO_PENDING, http_stream2->SendRequest(headers2, &response2, 288 callback2.callback())); 289 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 290 291 deterministic_data()->RunFor(1); 292 EXPECT_LE(0, callback2.WaitForResult()); 293 TestLoadTimingReused(*http_stream2); 294 EXPECT_TRUE(http_stream2->GetLoadTimingInfo(&load_timing_info2)); 295 EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id); 296 297 // All the reads. 298 deterministic_data()->RunFor(6); 299 300 // Read stream 1 to completion, before making sure we can still read load 301 // timing from both streams. 302 scoped_refptr<IOBuffer> buf1(new IOBuffer(1)); 303 ASSERT_EQ( 304 0, http_stream1->ReadResponseBody(buf1.get(), 1, callback1.callback())); 305 306 // Stream 1 has been read to completion. 307 TestLoadTimingNotReused(*http_stream1); 308 // Stream 2 still has queued body data. 309 TestLoadTimingReused(*http_stream2); 310 } 311 312 TEST_P(SpdyHttpStreamTest, SendChunkedPost) { 313 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false); 314 315 scoped_ptr<SpdyFrame> req( 316 spdy_util_.ConstructChunkedSpdyPost(NULL, 0)); 317 scoped_ptr<SpdyFrame> body( 318 framer.CreateDataFrame(1, kUploadData, kUploadDataSize, DATA_FLAG_FIN)); 319 std::vector<MockWrite> writes; 320 int seq = 0; 321 writes.push_back(CreateMockWrite(*req, seq++)); 322 writes.push_back(CreateMockWrite(*body, seq++)); // POST upload frame 323 324 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0)); 325 std::vector<MockRead> reads; 326 reads.push_back(CreateMockRead(*resp, seq++)); 327 reads.push_back(CreateMockRead(*body, seq++)); 328 reads.push_back(MockRead(SYNCHRONOUS, 0, seq++)); // EOF 329 330 HostPortPair host_port_pair("www.google.com", 80); 331 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 332 kPrivacyModeDisabled); 333 InitSession(vector_as_array(&reads), reads.size(), 334 vector_as_array(&writes), writes.size(), 335 key); 336 EXPECT_EQ(spdy_util_.spdy_version(), session_->GetProtocolVersion()); 337 338 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0); 339 const int kFirstChunkSize = kUploadDataSize/2; 340 upload_stream.AppendChunk(kUploadData, kFirstChunkSize, false); 341 upload_stream.AppendChunk(kUploadData + kFirstChunkSize, 342 kUploadDataSize - kFirstChunkSize, true); 343 344 HttpRequestInfo request; 345 request.method = "POST"; 346 request.url = GURL("http://www.google.com/"); 347 request.upload_data_stream = &upload_stream; 348 349 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback())); 350 351 TestCompletionCallback callback; 352 HttpResponseInfo response; 353 HttpRequestHeaders headers; 354 BoundNetLog net_log; 355 SpdyHttpStream http_stream(session_, true); 356 ASSERT_EQ( 357 OK, 358 http_stream.InitializeStream(&request, DEFAULT_PRIORITY, 359 net_log, CompletionCallback())); 360 361 EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest( 362 headers, &response, callback.callback())); 363 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 364 365 // This results in writing the post body and reading the response headers. 366 callback.WaitForResult(); 367 368 // This triggers reading the body and the EOF, causing the session to shut 369 // down. 370 data()->CompleteRead(); 371 base::MessageLoop::current()->RunUntilIdle(); 372 373 // Because we abandoned the stream, we don't expect to find a session in the 374 // pool anymore. 375 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key)); 376 EXPECT_TRUE(data()->at_read_eof()); 377 EXPECT_TRUE(data()->at_write_eof()); 378 } 379 380 // Test to ensure the SpdyStream state machine does not get confused when a 381 // chunk becomes available while a write is pending. 382 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPost) { 383 const char kUploadData1[] = "12345678"; 384 const int kUploadData1Size = arraysize(kUploadData1)-1; 385 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0)); 386 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false)); 387 scoped_ptr<SpdyFrame> chunk2( 388 spdy_util_.ConstructSpdyBodyFrame( 389 1, kUploadData1, kUploadData1Size, false)); 390 scoped_ptr<SpdyFrame> chunk3(spdy_util_.ConstructSpdyBodyFrame(1, true)); 391 MockWrite writes[] = { 392 CreateMockWrite(*req.get(), 0), 393 CreateMockWrite(*chunk1, 1), // POST upload frames 394 CreateMockWrite(*chunk2, 2), 395 CreateMockWrite(*chunk3, 3), 396 }; 397 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0)); 398 MockRead reads[] = { 399 CreateMockRead(*resp, 4), 400 CreateMockRead(*chunk1, 5), 401 CreateMockRead(*chunk2, 6), 402 CreateMockRead(*chunk3, 7), 403 MockRead(ASYNC, 0, 8) // EOF 404 }; 405 406 HostPortPair host_port_pair("www.google.com", 80); 407 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 408 kPrivacyModeDisabled); 409 InitSessionDeterministic(reads, arraysize(reads), 410 writes, arraysize(writes), 411 key); 412 413 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0); 414 415 HttpRequestInfo request; 416 request.method = "POST"; 417 request.url = GURL("http://www.google.com/"); 418 request.upload_data_stream = &upload_stream; 419 420 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback())); 421 upload_stream.AppendChunk(kUploadData, kUploadDataSize, false); 422 423 BoundNetLog net_log; 424 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 425 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY, 426 net_log, CompletionCallback())); 427 428 TestCompletionCallback callback; 429 HttpRequestHeaders headers; 430 HttpResponseInfo response; 431 // This will attempt to Write() the initial request and headers, which will 432 // complete asynchronously. 433 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 434 callback.callback())); 435 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 436 437 // Complete the initial request write and the first chunk. 438 deterministic_data()->RunFor(2); 439 ASSERT_TRUE(callback.have_result()); 440 EXPECT_EQ(OK, callback.WaitForResult()); 441 442 // Now append the final two chunks which will enqueue two more writes. 443 upload_stream.AppendChunk(kUploadData1, kUploadData1Size, false); 444 upload_stream.AppendChunk(kUploadData, kUploadDataSize, true); 445 446 // Finish writing all the chunks. 447 deterministic_data()->RunFor(2); 448 449 // Read response headers. 450 deterministic_data()->RunFor(1); 451 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback())); 452 453 // Read and check |chunk1| response. 454 deterministic_data()->RunFor(1); 455 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); 456 ASSERT_EQ(kUploadDataSize, 457 http_stream->ReadResponseBody( 458 buf1.get(), kUploadDataSize, callback.callback())); 459 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize)); 460 461 // Read and check |chunk2| response. 462 deterministic_data()->RunFor(1); 463 scoped_refptr<IOBuffer> buf2(new IOBuffer(kUploadData1Size)); 464 ASSERT_EQ(kUploadData1Size, 465 http_stream->ReadResponseBody( 466 buf2.get(), kUploadData1Size, callback.callback())); 467 EXPECT_EQ(kUploadData1, std::string(buf2->data(), kUploadData1Size)); 468 469 // Read and check |chunk3| response. 470 deterministic_data()->RunFor(1); 471 scoped_refptr<IOBuffer> buf3(new IOBuffer(kUploadDataSize)); 472 ASSERT_EQ(kUploadDataSize, 473 http_stream->ReadResponseBody( 474 buf3.get(), kUploadDataSize, callback.callback())); 475 EXPECT_EQ(kUploadData, std::string(buf3->data(), kUploadDataSize)); 476 477 // Finish reading the |EOF|. 478 deterministic_data()->RunFor(1); 479 ASSERT_TRUE(response.headers.get()); 480 ASSERT_EQ(200, response.headers->response_code()); 481 EXPECT_TRUE(deterministic_data()->at_read_eof()); 482 EXPECT_TRUE(deterministic_data()->at_write_eof()); 483 } 484 485 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058 486 TEST_P(SpdyHttpStreamTest, SpdyURLTest) { 487 const char * const full_url = "http://www.google.com/foo?query=what#anchor"; 488 const char * const base_url = "http://www.google.com/foo?query=what"; 489 scoped_ptr<SpdyFrame> req( 490 spdy_util_.ConstructSpdyGet(base_url, false, 1, LOWEST)); 491 MockWrite writes[] = { 492 CreateMockWrite(*req.get(), 1), 493 }; 494 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); 495 MockRead reads[] = { 496 CreateMockRead(*resp, 2), 497 MockRead(SYNCHRONOUS, 0, 3) // EOF 498 }; 499 500 HostPortPair host_port_pair("www.google.com", 80); 501 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 502 kPrivacyModeDisabled); 503 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); 504 505 HttpRequestInfo request; 506 request.method = "GET"; 507 request.url = GURL(full_url); 508 TestCompletionCallback callback; 509 HttpResponseInfo response; 510 HttpRequestHeaders headers; 511 BoundNetLog net_log; 512 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 513 ASSERT_EQ(OK, 514 http_stream->InitializeStream( 515 &request, DEFAULT_PRIORITY, net_log, CompletionCallback())); 516 517 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 518 callback.callback())); 519 520 EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec()); 521 522 // This triggers the MockWrite and read 2 523 callback.WaitForResult(); 524 525 // This triggers read 3. The empty read causes the session to shut down. 526 data()->CompleteRead(); 527 528 // Because we abandoned the stream, we don't expect to find a session in the 529 // pool anymore. 530 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key)); 531 EXPECT_TRUE(data()->at_read_eof()); 532 EXPECT_TRUE(data()->at_write_eof()); 533 } 534 535 namespace { 536 537 void GetECServerBoundCertAndProof( 538 const std::string& host, 539 ServerBoundCertService* server_bound_cert_service, 540 std::string* cert, 541 std::string* proof) { 542 TestCompletionCallback callback; 543 std::string key; 544 ServerBoundCertService::RequestHandle request_handle; 545 int rv = server_bound_cert_service->GetDomainBoundCert( 546 host, &key, cert, callback.callback(), 547 &request_handle); 548 EXPECT_EQ(ERR_IO_PENDING, rv); 549 EXPECT_EQ(OK, callback.WaitForResult()); 550 551 SpdyCredential credential; 552 EXPECT_EQ(OK, 553 SpdyCredentialBuilder::Build( 554 MockClientSocket::kTlsUnique, key, *cert, 2, &credential)); 555 556 ASSERT_FALSE(credential.certs.empty()); 557 cert->assign(credential.certs[0]); 558 proof->assign(credential.proof); 559 } 560 561 // Constructs a standard SPDY SYN_STREAM frame for a GET request with 562 // a credential set. 563 SpdyFrame* ConstructCredentialRequestFrame(NextProto next_proto, 564 size_t slot, const GURL& url, 565 SpdyStreamId stream_id) { 566 SpdyTestUtil util(next_proto); 567 568 const SpdyHeaderInfo syn_headers = { 569 SYN_STREAM, 570 stream_id, 571 0, 572 ConvertRequestPriorityToSpdyPriority(LOWEST, 3), 573 slot, 574 CONTROL_FLAG_FIN, 575 false, 576 RST_STREAM_INVALID, 577 NULL, 578 0, 579 DATA_FLAG_NONE 580 }; 581 582 scoped_ptr<SpdyHeaderBlock> headers(util.ConstructGetHeaderBlock(url.spec())); 583 return util.ConstructSpdyFrame(syn_headers, headers.Pass()); 584 } 585 586 } // namespace 587 588 // TODO(rch): When openssl supports server bound certifictes, this 589 // guard can be removed 590 #if !defined(USE_OPENSSL) 591 // Test that if we request a resource for a new origin on a session that 592 // used domain bound certificates, that we send a CREDENTIAL frame for 593 // the new domain before we send the new request. 594 void SpdyHttpStreamTest::TestSendCredentials( 595 ServerBoundCertService* server_bound_cert_service, 596 const std::string& cert, 597 const std::string& proof) { 598 const char* kUrl1 = "https://www.google.com/"; 599 const char* kUrl2 = "https://www.gmail.com/"; 600 601 SpdyCredential cred; 602 cred.slot = 2; 603 cred.proof = proof; 604 cred.certs.push_back(cert); 605 606 scoped_ptr<SpdyFrame> req(ConstructCredentialRequestFrame( 607 GetParam(), 1, GURL(kUrl1), 1)); 608 scoped_ptr<SpdyFrame> credential( 609 spdy_util_.ConstructSpdyCredential(cred)); 610 scoped_ptr<SpdyFrame> req2(ConstructCredentialRequestFrame( 611 GetParam(), 2, GURL(kUrl2), 3)); 612 MockWrite writes[] = { 613 CreateMockWrite(*req.get(), 0), 614 CreateMockWrite(*credential.get(), 2), 615 CreateMockWrite(*req2.get(), 3), 616 }; 617 618 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); 619 scoped_ptr<SpdyFrame> resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3)); 620 MockRead reads[] = { 621 CreateMockRead(*resp, 1), 622 CreateMockRead(*resp2, 4), 623 MockRead(SYNCHRONOUS, 0, 5) // EOF 624 }; 625 626 HostPortPair host_port_pair(HostPortPair::FromURL(GURL(kUrl1))); 627 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 628 kPrivacyModeDisabled); 629 630 DeterministicMockClientSocketFactory* socket_factory = 631 session_deps_.deterministic_socket_factory.get(); 632 DeterministicSocketData data(reads, arraysize(reads), 633 writes, arraysize(writes)); 634 socket_factory->AddSocketDataProvider(&data); 635 SSLSocketDataProvider ssl(SYNCHRONOUS, OK); 636 ssl.channel_id_sent = true; 637 ssl.server_bound_cert_service = server_bound_cert_service; 638 ssl.protocol_negotiated = GetParam(); 639 socket_factory->AddSSLSocketDataProvider(&ssl); 640 http_session_ = SpdySessionDependencies::SpdyCreateSessionDeterministic( 641 &session_deps_); 642 session_ = CreateSecureSpdySession(http_session_, key, BoundNetLog()); 643 644 HttpRequestInfo request; 645 request.method = "GET"; 646 request.url = GURL(kUrl1); 647 HttpResponseInfo response; 648 HttpRequestHeaders headers; 649 BoundNetLog net_log; 650 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 651 ASSERT_EQ( 652 OK, 653 http_stream->InitializeStream(&request, DEFAULT_PRIORITY, 654 net_log, CompletionCallback())); 655 656 // EXPECT_FALSE(session_->NeedsCredentials(request.url)); 657 // GURL new_origin(kUrl2); 658 // EXPECT_TRUE(session_->NeedsCredentials(new_origin)); 659 660 TestCompletionCallback callback; 661 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 662 callback.callback())); 663 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 664 665 data.RunFor(2); 666 callback.WaitForResult(); 667 668 // Start up second request for resource on a new origin. 669 scoped_ptr<SpdyHttpStream> http_stream2(new SpdyHttpStream(session_, true)); 670 request.url = GURL(kUrl2); 671 ASSERT_EQ( 672 OK, 673 http_stream2->InitializeStream(&request, DEFAULT_PRIORITY, 674 net_log, CompletionCallback())); 675 EXPECT_EQ(ERR_IO_PENDING, http_stream2->SendRequest(headers, &response, 676 callback.callback())); 677 data.RunFor(2); 678 callback.WaitForResult(); 679 680 EXPECT_EQ(ERR_IO_PENDING, http_stream2->ReadResponseHeaders( 681 callback.callback())); 682 data.RunFor(1); 683 EXPECT_EQ(OK, callback.WaitForResult()); 684 ASSERT_TRUE(response.headers.get() != NULL); 685 ASSERT_EQ(200, response.headers->response_code()); 686 } 687 688 // The tests below are only for SPDY/3 and above. 689 690 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be 691 // made available is handled correctly. 692 TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) { 693 if (GetParam() < kProtoSPDY3) 694 return; 695 696 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0)); 697 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, true)); 698 MockWrite writes[] = { 699 CreateMockWrite(*req.get(), 0), 700 CreateMockWrite(*chunk1, 1), 701 }; 702 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0)); 703 scoped_ptr<SpdyFrame> window_update( 704 spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize)); 705 MockRead reads[] = { 706 CreateMockRead(*window_update, 2), 707 CreateMockRead(*resp, 3), 708 CreateMockRead(*chunk1, 4), 709 MockRead(ASYNC, 0, 5) // EOF 710 }; 711 712 HostPortPair host_port_pair("www.google.com", 80); 713 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 714 kPrivacyModeDisabled); 715 716 InitSessionDeterministic(reads, arraysize(reads), 717 writes, arraysize(writes), 718 key); 719 720 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0); 721 722 HttpRequestInfo request; 723 request.method = "POST"; 724 request.url = GURL("http://www.google.com/"); 725 request.upload_data_stream = &upload_stream; 726 727 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback())); 728 upload_stream.AppendChunk(kUploadData, kUploadDataSize, true); 729 730 BoundNetLog net_log; 731 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 732 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY, 733 net_log, CompletionCallback())); 734 735 HttpRequestHeaders headers; 736 HttpResponseInfo response; 737 // This will attempt to Write() the initial request and headers, which will 738 // complete asynchronously. 739 TestCompletionCallback callback; 740 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 741 callback.callback())); 742 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 743 744 // Complete the initial request write and first chunk. 745 deterministic_data_->RunFor(2); 746 ASSERT_TRUE(callback.have_result()); 747 EXPECT_EQ(OK, callback.WaitForResult()); 748 749 // Verify that the window size has decreased. 750 ASSERT_TRUE(http_stream->stream() != NULL); 751 EXPECT_NE(static_cast<int>(kSpdyStreamInitialWindowSize), 752 http_stream->stream()->send_window_size()); 753 754 // Read window update. 755 deterministic_data_->RunFor(1); 756 757 // Verify the window update. 758 ASSERT_TRUE(http_stream->stream() != NULL); 759 EXPECT_EQ(static_cast<int>(kSpdyStreamInitialWindowSize), 760 http_stream->stream()->send_window_size()); 761 762 // Read response headers. 763 deterministic_data_->RunFor(1); 764 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback())); 765 766 // Read and check |chunk1| response. 767 deterministic_data_->RunFor(1); 768 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); 769 ASSERT_EQ(kUploadDataSize, 770 http_stream->ReadResponseBody( 771 buf1.get(), kUploadDataSize, callback.callback())); 772 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize)); 773 774 // Finish reading the |EOF|. 775 deterministic_data_->RunFor(1); 776 ASSERT_TRUE(response.headers.get()); 777 ASSERT_EQ(200, response.headers->response_code()); 778 EXPECT_TRUE(deterministic_data_->at_read_eof()); 779 EXPECT_TRUE(deterministic_data_->at_write_eof()); 780 } 781 782 TEST_P(SpdyHttpStreamTest, SendCredentialsEC) { 783 if (GetParam() < kProtoSPDY3) 784 return; 785 786 scoped_refptr<base::SequencedWorkerPool> sequenced_worker_pool = 787 new base::SequencedWorkerPool(1, "SpdyHttpStreamSpdy3Test"); 788 scoped_ptr<ServerBoundCertService> server_bound_cert_service( 789 new ServerBoundCertService(new DefaultServerBoundCertStore(NULL), 790 sequenced_worker_pool)); 791 std::string cert; 792 std::string proof; 793 GetECServerBoundCertAndProof("www.gmail.com", 794 server_bound_cert_service.get(), 795 &cert, &proof); 796 797 TestSendCredentials(server_bound_cert_service.get(), cert, proof); 798 799 sequenced_worker_pool->Shutdown(); 800 } 801 802 TEST_P(SpdyHttpStreamTest, DontSendCredentialsForHttpUrlsEC) { 803 if (GetParam() < kProtoSPDY3) 804 return; 805 806 scoped_refptr<base::SequencedWorkerPool> sequenced_worker_pool = 807 new base::SequencedWorkerPool(1, "SpdyHttpStreamSpdy3Test"); 808 scoped_ptr<ServerBoundCertService> server_bound_cert_service( 809 new ServerBoundCertService(new DefaultServerBoundCertStore(NULL), 810 sequenced_worker_pool)); 811 std::string cert; 812 std::string proof; 813 GetECServerBoundCertAndProof("proxy.google.com", 814 server_bound_cert_service.get(), 815 &cert, &proof); 816 817 const char* kUrl1 = "http://www.google.com/"; 818 const char* kUrl2 = "http://www.gmail.com/"; 819 820 SpdyCredential cred; 821 cred.slot = 2; 822 cred.proof = proof; 823 cred.certs.push_back(cert); 824 825 scoped_ptr<SpdyFrame> req(ConstructCredentialRequestFrame( 826 GetParam(), 0, GURL(kUrl1), 1)); 827 scoped_ptr<SpdyFrame> req2(ConstructCredentialRequestFrame( 828 GetParam(), 0, GURL(kUrl2), 3)); 829 MockWrite writes[] = { 830 CreateMockWrite(*req.get(), 0), 831 CreateMockWrite(*req2.get(), 2), 832 }; 833 834 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); 835 scoped_ptr<SpdyFrame> resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3)); 836 MockRead reads[] = { 837 CreateMockRead(*resp, 1), 838 CreateMockRead(*resp2, 3), 839 MockRead(ASYNC, 0, 4) // EOF 840 }; 841 842 HostPortPair host_port_pair(HostPortPair::FromURL(GURL(kUrl1))); 843 SpdySessionKey key(host_port_pair, 844 ProxyServer::FromURI("proxy.google.com", 845 ProxyServer::SCHEME_HTTPS), 846 kPrivacyModeDisabled); 847 InitSessionDeterministic(reads, arraysize(reads), 848 writes, arraysize(writes), 849 key); 850 851 HttpRequestInfo request; 852 request.method = "GET"; 853 request.url = GURL(kUrl1); 854 HttpResponseInfo response; 855 HttpRequestHeaders headers; 856 BoundNetLog net_log; 857 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 858 ASSERT_EQ( 859 OK, 860 http_stream->InitializeStream(&request, DEFAULT_PRIORITY, 861 net_log, CompletionCallback())); 862 863 TestCompletionCallback callback; 864 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 865 callback.callback())); 866 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 867 868 deterministic_data_->RunFor(2); 869 EXPECT_EQ(OK, callback.WaitForResult()); 870 871 // Start up second request for resource on a new origin. 872 scoped_ptr<SpdyHttpStream> http_stream2(new SpdyHttpStream(session_, true)); 873 request.url = GURL(kUrl2); 874 ASSERT_EQ( 875 OK, 876 http_stream2->InitializeStream(&request, DEFAULT_PRIORITY, 877 net_log, CompletionCallback())); 878 EXPECT_EQ(ERR_IO_PENDING, http_stream2->SendRequest(headers, &response, 879 callback.callback())); 880 deterministic_data_->RunFor(1); 881 EXPECT_EQ(OK, callback.WaitForResult()); 882 883 EXPECT_EQ(ERR_IO_PENDING, http_stream2->ReadResponseHeaders( 884 callback.callback())); 885 deterministic_data_->RunFor(1); 886 EXPECT_EQ(OK, callback.WaitForResult()); 887 ASSERT_TRUE(response.headers.get() != NULL); 888 ASSERT_EQ(200, response.headers->response_code()); 889 deterministic_data_->RunFor(1); 890 sequenced_worker_pool->Shutdown(); 891 } 892 893 #endif // !defined(USE_OPENSSL) 894 895 // TODO(willchan): Write a longer test for SpdyStream that exercises all 896 // methods. 897 898 } // namespace net 899