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 "base/memory/ref_counted.h"
6 #include "net/base/completion_callback.h"
7 #include "net/base/net_log_unittest.h"
8 #include "net/spdy/buffered_spdy_framer.h"
9 #include "net/spdy/spdy_stream.h"
10 #include "net/spdy/spdy_http_utils.h"
11 #include "net/spdy/spdy_session.h"
12 #include "net/spdy/spdy_stream_test_util.h"
13 #include "net/spdy/spdy_test_util_spdy2.h"
14 #include "net/spdy/spdy_websocket_test_util_spdy2.h"
15 #include "testing/gtest/include/gtest/gtest.h"
17 using namespace net::test_spdy2
;
19 // TODO(ukai): factor out common part with spdy_http_stream_unittest.cc
25 SpdyFrame
* ConstructSpdyBodyFrame(const char* data
, int length
) {
26 BufferedSpdyFramer
framer(2, false);
27 return framer
.CreateDataFrame(1, data
, length
, DATA_FLAG_NONE
);
30 } // anonymous namespace
34 class SpdyStreamSpdy2Test
: public testing::Test
{
36 SpdyStreamSpdy2Test() {
39 scoped_refptr
<SpdySession
> CreateSpdySession() {
40 HostPortPair
host_port_pair("www.google.com", 80);
41 HostPortProxyPair
pair(host_port_pair
, ProxyServer::Direct());
42 scoped_refptr
<SpdySession
> session(
43 session_
->spdy_session_pool()->Get(pair
, BoundNetLog()));
47 virtual void TearDown() {
48 MessageLoop::current()->RunUntilIdle();
51 scoped_refptr
<HttpNetworkSession
> session_
;
54 TEST_F(SpdyStreamSpdy2Test
, SendDataAfterOpen
) {
55 SpdySessionDependencies session_deps
;
57 session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps
);
58 SpdySessionPoolPeer
pool_peer_(session_
->spdy_session_pool());
60 const SpdyHeaderInfo kSynStartHeader
= {
64 ConvertRequestPriorityToSpdyPriority(LOWEST
, 2),
72 static const char* const kGetHeaders
[] = {
84 scoped_ptr
<SpdyFrame
> req(
86 kSynStartHeader
, NULL
, 0, kGetHeaders
, arraysize(kGetHeaders
) / 2));
87 scoped_ptr
<SpdyFrame
> msg(
88 ConstructSpdyBodyFrame("\0hello!\xff", 8));
89 MockWrite writes
[] = {
90 CreateMockWrite(*req
),
91 CreateMockWrite(*msg
),
93 writes
[0].sequence_number
= 0;
94 writes
[1].sequence_number
= 2;
96 scoped_ptr
<SpdyFrame
> resp(ConstructSpdyGetSynReply(NULL
, 0, 1));
97 scoped_ptr
<SpdyFrame
> echo(
98 ConstructSpdyBodyFrame("\0hello!\xff", 8));
100 CreateMockRead(*resp
),
101 CreateMockRead(*echo
),
102 MockRead(ASYNC
, 0, 0), // EOF
104 reads
[0].sequence_number
= 1;
105 reads
[1].sequence_number
= 3;
106 reads
[2].sequence_number
= 4;
108 OrderedSocketData
data(reads
, arraysize(reads
),
109 writes
, arraysize(writes
));
110 MockConnect
connect_data(SYNCHRONOUS
, OK
);
111 data
.set_connect_data(connect_data
);
113 session_deps
.socket_factory
->AddSocketDataProvider(&data
);
115 scoped_refptr
<SpdySession
> session(CreateSpdySession());
116 const char* kStreamUrl
= "http://www.google.com/";
117 GURL
url(kStreamUrl
);
119 HostPortPair
host_port_pair("www.google.com", 80);
120 scoped_refptr
<TransportSocketParams
> transport_params(
121 new TransportSocketParams(host_port_pair
, LOWEST
, false, false,
122 OnHostResolutionCallback()));
124 scoped_ptr
<ClientSocketHandle
> connection(new ClientSocketHandle
);
125 EXPECT_EQ(OK
, connection
->Init(host_port_pair
.ToString(), transport_params
,
126 LOWEST
, CompletionCallback(),
127 session_
->GetTransportSocketPool(
128 HttpNetworkSession::NORMAL_SOCKET_POOL
),
130 session
->InitializeWithSocket(connection
.release(), false, OK
);
132 scoped_refptr
<SpdyStream
> stream
;
135 session
->CreateStream(url
, LOWEST
, &stream
, BoundNetLog(),
136 CompletionCallback()));
137 scoped_refptr
<IOBufferWithSize
> buf(new IOBufferWithSize(8));
138 memcpy(buf
->data(), "\0hello!\xff", 8);
139 TestCompletionCallback callback
;
141 scoped_ptr
<TestSpdyStreamDelegate
> delegate(
142 new TestSpdyStreamDelegate(
143 stream
.get(), NULL
, buf
.get(), callback
.callback()));
144 stream
->SetDelegate(delegate
.get());
146 EXPECT_FALSE(stream
->HasUrl());
148 scoped_ptr
<SpdyHeaderBlock
> headers(new SpdyHeaderBlock
);
149 (*headers
)["method"] = "GET";
150 (*headers
)["scheme"] = url
.scheme();
151 (*headers
)["host"] = url
.host();
152 (*headers
)["url"] = url
.path();
153 (*headers
)["version"] = "HTTP/1.1";
154 stream
->set_spdy_headers(headers
.Pass());
155 EXPECT_TRUE(stream
->HasUrl());
156 EXPECT_EQ(kStreamUrl
, stream
->GetUrl().spec());
158 EXPECT_EQ(ERR_IO_PENDING
, stream
->SendRequest(true));
160 EXPECT_EQ(OK
, callback
.WaitForResult());
162 EXPECT_TRUE(delegate
->send_headers_completed());
163 EXPECT_EQ("200", (*delegate
->response())["status"]);
164 EXPECT_EQ("HTTP/1.1", (*delegate
->response())["version"]);
165 EXPECT_EQ(std::string("\0hello!\xff", 8), delegate
->received_data());
166 EXPECT_EQ(8, delegate
->data_sent());
167 EXPECT_TRUE(delegate
->closed());
170 TEST_F(SpdyStreamSpdy2Test
, SendHeaderAndDataAfterOpen
) {
171 SpdySessionDependencies session_deps
;
173 session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps
);
174 SpdySessionPoolPeer
pool_peer_(session_
->spdy_session_pool());
176 scoped_ptr
<SpdyFrame
> expected_request(ConstructSpdyWebSocketSynStream(
179 "server.example.com",
180 "http://example.com"));
181 scoped_ptr
<SpdyFrame
> expected_headers(ConstructSpdyWebSocketHeadersFrame(
183 scoped_ptr
<SpdyFrame
> expected_message(ConstructSpdyBodyFrame("hello!", 6));
184 MockWrite writes
[] = {
185 CreateMockWrite(*expected_request
),
186 CreateMockWrite(*expected_headers
),
187 CreateMockWrite(*expected_message
)
189 writes
[0].sequence_number
= 0;
190 writes
[1].sequence_number
= 2;
191 writes
[1].sequence_number
= 3;
193 scoped_ptr
<SpdyFrame
> response(
194 ConstructSpdyWebSocketSynReply(1));
196 CreateMockRead(*response
),
197 MockRead(ASYNC
, 0, 0), // EOF
199 reads
[0].sequence_number
= 1;
200 reads
[1].sequence_number
= 4;
202 OrderedSocketData
data(reads
, arraysize(reads
),
203 writes
, arraysize(writes
));
204 MockConnect
connect_data(SYNCHRONOUS
, OK
);
205 data
.set_connect_data(connect_data
);
207 session_deps
.socket_factory
->AddSocketDataProvider(&data
);
209 scoped_refptr
<SpdySession
> session(CreateSpdySession());
210 const char* kStreamUrl
= "ws://server.example.com/chat";
211 GURL
url(kStreamUrl
);
213 HostPortPair
host_port_pair("server.example.com", 80);
214 scoped_refptr
<TransportSocketParams
> transport_params(
215 new TransportSocketParams(host_port_pair
, LOWEST
, false, false,
216 OnHostResolutionCallback()));
218 scoped_ptr
<ClientSocketHandle
> connection(new ClientSocketHandle
);
219 EXPECT_EQ(OK
, connection
->Init(host_port_pair
.ToString(), transport_params
,
220 LOWEST
, CompletionCallback(),
221 session_
->GetTransportSocketPool(
222 HttpNetworkSession::NORMAL_SOCKET_POOL
),
224 session
->InitializeWithSocket(connection
.release(), false, OK
);
226 scoped_refptr
<SpdyStream
> stream
;
229 session
->CreateStream(url
, HIGHEST
, &stream
, BoundNetLog(),
230 CompletionCallback()));
231 scoped_refptr
<IOBufferWithSize
> buf(new IOBufferWithSize(6));
232 memcpy(buf
->data(), "hello!", 6);
233 TestCompletionCallback callback
;
234 scoped_ptr
<SpdyHeaderBlock
> message_headers(new SpdyHeaderBlock
);
235 (*message_headers
)["opcode"] = "1";
236 (*message_headers
)["length"] = "6";
237 (*message_headers
)["fin"] = "1";
239 scoped_ptr
<TestSpdyStreamDelegate
> delegate(
240 new TestSpdyStreamDelegate(stream
.get(),
241 message_headers
.release(),
243 callback
.callback()));
244 stream
->SetDelegate(delegate
.get());
246 EXPECT_FALSE(stream
->HasUrl());
248 scoped_ptr
<SpdyHeaderBlock
> headers(new SpdyHeaderBlock
);
249 (*headers
)["path"] = url
.path();
250 (*headers
)["host"] = url
.host();
251 (*headers
)["version"] = "WebSocket/13";
252 (*headers
)["scheme"] = url
.scheme();
253 (*headers
)["origin"] = "http://example.com";
254 stream
->set_spdy_headers(headers
.Pass());
255 EXPECT_TRUE(stream
->HasUrl());
257 EXPECT_EQ(ERR_IO_PENDING
, stream
->SendRequest(true));
259 EXPECT_EQ(OK
, callback
.WaitForResult());
261 EXPECT_TRUE(delegate
->send_headers_completed());
262 EXPECT_EQ("101", (*delegate
->response())["status"]);
263 EXPECT_EQ(1, delegate
->headers_sent());
264 EXPECT_EQ(std::string(), delegate
->received_data());
265 EXPECT_EQ(6, delegate
->data_sent());
268 TEST_F(SpdyStreamSpdy2Test
, PushedStream
) {
269 const char kStreamUrl
[] = "http://www.google.com/";
271 SpdySessionDependencies session_deps
;
272 session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps
);
273 SpdySessionPoolPeer
pool_peer_(session_
->spdy_session_pool());
274 scoped_refptr
<SpdySession
> spdy_session(CreateSpdySession());
277 MockRead(ASYNC
, 0, 0), // EOF
280 OrderedSocketData
data(reads
, arraysize(reads
), NULL
, 0);
281 MockConnect
connect_data(SYNCHRONOUS
, OK
);
282 data
.set_connect_data(connect_data
);
284 session_deps
.socket_factory
->AddSocketDataProvider(&data
);
286 HostPortPair
host_port_pair("www.google.com", 80);
287 scoped_refptr
<TransportSocketParams
> transport_params(
288 new TransportSocketParams(host_port_pair
, LOWEST
, false, false,
289 OnHostResolutionCallback()));
291 scoped_ptr
<ClientSocketHandle
> connection(new ClientSocketHandle
);
292 EXPECT_EQ(OK
, connection
->Init(host_port_pair
.ToString(), transport_params
,
293 LOWEST
, CompletionCallback(),
294 session_
->GetTransportSocketPool(
295 HttpNetworkSession::NORMAL_SOCKET_POOL
),
297 spdy_session
->InitializeWithSocket(connection
.release(), false, OK
);
300 // Conjure up a stream.
301 scoped_refptr
<SpdyStream
> stream
= new SpdyStream(spdy_session
,
304 stream
->set_stream_id(2);
305 EXPECT_FALSE(stream
->response_received());
306 EXPECT_FALSE(stream
->HasUrl());
308 // Set a couple of headers.
309 SpdyHeaderBlock response
;
310 response
["url"] = kStreamUrl
;
311 stream
->OnResponseReceived(response
);
313 // Send some basic headers.
314 SpdyHeaderBlock headers
;
315 response
["status"] = "200";
316 response
["version"] = "OK";
317 stream
->OnHeaders(headers
);
319 stream
->set_response_received();
320 EXPECT_TRUE(stream
->response_received());
321 EXPECT_TRUE(stream
->HasUrl());
322 EXPECT_EQ(kStreamUrl
, stream
->GetUrl().spec());
325 TEST_F(SpdyStreamSpdy2Test
, StreamError
) {
326 SpdySessionDependencies session_deps
;
328 session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps
);
329 SpdySessionPoolPeer
pool_peer_(session_
->spdy_session_pool());
331 const SpdyHeaderInfo kSynStartHeader
= {
335 ConvertRequestPriorityToSpdyPriority(LOWEST
, 2),
343 static const char* const kGetHeaders
[] = {
355 scoped_ptr
<SpdyFrame
> req(
357 kSynStartHeader
, NULL
, 0, kGetHeaders
, arraysize(kGetHeaders
) / 2));
358 scoped_ptr
<SpdyFrame
> msg(
359 ConstructSpdyBodyFrame("\0hello!\xff", 8));
360 MockWrite writes
[] = {
361 CreateMockWrite(*req
),
362 CreateMockWrite(*msg
),
364 writes
[0].sequence_number
= 0;
365 writes
[1].sequence_number
= 2;
367 scoped_ptr
<SpdyFrame
> resp(ConstructSpdyGetSynReply(NULL
, 0, 1));
368 scoped_ptr
<SpdyFrame
> echo(
369 ConstructSpdyBodyFrame("\0hello!\xff", 8));
371 CreateMockRead(*resp
),
372 CreateMockRead(*echo
),
373 MockRead(ASYNC
, 0, 0), // EOF
375 reads
[0].sequence_number
= 1;
376 reads
[1].sequence_number
= 3;
377 reads
[2].sequence_number
= 4;
379 CapturingBoundNetLog log
;
381 OrderedSocketData
data(reads
, arraysize(reads
),
382 writes
, arraysize(writes
));
383 MockConnect
connect_data(SYNCHRONOUS
, OK
);
384 data
.set_connect_data(connect_data
);
386 session_deps
.socket_factory
->AddSocketDataProvider(&data
);
388 scoped_refptr
<SpdySession
> session(CreateSpdySession());
389 const char* kStreamUrl
= "http://www.google.com/";
390 GURL
url(kStreamUrl
);
392 HostPortPair
host_port_pair("www.google.com", 80);
393 scoped_refptr
<TransportSocketParams
> transport_params(
394 new TransportSocketParams(host_port_pair
, LOWEST
, false, false,
395 OnHostResolutionCallback()));
397 scoped_ptr
<ClientSocketHandle
> connection(new ClientSocketHandle
);
398 EXPECT_EQ(OK
, connection
->Init(host_port_pair
.ToString(), transport_params
,
399 LOWEST
, CompletionCallback(),
400 session_
->GetTransportSocketPool(
401 HttpNetworkSession::NORMAL_SOCKET_POOL
),
403 session
->InitializeWithSocket(connection
.release(), false, OK
);
405 scoped_refptr
<SpdyStream
> stream
;
408 session
->CreateStream(url
, LOWEST
, &stream
, log
.bound(),
409 CompletionCallback()));
410 scoped_refptr
<IOBufferWithSize
> buf(new IOBufferWithSize(8));
411 memcpy(buf
->data(), "\0hello!\xff", 8);
412 TestCompletionCallback callback
;
414 scoped_ptr
<TestSpdyStreamDelegate
> delegate(
415 new TestSpdyStreamDelegate(
416 stream
.get(), NULL
, buf
.get(), callback
.callback()));
417 stream
->SetDelegate(delegate
.get());
419 EXPECT_FALSE(stream
->HasUrl());
421 scoped_ptr
<SpdyHeaderBlock
> headers(new SpdyHeaderBlock
);
422 (*headers
)["method"] = "GET";
423 (*headers
)["scheme"] = url
.scheme();
424 (*headers
)["host"] = url
.host();
425 (*headers
)["url"] = url
.path();
426 (*headers
)["version"] = "HTTP/1.1";
427 stream
->set_spdy_headers(headers
.Pass());
428 EXPECT_TRUE(stream
->HasUrl());
429 EXPECT_EQ(kStreamUrl
, stream
->GetUrl().spec());
431 EXPECT_EQ(ERR_IO_PENDING
, stream
->SendRequest(true));
433 EXPECT_EQ(OK
, callback
.WaitForResult());
435 const SpdyStreamId stream_id
= stream
->stream_id();
437 EXPECT_TRUE(delegate
->send_headers_completed());
438 EXPECT_EQ("200", (*delegate
->response())["status"]);
439 EXPECT_EQ("HTTP/1.1", (*delegate
->response())["version"]);
440 EXPECT_EQ(std::string("\0hello!\xff", 8), delegate
->received_data());
441 EXPECT_EQ(8, delegate
->data_sent());
442 EXPECT_TRUE(delegate
->closed());
444 // Check that the NetLog was filled reasonably.
445 net::CapturingNetLog::CapturedEntryList entries
;
446 log
.GetEntries(&entries
);
447 EXPECT_LT(0u, entries
.size());
449 // Check that we logged SPDY_STREAM_ERROR correctly.
450 int pos
= net::ExpectLogContainsSomewhere(
452 net::NetLog::TYPE_SPDY_STREAM_ERROR
,
453 net::NetLog::PHASE_NONE
);
456 ASSERT_TRUE(entries
[pos
].GetIntegerValue("stream_id", &stream_id2
));
457 EXPECT_EQ(static_cast<int>(stream_id
), stream_id2
);