1 // Copyright (c) 2011 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.
4
5 #include "base/memory/ref_counted.h"
6 #include "net/spdy/spdy_stream.h"
7 #include "net/spdy/spdy_http_utils.h"
8 #include "net/spdy/spdy_session.h"
9 #include "net/spdy/spdy_test_util.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11
12 namespace net {
13
14 // TODO(ukai): factor out common part with spdy_http_stream_unittest.cc
15 class SpdySessionPoolPeer {
16 public:
SpdySessionPoolPeer(SpdySessionPool * pool)17 explicit SpdySessionPoolPeer(SpdySessionPool* pool)
18 : pool_(pool) {}
19
RemoveSpdySession(const scoped_refptr<SpdySession> & session)20 void RemoveSpdySession(const scoped_refptr<SpdySession>& session) {
21 pool_->Remove(session);
22 }
23
24 private:
25 SpdySessionPool* const pool_;
26
27 DISALLOW_COPY_AND_ASSIGN(SpdySessionPoolPeer);
28 };
29
30 namespace {
31
32 class TestSpdyStreamDelegate : public SpdyStream::Delegate {
33 public:
TestSpdyStreamDelegate(SpdyStream * stream,IOBufferWithSize * buf,CompletionCallback * callback)34 TestSpdyStreamDelegate(SpdyStream* stream,
35 IOBufferWithSize* buf,
36 CompletionCallback* callback)
37 : stream_(stream),
38 buf_(buf),
39 callback_(callback),
40 send_headers_completed_(false),
41 response_(new spdy::SpdyHeaderBlock),
42 data_sent_(0),
43 closed_(false) {}
~TestSpdyStreamDelegate()44 virtual ~TestSpdyStreamDelegate() {}
45
OnSendHeadersComplete(int status)46 virtual bool OnSendHeadersComplete(int status) {
47 send_headers_completed_ = true;
48 return true;
49 }
OnSendBody()50 virtual int OnSendBody() {
51 ADD_FAILURE() << "OnSendBody should not be called";
52 return ERR_UNEXPECTED;
53 }
OnSendBodyComplete(int,bool *)54 virtual int OnSendBodyComplete(int /*status*/, bool* /*eof*/) {
55 ADD_FAILURE() << "OnSendBodyComplete should not be called";
56 return ERR_UNEXPECTED;
57 }
58
OnResponseReceived(const spdy::SpdyHeaderBlock & response,base::Time response_time,int status)59 virtual int OnResponseReceived(const spdy::SpdyHeaderBlock& response,
60 base::Time response_time,
61 int status) {
62 EXPECT_TRUE(send_headers_completed_);
63 *response_ = response;
64 if (buf_) {
65 EXPECT_EQ(ERR_IO_PENDING,
66 stream_->WriteStreamData(buf_.get(), buf_->size(),
67 spdy::DATA_FLAG_NONE));
68 }
69 return status;
70 }
OnDataReceived(const char * buffer,int bytes)71 virtual void OnDataReceived(const char* buffer, int bytes) {
72 received_data_ += std::string(buffer, bytes);
73 }
OnDataSent(int length)74 virtual void OnDataSent(int length) {
75 data_sent_ += length;
76 }
OnClose(int status)77 virtual void OnClose(int status) {
78 closed_ = true;
79 CompletionCallback* callback = callback_;
80 callback_ = NULL;
81 callback->Run(OK);
82 }
set_chunk_callback(net::ChunkCallback *)83 virtual void set_chunk_callback(net::ChunkCallback *) {}
84
send_headers_completed() const85 bool send_headers_completed() const { return send_headers_completed_; }
response() const86 const linked_ptr<spdy::SpdyHeaderBlock>& response() const {
87 return response_;
88 }
received_data() const89 const std::string& received_data() const { return received_data_; }
data_sent() const90 int data_sent() const { return data_sent_; }
closed() const91 bool closed() const { return closed_; }
92
93 private:
94 SpdyStream* stream_;
95 scoped_refptr<IOBufferWithSize> buf_;
96 CompletionCallback* callback_;
97 bool send_headers_completed_;
98 linked_ptr<spdy::SpdyHeaderBlock> response_;
99 std::string received_data_;
100 int data_sent_;
101 bool closed_;
102 };
103
ConstructSpdyBodyFrame(const char * data,int length)104 spdy::SpdyFrame* ConstructSpdyBodyFrame(const char* data, int length) {
105 spdy::SpdyFramer framer;
106 return framer.CreateDataFrame(1, data, length, spdy::DATA_FLAG_NONE);
107 }
108
109 } // anonymous namespace
110
111 class SpdyStreamTest : public testing::Test {
112 protected:
SpdyStreamTest()113 SpdyStreamTest() {
114 }
115
CreateSpdySession()116 scoped_refptr<SpdySession> CreateSpdySession() {
117 spdy::SpdyFramer::set_enable_compression_default(false);
118 HostPortPair host_port_pair("www.google.com", 80);
119 HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
120 scoped_refptr<SpdySession> session(
121 session_->spdy_session_pool()->Get(pair, BoundNetLog()));
122 return session;
123 }
124
TearDown()125 virtual void TearDown() {
126 MessageLoop::current()->RunAllPending();
127 }
128
129 scoped_refptr<HttpNetworkSession> session_;
130 };
131
TEST_F(SpdyStreamTest,SendDataAfterOpen)132 TEST_F(SpdyStreamTest, SendDataAfterOpen) {
133 SpdySessionDependencies session_deps;
134
135 session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps);
136 SpdySessionPoolPeer pool_peer_(session_->spdy_session_pool());
137
138 const SpdyHeaderInfo kSynStartHeader = {
139 spdy::SYN_STREAM,
140 1,
141 0,
142 net::ConvertRequestPriorityToSpdyPriority(LOWEST),
143 spdy::CONTROL_FLAG_NONE,
144 false,
145 spdy::INVALID,
146 NULL,
147 0,
148 spdy::DATA_FLAG_NONE
149 };
150 static const char* const kGetHeaders[] = {
151 "method",
152 "GET",
153 "scheme",
154 "http",
155 "host",
156 "www.google.com",
157 "path",
158 "/",
159 "version",
160 "HTTP/1.1",
161 };
162 scoped_ptr<spdy::SpdyFrame> req(
163 ConstructSpdyPacket(
164 kSynStartHeader, NULL, 0, kGetHeaders, arraysize(kGetHeaders) / 2));
165 scoped_ptr<spdy::SpdyFrame> msg(
166 ConstructSpdyBodyFrame("\0hello!\xff", 8));
167 MockWrite writes[] = {
168 CreateMockWrite(*req),
169 CreateMockWrite(*msg),
170 };
171 writes[0].sequence_number = 0;
172 writes[1].sequence_number = 2;
173
174 scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
175 scoped_ptr<spdy::SpdyFrame> echo(
176 ConstructSpdyBodyFrame("\0hello!\xff", 8));
177 MockRead reads[] = {
178 CreateMockRead(*resp),
179 CreateMockRead(*echo),
180 MockRead(true, 0, 0), // EOF
181 };
182 reads[0].sequence_number = 1;
183 reads[1].sequence_number = 3;
184 reads[2].sequence_number = 4;
185
186 scoped_refptr<OrderedSocketData> data(
187 new OrderedSocketData(reads, arraysize(reads),
188 writes, arraysize(writes)));
189 MockConnect connect_data(false, OK);
190 data->set_connect_data(connect_data);
191
192 session_deps.socket_factory->AddSocketDataProvider(data.get());
193 SpdySession::SetSSLMode(false);
194
195 scoped_refptr<SpdySession> session(CreateSpdySession());
196 const char* kStreamUrl = "http://www.google.com/";
197 GURL url(kStreamUrl);
198
199 HostPortPair host_port_pair("www.google.com", 80);
200 scoped_refptr<TransportSocketParams> transport_params(
201 new TransportSocketParams(host_port_pair, LOWEST, GURL(), false, false));
202
203 scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
204 EXPECT_EQ(OK,
205 connection->Init(host_port_pair.ToString(),
206 transport_params,
207 LOWEST,
208 NULL,
209 session_->transport_socket_pool(),
210 BoundNetLog()));
211 session->InitializeWithSocket(connection.release(), false, OK);
212
213 scoped_refptr<SpdyStream> stream;
214 ASSERT_EQ(
215 OK,
216 session->CreateStream(url, LOWEST, &stream, BoundNetLog(), NULL));
217 scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(8));
218 memcpy(buf->data(), "\0hello!\xff", 8);
219 TestCompletionCallback callback;
220
221 scoped_ptr<TestSpdyStreamDelegate> delegate(
222 new TestSpdyStreamDelegate(stream.get(), buf.get(), &callback));
223 stream->SetDelegate(delegate.get());
224
225 EXPECT_FALSE(stream->HasUrl());
226
227 linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock);
228 (*headers)["method"] = "GET";
229 (*headers)["scheme"] = url.scheme();
230 (*headers)["host"] = url.host();
231 (*headers)["path"] = url.path();
232 (*headers)["version"] = "HTTP/1.1";
233 stream->set_spdy_headers(headers);
234 EXPECT_TRUE(stream->HasUrl());
235 EXPECT_EQ(kStreamUrl, stream->GetUrl().spec());
236
237 EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(true));
238
239 EXPECT_EQ(OK, callback.WaitForResult());
240
241 EXPECT_TRUE(delegate->send_headers_completed());
242 EXPECT_EQ("200", (*delegate->response())["status"]);
243 EXPECT_EQ("HTTP/1.1", (*delegate->response())["version"]);
244 EXPECT_EQ(std::string("\0hello!\xff", 8), delegate->received_data());
245 EXPECT_EQ(8, delegate->data_sent());
246 EXPECT_TRUE(delegate->closed());
247 }
248
TEST_F(SpdyStreamTest,PushedStream)249 TEST_F(SpdyStreamTest, PushedStream) {
250 const char kStreamUrl[] = "http://www.google.com/";
251
252 SpdySessionDependencies session_deps;
253 session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps);
254 SpdySessionPoolPeer pool_peer_(session_->spdy_session_pool());
255 scoped_refptr<SpdySession> spdy_session(CreateSpdySession());
256 BoundNetLog net_log;
257
258 // Conjure up a stream.
259 scoped_refptr<SpdyStream> stream = new SpdyStream(spdy_session,
260 2,
261 true,
262 net_log);
263 EXPECT_FALSE(stream->response_received());
264 EXPECT_FALSE(stream->HasUrl());
265
266 // Set a couple of headers.
267 spdy::SpdyHeaderBlock response;
268 response["url"] = kStreamUrl;
269 stream->OnResponseReceived(response);
270
271 // Send some basic headers.
272 spdy::SpdyHeaderBlock headers;
273 response["status"] = "200";
274 response["version"] = "OK";
275 stream->OnHeaders(headers);
276
277 stream->set_response_received();
278 EXPECT_TRUE(stream->response_received());
279 EXPECT_TRUE(stream->HasUrl());
280 EXPECT_EQ(kStreamUrl, stream->GetUrl().spec());
281 }
282
283
284 } // namespace net
285