Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(393)

Side by Side Diff: net/url_request/sdch_dictionary_fetcher_unittest.cc

Issue 1880283002: Disallow redirects in SDCH dictionary fetches. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/url_request/sdch_dictionary_fetcher.h" 5 #include "net/url_request/sdch_dictionary_fetcher.h"
6 6
7 #include <string> 7 #include <string>
8 #include <utility> 8 #include <utility>
9 #include <vector> 9 #include <vector>
10 10
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/callback.h" 12 #include "base/callback.h"
13 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "base/macros.h" 14 #include "base/macros.h"
15 #include "base/run_loop.h" 15 #include "base/run_loop.h"
16 #include "base/strings/stringprintf.h" 16 #include "base/strings/stringprintf.h"
17 #include "base/thread_task_runner_handle.h" 17 #include "base/thread_task_runner_handle.h"
18 #include "net/base/load_flags.h" 18 #include "net/base/load_flags.h"
19 #include "net/base/sdch_manager.h" 19 #include "net/base/sdch_manager.h"
20 #include "net/http/http_response_headers.h" 20 #include "net/http/http_response_headers.h"
21 #include "net/url_request/url_request_data_job.h" 21 #include "net/url_request/url_request_data_job.h"
22 #include "net/url_request/url_request_filter.h" 22 #include "net/url_request/url_request_filter.h"
23 #include "net/url_request/url_request_interceptor.h" 23 #include "net/url_request/url_request_interceptor.h"
24 #include "net/url_request/url_request_redirect_job.h"
24 #include "net/url_request/url_request_test_util.h" 25 #include "net/url_request/url_request_test_util.h"
25 #include "testing/gtest/include/gtest/gtest.h" 26 #include "testing/gtest/include/gtest/gtest.h"
26 27
27 namespace net { 28 namespace net {
28 29
29 namespace { 30 namespace {
30 31
31 const char kSampleBufferContext[] = "This is a sample buffer."; 32 const char kSampleBufferContext[] = "This is a sample buffer.";
32 const char kTestDomain[] = "top.domain.test"; 33 const char kTestDomain1[] = "top.domain.test";
34 const char kTestDomain2[] = "top2.domain.test";
33 35
34 class URLRequestSpecifiedResponseJob : public URLRequestSimpleJob { 36 class URLRequestSpecifiedResponseJob : public URLRequestSimpleJob {
35 public: 37 public:
36 // Called on destruction with load flags used for this request. 38 // Called on destruction with load flags used for this request.
37 typedef base::Callback<void(int)> DestructionCallback; 39 typedef base::Callback<void(int)> DestructionCallback;
38 40
39 URLRequestSpecifiedResponseJob( 41 URLRequestSpecifiedResponseJob(
40 URLRequest* request, 42 URLRequest* request,
41 NetworkDelegate* network_delegate, 43 NetworkDelegate* network_delegate,
42 const HttpResponseInfo& response_info_to_return, 44 const HttpResponseInfo& response_info_to_return,
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
75 return OK; 77 return OK;
76 } 78 }
77 79
78 const HttpResponseInfo response_info_to_return_; 80 const HttpResponseInfo response_info_to_return_;
79 int last_load_flags_seen_; 81 int last_load_flags_seen_;
80 const DestructionCallback destruction_callback_; 82 const DestructionCallback destruction_callback_;
81 83
82 DISALLOW_COPY_AND_ASSIGN(URLRequestSpecifiedResponseJob); 84 DISALLOW_COPY_AND_ASSIGN(URLRequestSpecifiedResponseJob);
83 }; 85 };
84 86
85 class SpecifiedResponseJobInterceptor : public URLRequestInterceptor { 87 // Wrap URLRequestRedirectJob in a destruction callback.
88 class TestURLRequestRedirectJob : public URLRequestRedirectJob {
86 public: 89 public:
87 // A callback to be called whenever a URLRequestSpecifiedResponseJob is 90 TestURLRequestRedirectJob(URLRequest* request,
88 // created or destroyed. The first argument will be the change in number of 91 NetworkDelegate* network_delegate,
89 // jobs (i.e. +1 for created, -1 for destroyed). 92 const GURL& redirect_destination,
90 // The second argument will be undefined if the job is being created, 93 ResponseCode response_code,
91 // and will contain the load flags passed to the request the 94 const std::string& redirect_reason,
92 // job was created for if the job is being destroyed. 95 base::Closure destruction_callback)
96 : URLRequestRedirectJob(request,
97 network_delegate,
98 redirect_destination,
99 response_code,
100 redirect_reason),
101 destruction_callback_(destruction_callback) {}
102 ~TestURLRequestRedirectJob() override { destruction_callback_.Run(); }
103
104 private:
105 const base::Closure destruction_callback_;
106 };
107
108 static const char* redirect_signal = "/redirect/";
109 class SDCHTestRequestInterceptor : public URLRequestInterceptor {
110 public:
111 // A callback to be called whenever a URLRequestJob child of this
112 // interceptor is created or destroyed. The first argument will be the
113 // change in number of jobs (i.e. +1 for created, -1 for destroyed).
114 // The second argument will be undefined if the job is being created
115 // or a redirect job is being destroyed, and (for non-redirect job
116 // destruction) will contain the load flags passed to the request the
117 // job was created for.
93 typedef base::Callback<void(int outstanding_job_delta, 118 typedef base::Callback<void(int outstanding_job_delta,
94 int destruction_load_flags)> LifecycleCallback; 119 int destruction_load_flags)> LifecycleCallback;
95 120
96 // |*info| will be returned from all child URLRequestSpecifiedResponseJobs. 121 // |*info| will be returned from all child URLRequestSpecifiedResponseJobs.
97 // Note that: a) this pointer is shared with the caller, and the caller must 122 // Note that: a) this pointer is shared with the caller, and the caller must
98 // guarantee that |*info| outlives the SpecifiedResponseJobInterceptor, and 123 // guarantee that |*info| outlives the SDCHTestRequestInterceptor, and
99 // b) |*info| is mutable, and changes to should propagate to 124 // b) |*info| is mutable, and changes to should propagate to
100 // URLRequestSpecifiedResponseJobs created after any change. 125 // URLRequestSpecifiedResponseJobs created after any change.
101 SpecifiedResponseJobInterceptor(HttpResponseInfo* http_response_info, 126 SDCHTestRequestInterceptor(HttpResponseInfo* http_response_info,
102 const LifecycleCallback& lifecycle_callback) 127 const LifecycleCallback& lifecycle_callback)
103 : http_response_info_(http_response_info), 128 : http_response_info_(http_response_info),
104 lifecycle_callback_(lifecycle_callback) { 129 lifecycle_callback_(lifecycle_callback) {
105 DCHECK(!lifecycle_callback_.is_null()); 130 DCHECK(!lifecycle_callback_.is_null());
106 } 131 }
107 ~SpecifiedResponseJobInterceptor() override {} 132 ~SDCHTestRequestInterceptor() override {}
108 133
109 URLRequestJob* MaybeInterceptRequest( 134 URLRequestJob* MaybeInterceptRequest(
110 URLRequest* request, 135 URLRequest* request,
111 NetworkDelegate* network_delegate) const override { 136 NetworkDelegate* network_delegate) const override {
112 lifecycle_callback_.Run(1, 0); 137 lifecycle_callback_.Run(1, 0);
113 138
139 std::string path = request->url().path();
140 if (path.substr(0, strlen(redirect_signal)) == redirect_signal) {
141 return new TestURLRequestRedirectJob(
142 request, network_delegate, GURL(path.substr(strlen(redirect_signal))),
143 URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, "testing",
144 base::Bind(lifecycle_callback_, -1, 0));
145 }
146
114 return new URLRequestSpecifiedResponseJob( 147 return new URLRequestSpecifiedResponseJob(
115 request, network_delegate, *http_response_info_, 148 request, network_delegate, *http_response_info_,
116 base::Bind(lifecycle_callback_, -1)); 149 base::Bind(lifecycle_callback_, -1));
117 } 150 }
118 151
119 // The caller must ensure that both |*http_response_info| and the 152 // The caller must ensure that both |*http_response_info| and the
120 // callback remain valid for the lifetime of the 153 // callback remain valid for the lifetime of the
121 // SpecifiedResponseJobInterceptor (i.e. until Unregister() is called). 154 // SDCHTestRequestInterceptor (i.e. until Unregister() is called).
122 static void RegisterWithFilter(HttpResponseInfo* http_response_info, 155 static void RegisterWithFilter(HttpResponseInfo* http_response_info,
123 const LifecycleCallback& lifecycle_callback) { 156 const LifecycleCallback& lifecycle_callback) {
124 scoped_ptr<SpecifiedResponseJobInterceptor> interceptor( 157 URLRequestFilter::GetInstance()->AddHostnameInterceptor(
125 new SpecifiedResponseJobInterceptor(http_response_info, 158 "http", kTestDomain1,
126 lifecycle_callback)); 159 scoped_ptr<URLRequestInterceptor>(new SDCHTestRequestInterceptor(
160 http_response_info, lifecycle_callback)));
127 161
128 URLRequestFilter::GetInstance()->AddHostnameInterceptor( 162 URLRequestFilter::GetInstance()->AddHostnameInterceptor(
129 "http", kTestDomain, std::move(interceptor)); 163 "https", kTestDomain1,
164 scoped_ptr<URLRequestInterceptor>(new SDCHTestRequestInterceptor(
165 http_response_info, lifecycle_callback)));
166
167 URLRequestFilter::GetInstance()->AddHostnameInterceptor(
168 "http", kTestDomain2,
169 scoped_ptr<URLRequestInterceptor>(new SDCHTestRequestInterceptor(
170 http_response_info, lifecycle_callback)));
171
172 URLRequestFilter::GetInstance()->AddHostnameInterceptor(
173 "https", kTestDomain2,
174 scoped_ptr<URLRequestInterceptor>(new SDCHTestRequestInterceptor(
175 http_response_info, lifecycle_callback)));
130 } 176 }
131 177
132 static void Unregister() { 178 static void Unregister() {
133 URLRequestFilter::GetInstance()->RemoveHostnameHandler("http", kTestDomain); 179 URLRequestFilter::GetInstance()->RemoveHostnameHandler("http",
180 kTestDomain1);
181 URLRequestFilter::GetInstance()->RemoveHostnameHandler("https",
182 kTestDomain1);
183 URLRequestFilter::GetInstance()->RemoveHostnameHandler("http",
184 kTestDomain2);
185 URLRequestFilter::GetInstance()->RemoveHostnameHandler("https",
186 kTestDomain2);
134 } 187 }
135 188
136 private: 189 private:
137 HttpResponseInfo* http_response_info_; 190 HttpResponseInfo* http_response_info_;
138 LifecycleCallback lifecycle_callback_; 191 LifecycleCallback lifecycle_callback_;
139 DISALLOW_COPY_AND_ASSIGN(SpecifiedResponseJobInterceptor); 192 DISALLOW_COPY_AND_ASSIGN(SDCHTestRequestInterceptor);
140 }; 193 };
141 194
142 // Local test infrastructure 195 // Local test infrastructure
143 // * URLRequestSpecifiedResponseJob: A URLRequestJob that returns 196 // * URLRequestSpecifiedResponseJob: A URLRequestJob that returns
144 // a different but derivable response for each URL (used for all 197 // a different but derivable response for each URL (used for all
145 // url requests in this file). This class is initialized with 198 // url requests in this file). This class is initialized with
146 // the HttpResponseInfo to return (if any), as well as a callback 199 // the HttpResponseInfo to return (if any), as well as a callback
147 // that is called when the class is destroyed. That callback 200 // that is called when the class is destroyed. That callback
148 // takes as arguemnt the load flags used for the request the 201 // takes as arguemnt the load flags used for the request the
149 // job was created for. 202 // job was created for.
150 // * SpecifiedResponseJobInterceptor: This class is a 203 // * SDCHTestRequestInterceptor: This class is a
151 // URLRequestInterceptor that generates the class above. It is constructed 204 // URLRequestInterceptor that generates either the class above or an
205 // instance of URLRequestRedirectJob (if the first component of the path
206 // is "redirect"). It is constructed
152 // with a pointer to the (mutable) resposne info that should be 207 // with a pointer to the (mutable) resposne info that should be
153 // returned from the URLRequestSpecifiedResponseJob children, as well as 208 // returned from constructed URLRequestSpecifiedResponseJobs, as well as
154 // a callback that is run when URLRequestSpecifiedResponseJobs are 209 // a callback that is run when URLRequestSpecifiedResponseJobs are
155 // created or destroyed. 210 // created or destroyed.
156 // * SdchDictionaryFetcherTest: This class registers the above interceptor, 211 // * SdchDictionaryFetcherTest: This class registers the above interceptor,
157 // tracks the number of jobs requested and the subset of those 212 // tracks the number of jobs requested and the subset of those
158 // that are still outstanding. It exports an interface to wait until there 213 // that are still outstanding. It exports an interface to wait until there
159 // are no jobs outstanding. It shares an HttpResponseInfo structure 214 // are no jobs outstanding. It shares an HttpResponseInfo structure
160 // with the SpecifiedResponseJobInterceptor to control the response 215 // with the SDCHTestRequestInterceptor to control the response
161 // information returned by the jbos. 216 // information returned by the jbos.
162 // The standard pattern for tests is to schedule a dictionary fetch, wait 217 // The standard pattern for tests is to schedule a dictionary fetch, wait
163 // for no jobs outstanding, then test that the fetch results are as expected. 218 // for no jobs outstanding, then test that the fetch results are as expected.
164 219
165 class SdchDictionaryFetcherTest : public ::testing::Test { 220 class SdchDictionaryFetcherTest : public ::testing::Test {
166 public: 221 public:
167 struct DictionaryAdditions { 222 struct DictionaryAdditions {
168 DictionaryAdditions(const std::string& dictionary_text, 223 DictionaryAdditions(const std::string& dictionary_text,
169 const GURL& dictionary_url) 224 const GURL& dictionary_url)
170 : dictionary_text(dictionary_text), dictionary_url(dictionary_url) {} 225 : dictionary_text(dictionary_text), dictionary_url(dictionary_url) {}
171 226
172 std::string dictionary_text; 227 std::string dictionary_text;
173 GURL dictionary_url; 228 GURL dictionary_url;
174 }; 229 };
175 230
176 SdchDictionaryFetcherTest() 231 SdchDictionaryFetcherTest()
177 : jobs_requested_(0), 232 : jobs_requested_(0),
178 jobs_outstanding_(0), 233 jobs_outstanding_(0),
179 last_load_flags_seen_(LOAD_NORMAL), 234 last_load_flags_seen_(LOAD_NORMAL),
180 context_(new TestURLRequestContext), 235 context_(new TestURLRequestContext),
181 fetcher_(new SdchDictionaryFetcher(context_.get())), 236 fetcher_(new SdchDictionaryFetcher(context_.get())),
182 factory_(this) { 237 factory_(this) {
183 response_info_to_return_.request_time = base::Time::Now(); 238 response_info_to_return_.request_time = base::Time::Now();
184 response_info_to_return_.response_time = base::Time::Now(); 239 response_info_to_return_.response_time = base::Time::Now();
185 SpecifiedResponseJobInterceptor::RegisterWithFilter( 240 SDCHTestRequestInterceptor::RegisterWithFilter(
186 &response_info_to_return_, 241 &response_info_to_return_,
187 base::Bind(&SdchDictionaryFetcherTest::OnNumberJobsChanged, 242 base::Bind(&SdchDictionaryFetcherTest::OnNumberJobsChanged,
188 factory_.GetWeakPtr())); 243 factory_.GetWeakPtr()));
189 } 244 }
190 245
191 ~SdchDictionaryFetcherTest() override { 246 ~SdchDictionaryFetcherTest() override {
192 SpecifiedResponseJobInterceptor::Unregister(); 247 SDCHTestRequestInterceptor::Unregister();
193 } 248 }
194 249
195 void OnDictionaryFetched(const std::string& dictionary_text, 250 void OnDictionaryFetched(const std::string& dictionary_text,
196 const GURL& dictionary_url, 251 const GURL& dictionary_url,
197 const BoundNetLog& net_log, 252 const BoundNetLog& net_log,
198 bool was_from_cache) { 253 bool was_from_cache) {
199 dictionary_additions_.push_back( 254 dictionary_additions_.push_back(
200 DictionaryAdditions(dictionary_text, dictionary_url)); 255 DictionaryAdditions(dictionary_text, dictionary_url));
201 } 256 }
202 257
203 // Return (in |*out|) all dictionary additions since the last time 258 // Return (in |*out|) all dictionary additions since the last time
204 // this function was called. 259 // this function was called.
205 void GetDictionaryAdditions(std::vector<DictionaryAdditions>* out) { 260 void GetDictionaryAdditions(std::vector<DictionaryAdditions>* out) {
206 out->swap(dictionary_additions_); 261 out->swap(dictionary_additions_);
207 dictionary_additions_.clear(); 262 dictionary_additions_.clear();
208 } 263 }
209 264
210 SdchDictionaryFetcher* fetcher() { return fetcher_.get(); } 265 SdchDictionaryFetcher* fetcher() { return fetcher_.get(); }
211 266
212 // May not be called outside the SetUp()/TearDown() interval. 267 // May not be called outside the SetUp()/TearDown() interval.
213 int jobs_requested() const { return jobs_requested_; } 268 int jobs_requested() const { return jobs_requested_; }
214 269
215 GURL PathToGurl(const char* path) const { 270 GURL PathToGurl(const char* path) const {
216 std::string gurl_string("http://"); 271 std::string gurl_string("http://");
217 gurl_string += kTestDomain; 272 gurl_string += kTestDomain1;
218 gurl_string += "/"; 273 gurl_string += "/";
219 gurl_string += path; 274 gurl_string += path;
220 return GURL(gurl_string); 275 return GURL(gurl_string);
221 } 276 }
222 277
223 // Block until there are no outstanding URLRequestSpecifiedResponseJobs. 278 // Block until there are no outstanding URLRequestSpecifiedResponseJobs.
224 void WaitForNoJobs() { 279 void WaitForNoJobs() {
225 // A job may be started after the previous one was destroyed, with a brief 280 // A job may be started after the previous one was destroyed, with a brief
226 // period of 0 jobs in between, so may have to start the run loop multiple 281 // period of 0 jobs in between, so may have to start the run loop multiple
227 // times. 282 // times.
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after
427 EXPECT_EQ(1, jobs_requested()); 482 EXPECT_EQ(1, jobs_requested());
428 483
429 fetcher()->Cancel(); 484 fetcher()->Cancel();
430 WaitForNoJobs(); 485 WaitForNoJobs();
431 EXPECT_TRUE(fetcher()->Schedule(dictionary_url, GetDefaultCallback())); 486 EXPECT_TRUE(fetcher()->Schedule(dictionary_url, GetDefaultCallback()));
432 487
433 WaitForNoJobs(); 488 WaitForNoJobs();
434 EXPECT_EQ(2, jobs_requested()); 489 EXPECT_EQ(2, jobs_requested());
435 } 490 }
436 491
492 TEST_F(SdchDictionaryFetcherTest, InOriginRedirect) {
493 GURL dictionary_url(PathToGurl("dictionary"));
494 GURL local_redirect_url(dictionary_url.GetWithEmptyPath().spec() +
495 "redirect/" + dictionary_url.spec());
496 EXPECT_TRUE(fetcher()->Schedule(local_redirect_url, GetDefaultCallback()));
497 WaitForNoJobs();
498
499 // Both the redirect job and the job retrieving the actual dictionary
500 // should have been executed.
501 EXPECT_EQ(2, jobs_requested());
502 std::vector<DictionaryAdditions> additions;
503 GetDictionaryAdditions(&additions);
504 EXPECT_EQ(1u, additions.size());
505 }
506
507 TEST_F(SdchDictionaryFetcherTest, CrossSecurityRedirect) {
508 std::string dictionary_url_spec("https://" + std::string(kTestDomain1) +
509 "/dictionary");
510 GURL local_redirect_url("http://" + std::string(kTestDomain1) + "/redirect/" +
511 dictionary_url_spec);
512
513 EXPECT_TRUE(fetcher()->Schedule(local_redirect_url, GetDefaultCallback()));
514 WaitForNoJobs();
515
516 // The fetcher should have refused the redirect job.
517 EXPECT_EQ(1, jobs_requested());
518 std::vector<DictionaryAdditions> additions;
519 GetDictionaryAdditions(&additions);
520 // And there should be no dictionaries added.
521 EXPECT_EQ(0u, additions.size());
522 }
523
524 TEST_F(SdchDictionaryFetcherTest, CrossSiteRedirect) {
525 std::string dictionary_url_spec("https://" + std::string(kTestDomain1) +
526 "/dictionary");
527 GURL local_redirect_url("https://" + std::string(kTestDomain2) +
528 "/redirect/" + dictionary_url_spec);
529
530 EXPECT_TRUE(fetcher()->Schedule(local_redirect_url, GetDefaultCallback()));
531 WaitForNoJobs();
532
533 // The fetcher should have refused the redirect job.
534 EXPECT_EQ(1, jobs_requested());
535 std::vector<DictionaryAdditions> additions;
536 GetDictionaryAdditions(&additions);
537 // And there should be no dictionaries added.
538 EXPECT_EQ(0u, additions.size());
539 }
540
541 TEST_F(SdchDictionaryFetcherTest, CrossSiteSecurityRedirect) {
542 std::string dictionary_url_spec("https://" + std::string(kTestDomain1) +
543 "/dictionary");
544 GURL local_redirect_url("http://" + std::string(kTestDomain2) + "/redirect/" +
545 dictionary_url_spec);
546
547 EXPECT_TRUE(fetcher()->Schedule(local_redirect_url, GetDefaultCallback()));
548 WaitForNoJobs();
549
550 // The fetcher should have refused the redirect job.
551 EXPECT_EQ(1, jobs_requested());
552 std::vector<DictionaryAdditions> additions;
553 GetDictionaryAdditions(&additions);
554 // And there should be no dictionaries added.
555 EXPECT_EQ(0u, additions.size());
556 }
557
437 } // namespace 558 } // namespace
438 559
439 } // namespace net 560 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698