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/chunked_upload_data_stream.h"
17 #include "net/base/load_timing_info.h"
18 #include "net/base/load_timing_info_test_util.h"
19 #include "net/base/test_completion_callback.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/log/test_net_log.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();
80 void TearDown() override
{
81 crypto::ECSignatureCreator::SetFactoryForTesting(NULL
);
82 base::MessageLoop::current()->RunUntilIdle();
85 // Initializes the session using DeterministicSocketData.
86 void InitSession(MockRead
* reads
,
90 const SpdySessionKey
& key
) {
91 deterministic_data_
.reset(
92 new DeterministicSocketData(reads
, reads_count
, writes
, writes_count
));
93 session_deps_
.deterministic_socket_factory
->AddSocketDataProvider(
94 deterministic_data_
.get());
96 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
97 session_
= CreateInsecureSpdySession(http_session_
, key
, BoundNetLog());
100 void TestSendCredentials(
101 ChannelIDService
* channel_id_service
,
102 const std::string
& cert
,
103 const std::string
& proof
);
105 SpdyTestUtil spdy_util_
;
107 SpdySessionDependencies session_deps_
;
108 scoped_ptr
<DeterministicSocketData
> deterministic_data_
;
109 scoped_refptr
<HttpNetworkSession
> http_session_
;
110 base::WeakPtr
<SpdySession
> session_
;
113 MockECSignatureCreatorFactory ec_signature_creator_factory_
;
116 INSTANTIATE_TEST_CASE_P(NextProto
,
118 testing::Values(kProtoSPDY31
,
122 // SpdyHttpStream::GetUploadProgress() should still work even before the
123 // stream is initialized.
124 TEST_P(SpdyHttpStreamTest
, GetUploadProgressBeforeInitialization
) {
126 MockRead(ASYNC
, 0, 0) // EOF
129 HostPortPair
host_port_pair("www.example.org", 80);
130 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
131 PRIVACY_MODE_DISABLED
);
132 InitSession(reads
, arraysize(reads
), NULL
, 0, key
);
134 SpdyHttpStream
stream(session_
, false);
135 UploadProgress progress
= stream
.GetUploadProgress();
136 EXPECT_EQ(0u, progress
.size());
137 EXPECT_EQ(0u, progress
.position());
139 // Pump the event loop so |reads| is consumed before the function returns.
140 base::RunLoop().RunUntilIdle();
143 TEST_P(SpdyHttpStreamTest
, SendRequest
) {
144 scoped_ptr
<SpdyFrame
> req(
145 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
146 MockWrite writes
[] = {
147 CreateMockWrite(*req
.get(), 0),
149 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
151 CreateMockRead(*resp
, 1), MockRead(SYNCHRONOUS
, 0, 2) // EOF
154 HostPortPair
host_port_pair("www.example.org", 80);
155 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
156 PRIVACY_MODE_DISABLED
);
157 InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
), key
);
159 HttpRequestInfo request
;
160 request
.method
= "GET";
161 request
.url
= GURL("http://www.example.org/");
162 TestCompletionCallback callback
;
163 HttpResponseInfo response
;
164 HttpRequestHeaders headers
;
166 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
167 // Make sure getting load timing information the stream early does not crash.
168 LoadTimingInfo load_timing_info
;
169 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
173 http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
174 net_log
, CompletionCallback()));
175 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
177 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
178 callback
.callback()));
179 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
180 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
182 deterministic_data()->RunFor(3);
184 callback
.WaitForResult();
186 // Can get timing information once the stream connects.
187 TestLoadTimingNotReused(*http_stream
);
189 // Because we abandoned the stream, we don't expect to find a session in the
191 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
192 EXPECT_TRUE(deterministic_data()->at_read_eof());
193 EXPECT_TRUE(deterministic_data()->at_write_eof());
195 TestLoadTimingNotReused(*http_stream
);
196 http_stream
->Close(true);
197 // Test that there's no crash when trying to get the load timing after the
198 // stream has been closed.
199 TestLoadTimingNotReused(*http_stream
);
202 TEST_P(SpdyHttpStreamTest
, LoadTimingTwoRequests
) {
203 scoped_ptr
<SpdyFrame
> req1(
204 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
205 scoped_ptr
<SpdyFrame
> req2(
206 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 3, LOWEST
, true));
207 MockWrite writes
[] = {
208 CreateMockWrite(*req1
, 0),
209 CreateMockWrite(*req2
, 1),
211 scoped_ptr
<SpdyFrame
> resp1(
212 spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
213 scoped_ptr
<SpdyFrame
> body1(
214 spdy_util_
.ConstructSpdyBodyFrame(1, "", 0, true));
215 scoped_ptr
<SpdyFrame
> resp2(
216 spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 3));
217 scoped_ptr
<SpdyFrame
> body2(
218 spdy_util_
.ConstructSpdyBodyFrame(3, "", 0, true));
220 CreateMockRead(*resp1
, 2),
221 CreateMockRead(*body1
, 3),
222 CreateMockRead(*resp2
, 4),
223 CreateMockRead(*body2
, 5),
224 MockRead(ASYNC
, 0, 6) // EOF
227 HostPortPair
host_port_pair("www.example.org", 80);
228 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
229 PRIVACY_MODE_DISABLED
);
230 InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
), key
);
232 HttpRequestInfo request1
;
233 request1
.method
= "GET";
234 request1
.url
= GURL("http://www.example.org/");
235 TestCompletionCallback callback1
;
236 HttpResponseInfo response1
;
237 HttpRequestHeaders headers1
;
238 scoped_ptr
<SpdyHttpStream
> http_stream1(new SpdyHttpStream(session_
, true));
240 HttpRequestInfo request2
;
241 request2
.method
= "GET";
242 request2
.url
= GURL("http://www.example.org/");
243 TestCompletionCallback callback2
;
244 HttpResponseInfo response2
;
245 HttpRequestHeaders headers2
;
246 scoped_ptr
<SpdyHttpStream
> http_stream2(new SpdyHttpStream(session_
, true));
250 http_stream1
->InitializeStream(&request1
, DEFAULT_PRIORITY
,
252 CompletionCallback()));
253 EXPECT_EQ(ERR_IO_PENDING
, http_stream1
->SendRequest(headers1
, &response1
,
254 callback1
.callback()));
255 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
257 deterministic_data()->RunFor(1);
258 EXPECT_LE(0, callback1
.WaitForResult());
260 TestLoadTimingNotReused(*http_stream1
);
261 LoadTimingInfo load_timing_info1
;
262 LoadTimingInfo load_timing_info2
;
263 EXPECT_TRUE(http_stream1
->GetLoadTimingInfo(&load_timing_info1
));
264 EXPECT_FALSE(http_stream2
->GetLoadTimingInfo(&load_timing_info2
));
268 http_stream2
->InitializeStream(&request2
, DEFAULT_PRIORITY
,
270 CompletionCallback()));
271 EXPECT_EQ(ERR_IO_PENDING
, http_stream2
->SendRequest(headers2
, &response2
,
272 callback2
.callback()));
273 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
275 deterministic_data()->RunFor(1);
276 EXPECT_LE(0, callback2
.WaitForResult());
277 TestLoadTimingReused(*http_stream2
);
278 EXPECT_TRUE(http_stream2
->GetLoadTimingInfo(&load_timing_info2
));
279 EXPECT_EQ(load_timing_info1
.socket_log_id
, load_timing_info2
.socket_log_id
);
282 deterministic_data()->RunFor(6);
284 // Read stream 1 to completion, before making sure we can still read load
285 // timing from both streams.
286 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(1));
288 0, http_stream1
->ReadResponseBody(buf1
.get(), 1, callback1
.callback()));
290 // Stream 1 has been read to completion.
291 TestLoadTimingNotReused(*http_stream1
);
292 // Stream 2 still has queued body data.
293 TestLoadTimingReused(*http_stream2
);
296 TEST_P(SpdyHttpStreamTest
, SendChunkedPost
) {
297 BufferedSpdyFramer
framer(spdy_util_
.spdy_version(), false);
299 scoped_ptr
<SpdyFrame
> req(
300 spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
301 scoped_ptr
<SpdyFrame
> body(
302 framer
.CreateDataFrame(1, kUploadData
, kUploadDataSize
, DATA_FLAG_FIN
));
303 std::vector
<MockWrite
> writes
;
305 writes
.push_back(CreateMockWrite(*req
, seq
++));
306 writes
.push_back(CreateMockWrite(*body
, seq
++)); // POST upload frame
308 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
309 std::vector
<MockRead
> reads
;
310 reads
.push_back(CreateMockRead(*resp
, seq
++));
311 reads
.push_back(CreateMockRead(*body
, seq
++));
312 reads
.push_back(MockRead(SYNCHRONOUS
, 0, seq
++)); // EOF
314 HostPortPair
host_port_pair("www.example.org", 80);
315 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
316 PRIVACY_MODE_DISABLED
);
317 InitSession(vector_as_array(&reads
), reads
.size(), vector_as_array(&writes
),
319 EXPECT_EQ(spdy_util_
.spdy_version(), session_
->GetProtocolVersion());
321 ChunkedUploadDataStream
upload_stream(0);
322 const int kFirstChunkSize
= kUploadDataSize
/2;
323 upload_stream
.AppendData(kUploadData
, kFirstChunkSize
, false);
324 upload_stream
.AppendData(kUploadData
+ kFirstChunkSize
,
325 kUploadDataSize
- kFirstChunkSize
, true);
327 HttpRequestInfo request
;
328 request
.method
= "POST";
329 request
.url
= GURL("http://www.example.org/");
330 request
.upload_data_stream
= &upload_stream
;
332 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
334 TestCompletionCallback callback
;
335 HttpResponseInfo response
;
336 HttpRequestHeaders headers
;
338 SpdyHttpStream
http_stream(session_
, true);
341 http_stream
.InitializeStream(&request
, DEFAULT_PRIORITY
,
342 net_log
, CompletionCallback()));
344 EXPECT_EQ(ERR_IO_PENDING
, http_stream
.SendRequest(
345 headers
, &response
, callback
.callback()));
346 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
348 deterministic_data()->RunFor(seq
);
349 callback
.WaitForResult();
351 // Because we abandoned the stream, we don't expect to find a session in the
353 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
354 EXPECT_TRUE(deterministic_data()->at_read_eof());
355 EXPECT_TRUE(deterministic_data()->at_write_eof());
358 // Test to ensure the SpdyStream state machine does not get confused when a
359 // chunk becomes available while a write is pending.
360 TEST_P(SpdyHttpStreamTest
, DelayedSendChunkedPost
) {
361 const char kUploadData1
[] = "12345678";
362 const int kUploadData1Size
= arraysize(kUploadData1
)-1;
363 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
364 scoped_ptr
<SpdyFrame
> chunk1(spdy_util_
.ConstructSpdyBodyFrame(1, false));
365 scoped_ptr
<SpdyFrame
> chunk2(
366 spdy_util_
.ConstructSpdyBodyFrame(
367 1, kUploadData1
, kUploadData1Size
, false));
368 scoped_ptr
<SpdyFrame
> chunk3(spdy_util_
.ConstructSpdyBodyFrame(1, true));
369 MockWrite writes
[] = {
370 CreateMockWrite(*req
.get(), 0),
371 CreateMockWrite(*chunk1
, 1), // POST upload frames
372 CreateMockWrite(*chunk2
, 2),
373 CreateMockWrite(*chunk3
, 3),
375 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
377 CreateMockRead(*resp
, 4),
378 CreateMockRead(*chunk1
, 5),
379 CreateMockRead(*chunk2
, 6),
380 CreateMockRead(*chunk3
, 7),
381 MockRead(ASYNC
, 0, 8) // EOF
384 HostPortPair
host_port_pair("www.example.org", 80);
385 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
386 PRIVACY_MODE_DISABLED
);
387 InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
), key
);
389 ChunkedUploadDataStream
upload_stream(0);
391 HttpRequestInfo request
;
392 request
.method
= "POST";
393 request
.url
= GURL("http://www.example.org/");
394 request
.upload_data_stream
= &upload_stream
;
396 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
397 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, false);
400 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
401 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
402 net_log
, CompletionCallback()));
404 TestCompletionCallback callback
;
405 HttpRequestHeaders headers
;
406 HttpResponseInfo response
;
407 // This will attempt to Write() the initial request and headers, which will
408 // complete asynchronously.
409 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
410 callback
.callback()));
411 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
413 // Complete the initial request write and the first chunk.
414 deterministic_data()->RunFor(2);
415 ASSERT_TRUE(callback
.have_result());
416 EXPECT_EQ(OK
, callback
.WaitForResult());
418 // Now append the final two chunks which will enqueue two more writes.
419 upload_stream
.AppendData(kUploadData1
, kUploadData1Size
, false);
420 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, true);
422 // Finish writing all the chunks.
423 deterministic_data()->RunFor(2);
425 // Read response headers.
426 deterministic_data()->RunFor(1);
427 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
429 // Read and check |chunk1| response.
430 deterministic_data()->RunFor(1);
431 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(kUploadDataSize
));
432 ASSERT_EQ(kUploadDataSize
,
433 http_stream
->ReadResponseBody(
434 buf1
.get(), kUploadDataSize
, callback
.callback()));
435 EXPECT_EQ(kUploadData
, std::string(buf1
->data(), kUploadDataSize
));
437 // Read and check |chunk2| response.
438 deterministic_data()->RunFor(1);
439 scoped_refptr
<IOBuffer
> buf2(new IOBuffer(kUploadData1Size
));
440 ASSERT_EQ(kUploadData1Size
,
441 http_stream
->ReadResponseBody(
442 buf2
.get(), kUploadData1Size
, callback
.callback()));
443 EXPECT_EQ(kUploadData1
, std::string(buf2
->data(), kUploadData1Size
));
445 // Read and check |chunk3| response.
446 deterministic_data()->RunFor(1);
447 scoped_refptr
<IOBuffer
> buf3(new IOBuffer(kUploadDataSize
));
448 ASSERT_EQ(kUploadDataSize
,
449 http_stream
->ReadResponseBody(
450 buf3
.get(), kUploadDataSize
, callback
.callback()));
451 EXPECT_EQ(kUploadData
, std::string(buf3
->data(), kUploadDataSize
));
453 // Finish reading the |EOF|.
454 deterministic_data()->RunFor(1);
455 ASSERT_TRUE(response
.headers
.get());
456 ASSERT_EQ(200, response
.headers
->response_code());
457 EXPECT_TRUE(deterministic_data()->at_read_eof());
458 EXPECT_TRUE(deterministic_data()->at_write_eof());
461 // Test that the SpdyStream state machine can handle sending a final empty data
462 // frame when uploading a chunked data stream.
463 TEST_P(SpdyHttpStreamTest
, DelayedSendChunkedPostWithEmptyFinalDataFrame
) {
464 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
465 scoped_ptr
<SpdyFrame
> chunk1(spdy_util_
.ConstructSpdyBodyFrame(1, false));
466 scoped_ptr
<SpdyFrame
> chunk2(
467 spdy_util_
.ConstructSpdyBodyFrame(1, "", 0, true));
468 MockWrite writes
[] = {
469 CreateMockWrite(*req
.get(), 0),
470 CreateMockWrite(*chunk1
, 1), // POST upload frames
471 CreateMockWrite(*chunk2
, 2),
473 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
475 CreateMockRead(*resp
, 3),
476 CreateMockRead(*chunk1
, 4),
477 CreateMockRead(*chunk2
, 5),
478 MockRead(ASYNC
, 0, 6) // EOF
481 HostPortPair
host_port_pair("www.example.org", 80);
482 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
483 PRIVACY_MODE_DISABLED
);
484 InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
), key
);
486 ChunkedUploadDataStream
upload_stream(0);
488 HttpRequestInfo request
;
489 request
.method
= "POST";
490 request
.url
= GURL("http://www.example.org/");
491 request
.upload_data_stream
= &upload_stream
;
493 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
494 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, false);
497 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
498 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
499 net_log
, CompletionCallback()));
501 TestCompletionCallback callback
;
502 HttpRequestHeaders headers
;
503 HttpResponseInfo response
;
504 // This will attempt to Write() the initial request and headers, which will
505 // complete asynchronously.
506 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
507 callback
.callback()));
508 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
510 // Complete the initial request write and the first chunk.
511 deterministic_data()->RunFor(2);
512 ASSERT_TRUE(callback
.have_result());
513 EXPECT_EQ(OK
, callback
.WaitForResult());
515 // Now end the stream with an empty data frame and the FIN set.
516 upload_stream
.AppendData(NULL
, 0, true);
518 // Finish writing the final frame.
519 deterministic_data()->RunFor(1);
521 // Read response headers.
522 deterministic_data()->RunFor(1);
523 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
525 // Read and check |chunk1| response.
526 deterministic_data()->RunFor(1);
527 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(kUploadDataSize
));
528 ASSERT_EQ(kUploadDataSize
,
529 http_stream
->ReadResponseBody(
530 buf1
.get(), kUploadDataSize
, callback
.callback()));
531 EXPECT_EQ(kUploadData
, std::string(buf1
->data(), kUploadDataSize
));
533 // Read and check |chunk2| response.
534 deterministic_data()->RunFor(1);
536 http_stream
->ReadResponseBody(
537 buf1
.get(), kUploadDataSize
, callback
.callback()));
539 // Finish reading the |EOF|.
540 deterministic_data()->RunFor(1);
541 ASSERT_TRUE(response
.headers
.get());
542 ASSERT_EQ(200, response
.headers
->response_code());
543 EXPECT_TRUE(deterministic_data()->at_read_eof());
544 EXPECT_TRUE(deterministic_data()->at_write_eof());
547 // Test that the SpdyStream state machine handles a chunked upload with no
548 // payload. Unclear if this is a case worth supporting.
549 TEST_P(SpdyHttpStreamTest
, ChunkedPostWithEmptyPayload
) {
550 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
551 scoped_ptr
<SpdyFrame
> chunk(
552 spdy_util_
.ConstructSpdyBodyFrame(1, "", 0, true));
553 MockWrite writes
[] = {
554 CreateMockWrite(*req
.get(), 0),
555 CreateMockWrite(*chunk
, 1),
557 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
559 CreateMockRead(*resp
, 2),
560 CreateMockRead(*chunk
, 3),
561 MockRead(ASYNC
, 0, 4) // EOF
564 HostPortPair
host_port_pair("www.example.org", 80);
565 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
566 PRIVACY_MODE_DISABLED
);
567 InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
), key
);
569 ChunkedUploadDataStream
upload_stream(0);
571 HttpRequestInfo request
;
572 request
.method
= "POST";
573 request
.url
= GURL("http://www.example.org/");
574 request
.upload_data_stream
= &upload_stream
;
576 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
577 upload_stream
.AppendData("", 0, true);
580 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
581 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
582 net_log
, CompletionCallback()));
584 TestCompletionCallback callback
;
585 HttpRequestHeaders headers
;
586 HttpResponseInfo response
;
587 // This will attempt to Write() the initial request and headers, which will
588 // complete asynchronously.
589 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
590 callback
.callback()));
591 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
593 // Complete writing request, followed by a FIN.
594 deterministic_data()->RunFor(2);
595 ASSERT_TRUE(callback
.have_result());
596 EXPECT_EQ(OK
, callback
.WaitForResult());
598 // Read response headers.
599 deterministic_data()->RunFor(1);
600 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
602 // Read and check |chunk| response.
603 deterministic_data()->RunFor(1);
604 scoped_refptr
<IOBuffer
> buf(new IOBuffer(1));
606 http_stream
->ReadResponseBody(
607 buf
.get(), 1, callback
.callback()));
609 // Finish reading the |EOF|.
610 deterministic_data()->RunFor(1);
611 ASSERT_TRUE(response
.headers
.get());
612 ASSERT_EQ(200, response
.headers
->response_code());
613 EXPECT_TRUE(deterministic_data()->at_read_eof());
614 EXPECT_TRUE(deterministic_data()->at_write_eof());
617 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
618 TEST_P(SpdyHttpStreamTest
, SpdyURLTest
) {
619 const char* const full_url
= "http://www.example.org/foo?query=what#anchor";
620 const char* const base_url
= "http://www.example.org/foo?query=what";
621 scoped_ptr
<SpdyFrame
> req(
622 spdy_util_
.ConstructSpdyGet(base_url
, false, 1, LOWEST
));
623 MockWrite writes
[] = {
624 CreateMockWrite(*req
.get(), 0),
626 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
628 CreateMockRead(*resp
, 1), MockRead(SYNCHRONOUS
, 0, 2) // EOF
631 HostPortPair
host_port_pair("www.example.org", 80);
632 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
633 PRIVACY_MODE_DISABLED
);
634 InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
), key
);
636 HttpRequestInfo request
;
637 request
.method
= "GET";
638 request
.url
= GURL(full_url
);
639 TestCompletionCallback callback
;
640 HttpResponseInfo response
;
641 HttpRequestHeaders headers
;
643 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
645 http_stream
->InitializeStream(
646 &request
, DEFAULT_PRIORITY
, net_log
, CompletionCallback()));
648 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
649 callback
.callback()));
651 EXPECT_EQ(base_url
, http_stream
->stream()->GetUrlFromHeaders().spec());
653 deterministic_data()->RunFor(3);
654 callback
.WaitForResult();
656 // Because we abandoned the stream, we don't expect to find a session in the
658 EXPECT_FALSE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
659 EXPECT_TRUE(deterministic_data()->at_read_eof());
660 EXPECT_TRUE(deterministic_data()->at_write_eof());
663 // The tests below are only for SPDY/3 and above.
665 // Test the receipt of a WINDOW_UPDATE frame while waiting for a chunk to be
666 // made available is handled correctly.
667 TEST_P(SpdyHttpStreamTest
, DelayedSendChunkedPostWithWindowUpdate
) {
668 scoped_ptr
<SpdyFrame
> req(spdy_util_
.ConstructChunkedSpdyPost(NULL
, 0));
669 scoped_ptr
<SpdyFrame
> chunk1(spdy_util_
.ConstructSpdyBodyFrame(1, true));
670 MockWrite writes
[] = {
671 CreateMockWrite(*req
.get(), 0),
672 CreateMockWrite(*chunk1
, 1),
674 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
675 scoped_ptr
<SpdyFrame
> window_update(
676 spdy_util_
.ConstructSpdyWindowUpdate(1, kUploadDataSize
));
678 CreateMockRead(*window_update
, 2),
679 CreateMockRead(*resp
, 3),
680 CreateMockRead(*chunk1
, 4),
681 MockRead(ASYNC
, 0, 5) // EOF
684 HostPortPair
host_port_pair("www.example.org", 80);
685 SpdySessionKey
key(host_port_pair
, ProxyServer::Direct(),
686 PRIVACY_MODE_DISABLED
);
688 InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
), key
);
690 ChunkedUploadDataStream
upload_stream(0);
692 HttpRequestInfo request
;
693 request
.method
= "POST";
694 request
.url
= GURL("http://www.example.org/");
695 request
.upload_data_stream
= &upload_stream
;
697 ASSERT_EQ(OK
, upload_stream
.Init(TestCompletionCallback().callback()));
698 upload_stream
.AppendData(kUploadData
, kUploadDataSize
, true);
701 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
702 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
, DEFAULT_PRIORITY
,
703 net_log
, CompletionCallback()));
705 HttpRequestHeaders headers
;
706 HttpResponseInfo response
;
707 // This will attempt to Write() the initial request and headers, which will
708 // complete asynchronously.
709 TestCompletionCallback callback
;
710 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
711 callback
.callback()));
712 EXPECT_TRUE(HasSpdySession(http_session_
->spdy_session_pool(), key
));
714 // Complete the initial request write and first chunk.
715 deterministic_data_
->RunFor(2);
716 ASSERT_TRUE(callback
.have_result());
717 EXPECT_EQ(OK
, callback
.WaitForResult());
719 // Verify that the window size has decreased.
720 ASSERT_TRUE(http_stream
->stream() != NULL
);
721 EXPECT_NE(static_cast<int>(
722 SpdySession::GetDefaultInitialWindowSize(session_
->protocol())),
723 http_stream
->stream()->send_window_size());
725 // Read window update.
726 deterministic_data_
->RunFor(1);
728 // Verify the window update.
729 ASSERT_TRUE(http_stream
->stream() != NULL
);
730 EXPECT_EQ(static_cast<int>(
731 SpdySession::GetDefaultInitialWindowSize(session_
->protocol())),
732 http_stream
->stream()->send_window_size());
734 // Read response headers.
735 deterministic_data_
->RunFor(1);
736 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
738 // Read and check |chunk1| response.
739 deterministic_data_
->RunFor(1);
740 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(kUploadDataSize
));
741 ASSERT_EQ(kUploadDataSize
,
742 http_stream
->ReadResponseBody(
743 buf1
.get(), kUploadDataSize
, callback
.callback()));
744 EXPECT_EQ(kUploadData
, std::string(buf1
->data(), kUploadDataSize
));
746 // Finish reading the |EOF|.
747 deterministic_data_
->RunFor(1);
748 ASSERT_TRUE(response
.headers
.get());
749 ASSERT_EQ(200, response
.headers
->response_code());
750 EXPECT_TRUE(deterministic_data_
->at_read_eof());
751 EXPECT_TRUE(deterministic_data_
->at_write_eof());
754 // TODO(willchan): Write a longer test for SpdyStream that exercises all