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.
5 #include "net/spdy/spdy_http_stream.h"
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/chunked_upload_data_stream.h"
18 #include "net/base/load_timing_info.h"
19 #include "net/base/load_timing_info_test_util.h"
20 #include "net/base/test_completion_callback.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"
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
));
43 EXPECT_TRUE(load_timing_info
.socket_reused
);
44 EXPECT_NE(NetLog::Source::kInvalidId
, load_timing_info
.socket_log_id
);
46 ExpectConnectTimingHasNoTimes(load_timing_info
.connect_timing
);
47 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info
);
50 // Tests the load timing of a stream that's connected and using a fresh
52 void TestLoadTimingNotReused(const HttpStream
& stream
) {
53 LoadTimingInfo load_timing_info
;
54 EXPECT_TRUE(stream
.GetLoadTimingInfo(&load_timing_info
));
56 EXPECT_FALSE(load_timing_info
.socket_reused
);
57 EXPECT_NE(NetLog::Source::kInvalidId
, load_timing_info
.socket_log_id
);
59 ExpectConnectTimingHasTimes(load_timing_info
.connect_timing
,
60 CONNECT_TIMING_HAS_DNS_TIMES
);
61 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info
);
66 class SpdyHttpStreamTest
: public testing::Test
,
67 public testing::WithParamInterface
<NextProto
> {
70 : spdy_util_(GetParam()),
71 session_deps_(GetParam()) {
72 session_deps_
.net_log
= &net_log_
;
75 DeterministicSocketData
* deterministic_data() {
76 return deterministic_data_
.get();
79 OrderedSocketData
* data() { return data_
.get(); }
82 void TearDown() override
{
83 crypto::ECSignatureCreator::SetFactoryForTesting(NULL
);
84 base::MessageLoop::current()->RunUntilIdle();
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());
98 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
99 session_
= CreateInsecureSpdySession(http_session_
, key
, BoundNetLog());
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());
113 void TestSendCredentials(
114 ChannelIDService
* channel_id_service
,
115 const std::string
& cert
,
116 const std::string
& proof
);
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_
;
127 MockECSignatureCreatorFactory ec_signature_creator_factory_
;
130 INSTANTIATE_TEST_CASE_P(
133 testing::Values(kProtoSPDY31
, kProtoSPDY4_14
, kProtoSPDY4_15
));
135 // SpdyHttpStream::GetUploadProgress() should still work even before the
136 // stream is initialized.
137 TEST_P(SpdyHttpStreamTest
, GetUploadProgressBeforeInitialization
) {
139 MockRead(ASYNC
, 0, 0) // EOF
142 HostPortPair
host_port_pair("www.google.com", 80);
143 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
144 PRIVACY_MODE_DISABLED
);
145 InitSession(reads
, arraysize(reads
), NULL
, 0, key
);
147 SpdyHttpStream
stream(session_
, false);
148 UploadProgress progress
= stream
.GetUploadProgress();
149 EXPECT_EQ(0u, progress
.size());
150 EXPECT_EQ(0u, progress
.position());
152 // Pump the event loop so |reads| is consumed before the function returns.
153 base::RunLoop().RunUntilIdle();
156 TEST_P(SpdyHttpStreamTest
, SendRequest
) {
157 scoped_ptr
<SpdyFrame
> req(
158 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
159 MockWrite writes
[] = {
160 CreateMockWrite(*req
.get(), 1),
162 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
164 CreateMockRead(*resp
, 2),
165 MockRead(SYNCHRONOUS
, 0, 3) // EOF
168 HostPortPair
host_port_pair("www.google.com", 80);
169 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
170 PRIVACY_MODE_DISABLED
);
171 InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
), key
);
173 HttpRequestInfo request
;
174 request
.method
= "GET";
175 request
.url
= GURL("http://www.google.com/");
176 TestCompletionCallback callback
;
177 HttpResponseInfo response
;
178 HttpRequestHeaders headers
;
180 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
181 // Make sure getting load timing information the stream early does not crash.
182 LoadTimingInfo load_timing_info
;
183 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
187 http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
188 net_log
, CompletionCallback()));
189 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
191 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
192 callback
.callback()));
193 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
194 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
196 // This triggers the MockWrite and read 2
197 callback
.WaitForResult();
199 // Can get timing information once the stream connects.
200 TestLoadTimingNotReused(*http_stream
);
202 // This triggers read 3. The empty read causes the session to shut down.
203 data()->CompleteRead();
205 // Because we abandoned the stream, we don't expect to find a session in the
207 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
208 EXPECT_TRUE(data()->at_read_eof());
209 EXPECT_TRUE(data()->at_write_eof());
211 TestLoadTimingNotReused(*http_stream
);
212 http_stream
->Close(true);
213 // Test that there's no crash when trying to get the load timing after the
214 // stream has been closed.
215 TestLoadTimingNotReused(*http_stream
);
218 TEST_P(SpdyHttpStreamTest
, LoadTimingTwoRequests
) {
219 scoped_ptr
<SpdyFrame
> req1(
220 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
221 scoped_ptr
<SpdyFrame
> req2(
222 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 3, LOWEST
, true));
223 MockWrite writes
[] = {
224 CreateMockWrite(*req1
, 0),
225 CreateMockWrite(*req2
, 1),
227 scoped_ptr
<SpdyFrame
> resp1(
228 spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
229 scoped_ptr
<SpdyFrame
> body1(
230 spdy_util_
.ConstructSpdyBodyFrame(1, "", 0, true));
231 scoped_ptr
<SpdyFrame
> resp2(
232 spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 3));
233 scoped_ptr
<SpdyFrame
> body2(
234 spdy_util_
.ConstructSpdyBodyFrame(3, "", 0, true));
236 CreateMockRead(*resp1
, 2),
237 CreateMockRead(*body1
, 3),
238 CreateMockRead(*resp2
, 4),
239 CreateMockRead(*body2
, 5),
240 MockRead(ASYNC
, 0, 6) // EOF
243 HostPortPair
host_port_pair("www.google.com", 80);
244 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
245 PRIVACY_MODE_DISABLED
);
246 InitSessionDeterministic(reads
, arraysize(reads
),
247 writes
, arraysize(writes
),
250 HttpRequestInfo request1
;
251 request1
.method
= "GET";
252 request1
.url
= GURL("http://www.google.com/");
253 TestCompletionCallback callback1
;
254 HttpResponseInfo response1
;
255 HttpRequestHeaders headers1
;
256 scoped_ptr
<SpdyHttpStream
> http_stream1(new SpdyHttpStream(session_
, true));
258 HttpRequestInfo request2
;
259 request2
.method
= "GET";
260 request2
.url
= GURL("http://www.google.com/");
261 TestCompletionCallback callback2
;
262 HttpResponseInfo response2
;
263 HttpRequestHeaders headers2
;
264 scoped_ptr
<SpdyHttpStream
> http_stream2(new SpdyHttpStream(session_
, true));
268 http_stream1
->InitializeStream(&request1
, DEFAULT_PRIORITY
,
270 CompletionCallback()));
271 EXPECT_EQ(ERR_IO_PENDING
, http_stream1
->SendRequest(headers1
, &response1
,
272 callback1
.callback()));
273 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
275 deterministic_data()->RunFor(1);
276 EXPECT_LE(0, callback1
.WaitForResult());
278 TestLoadTimingNotReused(*http_stream1
);
279 LoadTimingInfo load_timing_info1
;
280 LoadTimingInfo load_timing_info2
;
281 EXPECT_TRUE(http_stream1
->GetLoadTimingInfo(&load_timing_info1
));
282 EXPECT_FALSE(http_stream2
->GetLoadTimingInfo(&load_timing_info2
));
286 http_stream2
->InitializeStream(&request2
, DEFAULT_PRIORITY
,
288 CompletionCallback()));
289 EXPECT_EQ(ERR_IO_PENDING
, http_stream2
->SendRequest(headers2
, &response2
,
290 callback2
.callback()));
291 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
293 deterministic_data()->RunFor(1);
294 EXPECT_LE(0, callback2
.WaitForResult());
295 TestLoadTimingReused(*http_stream2
);
296 EXPECT_TRUE(http_stream2
->GetLoadTimingInfo(&load_timing_info2
));
297 EXPECT_EQ(load_timing_info1
.socket_log_id
, load_timing_info2
.socket_log_id
);
300 deterministic_data()->RunFor(6);
302 // Read stream 1 to completion, before making sure we can still read load
303 // timing from both streams.
304 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(1));
306 0, http_stream1
->ReadResponseBody(buf1
.get(), 1, callback1
.callback()));
308 // Stream 1 has been read to completion.
309 TestLoadTimingNotReused(*http_stream1
);
310 // Stream 2 still has queued body data.
311 TestLoadTimingReused(*http_stream2
);
314 TEST_P(SpdyHttpStreamTest
, SendChunkedPost
) {
315 BufferedSpdyFramer
framer(spdy_util_
.spdy_version(), false);
317 scoped_ptr
<SpdyFrame
> req(
318 spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
319 scoped_ptr
<SpdyFrame
> body(
320 framer
.CreateDataFrame(1, kUploadData
, kUploadDataSize
, DATA_FLAG_FIN
));
321 std::vector
<MockWrite
> writes
;
323 writes
.push_back(CreateMockWrite(*req
, seq
++));
324 writes
.push_back(CreateMockWrite(*body
, seq
++)); // POST upload frame
326 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
327 std::vector
<MockRead
> reads
;
328 reads
.push_back(CreateMockRead(*resp
, seq
++));
329 reads
.push_back(CreateMockRead(*body
, seq
++));
330 reads
.push_back(MockRead(SYNCHRONOUS
, 0, seq
++)); // EOF
332 HostPortPair
host_port_pair("www.google.com", 80);
333 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
334 PRIVACY_MODE_DISABLED
);
335 InitSession(vector_as_array(&reads
), reads
.size(),
336 vector_as_array(&writes
), writes
.size(),
338 EXPECT_EQ(spdy_util_
.spdy_version(), session_
->GetProtocolVersion());
340 ChunkedUploadDataStream
upload_stream(0);
341 const int kFirstChunkSize
= kUploadDataSize
/2;
342 upload_stream
.AppendData(kUploadData
, kFirstChunkSize
, false);
343 upload_stream
.AppendData(kUploadData
+ kFirstChunkSize
,
344 kUploadDataSize
- kFirstChunkSize
, true);
346 HttpRequestInfo request
;
347 request
.method
= "POST";
348 request
.url
= GURL("http://www.google.com/");
349 request
.upload_data_stream
= &upload_stream
;
351 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
353 TestCompletionCallback callback
;
354 HttpResponseInfo response
;
355 HttpRequestHeaders headers
;
357 SpdyHttpStream
http_stream(session_
, true);
360 http_stream
.InitializeStream(&request
, DEFAULT_PRIORITY
,
361 net_log
, CompletionCallback()));
363 EXPECT_EQ(ERR_IO_PENDING
, http_stream
.SendRequest(
364 headers
, &response
, callback
.callback()));
365 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
367 // This results in writing the post body and reading the response headers.
368 callback
.WaitForResult();
370 // This triggers reading the body and the EOF, causing the session to shut
372 data()->CompleteRead();
373 base::MessageLoop::current()->RunUntilIdle();
375 // Because we abandoned the stream, we don't expect to find a session in the
377 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
378 EXPECT_TRUE(data()->at_read_eof());
379 EXPECT_TRUE(data()->at_write_eof());
382 // Test to ensure the SpdyStream state machine does not get confused when a
383 // chunk becomes available while a write is pending.
384 TEST_P(SpdyHttpStreamTest
, DelayedSendChunkedPost
) {
385 const char kUploadData1
[] = "12345678";
386 const int kUploadData1Size
= arraysize(kUploadData1
)-1;
387 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
388 scoped_ptr
<SpdyFrame
> chunk1(spdy_util_
.ConstructSpdyBodyFrame(1, false));
389 scoped_ptr
<SpdyFrame
> chunk2(
390 spdy_util_
.ConstructSpdyBodyFrame(
391 1, kUploadData1
, kUploadData1Size
, false));
392 scoped_ptr
<SpdyFrame
> chunk3(spdy_util_
.ConstructSpdyBodyFrame(1, true));
393 MockWrite writes
[] = {
394 CreateMockWrite(*req
.get(), 0),
395 CreateMockWrite(*chunk1
, 1), // POST upload frames
396 CreateMockWrite(*chunk2
, 2),
397 CreateMockWrite(*chunk3
, 3),
399 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
401 CreateMockRead(*resp
, 4),
402 CreateMockRead(*chunk1
, 5),
403 CreateMockRead(*chunk2
, 6),
404 CreateMockRead(*chunk3
, 7),
405 MockRead(ASYNC
, 0, 8) // EOF
408 HostPortPair
host_port_pair("www.google.com", 80);
409 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
410 PRIVACY_MODE_DISABLED
);
411 InitSessionDeterministic(reads
, arraysize(reads
),
412 writes
, arraysize(writes
),
415 ChunkedUploadDataStream
upload_stream(0);
417 HttpRequestInfo request
;
418 request
.method
= "POST";
419 request
.url
= GURL("http://www.google.com/");
420 request
.upload_data_stream
= &upload_stream
;
422 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
423 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, false);
426 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
427 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
428 net_log
, CompletionCallback()));
430 TestCompletionCallback callback
;
431 HttpRequestHeaders headers
;
432 HttpResponseInfo response
;
433 // This will attempt to Write() the initial request and headers, which will
434 // complete asynchronously.
435 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
436 callback
.callback()));
437 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
439 // Complete the initial request write and the first chunk.
440 deterministic_data()->RunFor(2);
441 ASSERT_TRUE(callback
.have_result());
442 EXPECT_EQ(OK
, callback
.WaitForResult());
444 // Now append the final two chunks which will enqueue two more writes.
445 upload_stream
.AppendData(kUploadData1
, kUploadData1Size
, false);
446 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, true);
448 // Finish writing all the chunks.
449 deterministic_data()->RunFor(2);
451 // Read response headers.
452 deterministic_data()->RunFor(1);
453 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
455 // Read and check |chunk1| response.
456 deterministic_data()->RunFor(1);
457 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(kUploadDataSize
));
458 ASSERT_EQ(kUploadDataSize
,
459 http_stream
->ReadResponseBody(
460 buf1
.get(), kUploadDataSize
, callback
.callback()));
461 EXPECT_EQ(kUploadData
, std::string(buf1
->data(), kUploadDataSize
));
463 // Read and check |chunk2| response.
464 deterministic_data()->RunFor(1);
465 scoped_refptr
<IOBuffer
> buf2(new IOBuffer(kUploadData1Size
));
466 ASSERT_EQ(kUploadData1Size
,
467 http_stream
->ReadResponseBody(
468 buf2
.get(), kUploadData1Size
, callback
.callback()));
469 EXPECT_EQ(kUploadData1
, std::string(buf2
->data(), kUploadData1Size
));
471 // Read and check |chunk3| response.
472 deterministic_data()->RunFor(1);
473 scoped_refptr
<IOBuffer
> buf3(new IOBuffer(kUploadDataSize
));
474 ASSERT_EQ(kUploadDataSize
,
475 http_stream
->ReadResponseBody(
476 buf3
.get(), kUploadDataSize
, callback
.callback()));
477 EXPECT_EQ(kUploadData
, std::string(buf3
->data(), kUploadDataSize
));
479 // Finish reading the |EOF|.
480 deterministic_data()->RunFor(1);
481 ASSERT_TRUE(response
.headers
.get());
482 ASSERT_EQ(200, response
.headers
->response_code());
483 EXPECT_TRUE(deterministic_data()->at_read_eof());
484 EXPECT_TRUE(deterministic_data()->at_write_eof());
487 // Test that the SpdyStream state machine can handle sending a final empty data
488 // frame when uploading a chunked data stream.
489 TEST_P(SpdyHttpStreamTest
, DelayedSendChunkedPostWithEmptyFinalDataFrame
) {
490 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
491 scoped_ptr
<SpdyFrame
> chunk1(spdy_util_
.ConstructSpdyBodyFrame(1, false));
492 scoped_ptr
<SpdyFrame
> chunk2(
493 spdy_util_
.ConstructSpdyBodyFrame(1, "", 0, true));
494 MockWrite writes
[] = {
495 CreateMockWrite(*req
.get(), 0),
496 CreateMockWrite(*chunk1
, 1), // POST upload frames
497 CreateMockWrite(*chunk2
, 2),
499 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
501 CreateMockRead(*resp
, 3),
502 CreateMockRead(*chunk1
, 4),
503 CreateMockRead(*chunk2
, 5),
504 MockRead(ASYNC
, 0, 6) // EOF
507 HostPortPair
host_port_pair("www.google.com", 80);
508 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
509 PRIVACY_MODE_DISABLED
);
510 InitSessionDeterministic(reads
, arraysize(reads
),
511 writes
, arraysize(writes
),
514 ChunkedUploadDataStream
upload_stream(0);
516 HttpRequestInfo request
;
517 request
.method
= "POST";
518 request
.url
= GURL("http://www.google.com/");
519 request
.upload_data_stream
= &upload_stream
;
521 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
522 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, false);
525 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
526 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
527 net_log
, CompletionCallback()));
529 TestCompletionCallback callback
;
530 HttpRequestHeaders headers
;
531 HttpResponseInfo response
;
532 // This will attempt to Write() the initial request and headers, which will
533 // complete asynchronously.
534 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
535 callback
.callback()));
536 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
538 // Complete the initial request write and the first chunk.
539 deterministic_data()->RunFor(2);
540 ASSERT_TRUE(callback
.have_result());
541 EXPECT_EQ(OK
, callback
.WaitForResult());
543 // Now end the stream with an empty data frame and the FIN set.
544 upload_stream
.AppendData(NULL
, 0, true);
546 // Finish writing the final frame.
547 deterministic_data()->RunFor(1);
549 // Read response headers.
550 deterministic_data()->RunFor(1);
551 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
553 // Read and check |chunk1| response.
554 deterministic_data()->RunFor(1);
555 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(kUploadDataSize
));
556 ASSERT_EQ(kUploadDataSize
,
557 http_stream
->ReadResponseBody(
558 buf1
.get(), kUploadDataSize
, callback
.callback()));
559 EXPECT_EQ(kUploadData
, std::string(buf1
->data(), kUploadDataSize
));
561 // Read and check |chunk2| response.
562 deterministic_data()->RunFor(1);
564 http_stream
->ReadResponseBody(
565 buf1
.get(), kUploadDataSize
, callback
.callback()));
567 // Finish reading the |EOF|.
568 deterministic_data()->RunFor(1);
569 ASSERT_TRUE(response
.headers
.get());
570 ASSERT_EQ(200, response
.headers
->response_code());
571 EXPECT_TRUE(deterministic_data()->at_read_eof());
572 EXPECT_TRUE(deterministic_data()->at_write_eof());
575 // Test that the SpdyStream state machine handles a chunked upload with no
576 // payload. Unclear if this is a case worth supporting.
577 TEST_P(SpdyHttpStreamTest
, ChunkedPostWithEmptyPayload
) {
578 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
579 scoped_ptr
<SpdyFrame
> chunk(
580 spdy_util_
.ConstructSpdyBodyFrame(1, "", 0, true));
581 MockWrite writes
[] = {
582 CreateMockWrite(*req
.get(), 0),
583 CreateMockWrite(*chunk
, 1),
585 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
587 CreateMockRead(*resp
, 2),
588 CreateMockRead(*chunk
, 3),
589 MockRead(ASYNC
, 0, 4) // EOF
592 HostPortPair
host_port_pair("www.google.com", 80);
593 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
594 PRIVACY_MODE_DISABLED
);
595 InitSessionDeterministic(reads
, arraysize(reads
),
596 writes
, arraysize(writes
),
599 ChunkedUploadDataStream
upload_stream(0);
601 HttpRequestInfo request
;
602 request
.method
= "POST";
603 request
.url
= GURL("http://www.google.com/");
604 request
.upload_data_stream
= &upload_stream
;
606 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
607 upload_stream
.AppendData("", 0, true);
610 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
611 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
612 net_log
, CompletionCallback()));
614 TestCompletionCallback callback
;
615 HttpRequestHeaders headers
;
616 HttpResponseInfo response
;
617 // This will attempt to Write() the initial request and headers, which will
618 // complete asynchronously.
619 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
620 callback
.callback()));
621 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
623 // Complete writing request, followed by a FIN.
624 deterministic_data()->RunFor(2);
625 ASSERT_TRUE(callback
.have_result());
626 EXPECT_EQ(OK
, callback
.WaitForResult());
628 // Read response headers.
629 deterministic_data()->RunFor(1);
630 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
632 // Read and check |chunk| response.
633 deterministic_data()->RunFor(1);
634 scoped_refptr
<IOBuffer
> buf(new IOBuffer(1));
636 http_stream
->ReadResponseBody(
637 buf
.get(), 1, callback
.callback()));
639 // Finish reading the |EOF|.
640 deterministic_data()->RunFor(1);
641 ASSERT_TRUE(response
.headers
.get());
642 ASSERT_EQ(200, response
.headers
->response_code());
643 EXPECT_TRUE(deterministic_data()->at_read_eof());
644 EXPECT_TRUE(deterministic_data()->at_write_eof());
647 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
648 TEST_P(SpdyHttpStreamTest
, SpdyURLTest
) {
649 const char * const full_url
= "http://www.google.com/foo?query=what#anchor";
650 const char * const base_url
= "http://www.google.com/foo?query=what";
651 scoped_ptr
<SpdyFrame
> req(
652 spdy_util_
.ConstructSpdyGet(base_url
, false, 1, LOWEST
));
653 MockWrite writes
[] = {
654 CreateMockWrite(*req
.get(), 1),
656 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
658 CreateMockRead(*resp
, 2),
659 MockRead(SYNCHRONOUS
, 0, 3) // EOF
662 HostPortPair
host_port_pair("www.google.com", 80);
663 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
664 PRIVACY_MODE_DISABLED
);
665 InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
), key
);
667 HttpRequestInfo request
;
668 request
.method
= "GET";
669 request
.url
= GURL(full_url
);
670 TestCompletionCallback callback
;
671 HttpResponseInfo response
;
672 HttpRequestHeaders headers
;
674 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
676 http_stream
->InitializeStream(
677 &request
, DEFAULT_PRIORITY
, net_log
, CompletionCallback()));
679 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
680 callback
.callback()));
682 EXPECT_EQ(base_url
, http_stream
->stream()->GetUrlFromHeaders().spec());
684 // This triggers the MockWrite and read 2
685 callback
.WaitForResult();
687 // This triggers read 3. The empty read causes the session to shut down.
688 data()->CompleteRead();
690 // Because we abandoned the stream, we don't expect to find a session in the
692 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
693 EXPECT_TRUE(data()->at_read_eof());
694 EXPECT_TRUE(data()->at_write_eof());
697 // The tests below are only for SPDY/3 and above.
699 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be
700 // made available is handled correctly.
701 TEST_P(SpdyHttpStreamTest
, DelayedSendChunkedPostWithWindowUpdate
) {
702 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
703 scoped_ptr
<SpdyFrame
> chunk1(spdy_util_
.ConstructSpdyBodyFrame(1, true));
704 MockWrite writes
[] = {
705 CreateMockWrite(*req
.get(), 0),
706 CreateMockWrite(*chunk1
, 1),
708 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
709 scoped_ptr
<SpdyFrame
> window_update(
710 spdy_util_
.ConstructSpdyWindowUpdate(1, kUploadDataSize
));
712 CreateMockRead(*window_update
, 2),
713 CreateMockRead(*resp
, 3),
714 CreateMockRead(*chunk1
, 4),
715 MockRead(ASYNC
, 0, 5) // EOF
718 HostPortPair
host_port_pair("www.google.com", 80);
719 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
720 PRIVACY_MODE_DISABLED
);
722 InitSessionDeterministic(reads
, arraysize(reads
),
723 writes
, arraysize(writes
),
726 ChunkedUploadDataStream
upload_stream(0);
728 HttpRequestInfo request
;
729 request
.method
= "POST";
730 request
.url
= GURL("http://www.google.com/");
731 request
.upload_data_stream
= &upload_stream
;
733 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
734 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, true);
737 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
738 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
739 net_log
, CompletionCallback()));
741 HttpRequestHeaders headers
;
742 HttpResponseInfo response
;
743 // This will attempt to Write() the initial request and headers, which will
744 // complete asynchronously.
745 TestCompletionCallback callback
;
746 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
747 callback
.callback()));
748 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
750 // Complete the initial request write and first chunk.
751 deterministic_data_
->RunFor(2);
752 ASSERT_TRUE(callback
.have_result());
753 EXPECT_EQ(OK
, callback
.WaitForResult());
755 // Verify that the window size has decreased.
756 ASSERT_TRUE(http_stream
->stream() != NULL
);
758 static_cast<int>(SpdySession::GetInitialWindowSize(session_
->protocol())),
759 http_stream
->stream()->send_window_size());
761 // Read window update.
762 deterministic_data_
->RunFor(1);
764 // Verify the window update.
765 ASSERT_TRUE(http_stream
->stream() != NULL
);
767 static_cast<int>(SpdySession::GetInitialWindowSize(session_
->protocol())),
768 http_stream
->stream()->send_window_size());
770 // Read response headers.
771 deterministic_data_
->RunFor(1);
772 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
774 // Read and check |chunk1| response.
775 deterministic_data_
->RunFor(1);
776 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(kUploadDataSize
));
777 ASSERT_EQ(kUploadDataSize
,
778 http_stream
->ReadResponseBody(
779 buf1
.get(), kUploadDataSize
, callback
.callback()));
780 EXPECT_EQ(kUploadData
, std::string(buf1
->data(), kUploadDataSize
));
782 // Finish reading the |EOF|.
783 deterministic_data_
->RunFor(1);
784 ASSERT_TRUE(response
.headers
.get());
785 ASSERT_EQ(200, response
.headers
->response_code());
786 EXPECT_TRUE(deterministic_data_
->at_read_eof());
787 EXPECT_TRUE(deterministic_data_
->at_write_eof());
790 // TODO(willchan): Write a longer test for SpdyStream that exercises all