1// Copyright (c) 2013 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/url_request/url_request_http_job.h"
6
7#include <cstddef>
8
9#include "base/compiler_specific.h"
10#include "base/memory/ref_counted.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/run_loop.h"
13#include "net/base/auth.h"
14#include "net/base/request_priority.h"
15#include "net/http/http_transaction_factory.h"
16#include "net/http/http_transaction_test_util.h"
17#include "net/socket/socket_test_util.h"
18#include "net/url_request/url_request.h"
19#include "net/url_request/url_request_status.h"
20#include "net/url_request/url_request_test_util.h"
21#include "net/websockets/websocket_handshake_stream_base.h"
22#include "testing/gmock/include/gmock/gmock.h"
23#include "testing/gtest/include/gtest/gtest.h"
24#include "url/gurl.h"
25
26namespace net {
27
28namespace {
29
30using ::testing::Return;
31
32// Inherit from URLRequestHttpJob to expose the priority and some
33// other hidden functions.
34class TestURLRequestHttpJob : public URLRequestHttpJob {
35 public:
36  explicit TestURLRequestHttpJob(URLRequest* request)
37      : URLRequestHttpJob(request, request->context()->network_delegate(),
38                          request->context()->http_user_agent_settings()) {}
39
40  using URLRequestHttpJob::SetPriority;
41  using URLRequestHttpJob::Start;
42  using URLRequestHttpJob::Kill;
43  using URLRequestHttpJob::priority;
44
45 protected:
46  virtual ~TestURLRequestHttpJob() {}
47};
48
49class URLRequestHttpJobTest : public ::testing::Test {
50 protected:
51  URLRequestHttpJobTest()
52      : req_(context_.CreateRequest(GURL("http://www.example.com"),
53                                    DEFAULT_PRIORITY,
54                                    &delegate_,
55                                    NULL)) {
56    context_.set_http_transaction_factory(&network_layer_);
57  }
58
59  bool TransactionAcceptsSdchEncoding() {
60    base::WeakPtr<MockNetworkTransaction> transaction(
61        network_layer_.last_transaction());
62    EXPECT_TRUE(transaction);
63    if (!transaction) return false;
64
65    const HttpRequestInfo* request_info = transaction->request();
66    EXPECT_TRUE(request_info);
67    if (!request_info) return false;
68
69    std::string encoding_headers;
70    bool get_success = request_info->extra_headers.GetHeader(
71        "Accept-Encoding", &encoding_headers);
72    EXPECT_TRUE(get_success);
73    if (!get_success) return false;
74
75    // This check isn't wrapped with EXPECT* macros because different
76    // results from this function may be expected in different tests.
77    std::vector<std::string> tokens;
78    size_t num_tokens = Tokenize(encoding_headers, ", ", &tokens);
79    for (size_t i = 0; i < num_tokens; i++) {
80      if (!base::strncasecmp(tokens[i].data(), "sdch", tokens[i].length()))
81        return true;
82    }
83    return false;
84  }
85
86  void EnableSdch() {
87    context_.SetSdchManager(scoped_ptr<SdchManager>(new SdchManager).Pass());
88  }
89
90  MockNetworkLayer network_layer_;
91  TestURLRequestContext context_;
92  TestDelegate delegate_;
93  scoped_ptr<URLRequest> req_;
94};
95
96// Make sure that SetPriority actually sets the URLRequestHttpJob's
97// priority, both before and after start.
98TEST_F(URLRequestHttpJobTest, SetPriorityBasic) {
99  scoped_refptr<TestURLRequestHttpJob> job(
100      new TestURLRequestHttpJob(req_.get()));
101  EXPECT_EQ(DEFAULT_PRIORITY, job->priority());
102
103  job->SetPriority(LOWEST);
104  EXPECT_EQ(LOWEST, job->priority());
105
106  job->SetPriority(LOW);
107  EXPECT_EQ(LOW, job->priority());
108
109  job->Start();
110  EXPECT_EQ(LOW, job->priority());
111
112  job->SetPriority(MEDIUM);
113  EXPECT_EQ(MEDIUM, job->priority());
114}
115
116// Make sure that URLRequestHttpJob passes on its priority to its
117// transaction on start.
118TEST_F(URLRequestHttpJobTest, SetTransactionPriorityOnStart) {
119  scoped_refptr<TestURLRequestHttpJob> job(
120      new TestURLRequestHttpJob(req_.get()));
121  job->SetPriority(LOW);
122
123  EXPECT_FALSE(network_layer_.last_transaction());
124
125  job->Start();
126
127  ASSERT_TRUE(network_layer_.last_transaction());
128  EXPECT_EQ(LOW, network_layer_.last_transaction()->priority());
129}
130
131// Make sure that URLRequestHttpJob passes on its priority updates to
132// its transaction.
133TEST_F(URLRequestHttpJobTest, SetTransactionPriority) {
134  scoped_refptr<TestURLRequestHttpJob> job(
135      new TestURLRequestHttpJob(req_.get()));
136  job->SetPriority(LOW);
137  job->Start();
138  ASSERT_TRUE(network_layer_.last_transaction());
139  EXPECT_EQ(LOW, network_layer_.last_transaction()->priority());
140
141  job->SetPriority(HIGHEST);
142  EXPECT_EQ(HIGHEST, network_layer_.last_transaction()->priority());
143}
144
145// Make sure that URLRequestHttpJob passes on its priority updates to
146// newly-created transactions after the first one.
147TEST_F(URLRequestHttpJobTest, SetSubsequentTransactionPriority) {
148  scoped_refptr<TestURLRequestHttpJob> job(
149      new TestURLRequestHttpJob(req_.get()));
150  job->Start();
151
152  job->SetPriority(LOW);
153  ASSERT_TRUE(network_layer_.last_transaction());
154  EXPECT_EQ(LOW, network_layer_.last_transaction()->priority());
155
156  job->Kill();
157  network_layer_.ClearLastTransaction();
158
159  // Creates a second transaction.
160  job->Start();
161  ASSERT_TRUE(network_layer_.last_transaction());
162  EXPECT_EQ(LOW, network_layer_.last_transaction()->priority());
163}
164
165// Confirm we do advertise SDCH encoding in the case of a GET.
166TEST_F(URLRequestHttpJobTest, SdchAdvertisementGet) {
167  EnableSdch();
168  req_->set_method("GET");  // Redundant with default.
169  scoped_refptr<TestURLRequestHttpJob> job(
170      new TestURLRequestHttpJob(req_.get()));
171  job->Start();
172  EXPECT_TRUE(TransactionAcceptsSdchEncoding());
173}
174
175// Confirm we don't advertise SDCH encoding in the case of a POST.
176TEST_F(URLRequestHttpJobTest, SdchAdvertisementPost) {
177  EnableSdch();
178  req_->set_method("POST");
179  scoped_refptr<TestURLRequestHttpJob> job(
180      new TestURLRequestHttpJob(req_.get()));
181  job->Start();
182  EXPECT_FALSE(TransactionAcceptsSdchEncoding());
183}
184
185// This base class just serves to set up some things before the TestURLRequest
186// constructor is called.
187class URLRequestHttpJobWebSocketTestBase : public ::testing::Test {
188 protected:
189  URLRequestHttpJobWebSocketTestBase() : socket_data_(NULL, 0, NULL, 0),
190                                         context_(true) {
191    // A Network Delegate is required for the WebSocketHandshakeStreamBase
192    // object to be passed on to the HttpNetworkTransaction.
193    context_.set_network_delegate(&network_delegate_);
194
195    // Attempting to create real ClientSocketHandles is not going to work out so
196    // well. Set up a fake socket factory.
197    socket_factory_.AddSocketDataProvider(&socket_data_);
198    context_.set_client_socket_factory(&socket_factory_);
199    context_.Init();
200  }
201
202  StaticSocketDataProvider socket_data_;
203  TestNetworkDelegate network_delegate_;
204  MockClientSocketFactory socket_factory_;
205  TestURLRequestContext context_;
206};
207
208class URLRequestHttpJobWebSocketTest
209    : public URLRequestHttpJobWebSocketTestBase {
210 protected:
211  URLRequestHttpJobWebSocketTest()
212      : req_(context_.CreateRequest(GURL("ws://www.example.com"),
213                                    DEFAULT_PRIORITY,
214                                    &delegate_,
215                                    NULL)) {
216    // The TestNetworkDelegate expects a call to NotifyBeforeURLRequest before
217    // anything else happens.
218    GURL url("ws://localhost/");
219    TestCompletionCallback dummy;
220    network_delegate_.NotifyBeforeURLRequest(
221        req_.get(), dummy.callback(), &url);
222  }
223
224  TestDelegate delegate_;
225  scoped_ptr<URLRequest> req_;
226};
227
228class MockCreateHelper : public WebSocketHandshakeStreamBase::CreateHelper {
229 public:
230  // GoogleMock does not appear to play nicely with move-only types like
231  // scoped_ptr, so this forwarding method acts as a workaround.
232  virtual WebSocketHandshakeStreamBase* CreateBasicStream(
233      scoped_ptr<ClientSocketHandle> connection,
234      bool using_proxy) OVERRIDE {
235    // Discard the arguments since we don't need them anyway.
236    return CreateBasicStreamMock();
237  }
238
239  MOCK_METHOD0(CreateBasicStreamMock,
240               WebSocketHandshakeStreamBase*());
241
242  MOCK_METHOD2(CreateSpdyStream,
243               WebSocketHandshakeStreamBase*(const base::WeakPtr<SpdySession>&,
244                                             bool));
245};
246
247class FakeWebSocketHandshakeStream : public WebSocketHandshakeStreamBase {
248 public:
249  FakeWebSocketHandshakeStream() : initialize_stream_was_called_(false) {}
250
251  bool initialize_stream_was_called() const {
252    return initialize_stream_was_called_;
253  }
254
255  // Fake implementation of HttpStreamBase methods.
256  virtual int InitializeStream(const HttpRequestInfo* request_info,
257                               RequestPriority priority,
258                               const BoundNetLog& net_log,
259                               const CompletionCallback& callback) OVERRIDE {
260    initialize_stream_was_called_ = true;
261    return ERR_IO_PENDING;
262  }
263
264  virtual int SendRequest(const HttpRequestHeaders& request_headers,
265                          HttpResponseInfo* response,
266                          const CompletionCallback& callback) OVERRIDE {
267    return ERR_IO_PENDING;
268  }
269
270  virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE {
271    return ERR_IO_PENDING;
272  }
273
274  virtual int ReadResponseBody(IOBuffer* buf,
275                               int buf_len,
276                               const CompletionCallback& callback) OVERRIDE {
277    return ERR_IO_PENDING;
278  }
279
280  virtual void Close(bool not_reusable) OVERRIDE {}
281
282  virtual bool IsResponseBodyComplete() const OVERRIDE { return false; }
283
284  virtual bool CanFindEndOfResponse() const OVERRIDE { return false; }
285
286  virtual bool IsConnectionReused() const OVERRIDE { return false; }
287  virtual void SetConnectionReused() OVERRIDE {}
288
289  virtual bool IsConnectionReusable() const OVERRIDE { return false; }
290
291  virtual int64 GetTotalReceivedBytes() const OVERRIDE { return 0; }
292
293  virtual bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const
294      OVERRIDE {
295    return false;
296  }
297
298  virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE {}
299
300  virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info)
301      OVERRIDE {}
302
303  virtual bool IsSpdyHttpStream() const OVERRIDE { return false; }
304
305  virtual void Drain(HttpNetworkSession* session) OVERRIDE {}
306
307  virtual void SetPriority(RequestPriority priority) OVERRIDE {}
308
309  // Fake implementation of WebSocketHandshakeStreamBase method(s)
310  virtual scoped_ptr<WebSocketStream> Upgrade() OVERRIDE {
311    return scoped_ptr<WebSocketStream>();
312  }
313
314 private:
315  bool initialize_stream_was_called_;
316};
317
318TEST_F(URLRequestHttpJobWebSocketTest, RejectedWithoutCreateHelper) {
319  scoped_refptr<TestURLRequestHttpJob> job(
320      new TestURLRequestHttpJob(req_.get()));
321  job->Start();
322  base::RunLoop().RunUntilIdle();
323  EXPECT_EQ(URLRequestStatus::FAILED, req_->status().status());
324  EXPECT_EQ(ERR_DISALLOWED_URL_SCHEME, req_->status().error());
325}
326
327TEST_F(URLRequestHttpJobWebSocketTest, CreateHelperPassedThrough) {
328  scoped_refptr<TestURLRequestHttpJob> job(
329      new TestURLRequestHttpJob(req_.get()));
330  scoped_ptr<MockCreateHelper> create_helper(
331      new ::testing::StrictMock<MockCreateHelper>());
332  FakeWebSocketHandshakeStream* fake_handshake_stream(
333      new FakeWebSocketHandshakeStream);
334  // Ownership of fake_handshake_stream is transferred when CreateBasicStream()
335  // is called.
336  EXPECT_CALL(*create_helper, CreateBasicStreamMock())
337      .WillOnce(Return(fake_handshake_stream));
338  req_->SetUserData(WebSocketHandshakeStreamBase::CreateHelper::DataKey(),
339                    create_helper.release());
340  req_->SetLoadFlags(LOAD_DISABLE_CACHE);
341  job->Start();
342  base::RunLoop().RunUntilIdle();
343  EXPECT_EQ(URLRequestStatus::IO_PENDING, req_->status().status());
344  EXPECT_TRUE(fake_handshake_stream->initialize_stream_was_called());
345}
346
347}  // namespace
348
349}  // namespace net
350