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/default_server_bound_cert_store.h"
12 #include "net/base/upload_data_stream.h"
13 #include "net/base/upload_element_reader.h"
14 #include "net/http/http_request_info.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/http/http_response_info.h"
17 #include "net/spdy/spdy_session.h"
18 #include "net/spdy/spdy_test_util_spdy2.h"
19 #include "testing/gtest/include/gtest/gtest.h"
21 using namespace net::test_spdy2
;
25 class SpdyHttpStreamSpdy2Test
: public testing::Test
{
27 OrderedSocketData
* data() { return data_
.get(); }
29 SpdyHttpStreamSpdy2Test() {}
31 virtual void TearDown() {
32 crypto::ECSignatureCreator::SetFactoryForTesting(NULL
);
33 UploadDataStream::ResetMergeChunks();
34 MessageLoop::current()->RunUntilIdle();
37 void set_merge_chunks(bool merge
) {
38 UploadDataStream::set_merge_chunks(merge
);
41 int InitSession(MockRead
* reads
, size_t reads_count
,
42 MockWrite
* writes
, size_t writes_count
,
43 HostPortPair
& host_port_pair
) {
44 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
45 data_
.reset(new OrderedSocketData(reads
, reads_count
,
46 writes
, writes_count
));
47 session_deps_
.socket_factory
->AddSocketDataProvider(data_
.get());
48 http_session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps_
);
49 session_
= http_session_
->spdy_session_pool()->Get(pair
, BoundNetLog());
50 transport_params_
= new TransportSocketParams(host_port_pair
,
52 OnHostResolutionCallback());
53 TestCompletionCallback callback
;
54 scoped_ptr
<ClientSocketHandle
> connection(new ClientSocketHandle
);
55 EXPECT_EQ(ERR_IO_PENDING
,
56 connection
->Init(host_port_pair
.ToString(),
60 http_session_
->GetTransportSocketPool(
61 HttpNetworkSession::NORMAL_SOCKET_POOL
),
63 EXPECT_EQ(OK
, callback
.WaitForResult());
64 return session_
->InitializeWithSocket(connection
.release(), false, OK
);
67 SpdySessionDependencies session_deps_
;
68 scoped_ptr
<OrderedSocketData
> data_
;
69 scoped_refptr
<HttpNetworkSession
> http_session_
;
70 scoped_refptr
<SpdySession
> session_
;
71 scoped_refptr
<TransportSocketParams
> transport_params_
;
74 TEST_F(SpdyHttpStreamSpdy2Test
, SendRequest
) {
75 scoped_ptr
<SpdyFrame
> req(ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
));
76 MockWrite writes
[] = {
77 CreateMockWrite(*req
.get(), 1),
79 scoped_ptr
<SpdyFrame
> resp(ConstructSpdyGetSynReply(NULL
, 0, 1));
81 CreateMockRead(*resp
, 2),
82 MockRead(SYNCHRONOUS
, 0, 3) // EOF
85 HostPortPair
host_port_pair("www.google.com", 80);
86 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
87 EXPECT_EQ(OK
, InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
),
90 HttpRequestInfo request
;
91 request
.method
= "GET";
92 request
.url
= GURL("http://www.google.com/");
93 TestCompletionCallback callback
;
94 HttpResponseInfo response
;
95 HttpRequestHeaders headers
;
97 scoped_ptr
<SpdyHttpStream
> http_stream(
98 new SpdyHttpStream(session_
.get(), true));
101 http_stream
->InitializeStream(&request
, net_log
, CompletionCallback()));
103 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
104 callback
.callback()));
105 EXPECT_TRUE(http_session_
->spdy_session_pool()->HasSession(pair
));
107 // This triggers the MockWrite and read 2
108 callback
.WaitForResult();
110 // This triggers read 3. The empty read causes the session to shut down.
111 data()->CompleteRead();
113 // Because we abandoned the stream, we don't expect to find a session in the
115 EXPECT_FALSE(http_session_
->spdy_session_pool()->HasSession(pair
));
116 EXPECT_TRUE(data()->at_read_eof());
117 EXPECT_TRUE(data()->at_write_eof());
120 TEST_F(SpdyHttpStreamSpdy2Test
, SendChunkedPost
) {
121 set_merge_chunks(false);
123 scoped_ptr
<SpdyFrame
> req(ConstructChunkedSpdyPost(NULL
, 0));
124 scoped_ptr
<SpdyFrame
> chunk1(ConstructSpdyBodyFrame(1, false));
125 scoped_ptr
<SpdyFrame
> chunk2(ConstructSpdyBodyFrame(1, true));
126 MockWrite writes
[] = {
127 CreateMockWrite(*req
.get(), 0),
128 CreateMockWrite(*chunk1
, 1), // POST upload frames
129 CreateMockWrite(*chunk2
, 2),
131 scoped_ptr
<SpdyFrame
> resp(ConstructSpdyPostSynReply(NULL
, 0));
133 CreateMockRead(*resp
, 3),
134 CreateMockRead(*chunk1
, 4),
135 CreateMockRead(*chunk2
, 5),
136 MockRead(SYNCHRONOUS
, 0, 6) // EOF
139 HostPortPair
host_port_pair("www.google.com", 80);
140 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
141 EXPECT_EQ(OK
, InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
),
144 UploadDataStream
upload_stream(UploadDataStream::CHUNKED
, 0);
145 upload_stream
.AppendChunk(kUploadData
, kUploadDataSize
, false);
146 upload_stream
.AppendChunk(kUploadData
, kUploadDataSize
, true);
148 HttpRequestInfo request
;
149 request
.method
= "POST";
150 request
.url
= GURL("http://www.google.com/");
151 request
.upload_data_stream
= &upload_stream
;
153 ASSERT_EQ(OK
, upload_stream
.Init(CompletionCallback()));
155 TestCompletionCallback callback
;
156 HttpResponseInfo response
;
157 HttpRequestHeaders headers
;
159 SpdyHttpStream
http_stream(session_
.get(), true);
162 http_stream
.InitializeStream(&request
, net_log
, CompletionCallback()));
164 EXPECT_EQ(ERR_IO_PENDING
, http_stream
.SendRequest(
165 headers
, &response
, callback
.callback()));
166 EXPECT_TRUE(http_session_
->spdy_session_pool()->HasSession(pair
));
168 // This triggers the MockWrite and read 2
169 callback
.WaitForResult();
171 // This triggers read 3. The empty read causes the session to shut down.
172 data()->CompleteRead();
173 MessageLoop::current()->RunUntilIdle();
175 // Because we abandoned the stream, we don't expect to find a session in the
177 EXPECT_FALSE(http_session_
->spdy_session_pool()->HasSession(pair
));
178 EXPECT_TRUE(data()->at_read_eof());
179 EXPECT_TRUE(data()->at_write_eof());
182 // Test to ensure the SpdyStream state machine does not get confused when a
183 // chunk becomes available while a write is pending.
184 TEST_F(SpdyHttpStreamSpdy2Test
, DelayedSendChunkedPost
) {
185 set_merge_chunks(false);
187 const char kUploadData1
[] = "12345678";
188 const int kUploadData1Size
= arraysize(kUploadData1
)-1;
189 scoped_ptr
<SpdyFrame
> req(ConstructChunkedSpdyPost(NULL
, 0));
190 scoped_ptr
<SpdyFrame
> chunk1(ConstructSpdyBodyFrame(1, false));
191 scoped_ptr
<SpdyFrame
> chunk2(
192 ConstructSpdyBodyFrame(1, kUploadData1
, kUploadData1Size
, false));
193 scoped_ptr
<SpdyFrame
> chunk3(ConstructSpdyBodyFrame(1, true));
194 MockWrite writes
[] = {
195 CreateMockWrite(*req
.get(), 0),
196 CreateMockWrite(*chunk1
, 1), // POST upload frames
197 CreateMockWrite(*chunk2
, 2),
198 CreateMockWrite(*chunk3
, 3),
200 scoped_ptr
<SpdyFrame
> resp(ConstructSpdyPostSynReply(NULL
, 0));
202 CreateMockRead(*resp
, 4),
203 CreateMockRead(*chunk1
, 5),
204 CreateMockRead(*chunk2
, 6),
205 CreateMockRead(*chunk3
, 7),
206 MockRead(ASYNC
, 0, 8) // EOF
209 HostPortPair
host_port_pair("www.google.com", 80);
210 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
212 DeterministicSocketData
data(reads
, arraysize(reads
),
213 writes
, arraysize(writes
));
215 DeterministicMockClientSocketFactory
* socket_factory
=
216 session_deps_
.deterministic_socket_factory
.get();
217 socket_factory
->AddSocketDataProvider(&data
);
219 http_session_
= SpdySessionDependencies::SpdyCreateSessionDeterministic(
221 session_
= http_session_
->spdy_session_pool()->Get(pair
, BoundNetLog());
222 transport_params_
= new TransportSocketParams(host_port_pair
,
223 MEDIUM
, false, false,
224 OnHostResolutionCallback());
226 TestCompletionCallback callback
;
227 scoped_ptr
<ClientSocketHandle
> connection(new ClientSocketHandle
);
229 EXPECT_EQ(ERR_IO_PENDING
,
230 connection
->Init(host_port_pair
.ToString(),
234 http_session_
->GetTransportSocketPool(
235 HttpNetworkSession::NORMAL_SOCKET_POOL
),
238 callback
.WaitForResult();
240 session_
->InitializeWithSocket(connection
.release(), false, OK
));
242 UploadDataStream
upload_stream(UploadDataStream::CHUNKED
, 0);
244 HttpRequestInfo request
;
245 request
.method
= "POST";
246 request
.url
= GURL("http://www.google.com/");
247 request
.upload_data_stream
= &upload_stream
;
249 ASSERT_EQ(OK
, upload_stream
.Init(CompletionCallback()));
250 upload_stream
.AppendChunk(kUploadData
, kUploadDataSize
, false);
253 scoped_ptr
<SpdyHttpStream
> http_stream(
254 new SpdyHttpStream(session_
.get(), true));
255 ASSERT_EQ(OK
, http_stream
->InitializeStream(&request
,
257 CompletionCallback()));
259 HttpRequestHeaders headers
;
260 HttpResponseInfo response
;
261 // This will attempt to Write() the initial request and headers, which will
262 // complete asynchronously.
263 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
264 callback
.callback()));
265 EXPECT_TRUE(http_session_
->spdy_session_pool()->HasSession(pair
));
267 // Complete the initial request write and the first chunk.
269 ASSERT_TRUE(callback
.have_result());
270 EXPECT_GT(callback
.WaitForResult(), 0);
272 // Now append the final two chunks which will enqueue two more writes.
273 upload_stream
.AppendChunk(kUploadData1
, kUploadData1Size
, false);
274 upload_stream
.AppendChunk(kUploadData
, kUploadDataSize
, true);
276 // Finish writing all the chunks.
279 // Read response headers.
281 ASSERT_EQ(OK
, http_stream
->ReadResponseHeaders(callback
.callback()));
283 // Read and check |chunk1| response.
285 scoped_refptr
<IOBuffer
> buf1(new IOBuffer(kUploadDataSize
));
286 ASSERT_EQ(kUploadDataSize
,
287 http_stream
->ReadResponseBody(buf1
,
289 callback
.callback()));
290 EXPECT_EQ(kUploadData
, std::string(buf1
->data(), kUploadDataSize
));
292 // Read and check |chunk2| response.
294 scoped_refptr
<IOBuffer
> buf2(new IOBuffer(kUploadData1Size
));
295 ASSERT_EQ(kUploadData1Size
,
296 http_stream
->ReadResponseBody(buf2
,
298 callback
.callback()));
299 EXPECT_EQ(kUploadData1
, std::string(buf2
->data(), kUploadData1Size
));
301 // Read and check |chunk3| response.
303 scoped_refptr
<IOBuffer
> buf3(new IOBuffer(kUploadDataSize
));
304 ASSERT_EQ(kUploadDataSize
,
305 http_stream
->ReadResponseBody(buf3
,
307 callback
.callback()));
308 EXPECT_EQ(kUploadData
, std::string(buf3
->data(), kUploadDataSize
));
310 // Finish reading the |EOF|.
312 ASSERT_TRUE(response
.headers
.get());
313 ASSERT_EQ(200, response
.headers
->response_code());
314 EXPECT_TRUE(data
.at_read_eof());
315 EXPECT_TRUE(data
.at_write_eof());
318 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
319 TEST_F(SpdyHttpStreamSpdy2Test
, SpdyURLTest
) {
320 const char * const full_url
= "http://www.google.com/foo?query=what#anchor";
321 const char * const base_url
= "http://www.google.com/foo?query=what";
322 scoped_ptr
<SpdyFrame
> req(ConstructSpdyGet(base_url
, false, 1, LOWEST
));
323 MockWrite writes
[] = {
324 CreateMockWrite(*req
.get(), 1),
326 scoped_ptr
<SpdyFrame
> resp(ConstructSpdyGetSynReply(NULL
, 0, 1));
328 CreateMockRead(*resp
, 2),
329 MockRead(SYNCHRONOUS
, 0, 3) // EOF
332 HostPortPair
host_port_pair("www.google.com", 80);
333 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
334 EXPECT_EQ(OK
, InitSession(reads
, arraysize(reads
), writes
, arraysize(writes
),
337 HttpRequestInfo request
;
338 request
.method
= "GET";
339 request
.url
= GURL(full_url
);
340 TestCompletionCallback callback
;
341 HttpResponseInfo response
;
342 HttpRequestHeaders headers
;
344 scoped_ptr
<SpdyHttpStream
> http_stream(new SpdyHttpStream(session_
, true));
347 http_stream
->InitializeStream(&request
, net_log
, CompletionCallback()));
349 EXPECT_EQ(ERR_IO_PENDING
, http_stream
->SendRequest(headers
, &response
,
350 callback
.callback()));
352 const SpdyHeaderBlock
& spdy_header
=
353 http_stream
->stream()->spdy_headers();
354 if (spdy_header
.find("url") != spdy_header
.end())
355 EXPECT_EQ("/foo?query=what", spdy_header
.find("url")->second
);
357 FAIL() << "No url is set in spdy_header!";
359 // This triggers the MockWrite and read 2
360 callback
.WaitForResult();
362 // This triggers read 3. The empty read causes the session to shut down.
363 data()->CompleteRead();
365 // Because we abandoned the stream, we don't expect to find a session in the
367 EXPECT_FALSE(http_session_
->spdy_session_pool()->HasSession(pair
));
368 EXPECT_TRUE(data()->at_read_eof());
369 EXPECT_TRUE(data()->at_write_eof());
372 // TODO(willchan): Write a longer test for SpdyStream that exercises all