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 "chrome/browser/prerender/prerender_resource_handler.h"
6#include "content/common/resource_response.h"
7#include "net/http/http_response_headers.h"
8#include "net/url_request/url_request_test_util.h"
9#include "testing/gtest/include/gtest/gtest.h"
10
11namespace prerender {
12
13namespace {
14
15class MockResourceHandler : public ResourceHandler {
16 public:
17  MockResourceHandler() {}
18
19  virtual bool OnUploadProgress(int request_id,
20                                uint64 position,
21                                uint64 size) {
22    return true;
23  }
24
25  virtual bool OnRequestRedirected(int request_id, const GURL& url,
26                                   ResourceResponse* response,
27                                   bool* defer) {
28    *defer = false;
29    return true;
30  }
31
32  virtual bool OnResponseStarted(int request_id,
33                                 ResourceResponse* response) {
34    return true;
35  }
36
37  virtual bool OnWillStart(int request_id, const GURL& url, bool* defer) {
38    *defer = false;
39    return true;
40  }
41
42  virtual bool OnWillRead(int request_id,
43                          net::IOBuffer** buf,
44                          int* buf_size,
45                          int min_size) {
46    return true;
47  }
48
49  virtual bool OnReadCompleted(int request_id, int* bytes_read) {
50    return true;
51  }
52
53  virtual bool OnResponseCompleted(int request_id,
54                                   const net::URLRequestStatus& status,
55                                   const std::string& security_info) {
56    return true;
57  }
58
59  virtual void OnRequestClosed() {
60  }
61
62  virtual void OnDataDownloaded(int request_id, int bytes_downloaded) {}
63};
64
65// HttpResponseHeaders expects the raw input for it's constructor
66// to be a NUL ('\0') separated string for each line. This is a little
67// difficult to do for string literals, so this helper function accepts
68// newline-separated string literals and does the substitution. The
69// returned object is expected to be deleted by the caller.
70net::HttpResponseHeaders* CreateResponseHeaders(
71    const char* newline_separated_headers) {
72  std::string headers(newline_separated_headers);
73  std::string::iterator i = headers.begin();
74  std::string::iterator end = headers.end();
75  while (i != end) {
76    if (*i == '\n')
77      *i = '\0';
78    ++i;
79  }
80  return new net::HttpResponseHeaders(headers);
81}
82
83}  // namespace
84
85class PrerenderResourceHandlerTest : public testing::Test {
86 protected:
87  PrerenderResourceHandlerTest()
88      : loop_(MessageLoop::TYPE_IO),
89        ui_thread_(BrowserThread::UI, &loop_),
90        io_thread_(BrowserThread::IO, &loop_),
91        test_url_request_(GURL("http://www.referrer.com"),
92                          &test_url_request_delegate_),
93        ALLOW_THIS_IN_INITIALIZER_LIST(
94            pre_handler_(new PrerenderResourceHandler(
95                test_url_request_,
96                new MockResourceHandler(),
97                NewCallback(
98                    this,
99                    &PrerenderResourceHandlerTest::SetLastHandledURL)))),
100        default_url_("http://www.prerender.com") {
101  }
102
103  virtual ~PrerenderResourceHandlerTest() {
104    // When a ResourceHandler's reference count drops to 0, it is not
105    // deleted immediately. Instead, a task is posted to the IO thread's
106    // message loop to delete it.
107    // So, drop the reference count to 0 and run the message loop once
108    // to ensure that all resources are cleaned up before the test exits.
109    pre_handler_ = NULL;
110    loop_.RunAllPending();
111  }
112
113  void SetLastHandledURL(const std::pair<int, int>& child_route_id_pair,
114                         const GURL& url, const std::vector<GURL>& alias_urls,
115                         const GURL& referrer, bool make_pending) {
116    last_handled_url_ = url;
117    alias_urls_ = alias_urls;
118    referrer_ = referrer;
119  }
120
121  // Common logic shared by many of the tests
122  void StartPrerendering(const std::string& mime_type,
123                         const char* headers) {
124    int request_id = 1;
125    bool defer = false;
126    EXPECT_TRUE(pre_handler_->OnWillStart(request_id, default_url_, &defer));
127    EXPECT_FALSE(defer);
128    scoped_refptr<ResourceResponse> response(new ResourceResponse);
129    response->response_head.mime_type = mime_type;
130    response->response_head.headers = CreateResponseHeaders(headers);
131    EXPECT_TRUE(last_handled_url_.is_empty());
132
133    // Start the response. If it is able to prerender, a task will
134    // be posted to the UI thread and |SetLastHandledURL| will be called.
135    EXPECT_TRUE(pre_handler_->OnResponseStarted(request_id, response));
136    loop_.RunAllPending();
137  }
138
139  // Test whether a given URL is part of alias_urls_.
140  bool ContainsAliasURL(const GURL& url) {
141    return std::find(alias_urls_.begin(), alias_urls_.end(), url)
142        != alias_urls_.end();
143  }
144
145  // Must be initialized before |test_url_request_|.
146  MessageLoop loop_;
147  BrowserThread ui_thread_;
148  BrowserThread io_thread_;
149
150  TestDelegate test_url_request_delegate_;
151  TestURLRequest test_url_request_;
152
153  scoped_refptr<PrerenderResourceHandler> pre_handler_;
154  GURL last_handled_url_;
155  GURL default_url_;
156  std::vector<GURL> alias_urls_;
157  GURL referrer_;
158};
159
160namespace {
161
162TEST_F(PrerenderResourceHandlerTest, NoOp) {
163}
164
165// Tests that a valid HTML resource will correctly get diverted
166// to the PrerenderManager.
167TEST_F(PrerenderResourceHandlerTest, Prerender) {
168  StartPrerendering("text/html",
169                    "HTTP/1.1 200 OK\n");
170  EXPECT_EQ(default_url_, last_handled_url_);
171}
172
173static const int kRequestId = 1;
174
175// Tests that the final request in a redirect chain will
176// get diverted to the PrerenderManager.
177TEST_F(PrerenderResourceHandlerTest, PrerenderRedirect) {
178  GURL url_redirect("http://www.redirect.com");
179  bool defer = false;
180  EXPECT_TRUE(pre_handler_->OnWillStart(kRequestId, default_url_, &defer));
181  EXPECT_FALSE(defer);
182  EXPECT_TRUE(pre_handler_->OnRequestRedirected(kRequestId,
183                                                url_redirect,
184                                                NULL,
185                                                &defer));
186  EXPECT_FALSE(defer);
187  scoped_refptr<ResourceResponse> response(new ResourceResponse);
188  response->response_head.mime_type = "text/html";
189  response->response_head.headers = CreateResponseHeaders(
190      "HTTP/1.1 200 OK\n");
191  EXPECT_TRUE(pre_handler_->OnResponseStarted(kRequestId, response));
192  EXPECT_TRUE(last_handled_url_.is_empty());
193  loop_.RunAllPending();
194  EXPECT_EQ(url_redirect, last_handled_url_);
195  EXPECT_EQ(true, ContainsAliasURL(url_redirect));
196  EXPECT_EQ(true, ContainsAliasURL(default_url_));
197  EXPECT_EQ(2, static_cast<int>(alias_urls_.size()));
198}
199
200// Tests that https requests will not be prerendered.
201TEST_F(PrerenderResourceHandlerTest, PrerenderHttps) {
202  GURL url_https("https://www.google.com");
203  bool defer = false;
204  EXPECT_FALSE(pre_handler_->OnWillStart(kRequestId, url_https, &defer));
205  EXPECT_FALSE(defer);
206}
207
208TEST_F(PrerenderResourceHandlerTest, PrerenderRedirectToHttps) {
209  bool defer = false;
210  EXPECT_TRUE(pre_handler_->OnWillStart(kRequestId, default_url_, &defer));
211  EXPECT_FALSE(defer);
212  GURL url_https("https://www.google.com");
213  EXPECT_FALSE(pre_handler_->OnRequestRedirected(kRequestId,
214                                                 url_https,
215                                                 NULL,
216                                                 &defer));
217  EXPECT_FALSE(defer);
218}
219
220}  // namespace
221
222}  // namespace prerender
223