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_channel_id_store.h" 31#include "testing/gtest/include/gtest/gtest.h" 32 33namespace net { 34 35namespace { 36 37// Tests the load timing of a stream that's connected and is not the first 38// request sent on a connection. 39void 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. 52void 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 66class 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 ChannelIDService* channel_id_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 130INSTANTIATE_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. 138TEST_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 157TEST_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 219TEST_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 315TEST_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. 385TEST_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 that the SpdyStream state machine can handle sending a final empty data 489// frame when uploading a chunked data stream. 490TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithEmptyFinalDataFrame) { 491 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0)); 492 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, false)); 493 scoped_ptr<SpdyFrame> chunk2( 494 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true)); 495 MockWrite writes[] = { 496 CreateMockWrite(*req.get(), 0), 497 CreateMockWrite(*chunk1, 1), // POST upload frames 498 CreateMockWrite(*chunk2, 2), 499 }; 500 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0)); 501 MockRead reads[] = { 502 CreateMockRead(*resp, 3), 503 CreateMockRead(*chunk1, 4), 504 CreateMockRead(*chunk2, 5), 505 MockRead(ASYNC, 0, 6) // EOF 506 }; 507 508 HostPortPair host_port_pair("www.google.com", 80); 509 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 510 PRIVACY_MODE_DISABLED); 511 InitSessionDeterministic(reads, arraysize(reads), 512 writes, arraysize(writes), 513 key); 514 515 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0); 516 517 HttpRequestInfo request; 518 request.method = "POST"; 519 request.url = GURL("http://www.google.com/"); 520 request.upload_data_stream = &upload_stream; 521 522 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback())); 523 upload_stream.AppendChunk(kUploadData, kUploadDataSize, false); 524 525 BoundNetLog net_log; 526 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 527 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY, 528 net_log, CompletionCallback())); 529 530 TestCompletionCallback callback; 531 HttpRequestHeaders headers; 532 HttpResponseInfo response; 533 // This will attempt to Write() the initial request and headers, which will 534 // complete asynchronously. 535 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 536 callback.callback())); 537 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 538 539 // Complete the initial request write and the first chunk. 540 deterministic_data()->RunFor(2); 541 ASSERT_TRUE(callback.have_result()); 542 EXPECT_EQ(OK, callback.WaitForResult()); 543 544 // Now end the stream with an empty data frame and the FIN set. 545 upload_stream.AppendChunk(NULL, 0, true); 546 547 // Finish writing the final frame. 548 deterministic_data()->RunFor(1); 549 550 // Read response headers. 551 deterministic_data()->RunFor(1); 552 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback())); 553 554 // Read and check |chunk1| response. 555 deterministic_data()->RunFor(1); 556 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); 557 ASSERT_EQ(kUploadDataSize, 558 http_stream->ReadResponseBody( 559 buf1.get(), kUploadDataSize, callback.callback())); 560 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize)); 561 562 // Read and check |chunk2| response. 563 deterministic_data()->RunFor(1); 564 ASSERT_EQ(0, 565 http_stream->ReadResponseBody( 566 buf1.get(), kUploadDataSize, callback.callback())); 567 568 // Finish reading the |EOF|. 569 deterministic_data()->RunFor(1); 570 ASSERT_TRUE(response.headers.get()); 571 ASSERT_EQ(200, response.headers->response_code()); 572 EXPECT_TRUE(deterministic_data()->at_read_eof()); 573 EXPECT_TRUE(deterministic_data()->at_write_eof()); 574} 575 576// Test that the SpdyStream state machine handles a chunked upload with no 577// payload. Unclear if this is a case worth supporting. 578TEST_P(SpdyHttpStreamTest, ChunkedPostWithEmptyPayload) { 579 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0)); 580 scoped_ptr<SpdyFrame> chunk( 581 spdy_util_.ConstructSpdyBodyFrame(1, "", 0, true)); 582 MockWrite writes[] = { 583 CreateMockWrite(*req.get(), 0), 584 CreateMockWrite(*chunk, 1), 585 }; 586 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0)); 587 MockRead reads[] = { 588 CreateMockRead(*resp, 2), 589 CreateMockRead(*chunk, 3), 590 MockRead(ASYNC, 0, 4) // EOF 591 }; 592 593 HostPortPair host_port_pair("www.google.com", 80); 594 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 595 PRIVACY_MODE_DISABLED); 596 InitSessionDeterministic(reads, arraysize(reads), 597 writes, arraysize(writes), 598 key); 599 600 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0); 601 602 HttpRequestInfo request; 603 request.method = "POST"; 604 request.url = GURL("http://www.google.com/"); 605 request.upload_data_stream = &upload_stream; 606 607 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback())); 608 upload_stream.AppendChunk("", 0, true); 609 610 BoundNetLog net_log; 611 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 612 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY, 613 net_log, CompletionCallback())); 614 615 TestCompletionCallback callback; 616 HttpRequestHeaders headers; 617 HttpResponseInfo response; 618 // This will attempt to Write() the initial request and headers, which will 619 // complete asynchronously. 620 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 621 callback.callback())); 622 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 623 624 // Complete writing request, followed by a FIN. 625 deterministic_data()->RunFor(2); 626 ASSERT_TRUE(callback.have_result()); 627 EXPECT_EQ(OK, callback.WaitForResult()); 628 629 // Read response headers. 630 deterministic_data()->RunFor(1); 631 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback())); 632 633 // Read and check |chunk| response. 634 deterministic_data()->RunFor(1); 635 scoped_refptr<IOBuffer> buf(new IOBuffer(1)); 636 ASSERT_EQ(0, 637 http_stream->ReadResponseBody( 638 buf.get(), 1, callback.callback())); 639 640 // Finish reading the |EOF|. 641 deterministic_data()->RunFor(1); 642 ASSERT_TRUE(response.headers.get()); 643 ASSERT_EQ(200, response.headers->response_code()); 644 EXPECT_TRUE(deterministic_data()->at_read_eof()); 645 EXPECT_TRUE(deterministic_data()->at_write_eof()); 646} 647 648// Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058 649TEST_P(SpdyHttpStreamTest, SpdyURLTest) { 650 const char * const full_url = "http://www.google.com/foo?query=what#anchor"; 651 const char * const base_url = "http://www.google.com/foo?query=what"; 652 scoped_ptr<SpdyFrame> req( 653 spdy_util_.ConstructSpdyGet(base_url, false, 1, LOWEST)); 654 MockWrite writes[] = { 655 CreateMockWrite(*req.get(), 1), 656 }; 657 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); 658 MockRead reads[] = { 659 CreateMockRead(*resp, 2), 660 MockRead(SYNCHRONOUS, 0, 3) // EOF 661 }; 662 663 HostPortPair host_port_pair("www.google.com", 80); 664 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 665 PRIVACY_MODE_DISABLED); 666 InitSession(reads, arraysize(reads), writes, arraysize(writes), key); 667 668 HttpRequestInfo request; 669 request.method = "GET"; 670 request.url = GURL(full_url); 671 TestCompletionCallback callback; 672 HttpResponseInfo response; 673 HttpRequestHeaders headers; 674 BoundNetLog net_log; 675 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 676 ASSERT_EQ(OK, 677 http_stream->InitializeStream( 678 &request, DEFAULT_PRIORITY, net_log, CompletionCallback())); 679 680 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 681 callback.callback())); 682 683 EXPECT_EQ(base_url, http_stream->stream()->GetUrlFromHeaders().spec()); 684 685 // This triggers the MockWrite and read 2 686 callback.WaitForResult(); 687 688 // This triggers read 3. The empty read causes the session to shut down. 689 data()->CompleteRead(); 690 691 // Because we abandoned the stream, we don't expect to find a session in the 692 // pool anymore. 693 EXPECT_FALSE(HasSpdySession(http_session_->spdy_session_pool(), key)); 694 EXPECT_TRUE(data()->at_read_eof()); 695 EXPECT_TRUE(data()->at_write_eof()); 696} 697 698// The tests below are only for SPDY/3 and above. 699 700// Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be 701// made available is handled correctly. 702TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) { 703 if (GetParam() < kProtoSPDY3) 704 return; 705 706 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0)); 707 scoped_ptr<SpdyFrame> chunk1(spdy_util_.ConstructSpdyBodyFrame(1, true)); 708 MockWrite writes[] = { 709 CreateMockWrite(*req.get(), 0), 710 CreateMockWrite(*chunk1, 1), 711 }; 712 scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0)); 713 scoped_ptr<SpdyFrame> window_update( 714 spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize)); 715 MockRead reads[] = { 716 CreateMockRead(*window_update, 2), 717 CreateMockRead(*resp, 3), 718 CreateMockRead(*chunk1, 4), 719 MockRead(ASYNC, 0, 5) // EOF 720 }; 721 722 HostPortPair host_port_pair("www.google.com", 80); 723 SpdySessionKey key(host_port_pair, ProxyServer::Direct(), 724 PRIVACY_MODE_DISABLED); 725 726 InitSessionDeterministic(reads, arraysize(reads), 727 writes, arraysize(writes), 728 key); 729 730 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0); 731 732 HttpRequestInfo request; 733 request.method = "POST"; 734 request.url = GURL("http://www.google.com/"); 735 request.upload_data_stream = &upload_stream; 736 737 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback())); 738 upload_stream.AppendChunk(kUploadData, kUploadDataSize, true); 739 740 BoundNetLog net_log; 741 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 742 ASSERT_EQ(OK, http_stream->InitializeStream(&request, DEFAULT_PRIORITY, 743 net_log, CompletionCallback())); 744 745 HttpRequestHeaders headers; 746 HttpResponseInfo response; 747 // This will attempt to Write() the initial request and headers, which will 748 // complete asynchronously. 749 TestCompletionCallback callback; 750 EXPECT_EQ(ERR_IO_PENDING, http_stream->SendRequest(headers, &response, 751 callback.callback())); 752 EXPECT_TRUE(HasSpdySession(http_session_->spdy_session_pool(), key)); 753 754 // Complete the initial request write and first chunk. 755 deterministic_data_->RunFor(2); 756 ASSERT_TRUE(callback.have_result()); 757 EXPECT_EQ(OK, callback.WaitForResult()); 758 759 // Verify that the window size has decreased. 760 ASSERT_TRUE(http_stream->stream() != NULL); 761 EXPECT_NE(static_cast<int>(kSpdyStreamInitialWindowSize), 762 http_stream->stream()->send_window_size()); 763 764 // Read window update. 765 deterministic_data_->RunFor(1); 766 767 // Verify the window update. 768 ASSERT_TRUE(http_stream->stream() != NULL); 769 EXPECT_EQ(static_cast<int>(kSpdyStreamInitialWindowSize), 770 http_stream->stream()->send_window_size()); 771 772 // Read response headers. 773 deterministic_data_->RunFor(1); 774 ASSERT_EQ(OK, http_stream->ReadResponseHeaders(callback.callback())); 775 776 // Read and check |chunk1| response. 777 deterministic_data_->RunFor(1); 778 scoped_refptr<IOBuffer> buf1(new IOBuffer(kUploadDataSize)); 779 ASSERT_EQ(kUploadDataSize, 780 http_stream->ReadResponseBody( 781 buf1.get(), kUploadDataSize, callback.callback())); 782 EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize)); 783 784 // Finish reading the |EOF|. 785 deterministic_data_->RunFor(1); 786 ASSERT_TRUE(response.headers.get()); 787 ASSERT_EQ(200, response.headers->response_code()); 788 EXPECT_TRUE(deterministic_data_->at_read_eof()); 789 EXPECT_TRUE(deterministic_data_->at_write_eof()); 790} 791 792// TODO(willchan): Write a longer test for SpdyStream that exercises all 793// methods. 794 795} // namespace net 796