1 // Copyright 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.
5 #include "chrome/browser/policy/cloud/external_policy_data_fetcher.h"
8 #include "base/bind_helpers.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/sequenced_task_runner.h"
12 #include "base/stl_util.h"
13 #include "net/base/load_flags.h"
14 #include "net/base/net_errors.h"
15 #include "net/url_request/url_fetcher.h"
16 #include "net/url_request/url_request_context_getter.h"
17 #include "net/url_request/url_request_status.h"
23 // Helper that forwards the result of a fetch job from the thread that the
24 // ExternalPolicyDataFetcherBackend runs on to the thread that the
25 // ExternalPolicyDataFetcher which started the job runs on.
26 void ForwardJobFinished(
27 scoped_refptr<base::SequencedTaskRunner> task_runner,
28 const ExternalPolicyDataFetcherBackend::FetchCallback& callback,
29 ExternalPolicyDataFetcher::Job* job,
30 ExternalPolicyDataFetcher::Result result,
31 scoped_ptr<std::string> data) {
32 task_runner->PostTask(FROM_HERE,
33 base::Bind(callback, job, result, base::Passed(&data)));
36 // Helper that forwards a job cancelation confirmation from the thread that the
37 // ExternalPolicyDataFetcherBackend runs on to the thread that the
38 // ExternalPolicyDataFetcher which canceled the job runs on.
39 void ForwardJobCanceled(
40 scoped_refptr<base::SequencedTaskRunner> task_runner,
41 const base::Closure& callback) {
42 task_runner->PostTask(FROM_HERE, callback);
45 // Helper invoked when a job cancelation confirmation has been forwarded to the
46 // thread which canceled the job. The helper itself does nothing. It exists so
47 // that the |job| can be passed as base::Owned(), allowing it to be deleted on
48 // the correct thread and after any pending callbacks for the |job| have been
50 void DoNothing(ExternalPolicyDataFetcher::Job* job) {
55 struct ExternalPolicyDataFetcher::Job {
58 const ExternalPolicyDataFetcherBackend::FetchCallback& callback);
62 const ExternalPolicyDataFetcherBackend::FetchCallback callback;
65 DISALLOW_COPY_AND_ASSIGN(Job);
68 ExternalPolicyDataFetcher::Job::Job(
71 const ExternalPolicyDataFetcherBackend::FetchCallback& callback)
77 ExternalPolicyDataFetcher::ExternalPolicyDataFetcher(
78 scoped_refptr<base::SequencedTaskRunner> task_runner,
79 scoped_refptr<base::SequencedTaskRunner> io_task_runner,
80 const base::WeakPtr<ExternalPolicyDataFetcherBackend>& backend)
81 : task_runner_(task_runner),
82 io_task_runner_(io_task_runner),
87 ExternalPolicyDataFetcher::~ExternalPolicyDataFetcher() {
88 DCHECK(task_runner_->RunsTasksOnCurrentThread());
89 for (JobSet::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
93 ExternalPolicyDataFetcher::Job* ExternalPolicyDataFetcher::StartJob(
96 const FetchCallback& callback) {
97 DCHECK(task_runner_->RunsTasksOnCurrentThread());
100 base::Bind(&ForwardJobFinished,
102 base::Bind(&ExternalPolicyDataFetcher::OnJobFinished,
103 weak_factory_.GetWeakPtr(),
106 io_task_runner_->PostTask(
108 base::Bind(&ExternalPolicyDataFetcherBackend::StartJob, backend_, job));
112 void ExternalPolicyDataFetcher::CancelJob(Job* job) {
113 DCHECK(task_runner_->RunsTasksOnCurrentThread());
114 DCHECK(jobs_.find(job) != jobs_.end());
116 // Post a task that will cancel the |job| in the |backend_|. The |job| is
117 // removed from |jobs_| immediately to indicate that it has been canceled but
118 // is not actually deleted until the cancelation has reached the |backend_|
119 // and a confirmation has been posted back. This ensures that no new job can
120 // be allocated at the same address while an OnJobFinished() callback may
121 // still be pending for the canceled |job|.
122 io_task_runner_->PostTask(
124 base::Bind(&ExternalPolicyDataFetcherBackend::CancelJob,
127 base::Bind(&ForwardJobCanceled,
129 base::Bind(&DoNothing, base::Owned(job)))));
132 void ExternalPolicyDataFetcher::OnJobFinished(const FetchCallback& callback,
135 scoped_ptr<std::string> data) {
136 DCHECK(task_runner_->RunsTasksOnCurrentThread());
137 JobSet::iterator it = jobs_.find(job);
138 if (it == jobs_.end()) {
139 // The |job| has been canceled and removed from |jobs_| already. This can
140 // happen because the |backend_| runs on a different thread and a |job| may
141 // finish before the cancellation has reached that thread.
144 callback.Run(result, data.Pass());
149 ExternalPolicyDataFetcherBackend::ExternalPolicyDataFetcherBackend(
150 scoped_refptr<base::SequencedTaskRunner> io_task_runner,
151 scoped_refptr<net::URLRequestContextGetter> request_context)
152 : io_task_runner_(io_task_runner),
153 request_context_(request_context),
155 weak_factory_(this) {
158 ExternalPolicyDataFetcherBackend::~ExternalPolicyDataFetcherBackend() {
159 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
160 STLDeleteContainerPairFirstPointers(job_map_.begin(), job_map_.end());
163 scoped_ptr<ExternalPolicyDataFetcher>
164 ExternalPolicyDataFetcherBackend::CreateFrontend(
165 scoped_refptr<base::SequencedTaskRunner> task_runner) {
166 return make_scoped_ptr(new ExternalPolicyDataFetcher(
167 task_runner, io_task_runner_, weak_factory_.GetWeakPtr()));
170 void ExternalPolicyDataFetcherBackend::StartJob(
171 ExternalPolicyDataFetcher::Job* job) {
172 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
173 net::URLFetcher* fetcher = net::URLFetcher::Create(
174 ++last_fetch_id_, job->url, net::URLFetcher::GET, this);
175 fetcher->SetRequestContext(request_context_.get());
176 fetcher->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
177 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_IS_DOWNLOAD |
178 net::LOAD_DO_NOT_SEND_COOKIES |
179 net::LOAD_DO_NOT_SEND_AUTH_DATA);
180 fetcher->SetAutomaticallyRetryOnNetworkChanges(3);
182 job_map_[fetcher] = job;
185 void ExternalPolicyDataFetcherBackend::CancelJob(
186 ExternalPolicyDataFetcher::Job* job,
187 const base::Closure& callback) {
188 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
189 for (JobMap::iterator it = job_map_.begin(); it != job_map_.end(); ) {
190 if (it->second == job) {
192 job_map_.erase(it++);
200 void ExternalPolicyDataFetcherBackend::OnURLFetchComplete(
201 const net::URLFetcher* source) {
202 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
203 JobMap::iterator it = job_map_.find(const_cast<net::URLFetcher*>(source));
204 if (it == job_map_.end()) {
209 ExternalPolicyDataFetcher::Result result = ExternalPolicyDataFetcher::SUCCESS;
210 scoped_ptr<std::string> data;
212 const net::URLRequestStatus status = it->first->GetStatus();
213 if (status.error() == net::ERR_CONNECTION_RESET ||
214 status.error() == net::ERR_TEMPORARILY_THROTTLED) {
215 // The connection was interrupted.
216 result = ExternalPolicyDataFetcher::CONNECTION_INTERRUPTED;
217 } else if (status.status() != net::URLRequestStatus::SUCCESS) {
218 // Another network error occurred.
219 result = ExternalPolicyDataFetcher::NETWORK_ERROR;
220 } else if (source->GetResponseCode() >= 500) {
221 // Problem at the server.
222 result = ExternalPolicyDataFetcher::SERVER_ERROR;
223 } else if (source->GetResponseCode() >= 400) {
225 result = ExternalPolicyDataFetcher::CLIENT_ERROR;
226 } else if (source->GetResponseCode() != 200) {
227 // Any other type of HTTP failure.
228 result = ExternalPolicyDataFetcher::HTTP_ERROR;
230 data.reset(new std::string);
231 source->GetResponseAsString(data.get());
232 if (static_cast<int64>(data->size()) > it->second->max_size) {
233 // Received |data| exceeds maximum allowed size.
235 result = ExternalPolicyDataFetcher::MAX_SIZE_EXCEEDED;
239 ExternalPolicyDataFetcher::Job* job = it->second;
242 job->callback.Run(job, result, data.Pass());
245 void ExternalPolicyDataFetcherBackend::OnURLFetchDownloadProgress(
246 const net::URLFetcher* source,
249 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
250 JobMap::iterator it = job_map_.find(const_cast<net::URLFetcher*>(source));
251 DCHECK(it != job_map_.end());
252 if (it == job_map_.end())
255 // Reject the data if it exceeds the size limit. The content length is in
256 // |total|, and it may be -1 when not known.
257 if (current > it->second->max_size || total > it->second->max_size) {
258 ExternalPolicyDataFetcher::Job* job = it->second;
261 job->callback.Run(job,
262 ExternalPolicyDataFetcher::MAX_SIZE_EXCEEDED,
263 scoped_ptr<std::string>());
267 } // namespace policy