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 "net/spdy/spdy_http_stream.h"
6#include "net/spdy/spdy_session.h"
7#include "net/spdy/spdy_test_util.h"
8#include "testing/gtest/include/gtest/gtest.h"
9
10namespace net {
11
12class SpdyHttpStreamTest : public testing::Test {
13 public:
14  OrderedSocketData* data() { return data_; }
15 protected:
16  SpdyHttpStreamTest() {}
17
18  void EnableCompression(bool enabled) {
19    spdy::SpdyFramer::set_enable_compression_default(enabled);
20  }
21
22  virtual void TearDown() {
23    MessageLoop::current()->RunAllPending();
24  }
25  int InitSession(MockRead* reads, size_t reads_count,
26                  MockWrite* writes, size_t writes_count,
27                  HostPortPair& host_port_pair) {
28    HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
29    data_ = new OrderedSocketData(reads, reads_count, writes, writes_count);
30    session_deps_.socket_factory->AddSocketDataProvider(data_.get());
31    http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
32    session_ = http_session_->spdy_session_pool()->Get(pair, BoundNetLog());
33    transport_params_ = new TransportSocketParams(host_port_pair,
34                                      MEDIUM, GURL(), false, false);
35    TestCompletionCallback callback;
36    scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
37    EXPECT_EQ(ERR_IO_PENDING,
38              connection->Init(host_port_pair.ToString(),
39                                transport_params_,
40                                MEDIUM,
41                                &callback,
42                                http_session_->transport_socket_pool(),
43                                BoundNetLog()));
44    EXPECT_EQ(OK, callback.WaitForResult());
45    return session_->InitializeWithSocket(connection.release(), false, OK);
46  }
47  SpdySessionDependencies session_deps_;
48  scoped_refptr<OrderedSocketData> data_;
49  scoped_refptr<HttpNetworkSession> http_session_;
50  scoped_refptr<SpdySession> session_;
51  scoped_refptr<TransportSocketParams> transport_params_;
52};
53
54TEST_F(SpdyHttpStreamTest, SendRequest) {
55  EnableCompression(false);
56  SpdySession::SetSSLMode(false);
57
58  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
59  MockWrite writes[] = {
60    CreateMockWrite(*req.get(), 1),
61  };
62  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
63  MockRead reads[] = {
64    CreateMockRead(*resp, 2),
65    MockRead(false, 0, 3)  // EOF
66  };
67
68  HostPortPair host_port_pair("www.google.com", 80);
69  HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
70  EXPECT_EQ(OK, InitSession(reads, arraysize(reads), writes, arraysize(writes),
71      host_port_pair));
72
73  HttpRequestInfo request;
74  request.method = "GET";
75  request.url = GURL("http://www.google.com/");
76  TestCompletionCallback callback;
77  HttpResponseInfo response;
78  HttpRequestHeaders headers;
79  BoundNetLog net_log;
80  scoped_ptr<SpdyHttpStream> http_stream(
81      new SpdyHttpStream(session_.get(), true));
82  ASSERT_EQ(
83      OK,
84      http_stream->InitializeStream(&request, net_log, NULL));
85
86  EXPECT_EQ(ERR_IO_PENDING,
87            http_stream->SendRequest(headers, NULL, &response, &callback));
88  EXPECT_TRUE(http_session_->spdy_session_pool()->HasSession(pair));
89
90  // This triggers the MockWrite and read 2
91  callback.WaitForResult();
92
93  // This triggers read 3. The empty read causes the session to shut down.
94  data()->CompleteRead();
95
96  // Because we abandoned the stream, we don't expect to find a session in the
97  // pool anymore.
98  EXPECT_FALSE(http_session_->spdy_session_pool()->HasSession(pair));
99  EXPECT_TRUE(data()->at_read_eof());
100  EXPECT_TRUE(data()->at_write_eof());
101}
102
103TEST_F(SpdyHttpStreamTest, SendChunkedPost) {
104  EnableCompression(false);
105  SpdySession::SetSSLMode(false);
106  UploadDataStream::set_merge_chunks(false);
107
108  scoped_ptr<spdy::SpdyFrame> req(ConstructChunkedSpdyPost(NULL, 0));
109  scoped_ptr<spdy::SpdyFrame> chunk1(ConstructSpdyBodyFrame(1, false));
110  scoped_ptr<spdy::SpdyFrame> chunk2(ConstructSpdyBodyFrame(1, true));
111  MockWrite writes[] = {
112    CreateMockWrite(*req.get(), 1),
113    CreateMockWrite(*chunk1, 2),  // POST upload frames
114    CreateMockWrite(*chunk2, 3),
115  };
116  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyPostSynReply(NULL, 0));
117  MockRead reads[] = {
118    CreateMockRead(*resp, 4),
119    CreateMockRead(*chunk1, 5),
120    CreateMockRead(*chunk2, 5),
121    MockRead(false, 0, 6)  // EOF
122  };
123
124  HostPortPair host_port_pair("www.google.com", 80);
125  HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
126  EXPECT_EQ(OK, InitSession(reads, arraysize(reads), writes, arraysize(writes),
127                            host_port_pair));
128
129  HttpRequestInfo request;
130  request.method = "POST";
131  request.url = GURL("http://www.google.com/");
132  request.upload_data = new UploadData();
133  request.upload_data->set_is_chunked(true);
134  request.upload_data->AppendChunk(kUploadData, kUploadDataSize, false);
135  request.upload_data->AppendChunk(kUploadData, kUploadDataSize, true);
136  TestCompletionCallback callback;
137  HttpResponseInfo response;
138  HttpRequestHeaders headers;
139  BoundNetLog net_log;
140  SpdyHttpStream http_stream(session_.get(), true);
141  ASSERT_EQ(
142      OK,
143      http_stream.InitializeStream(&request, net_log, NULL));
144
145  UploadDataStream* upload_stream =
146      UploadDataStream::Create(request.upload_data, NULL);
147  EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest(
148      headers, upload_stream, &response, &callback));
149  EXPECT_TRUE(http_session_->spdy_session_pool()->HasSession(pair));
150
151  // This triggers the MockWrite and read 2
152  callback.WaitForResult();
153
154  // This triggers read 3. The empty read causes the session to shut down.
155  data()->CompleteRead();
156  MessageLoop::current()->RunAllPending();
157
158  // Because we abandoned the stream, we don't expect to find a session in the
159  // pool anymore.
160  EXPECT_FALSE(http_session_->spdy_session_pool()->HasSession(pair));
161  EXPECT_TRUE(data()->at_read_eof());
162  EXPECT_TRUE(data()->at_write_eof());
163}
164
165// Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
166TEST_F(SpdyHttpStreamTest, SpdyURLTest) {
167  EnableCompression(false);
168  SpdySession::SetSSLMode(false);
169
170  const char * const full_url = "http://www.google.com/foo?query=what#anchor";
171  const char * const base_url = "http://www.google.com/foo?query=what";
172  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(base_url, false, 1, LOWEST));
173  MockWrite writes[] = {
174    CreateMockWrite(*req.get(), 1),
175  };
176  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
177  MockRead reads[] = {
178    CreateMockRead(*resp, 2),
179    MockRead(false, 0, 3)  // EOF
180  };
181
182  HostPortPair host_port_pair("www.google.com", 80);
183  HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
184  EXPECT_EQ(OK, InitSession(reads, arraysize(reads), writes, arraysize(writes),
185      host_port_pair));
186
187  HttpRequestInfo request;
188  request.method = "GET";
189  request.url = GURL(full_url);
190  TestCompletionCallback callback;
191  HttpResponseInfo response;
192  HttpRequestHeaders headers;
193  BoundNetLog net_log;
194  scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
195  ASSERT_EQ(
196      OK,
197      http_stream->InitializeStream(&request, net_log, NULL));
198
199  EXPECT_EQ(ERR_IO_PENDING,
200            http_stream->SendRequest(headers, NULL, &response, &callback));
201
202  spdy::SpdyHeaderBlock* spdy_header =
203    http_stream->stream()->spdy_headers().get();
204  EXPECT_TRUE(spdy_header != NULL);
205  if (spdy_header->find("url") != spdy_header->end())
206    EXPECT_EQ("/foo?query=what", spdy_header->find("url")->second);
207  else
208    FAIL() << "No url is set in spdy_header!";
209
210  // This triggers the MockWrite and read 2
211  callback.WaitForResult();
212
213  // This triggers read 3. The empty read causes the session to shut down.
214  data()->CompleteRead();
215
216  // Because we abandoned the stream, we don't expect to find a session in the
217  // pool anymore.
218  EXPECT_FALSE(http_session_->spdy_session_pool()->HasSession(pair));
219  EXPECT_TRUE(data()->at_read_eof());
220  EXPECT_TRUE(data()->at_write_eof());
221}
222
223// TODO(willchan): Write a longer test for SpdyStream that exercises all
224// methods.
225
226}  // namespace net
227