spdy_stream_unittest.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2010 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 "net/spdy/spdy_stream.h"
6#include "base/ref_counted.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 bool OnSendBodyComplete(int status) {
55    ADD_FAILURE() << "OnSendBodyComplete should not be called";
56    return true;
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
84  bool send_headers_completed() const { return send_headers_completed_; }
85  const linked_ptr<spdy::SpdyHeaderBlock>& response() const {
86    return response_;
87  }
88  const std::string& received_data() const { return received_data_; }
89  int data_sent() const { return data_sent_; }
90  bool closed() const {  return closed_; }
91
92 private:
93  SpdyStream* stream_;
94  scoped_refptr<IOBufferWithSize> buf_;
95  CompletionCallback* callback_;
96  bool send_headers_completed_;
97  linked_ptr<spdy::SpdyHeaderBlock> response_;
98  std::string received_data_;
99  int data_sent_;
100  bool closed_;
101};
102
103spdy::SpdyFrame* ConstructSpdyBodyFrame(const char* data, int length) {
104  spdy::SpdyFramer framer;
105  return framer.CreateDataFrame(1, data, length, spdy::DATA_FLAG_NONE);
106}
107
108}  // anonymous namespace
109
110class SpdyStreamTest : public testing::Test {
111 protected:
112  SpdyStreamTest() {
113  }
114
115  scoped_refptr<SpdySession> CreateSpdySession() {
116    spdy::SpdyFramer::set_enable_compression_default(false);
117    HostPortPair host_port_pair("www.google.com", 80);
118    HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
119    scoped_refptr<SpdySession> session(
120        session_->spdy_session_pool()->Get(pair,
121                                           session_->mutable_spdy_settings(),
122                                           BoundNetLog()));
123    return session;
124  }
125
126  virtual void TearDown() {
127    MessageLoop::current()->RunAllPending();
128  }
129
130  scoped_refptr<HttpNetworkSession> session_;
131};
132
133TEST_F(SpdyStreamTest, SendDataAfterOpen) {
134  SpdySessionDependencies session_deps;
135
136  session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps);
137  SpdySessionPoolPeer pool_peer_(session_->spdy_session_pool());
138
139  const SpdyHeaderInfo kSynStartHeader = {
140    spdy::SYN_STREAM,
141    1,
142    0,
143    net::ConvertRequestPriorityToSpdyPriority(LOWEST),
144    spdy::CONTROL_FLAG_NONE,
145    false,
146    spdy::INVALID,
147    NULL,
148    0,
149    spdy::DATA_FLAG_NONE
150  };
151  static const char* const kGetHeaders[] = {
152    "method",
153    "GET",
154    "scheme",
155    "http",
156    "host",
157    "www.google.com",
158    "path",
159    "/",
160    "version",
161    "HTTP/1.1",
162  };
163  scoped_ptr<spdy::SpdyFrame> req(
164      ConstructSpdyPacket(
165          kSynStartHeader, NULL, 0, kGetHeaders, arraysize(kGetHeaders) / 2));
166  scoped_ptr<spdy::SpdyFrame> msg(
167      ConstructSpdyBodyFrame("\0hello!\xff", 8));
168  MockWrite writes[] = {
169    CreateMockWrite(*req),
170    CreateMockWrite(*msg),
171  };
172  writes[0].sequence_number = 0;
173  writes[1].sequence_number = 2;
174
175  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
176  scoped_ptr<spdy::SpdyFrame> echo(
177      ConstructSpdyBodyFrame("\0hello!\xff", 8));
178  MockRead reads[] = {
179    CreateMockRead(*resp),
180    CreateMockRead(*echo),
181    MockRead(true, 0, 0), // EOF
182  };
183  reads[0].sequence_number = 1;
184  reads[1].sequence_number = 3;
185  reads[2].sequence_number = 4;
186
187  scoped_refptr<OrderedSocketData> data(
188      new OrderedSocketData(reads, arraysize(reads),
189                            writes, arraysize(writes)));
190  MockConnect connect_data(false, OK);
191  data->set_connect_data(connect_data);
192
193  session_deps.socket_factory->AddSocketDataProvider(data.get());
194  SpdySession::SetSSLMode(false);
195
196  scoped_refptr<SpdySession> session(CreateSpdySession());
197  const char* kStreamUrl = "http://www.google.com/";
198  GURL url(kStreamUrl);
199
200  HostPortPair host_port_pair("www.google.com", 80);
201  scoped_refptr<TCPSocketParams> tcp_params(
202      new TCPSocketParams(host_port_pair, LOWEST, GURL(), false));
203
204  scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
205  EXPECT_EQ(OK,
206            connection->Init(host_port_pair.ToString(), tcp_params, LOWEST,
207                             NULL, session_->tcp_socket_pool(), BoundNetLog()));
208  session->InitializeWithSocket(connection.release(), false, OK);
209
210  scoped_refptr<SpdyStream> stream;
211  ASSERT_EQ(
212      OK,
213      session->CreateStream(url, LOWEST, &stream, BoundNetLog(), NULL));
214  scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(8));
215  memcpy(buf->data(), "\0hello!\xff", 8);
216  TestCompletionCallback callback;
217
218  scoped_ptr<TestSpdyStreamDelegate> delegate(
219      new TestSpdyStreamDelegate(stream.get(), buf.get(), &callback));
220  stream->SetDelegate(delegate.get());
221
222  EXPECT_FALSE(stream->HasUrl());
223
224  linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock);
225  (*headers)["method"] = "GET";
226  (*headers)["scheme"] = url.scheme();
227  (*headers)["host"] = url.host();
228  (*headers)["path"] = url.path();
229  (*headers)["version"] = "HTTP/1.1";
230  stream->set_spdy_headers(headers);
231  EXPECT_TRUE(stream->HasUrl());
232  EXPECT_EQ(kStreamUrl, stream->GetUrl().spec());
233
234  EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(true));
235
236  EXPECT_EQ(OK, callback.WaitForResult());
237
238  EXPECT_TRUE(delegate->send_headers_completed());
239  EXPECT_EQ("200", (*delegate->response())["status"]);
240  EXPECT_EQ("HTTP/1.1", (*delegate->response())["version"]);
241  EXPECT_EQ(std::string("\0hello!\xff", 8), delegate->received_data());
242  EXPECT_EQ(8, delegate->data_sent());
243  EXPECT_TRUE(delegate->closed());
244}
245
246TEST_F(SpdyStreamTest, PushedStream) {
247  const char kStreamUrl[] = "http://www.google.com/";
248
249  SpdySessionDependencies session_deps;
250  session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps);
251  SpdySessionPoolPeer pool_peer_(session_->spdy_session_pool());
252  scoped_refptr<SpdySession> spdy_session(CreateSpdySession());
253  BoundNetLog net_log;
254
255  // Conjure up a stream.
256  scoped_refptr<SpdyStream> stream = new SpdyStream(spdy_session,
257                                                    2,
258                                                    true,
259                                                    net_log);
260  EXPECT_FALSE(stream->response_received());
261  EXPECT_FALSE(stream->HasUrl());
262
263  // Set a couple of headers.
264  spdy::SpdyHeaderBlock response;
265  response["url"] = kStreamUrl;
266  stream->OnResponseReceived(response);
267
268  // Send some basic headers.
269  spdy::SpdyHeaderBlock headers;
270  response["status"] = "200";
271  response["version"] = "OK";
272  stream->OnHeaders(headers);
273
274  stream->set_response_received();
275  EXPECT_TRUE(stream->response_received());
276  EXPECT_TRUE(stream->HasUrl());
277  EXPECT_EQ(kStreamUrl, stream->GetUrl().spec());
278}
279
280
281}  // namespace net
282