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
12namespace net {
13
14// TODO(ukai): factor out common part with spdy_http_stream_unittest.cc
15class SpdySessionPoolPeer {
16 public:
17  explicit SpdySessionPoolPeer(SpdySessionPool* pool)
18      : pool_(pool) {}
19
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
30namespace {
31
32class TestSpdyStreamDelegate : public SpdyStream::Delegate {
33 public:
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) {}
44  virtual ~TestSpdyStreamDelegate() {}
45
46  virtual bool OnSendHeadersComplete(int status) {
47    send_headers_completed_ = true;
48    return true;
49  }
50  virtual int OnSendBody() {
51    ADD_FAILURE() << "OnSendBody should not be called";
52    return ERR_UNEXPECTED;
53  }
54  virtual int OnSendBodyComplete(int /*status*/, bool* /*eof*/) {
55    ADD_FAILURE() << "OnSendBodyComplete should not be called";
56    return ERR_UNEXPECTED;
57  }
58
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  }
71  virtual void OnDataReceived(const char* buffer, int bytes) {
72    received_data_ += std::string(buffer, bytes);
73  }
74  virtual void OnDataSent(int length) {
75    data_sent_ += length;
76  }
77  virtual void OnClose(int status) {
78    closed_ = true;
79    CompletionCallback* callback = callback_;
80    callback_ = NULL;
81    callback->Run(OK);
82  }
83  virtual void set_chunk_callback(net::ChunkCallback *) {}
84
85  bool send_headers_completed() const { return send_headers_completed_; }
86  const linked_ptr<spdy::SpdyHeaderBlock>& response() const {
87    return response_;
88  }
89  const std::string& received_data() const { return received_data_; }
90  int data_sent() const { return data_sent_; }
91  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
104spdy::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
111class SpdyStreamTest : public testing::Test {
112 protected:
113  SpdyStreamTest() {
114  }
115
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
125  virtual void TearDown() {
126    MessageLoop::current()->RunAllPending();
127  }
128
129  scoped_refptr<HttpNetworkSession> session_;
130};
131
132TEST_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
249TEST_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