1// Copyright 2014 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/command_line.h"
6#include "base/strings/string_util.h"
7#include "chrome/browser/browser_process.h"
8#include "chrome/browser/policy/cloud/policy_header_service_factory.h"
9#include "chrome/browser/profiles/profile.h"
10#include "chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.h"
11#include "chrome/browser/ui/browser.h"
12#include "chrome/test/base/in_process_browser_test.h"
13#include "chrome/test/base/ui_test_utils.h"
14#include "components/policy/core/common/cloud/cloud_policy_constants.h"
15#include "components/policy/core/common/cloud/policy_header_io_helper.h"
16#include "components/policy/core/common/cloud/policy_header_service.h"
17#include "components/policy/core/common/policy_switches.h"
18#include "content/public/browser/resource_dispatcher_host.h"
19#include "net/http/http_request_headers.h"
20#include "net/test/embedded_test_server/embedded_test_server.h"
21#include "net/test/embedded_test_server/http_request.h"
22#include "net/test/embedded_test_server/http_response.h"
23#include "net/url_request/url_request.h"
24
25using content::ResourceType;
26
27namespace {
28static const char kTestPolicyHeader[] = "test_header";
29static const char kServerRedirectUrl[] = "/server-redirect";
30
31scoped_ptr<net::test_server::HttpResponse> HandleTestRequest(
32    const net::test_server::HttpRequest& request) {
33  if (StartsWithASCII(request.relative_url, kServerRedirectUrl, true)) {
34    // Extract the target URL and redirect there.
35    size_t query_string_pos = request.relative_url.find('?');
36    std::string redirect_target =
37        request.relative_url.substr(query_string_pos + 1);
38
39    scoped_ptr<net::test_server::BasicHttpResponse> http_response(
40        new net::test_server::BasicHttpResponse);
41    http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
42    http_response->AddCustomHeader("Location", redirect_target);
43    return http_response.PassAs<net::test_server::HttpResponse>();
44  } else {
45    scoped_ptr<net::test_server::BasicHttpResponse> http_response(
46        new net::test_server::BasicHttpResponse);
47    http_response->set_code(net::HTTP_OK);
48    http_response->set_content("Success");
49    return http_response.PassAs<net::test_server::HttpResponse>();
50  }
51}
52
53class TestDispatcherHostDelegate : public ChromeResourceDispatcherHostDelegate {
54 public:
55  explicit TestDispatcherHostDelegate(
56      prerender::PrerenderTracker* prerender_tracker)
57      : ChromeResourceDispatcherHostDelegate(prerender_tracker) {
58  }
59
60  virtual ~TestDispatcherHostDelegate() {}
61
62  virtual void RequestBeginning(
63      net::URLRequest* request,
64      content::ResourceContext* resource_context,
65      content::AppCacheService* appcache_service,
66      ResourceType resource_type,
67      ScopedVector<content::ResourceThrottle>* throttles) OVERRIDE {
68    ChromeResourceDispatcherHostDelegate::RequestBeginning(
69        request,
70        resource_context,
71        appcache_service,
72        resource_type,
73        throttles);
74    request_headers_.MergeFrom(request->extra_request_headers());
75  }
76
77  virtual void OnRequestRedirected(
78      const GURL& redirect_url,
79      net::URLRequest* request,
80      content::ResourceContext* resource_context,
81      content::ResourceResponse* response) OVERRIDE {
82    ChromeResourceDispatcherHostDelegate::OnRequestRedirected(
83        redirect_url,
84        request,
85        resource_context,
86        response);
87    request_headers_.MergeFrom(request->extra_request_headers());
88  }
89
90  net::HttpRequestHeaders request_headers_;
91
92 private:
93  DISALLOW_COPY_AND_ASSIGN(TestDispatcherHostDelegate);
94};
95
96}  // namespace
97
98class ChromeResourceDispatcherHostDelegateBrowserTest :
99    public InProcessBrowserTest {
100 public:
101  ChromeResourceDispatcherHostDelegateBrowserTest() {}
102
103  virtual void SetUpOnMainThread() OVERRIDE {
104    InProcessBrowserTest::SetUpOnMainThread();
105    // Hook navigations with our delegate.
106    dispatcher_host_delegate_.reset(new TestDispatcherHostDelegate(
107        g_browser_process->prerender_tracker()));
108    content::ResourceDispatcherHost::Get()->SetDelegate(
109        dispatcher_host_delegate_.get());
110
111    embedded_test_server()->RegisterRequestHandler(
112        base::Bind(&HandleTestRequest));
113    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
114    // Tell chrome that this is our DM server.
115    dm_url_ = embedded_test_server()->GetURL("/DeviceManagement");
116
117    // At this point, the Profile is already initialized and it's too
118    // late to set the DMServer URL via command line flags, so directly
119    // inject it to the PolicyHeaderIOHelper.
120    policy::PolicyHeaderService* policy_header_service =
121        policy::PolicyHeaderServiceFactory::GetForBrowserContext(
122            browser()->profile());
123    std::vector<policy::PolicyHeaderIOHelper*> helpers =
124        policy_header_service->GetHelpersForTest();
125    for (std::vector<policy::PolicyHeaderIOHelper*>::const_iterator it =
126             helpers.begin();
127         it != helpers.end(); ++it) {
128      (*it)->SetServerURLForTest(dm_url_.spec());
129      (*it)->UpdateHeader(kTestPolicyHeader);
130    }
131  }
132
133  virtual void TearDownOnMainThread() OVERRIDE {
134    content::ResourceDispatcherHost::Get()->SetDelegate(NULL);
135    dispatcher_host_delegate_.reset();
136  }
137
138 protected:
139  // The fake URL for DMServer we are using.
140  GURL dm_url_;
141  scoped_ptr<TestDispatcherHostDelegate> dispatcher_host_delegate_;
142
143 private:
144  DISALLOW_COPY_AND_ASSIGN(ChromeResourceDispatcherHostDelegateBrowserTest);
145};
146
147
148IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest,
149                       NoPolicyHeader) {
150  // When fetching non-DMServer URLs, we should not add a policy header to the
151  // request.
152  DCHECK(!embedded_test_server()->base_url().spec().empty());
153  ui_test_utils::NavigateToURL(browser(), embedded_test_server()->base_url());
154  ASSERT_FALSE(dispatcher_host_delegate_->request_headers_.HasHeader(
155      policy::kChromePolicyHeader));
156}
157
158IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest,
159                       PolicyHeader) {
160  // When fetching a DMServer URL, we should add a policy header to the
161  // request.
162  ui_test_utils::NavigateToURL(browser(), dm_url_);
163  std::string value;
164  ASSERT_TRUE(dispatcher_host_delegate_->request_headers_.GetHeader(
165      policy::kChromePolicyHeader, &value));
166  ASSERT_EQ(kTestPolicyHeader, value);
167}
168
169IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest,
170                       PolicyHeaderForRedirect) {
171
172  // Build up a URL that results in a redirect to the DMServer URL to make
173  // sure the policy header is still added.
174  std::string redirect_url;
175  redirect_url += kServerRedirectUrl;
176  redirect_url += "?";
177  redirect_url += dm_url_.spec();
178  ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL(
179      redirect_url));
180  std::string value;
181  ASSERT_TRUE(dispatcher_host_delegate_->request_headers_.GetHeader(
182      policy::kChromePolicyHeader, &value));
183  ASSERT_EQ(kTestPolicyHeader, value);
184}
185