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"
7 #include "crypto/ec_private_key.h"
8 #include "crypto/ec_signature_creator.h"
9 #include "crypto/signature_creator.h"
10 #include "net/base/asn1_util.h"
11 #include "net/base/capturing_net_log.h"
12 #include "net/base/default_server_bound_cert_store.h"
13 #include "net/base/load_timing_info.h"
14 #include "net/base/load_timing_info_test_util.h"
15 #include "net/base/upload_data_stream.h"
16 #include "net/base/upload_element_reader.h"
17 #include "net/http/http_request_info.h"
18 #include "net/http/http_response_headers.h"
19 #include "net/http/http_response_info.h"
20 #include "net/spdy/spdy_session.h"
21 #include "net/spdy/spdy_test_util_spdy2.h"
22 #include "testing/gtest/include/gtest/gtest.h"
24 using namespace net::test_spdy2
;
30 // Tests the load timing of a stream that's connected and is not the first
31 // request sent on a connection.
32 void TestLoadTimingReused(const HttpStream
& stream
) {
33 LoadTimingInfo load_timing_info
;
34 EXPECT_TRUE(stream
.GetLoadTimingInfo(&load_timing_info
));
36 EXPECT_TRUE(load_timing_info
.socket_reused
);
37 EXPECT_NE(NetLog::Source::kInvalidId
, load_timing_info
.socket_log_id
);
39 ExpectConnectTimingHasNoTimes(load_timing_info
.connect_timing
);
40 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info
);
43 // Tests the load timing of a stream that's connected and using a fresh
45 void TestLoadTimingNotReused(const HttpStream
& stream
) {
46 LoadTimingInfo load_timing_info
;
47 EXPECT_TRUE(stream
.GetLoadTimingInfo(&load_timing_info
));
49 EXPECT_FALSE(load_timing_info
.socket_reused
);
50 EXPECT_NE(NetLog::Source::kInvalidId
, load_timing_info
.socket_log_id
);
52 ExpectConnectTimingHasTimes(load_timing_info
.connect_timing
,
53 CONNECT_TIMING_HAS_DNS_TIMES
);
54 ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info
);
59 class SpdyHttpStreamSpdy2Test
: public testing::Test
{
61 SpdyHttpStreamSpdy2Test() {
62 session_deps_
.net_log
= &net_log_
;
65 DeterministicSocketData
* deterministic_data() {
66 return deterministic_data_
.get();
69 OrderedSocketData
* data() { return data_
.get(); }
72 virtual void TearDown() {
73 crypto::ECSignatureCreator::SetFactoryForTesting(NULL
);
74 UploadDataStream::ResetMergeChunks();
75 MessageLoop::current()->RunUntilIdle();
78 void set_merge_chunks(bool merge
) {
79 UploadDataStream::set_merge_chunks(merge
);
82 // Initializes the session using DeterministicSocketData. It's advisable
83 // to use this function rather than the OrderedSocketData, since the
84 // DeterministicSocketData behaves in a reasonable manner.
85 int InitSessionDeterministic(MockRead
* reads
, size_t reads_count
,
86 MockWrite
* writes
, size_t writes_count
,
87 HostPortPair
& host_port_pair
) {
88 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
89 deterministic_data_
.reset(
90 new DeterministicSocketData(reads
, reads_count
, writes
, writes_count
));
91 session_deps_
.deterministic_socket_factory
->AddSocketDataProvider(
92 deterministic_data_
.get());
94 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
95 session_
= http_session_
->spdy_session_pool()->Get(pair
, BoundNetLog());
96 transport_params_
= new TransportSocketParams(host_port_pair
,
98 OnHostResolutionCallback());
99 TestCompletionCallback callback
;
100 scoped_ptr
<ClientSocketHandle
> connection(new ClientSocketHandle
);
101 EXPECT_EQ(ERR_IO_PENDING
,
102 connection
->Init(host_port_pair
.ToString(),
106 http_session_
->GetTransportSocketPool(
107 HttpNetworkSession::NORMAL_SOCKET_POOL
),
109 EXPECT_EQ(OK
, callback
.WaitForResult());
110 return session_
->InitializeWithSocket(connection
.release(), false, OK
);
113 // Initializes the session using the finicky OrderedSocketData class.
114 int InitSession(MockRead
* reads
, size_t reads_count
,
115 MockWrite
* writes
, size_t writes_count
,
116 HostPortPair
& host_port_pair
) {
117 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
118 data_
.reset(new OrderedSocketData(reads
, reads_count
,
119 writes
, writes_count
));
120 session_deps_
.socket_factory
->AddSocketDataProvider(data_
.get());
121 http_session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps_
);
122 session_
= http_session_
->spdy_session_pool()->Get(pair
, BoundNetLog());
123 transport_params_
= new TransportSocketParams(host_port_pair
,
124 MEDIUM
, false, false,
125 OnHostResolutionCallback());
126 TestCompletionCallback callback
;
127 scoped_ptr
<ClientSocketHandle
> connection(new ClientSocketHandle
);
128 EXPECT_EQ(ERR_IO_PENDING
,
129 connection
->Init(host_port_pair
.ToString(),
133 http_session_
->GetTransportSocketPool(
134 HttpNetworkSession::NORMAL_SOCKET_POOL
),
136 EXPECT_EQ(OK
, callback
.WaitForResult());
137 return session_
->InitializeWithSocket(connection
.release(), false, OK
);
140 CapturingNetLog net_log_
;
141 SpdySessionDependencies session_deps_
;
142 scoped_ptr
<OrderedSocketData
> data_
;
143 scoped_ptr
<DeterministicSocketData
> deterministic_data_
;
144 scoped_refptr
<HttpNetworkSession
> http_session_
;
145 scoped_refptr
<SpdySession
> session_
;
146 scoped_refptr
<TransportSocketParams
> transport_params_
;
149 TEST_F(SpdyHttpStreamSpdy2Test
, SendRequest
) {
150 scoped_ptr
<SpdyFrame
> req(ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
));
151 MockWrite writes
[] = {
152 CreateMockWrite(*req
.get(), 1),
154 scoped_ptr
<SpdyFrame
> resp(ConstructSpdyGetSynReply(NULL
, 0, 1));
156 CreateMockRead(*resp
, 2),
157 MockRead(SYNCHRONOUS
, 0, 3) // EOF
160 HostPortPair
host_port_pair("www.google.com", 80);
161 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
162 EXPECT_EQ(OK
, InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
),
165 HttpRequestInfo request
;
166 request
.method
= "GET";
167 request
.url
= GURL("http://www.google.com/");
168 TestCompletionCallback callback
;
169 HttpResponseInfo response
;
170 HttpRequestHeaders headers
;
172 scoped_ptr
<SpdyHttpStream
> http_stream(
173 new SpdyHttpStream(session_
.get(), true));
174 // Make sure getting load timing information the stream early does not crash.
175 LoadTimingInfo load_timing_info
;
176 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
180 http_stream
->InitializeStream(&request
, net_log
, CompletionCallback()));
181 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
183 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
184 callback
.callback()));
185 EXPECT_TRUE(http_session_
->spdy_session_pool()->HasSession(pair
));
186 EXPECT_FALSE(http_stream
->GetLoadTimingInfo(&load_timing_info
));
188 // This triggers the MockWrite and read 2
189 callback
.WaitForResult();
191 // Can get timing information once the stream connects.
192 TestLoadTimingNotReused(*http_stream
);
194 // This triggers read 3. The empty read causes the session to shut down.
195 data()->CompleteRead();
197 // Because we abandoned the stream, we don't expect to find a session in the
199 EXPECT_FALSE(http_session_
->spdy_session_pool()->HasSession(pair
));
200 EXPECT_TRUE(data()->at_read_eof());
201 EXPECT_TRUE(data()->at_write_eof());
203 TestLoadTimingNotReused(*http_stream
);
204 http_stream
->Close(true);
205 // Test that there's no crash when trying to get the load timing after the
206 // stream has been closed.
207 TestLoadTimingNotReused(*http_stream
);
210 TEST_F(SpdyHttpStreamSpdy2Test
, LoadTimingTwoRequests
) {
211 scoped_ptr
<SpdyFrame
> req1(ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
));
212 scoped_ptr
<SpdyFrame
> req2(ConstructSpdyGet(NULL
, 0, false, 3, LOWEST
));
213 MockWrite writes
[] = {
214 CreateMockWrite(*req1
, 0),
215 CreateMockWrite(*req2
, 1),
217 scoped_ptr
<SpdyFrame
> resp1(ConstructSpdyGetSynReply(NULL
, 0, 1));
218 scoped_ptr
<SpdyFrame
> body1(ConstructSpdyBodyFrame(1, "", 0, true));
219 scoped_ptr
<SpdyFrame
> resp2(ConstructSpdyGetSynReply(NULL
, 0, 3));
220 scoped_ptr
<SpdyFrame
> body2(ConstructSpdyBodyFrame(3, "", 0, true));
222 CreateMockRead(*resp1
, 2),
223 CreateMockRead(*body1
, 3),
224 CreateMockRead(*resp2
, 4),
225 CreateMockRead(*body2
, 5),
226 MockRead(ASYNC
, 0, 6) // EOF
229 HostPortPair
host_port_pair("www.google.com", 80);
230 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
231 ASSERT_EQ(OK
, InitSessionDeterministic(reads
, arraysize(reads
),
232 writes
, arraysize(writes
),
235 HttpRequestInfo request1
;
236 request1
.method
= "GET";
237 request1
.url
= GURL("http://www.google.com/");
238 TestCompletionCallback callback1
;
239 HttpResponseInfo response1
;
240 HttpRequestHeaders headers1
;
241 scoped_ptr
<SpdyHttpStream
> http_stream1(
242 new SpdyHttpStream(session_
.get(), true));
245 http_stream1
->InitializeStream(&request1
, BoundNetLog(),
246 CompletionCallback()));
247 EXPECT_EQ(ERR_IO_PENDING
, http_stream1
->SendRequest(headers1
, &response1
,
248 callback1
.callback()));
249 EXPECT_TRUE(http_session_
->spdy_session_pool()->HasSession(pair
));
251 HttpRequestInfo request2
;
252 request2
.method
= "GET";
253 request2
.url
= GURL("http://www.google.com/");
254 TestCompletionCallback callback2
;
255 HttpResponseInfo response2
;
256 HttpRequestHeaders headers2
;
257 scoped_ptr
<SpdyHttpStream
> http_stream2(
258 new SpdyHttpStream(session_
.get(), true));
261 http_stream2
->InitializeStream(&request2
, BoundNetLog(),
262 CompletionCallback()));
263 EXPECT_EQ(ERR_IO_PENDING
, http_stream2
->SendRequest(headers2
, &response2
,
264 callback2
.callback()));
265 EXPECT_TRUE(http_session_
->spdy_session_pool()->HasSession(pair
));
268 deterministic_data()->RunFor(1);
269 EXPECT_LE(0, callback1
.WaitForResult());
271 TestLoadTimingNotReused(*http_stream1
);
272 LoadTimingInfo load_timing_info1
;
273 LoadTimingInfo load_timing_info2
;
274 EXPECT_TRUE(http_stream1
->GetLoadTimingInfo(&load_timing_info1
));
275 EXPECT_FALSE(http_stream2
->GetLoadTimingInfo(&load_timing_info2
));
278 deterministic_data()->RunFor(1);
279 EXPECT_LE(0, callback2
.WaitForResult());
280 TestLoadTimingReused(*http_stream2
);
281 EXPECT_TRUE(http_stream2
->GetLoadTimingInfo(&load_timing_info2
));
282 EXPECT_EQ(load_timing_info1
.socket_log_id
, load_timing_info2
.socket_log_id
);
285 deterministic_data()->RunFor(6);
287 // Read stream 1 to completion, before making sure we can still read load
288 // timing from both streams.
289 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(1));
290 ASSERT_EQ(0, http_stream1
->ReadResponseBody(buf1
, 1, callback1
.callback()));
292 // Stream 1 has been read to completion.
293 TestLoadTimingNotReused(*http_stream1
);
294 // Stream 2 still has queued body data.
295 TestLoadTimingReused(*http_stream2
);
298 TEST_F(SpdyHttpStreamSpdy2Test
, SendChunkedPost
) {
299 set_merge_chunks(false);
301 scoped_ptr
<SpdyFrame
> req(ConstructChunkedSpdyPost(NULL
, 0));
302 scoped_ptr
<SpdyFrame
> chunk1(ConstructSpdyBodyFrame(1, false));
303 scoped_ptr
<SpdyFrame
> chunk2(ConstructSpdyBodyFrame(1, true));
304 MockWrite writes
[] = {
305 CreateMockWrite(*req
.get(), 0),
306 CreateMockWrite(*chunk1
, 1), // POST upload frames
307 CreateMockWrite(*chunk2
, 2),
309 scoped_ptr
<SpdyFrame
> resp(ConstructSpdyPostSynReply(NULL
, 0));
311 CreateMockRead(*resp
, 3),
312 CreateMockRead(*chunk1
, 4),
313 CreateMockRead(*chunk2
, 5),
314 MockRead(SYNCHRONOUS
, 0, 6) // EOF
317 HostPortPair
host_port_pair("www.google.com", 80);
318 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
319 EXPECT_EQ(OK
, InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
),
322 UploadDataStream
upload_stream(UploadDataStream::CHUNKED
, 0);
323 upload_stream
.AppendChunk(kUploadData
, kUploadDataSize
, false);
324 upload_stream
.AppendChunk(kUploadData
, kUploadDataSize
, true);
326 HttpRequestInfo request
;
327 request
.method
= "POST";
328 request
.url
= GURL("http://www.google.com/");
329 request
.upload_data_stream
= &upload_stream
;
331 ASSERT_EQ(OK
, upload_stream
.Init(CompletionCallback()));
333 TestCompletionCallback callback
;
334 HttpResponseInfo response
;
335 HttpRequestHeaders headers
;
337 SpdyHttpStream
http_stream(session_
.get(), true);
340 http_stream
.InitializeStream(&request
, net_log
, CompletionCallback()));
342 EXPECT_EQ(ERR_IO_PENDING
, http_stream
.SendRequest(
343 headers
, &response
, callback
.callback()));
344 EXPECT_TRUE(http_session_
->spdy_session_pool()->HasSession(pair
));
346 // This triggers the MockWrite and read 2
347 callback
.WaitForResult();
349 // This triggers read 3. The empty read causes the session to shut down.
350 data()->CompleteRead();
351 MessageLoop::current()->RunUntilIdle();
353 // Because we abandoned the stream, we don't expect to find a session in the
355 EXPECT_FALSE(http_session_
->spdy_session_pool()->HasSession(pair
));
356 EXPECT_TRUE(data()->at_read_eof());
357 EXPECT_TRUE(data()->at_write_eof());
360 // Test to ensure the SpdyStream state machine does not get confused when a
361 // chunk becomes available while a write is pending.
362 TEST_F(SpdyHttpStreamSpdy2Test
, DelayedSendChunkedPost
) {
363 set_merge_chunks(false);
365 const char kUploadData1
[] = "12345678";
366 const int kUploadData1Size
= arraysize(kUploadData1
)-1;
367 scoped_ptr
<SpdyFrame
> req(ConstructChunkedSpdyPost(NULL
, 0));
368 scoped_ptr
<SpdyFrame
> chunk1(ConstructSpdyBodyFrame(1, false));
369 scoped_ptr
<SpdyFrame
> chunk2(
370 ConstructSpdyBodyFrame(1, kUploadData1
, kUploadData1Size
, false));
371 scoped_ptr
<SpdyFrame
> chunk3(ConstructSpdyBodyFrame(1, true));
372 MockWrite writes
[] = {
373 CreateMockWrite(*req
.get(), 0),
374 CreateMockWrite(*chunk1
, 1), // POST upload frames
375 CreateMockWrite(*chunk2
, 2),
376 CreateMockWrite(*chunk3
, 3),
378 scoped_ptr
<SpdyFrame
> resp(ConstructSpdyPostSynReply(NULL
, 0));
380 CreateMockRead(*resp
, 4),
381 CreateMockRead(*chunk1
, 5),
382 CreateMockRead(*chunk2
, 6),
383 CreateMockRead(*chunk3
, 7),
384 MockRead(ASYNC
, 0, 8) // EOF
387 HostPortPair
host_port_pair("www.google.com", 80);
388 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
390 ASSERT_EQ(OK
, InitSessionDeterministic(reads
, arraysize(reads
),
391 writes
, arraysize(writes
),
394 UploadDataStream
upload_stream(UploadDataStream::CHUNKED
, 0);
396 HttpRequestInfo request
;
397 request
.method
= "POST";
398 request
.url
= GURL("http://www.google.com/");
399 request
.upload_data_stream
= &upload_stream
;
401 ASSERT_EQ(OK
, upload_stream
.Init(CompletionCallback()));
402 upload_stream
.AppendChunk(kUploadData
, kUploadDataSize
, false);
405 scoped_ptr
<SpdyHttpStream
> http_stream(
406 new SpdyHttpStream(session_
.get(), true));
407 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
,
409 CompletionCallback()));
411 HttpRequestHeaders headers
;
412 HttpResponseInfo response
;
413 TestCompletionCallback callback
;
414 // This will attempt to Write() the initial request and headers, which will
415 // complete asynchronously.
416 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
417 callback
.callback()));
418 EXPECT_TRUE(http_session_
->spdy_session_pool()->HasSession(pair
));
420 // Complete the initial request write and the first chunk.
421 deterministic_data()->RunFor(2);
422 ASSERT_TRUE(callback
.have_result());
423 EXPECT_GT(callback
.WaitForResult(), 0);
425 // Now append the final two chunks which will enqueue two more writes.
426 upload_stream
.AppendChunk(kUploadData1
, kUploadData1Size
, false);
427 upload_stream
.AppendChunk(kUploadData
, kUploadDataSize
, true);
429 // Finish writing all the chunks.
430 deterministic_data()->RunFor(2);
432 // Read response headers.
433 deterministic_data()->RunFor(1);
434 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
436 // Read and check |chunk1| response.
437 deterministic_data()->RunFor(1);
438 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(kUploadDataSize
));
439 ASSERT_EQ(kUploadDataSize
,
440 http_stream
->ReadResponseBody(buf1
,
442 callback
.callback()));
443 EXPECT_EQ(kUploadData
, std::string(buf1
->data(), kUploadDataSize
));
445 // Read and check |chunk2| response.
446 deterministic_data()->RunFor(1);
447 scoped_refptr
<IOBuffer
> buf2(new IOBuffer(kUploadData1Size
));
448 ASSERT_EQ(kUploadData1Size
,
449 http_stream
->ReadResponseBody(buf2
,
451 callback
.callback()));
452 EXPECT_EQ(kUploadData1
, std::string(buf2
->data(), kUploadData1Size
));
454 // Read and check |chunk3| response.
455 deterministic_data()->RunFor(1);
456 scoped_refptr
<IOBuffer
> buf3(new IOBuffer(kUploadDataSize
));
457 ASSERT_EQ(kUploadDataSize
,
458 http_stream
->ReadResponseBody(buf3
,
460 callback
.callback()));
461 EXPECT_EQ(kUploadData
, std::string(buf3
->data(), kUploadDataSize
));
463 // Finish reading the |EOF|.
464 deterministic_data()->RunFor(1);
465 ASSERT_TRUE(response
.headers
.get());
466 ASSERT_EQ(200, response
.headers
->response_code());
467 EXPECT_TRUE(deterministic_data()->at_read_eof());
468 EXPECT_TRUE(deterministic_data()->at_write_eof());
471 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
472 TEST_F(SpdyHttpStreamSpdy2Test
, SpdyURLTest
) {
473 const char * const full_url
= "http://www.google.com/foo?query=what#anchor";
474 const char * const base_url
= "http://www.google.com/foo?query=what";
475 scoped_ptr
<SpdyFrame
> req(ConstructSpdyGet(base_url
, false, 1, LOWEST
));
476 MockWrite writes
[] = {
477 CreateMockWrite(*req
.get(), 1),
479 scoped_ptr
<SpdyFrame
> resp(ConstructSpdyGetSynReply(NULL
, 0, 1));
481 CreateMockRead(*resp
, 2),
482 MockRead(SYNCHRONOUS
, 0, 3) // EOF
485 HostPortPair
host_port_pair("www.google.com", 80);
486 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
487 EXPECT_EQ(OK
, InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
),
490 HttpRequestInfo request
;
491 request
.method
= "GET";
492 request
.url
= GURL(full_url
);
493 TestCompletionCallback callback
;
494 HttpResponseInfo response
;
495 HttpRequestHeaders headers
;
497 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
500 http_stream
->InitializeStream(&request
, net_log
, CompletionCallback()));
502 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
503 callback
.callback()));
505 const SpdyHeaderBlock
& spdy_header
=
506 http_stream
->stream()->spdy_headers();
507 if (spdy_header
.find("url") != spdy_header
.end())
508 EXPECT_EQ("/foo?query=what", spdy_header
.find("url")->second
);
510 FAIL() << "No url is set in spdy_header!";
512 // This triggers the MockWrite and read 2
513 callback
.WaitForResult();
515 // This triggers read 3. The empty read causes the session to shut down.
516 data()->CompleteRead();
518 // Because we abandoned the stream, we don't expect to find a session in the
520 EXPECT_FALSE(http_session_
->spdy_session_pool()->HasSession(pair
));
521 EXPECT_TRUE(data()->at_read_eof());
522 EXPECT_TRUE(data()->at_write_eof());
525 // TODO(willchan): Write a longer test for SpdyStream that exercises all