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(NextProto
,
132 testing::Values(kProtoSPDY31
,
136 // SpdyHttpStream::GetUploadProgress() should still work even before the
137 // stream is initialized.
138 TEST_P(SpdyHttpStreamTest
, GetUploadProgressBeforeInitialization
) {
140 MockRead(ASYNC
, 0, 0) // EOF
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
);
148 SpdyHttpStream
stream(session_
, false);
149 UploadProgress progress
= stream
.GetUploadProgress();
150 EXPECT_EQ(0u, progress
.size());
151 EXPECT_EQ(0u, progress
.position());
153 // Pump the event loop so |reads| is consumed before the function returns.
154 base::RunLoop().RunUntilIdle();
157 TEST_P(SpdyHttpStreamTest
, SendRequest
) {
158 scoped_ptr
<SpdyFrame
> req(
159 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
160 MockWrite writes
[] = {
161 CreateMockWrite(*req
.get(), 1),
163 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
165 CreateMockRead(*resp
, 2),
166 MockRead(SYNCHRONOUS
, 0, 3) // EOF
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
);
174 HttpRequestInfo request
;
175 request
.method
= "GET";
176 request
.url
= GURL("http://www.google.com/");
177 TestCompletionCallback callback
;
178 HttpResponseInfo response
;
179 HttpRequestHeaders headers
;
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
));
188 http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
189 net_log
, CompletionCallback()));
190 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
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
));
197 // This triggers the MockWrite and read 2
198 callback
.WaitForResult();
200 // Can get timing information once the stream connects.
201 TestLoadTimingNotReused(*http_stream
);
203 // This triggers read 3. The empty read causes the session to shut down.
204 data()->CompleteRead();
206 // Because we abandoned the stream, we don't expect to find a session in the
208 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
209 EXPECT_TRUE(data()->at_read_eof());
210 EXPECT_TRUE(data()->at_write_eof());
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
);
219 TEST_P(SpdyHttpStreamTest
, LoadTimingTwoRequests
) {
220 scoped_ptr
<SpdyFrame
> req1(
221 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
222 scoped_ptr
<SpdyFrame
> req2(
223 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 3, LOWEST
, true));
224 MockWrite writes
[] = {
225 CreateMockWrite(*req1
, 0),
226 CreateMockWrite(*req2
, 1),
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));
237 CreateMockRead(*resp1
, 2),
238 CreateMockRead(*body1
, 3),
239 CreateMockRead(*resp2
, 4),
240 CreateMockRead(*body2
, 5),
241 MockRead(ASYNC
, 0, 6) // EOF
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
),
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));
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));
269 http_stream1
->InitializeStream(&request1
, DEFAULT_PRIORITY
,
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
));
276 deterministic_data()->RunFor(1);
277 EXPECT_LE(0, callback1
.WaitForResult());
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
));
287 http_stream2
->InitializeStream(&request2
, DEFAULT_PRIORITY
,
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
));
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
);
301 deterministic_data()->RunFor(6);
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));
307 0, http_stream1
->ReadResponseBody(buf1
.get(), 1, callback1
.callback()));
309 // Stream 1 has been read to completion.
310 TestLoadTimingNotReused(*http_stream1
);
311 // Stream 2 still has queued body data.
312 TestLoadTimingReused(*http_stream2
);
315 TEST_P(SpdyHttpStreamTest
, SendChunkedPost
) {
316 BufferedSpdyFramer
framer(spdy_util_
.spdy_version(), false);
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
;
324 writes
.push_back(CreateMockWrite(*req
, seq
++));
325 writes
.push_back(CreateMockWrite(*body
, seq
++)); // POST upload frame
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
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(),
339 EXPECT_EQ(spdy_util_
.spdy_version(), session_
->GetProtocolVersion());
341 ChunkedUploadDataStream
upload_stream(0);
342 const int kFirstChunkSize
= kUploadDataSize
/2;
343 upload_stream
.AppendData(kUploadData
, kFirstChunkSize
, false);
344 upload_stream
.AppendData(kUploadData
+ kFirstChunkSize
,
345 kUploadDataSize
- kFirstChunkSize
, true);
347 HttpRequestInfo request
;
348 request
.method
= "POST";
349 request
.url
= GURL("http://www.google.com/");
350 request
.upload_data_stream
= &upload_stream
;
352 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
354 TestCompletionCallback callback
;
355 HttpResponseInfo response
;
356 HttpRequestHeaders headers
;
358 SpdyHttpStream
http_stream(session_
, true);
361 http_stream
.InitializeStream(&request
, DEFAULT_PRIORITY
,
362 net_log
, CompletionCallback()));
364 EXPECT_EQ(ERR_IO_PENDING
, http_stream
.SendRequest(
365 headers
, &response
, callback
.callback()));
366 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
368 // This results in writing the post body and reading the response headers.
369 callback
.WaitForResult();
371 // This triggers reading the body and the EOF, causing the session to shut
373 data()->CompleteRead();
374 base::MessageLoop::current()->RunUntilIdle();
376 // Because we abandoned the stream, we don't expect to find a session in the
378 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
379 EXPECT_TRUE(data()->at_read_eof());
380 EXPECT_TRUE(data()->at_write_eof());
383 // Test to ensure the SpdyStream state machine does not get confused when a
384 // chunk becomes available while a write is pending.
385 TEST_P(SpdyHttpStreamTest
, DelayedSendChunkedPost
) {
386 const char kUploadData1
[] = "12345678";
387 const int kUploadData1Size
= arraysize(kUploadData1
)-1;
388 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
389 scoped_ptr
<SpdyFrame
> chunk1(spdy_util_
.ConstructSpdyBodyFrame(1, false));
390 scoped_ptr
<SpdyFrame
> chunk2(
391 spdy_util_
.ConstructSpdyBodyFrame(
392 1, kUploadData1
, kUploadData1Size
, false));
393 scoped_ptr
<SpdyFrame
> chunk3(spdy_util_
.ConstructSpdyBodyFrame(1, true));
394 MockWrite writes
[] = {
395 CreateMockWrite(*req
.get(), 0),
396 CreateMockWrite(*chunk1
, 1), // POST upload frames
397 CreateMockWrite(*chunk2
, 2),
398 CreateMockWrite(*chunk3
, 3),
400 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
402 CreateMockRead(*resp
, 4),
403 CreateMockRead(*chunk1
, 5),
404 CreateMockRead(*chunk2
, 6),
405 CreateMockRead(*chunk3
, 7),
406 MockRead(ASYNC
, 0, 8) // EOF
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
),
416 ChunkedUploadDataStream
upload_stream(0);
418 HttpRequestInfo request
;
419 request
.method
= "POST";
420 request
.url
= GURL("http://www.google.com/");
421 request
.upload_data_stream
= &upload_stream
;
423 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
424 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, false);
427 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
428 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
429 net_log
, CompletionCallback()));
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
));
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());
445 // Now append the final two chunks which will enqueue two more writes.
446 upload_stream
.AppendData(kUploadData1
, kUploadData1Size
, false);
447 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, true);
449 // Finish writing all the chunks.
450 deterministic_data()->RunFor(2);
452 // Read response headers.
453 deterministic_data()->RunFor(1);
454 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
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
));
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
));
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
));
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());
488 // Test that the SpdyStream state machine can handle sending a final empty data
489 // frame when uploading a chunked data stream.
490 TEST_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),
500 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
502 CreateMockRead(*resp
, 3),
503 CreateMockRead(*chunk1
, 4),
504 CreateMockRead(*chunk2
, 5),
505 MockRead(ASYNC
, 0, 6) // EOF
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
),
515 ChunkedUploadDataStream
upload_stream(0);
517 HttpRequestInfo request
;
518 request
.method
= "POST";
519 request
.url
= GURL("http://www.google.com/");
520 request
.upload_data_stream
= &upload_stream
;
522 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
523 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, false);
526 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
527 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
528 net_log
, CompletionCallback()));
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
));
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());
544 // Now end the stream with an empty data frame and the FIN set.
545 upload_stream
.AppendData(NULL
, 0, true);
547 // Finish writing the final frame.
548 deterministic_data()->RunFor(1);
550 // Read response headers.
551 deterministic_data()->RunFor(1);
552 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
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
));
562 // Read and check |chunk2| response.
563 deterministic_data()->RunFor(1);
565 http_stream
->ReadResponseBody(
566 buf1
.get(), kUploadDataSize
, callback
.callback()));
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());
576 // Test that the SpdyStream state machine handles a chunked upload with no
577 // payload. Unclear if this is a case worth supporting.
578 TEST_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),
586 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
588 CreateMockRead(*resp
, 2),
589 CreateMockRead(*chunk
, 3),
590 MockRead(ASYNC
, 0, 4) // EOF
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
),
600 ChunkedUploadDataStream
upload_stream(0);
602 HttpRequestInfo request
;
603 request
.method
= "POST";
604 request
.url
= GURL("http://www.google.com/");
605 request
.upload_data_stream
= &upload_stream
;
607 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
608 upload_stream
.AppendData("", 0, true);
611 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
612 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
613 net_log
, CompletionCallback()));
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
));
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());
629 // Read response headers.
630 deterministic_data()->RunFor(1);
631 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
633 // Read and check |chunk| response.
634 deterministic_data()->RunFor(1);
635 scoped_refptr
<IOBuffer
> buf(new IOBuffer(1));
637 http_stream
->ReadResponseBody(
638 buf
.get(), 1, callback
.callback()));
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());
648 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
649 TEST_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),
657 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
659 CreateMockRead(*resp
, 2),
660 MockRead(SYNCHRONOUS
, 0, 3) // EOF
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
);
668 HttpRequestInfo request
;
669 request
.method
= "GET";
670 request
.url
= GURL(full_url
);
671 TestCompletionCallback callback
;
672 HttpResponseInfo response
;
673 HttpRequestHeaders headers
;
675 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
677 http_stream
->InitializeStream(
678 &request
, DEFAULT_PRIORITY
, net_log
, CompletionCallback()));
680 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
681 callback
.callback()));
683 EXPECT_EQ(base_url
, http_stream
->stream()->GetUrlFromHeaders().spec());
685 // This triggers the MockWrite and read 2
686 callback
.WaitForResult();
688 // This triggers read 3. The empty read causes the session to shut down.
689 data()->CompleteRead();
691 // Because we abandoned the stream, we don't expect to find a session in the
693 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
694 EXPECT_TRUE(data()->at_read_eof());
695 EXPECT_TRUE(data()->at_write_eof());
698 // The tests below are only for SPDY/3 and above.
700 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be
701 // made available is handled correctly.
702 TEST_P(SpdyHttpStreamTest
, DelayedSendChunkedPostWithWindowUpdate
) {
703 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
704 scoped_ptr
<SpdyFrame
> chunk1(spdy_util_
.ConstructSpdyBodyFrame(1, true));
705 MockWrite writes
[] = {
706 CreateMockWrite(*req
.get(), 0),
707 CreateMockWrite(*chunk1
, 1),
709 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
710 scoped_ptr
<SpdyFrame
> window_update(
711 spdy_util_
.ConstructSpdyWindowUpdate(1, kUploadDataSize
));
713 CreateMockRead(*window_update
, 2),
714 CreateMockRead(*resp
, 3),
715 CreateMockRead(*chunk1
, 4),
716 MockRead(ASYNC
, 0, 5) // EOF
719 HostPortPair
host_port_pair("www.google.com", 80);
720 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
721 PRIVACY_MODE_DISABLED
);
723 InitSessionDeterministic(reads
, arraysize(reads
),
724 writes
, arraysize(writes
),
727 ChunkedUploadDataStream
upload_stream(0);
729 HttpRequestInfo request
;
730 request
.method
= "POST";
731 request
.url
= GURL("http://www.google.com/");
732 request
.upload_data_stream
= &upload_stream
;
734 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
735 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, true);
738 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
739 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
740 net_log
, CompletionCallback()));
742 HttpRequestHeaders headers
;
743 HttpResponseInfo response
;
744 // This will attempt to Write() the initial request and headers, which will
745 // complete asynchronously.
746 TestCompletionCallback callback
;
747 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
748 callback
.callback()));
749 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
751 // Complete the initial request write and first chunk.
752 deterministic_data_
->RunFor(2);
753 ASSERT_TRUE(callback
.have_result());
754 EXPECT_EQ(OK
, callback
.WaitForResult());
756 // Verify that the window size has decreased.
757 ASSERT_TRUE(http_stream
->stream() != NULL
);
759 static_cast<int>(SpdySession::GetInitialWindowSize(session_
->protocol())),
760 http_stream
->stream()->send_window_size());
762 // Read window update.
763 deterministic_data_
->RunFor(1);
765 // Verify the window update.
766 ASSERT_TRUE(http_stream
->stream() != NULL
);
768 static_cast<int>(SpdySession::GetInitialWindowSize(session_
->protocol())),
769 http_stream
->stream()->send_window_size());
771 // Read response headers.
772 deterministic_data_
->RunFor(1);
773 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
775 // Read and check |chunk1| response.
776 deterministic_data_
->RunFor(1);
777 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(kUploadDataSize
));
778 ASSERT_EQ(kUploadDataSize
,
779 http_stream
->ReadResponseBody(
780 buf1
.get(), kUploadDataSize
, callback
.callback()));
781 EXPECT_EQ(kUploadData
, std::string(buf1
->data(), kUploadDataSize
));
783 // Finish reading the |EOF|.
784 deterministic_data_
->RunFor(1);
785 ASSERT_TRUE(response
.headers
.get());
786 ASSERT_EQ(200, response
.headers
->response_code());
787 EXPECT_TRUE(deterministic_data_
->at_read_eof());
788 EXPECT_TRUE(deterministic_data_
->at_write_eof());
791 // TODO(willchan): Write a longer test for SpdyStream that exercises all