- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / policy / cloud / external_policy_data_fetcher.cc
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.
4
5 #include "chrome/browser/policy/cloud/external_policy_data_fetcher.h"
6
7 #include "base/bind.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"
18
19 namespace policy {
20
21 namespace {
22
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)));
34 }
35
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);
43 }
44
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
49 // processed.
50 void DoNothing(ExternalPolicyDataFetcher::Job* job) {
51 }
52
53 }  // namespace
54
55 struct ExternalPolicyDataFetcher::Job {
56   Job(const GURL& url,
57       int64 max_size,
58       const ExternalPolicyDataFetcherBackend::FetchCallback& callback);
59
60   const GURL url;
61   const int64 max_size;
62   const ExternalPolicyDataFetcherBackend::FetchCallback callback;
63
64  private:
65   DISALLOW_COPY_AND_ASSIGN(Job);
66 };
67
68 ExternalPolicyDataFetcher::Job::Job(
69     const GURL& url,
70     int64 max_size,
71     const ExternalPolicyDataFetcherBackend::FetchCallback& callback)
72     : url(url),
73       max_size(max_size),
74       callback(callback) {
75 }
76
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),
83       backend_(backend),
84       weak_factory_(this) {
85 }
86
87 ExternalPolicyDataFetcher::~ExternalPolicyDataFetcher() {
88   DCHECK(task_runner_->RunsTasksOnCurrentThread());
89   for (JobSet::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
90     CancelJob(*it);
91 }
92
93 ExternalPolicyDataFetcher::Job* ExternalPolicyDataFetcher::StartJob(
94     const GURL& url,
95     int64 max_size,
96     const FetchCallback& callback) {
97   DCHECK(task_runner_->RunsTasksOnCurrentThread());
98   Job* job = new Job(
99       url, max_size,
100       base::Bind(&ForwardJobFinished,
101                  task_runner_,
102                  base::Bind(&ExternalPolicyDataFetcher::OnJobFinished,
103                             weak_factory_.GetWeakPtr(),
104                             callback)));
105   jobs_.insert(job);
106   io_task_runner_->PostTask(
107       FROM_HERE,
108       base::Bind(&ExternalPolicyDataFetcherBackend::StartJob, backend_, job));
109   return job;
110 }
111
112 void ExternalPolicyDataFetcher::CancelJob(Job* job) {
113   DCHECK(task_runner_->RunsTasksOnCurrentThread());
114   DCHECK(jobs_.find(job) != jobs_.end());
115   jobs_.erase(job);
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(
123       FROM_HERE,
124       base::Bind(&ExternalPolicyDataFetcherBackend::CancelJob,
125                  backend_,
126                  job,
127                  base::Bind(&ForwardJobCanceled,
128                             task_runner_,
129                             base::Bind(&DoNothing, base::Owned(job)))));
130 }
131
132 void ExternalPolicyDataFetcher::OnJobFinished(const FetchCallback& callback,
133                                               Job* job,
134                                               Result result,
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.
142     return;
143   }
144   callback.Run(result, data.Pass());
145   jobs_.erase(it);
146   delete job;
147 }
148
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),
154       last_fetch_id_(-1),
155       weak_factory_(this) {
156 }
157
158 ExternalPolicyDataFetcherBackend::~ExternalPolicyDataFetcherBackend() {
159   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
160   STLDeleteContainerPairFirstPointers(job_map_.begin(), job_map_.end());
161 }
162
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()));
168 }
169
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);
181   fetcher->Start();
182   job_map_[fetcher] = job;
183 }
184
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) {
191       delete it->first;
192       job_map_.erase(it++);
193     } else {
194       ++it;
195     }
196   }
197   callback.Run();
198 }
199
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()) {
205     NOTREACHED();
206     return;
207   }
208
209   ExternalPolicyDataFetcher::Result result = ExternalPolicyDataFetcher::SUCCESS;
210   scoped_ptr<std::string> data;
211
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) {
224     // Client error.
225     result = ExternalPolicyDataFetcher::CLIENT_ERROR;
226   } else if (source->GetResponseCode() != 200) {
227     // Any other type of HTTP failure.
228     result = ExternalPolicyDataFetcher::HTTP_ERROR;
229   } else {
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.
234       data.reset();
235       result = ExternalPolicyDataFetcher::MAX_SIZE_EXCEEDED;
236     }
237   }
238
239   ExternalPolicyDataFetcher::Job* job = it->second;
240   delete it->first;
241   job_map_.erase(it);
242   job->callback.Run(job, result, data.Pass());
243 }
244
245 void ExternalPolicyDataFetcherBackend::OnURLFetchDownloadProgress(
246     const net::URLFetcher* source,
247     int64 current,
248     int64 total) {
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())
253     return;
254
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;
259     delete it->first;
260     job_map_.erase(it);
261     job->callback.Run(job,
262                       ExternalPolicyDataFetcher::MAX_SIZE_EXCEEDED,
263                       scoped_ptr<std::string>());
264   }
265 }
266
267 }  // namespace policy