Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / net / url_request / sdch_dictionary_fetcher.cc
blob7fa293fd3e219b37b4ce86b6b6986400c9e982df
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.
5 #include "net/url_request/sdch_dictionary_fetcher.h"
7 #include <stdint.h>
8 #include <queue>
9 #include <set>
11 #include "base/auto_reset.h"
12 #include "base/bind.h"
13 #include "base/compiler_specific.h"
14 #include "base/profiler/scoped_tracker.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/load_flags.h"
18 #include "net/base/sdch_net_log_params.h"
19 #include "net/http/http_response_headers.h"
20 #include "net/log/net_log.h"
21 #include "net/url_request/url_request_context.h"
22 #include "net/url_request/url_request_status.h"
23 #include "net/url_request/url_request_throttler_manager.h"
25 namespace net {
27 namespace {
29 const int kBufferSize = 4096;
31 // Map the bytes_read result from a read attempt and a URLRequest's
32 // status into a single net return value.
33 int GetReadResult(int bytes_read, const URLRequest* request) {
34 int rv = request->status().error();
35 if (request->status().is_success() && bytes_read < 0) {
36 rv = ERR_FAILED;
37 request->net_log().AddEventWithNetErrorCode(
38 NetLog::TYPE_SDCH_DICTIONARY_FETCH_IMPLIED_ERROR, rv);
41 if (rv == OK)
42 rv = bytes_read;
44 return rv;
47 struct FetchInfo {
48 FetchInfo(const GURL& url,
49 bool cache_only,
50 const SdchDictionaryFetcher::OnDictionaryFetchedCallback& callback)
51 : url(url), cache_only(cache_only), callback(callback) {}
52 FetchInfo() {}
54 GURL url;
55 bool cache_only;
56 SdchDictionaryFetcher::OnDictionaryFetchedCallback callback;
59 } // namespace
61 // A UniqueFetchQueue is used to queue outgoing requests, which are either cache
62 // requests or network requests (which *may* still be served from cache).
63 // The UniqueFetchQueue enforces that a URL can only be queued for network fetch
64 // at most once. Calling Clear() resets UniqueFetchQueue's memory of which URLs
65 // have been queued.
66 class SdchDictionaryFetcher::UniqueFetchQueue {
67 public:
68 UniqueFetchQueue();
69 ~UniqueFetchQueue();
71 bool Push(const FetchInfo& info);
72 bool Pop(FetchInfo* info);
73 bool IsEmpty() const;
74 void Clear();
76 private:
77 std::queue<FetchInfo> queue_;
78 std::set<GURL> ever_network_queued_;
80 DISALLOW_COPY_AND_ASSIGN(UniqueFetchQueue);
83 SdchDictionaryFetcher::UniqueFetchQueue::UniqueFetchQueue() {}
84 SdchDictionaryFetcher::UniqueFetchQueue::~UniqueFetchQueue() {}
86 bool SdchDictionaryFetcher::UniqueFetchQueue::Push(const FetchInfo& info) {
87 if (ever_network_queued_.count(info.url) != 0)
88 return false;
89 if (!info.cache_only)
90 ever_network_queued_.insert(info.url);
91 queue_.push(info);
92 return true;
95 bool SdchDictionaryFetcher::UniqueFetchQueue::Pop(FetchInfo* info) {
96 if (IsEmpty())
97 return false;
98 *info = queue_.front();
99 queue_.pop();
100 return true;
103 bool SdchDictionaryFetcher::UniqueFetchQueue::IsEmpty() const {
104 return queue_.empty();
107 void SdchDictionaryFetcher::UniqueFetchQueue::Clear() {
108 ever_network_queued_.clear();
109 while (!queue_.empty())
110 queue_.pop();
113 SdchDictionaryFetcher::SdchDictionaryFetcher(URLRequestContext* context)
114 : next_state_(STATE_NONE),
115 in_loop_(false),
116 fetch_queue_(new UniqueFetchQueue()),
117 context_(context) {
118 DCHECK(CalledOnValidThread());
119 DCHECK(context);
122 SdchDictionaryFetcher::~SdchDictionaryFetcher() {
125 bool SdchDictionaryFetcher::Schedule(
126 const GURL& dictionary_url,
127 const OnDictionaryFetchedCallback& callback) {
128 return ScheduleInternal(dictionary_url, false, callback);
131 bool SdchDictionaryFetcher::ScheduleReload(
132 const GURL& dictionary_url,
133 const OnDictionaryFetchedCallback& callback) {
134 return ScheduleInternal(dictionary_url, true, callback);
137 void SdchDictionaryFetcher::Cancel() {
138 DCHECK(CalledOnValidThread());
140 ResetRequest();
141 next_state_ = STATE_NONE;
143 fetch_queue_->Clear();
146 void SdchDictionaryFetcher::OnResponseStarted(URLRequest* request) {
147 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
148 tracked_objects::ScopedTracker tracking_profile(
149 FROM_HERE_WITH_EXPLICIT_FUNCTION(
150 "423948 SdchDictionaryFetcher::OnResponseStarted"));
152 DCHECK(CalledOnValidThread());
153 DCHECK_EQ(request, current_request_.get());
154 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE);
155 DCHECK(!in_loop_);
157 // Confirm that the response isn't a stale read from the cache (as
158 // may happen in the reload case). If the response was not retrieved over
159 // HTTP, it is presumed to be fresh.
160 HttpResponseHeaders* response_headers = request->response_headers();
161 int result = request->status().error();
162 if (result == OK && response_headers) {
163 ValidationType validation_type = response_headers->RequiresValidation(
164 request->response_info().request_time,
165 request->response_info().response_time, base::Time::Now());
166 // TODO(rdsmith): Maybe handle VALIDATION_ASYNCHRONOUS by queueing
167 // a non-reload request for the dictionary.
168 if (validation_type != VALIDATION_NONE)
169 result = ERR_FAILED;
172 DoLoop(result);
175 void SdchDictionaryFetcher::OnReadCompleted(URLRequest* request,
176 int bytes_read) {
177 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
178 tracked_objects::ScopedTracker tracking_profile(
179 FROM_HERE_WITH_EXPLICIT_FUNCTION(
180 "423948 SdchDictionaryFetcher::OnReadCompleted"));
182 DCHECK(CalledOnValidThread());
183 DCHECK_EQ(request, current_request_.get());
184 DCHECK_EQ(next_state_, STATE_READ_BODY_COMPLETE);
185 DCHECK(!in_loop_);
187 DoLoop(GetReadResult(bytes_read, current_request_.get()));
190 bool SdchDictionaryFetcher::ScheduleInternal(
191 const GURL& dictionary_url,
192 bool reload,
193 const OnDictionaryFetchedCallback& callback) {
194 DCHECK(CalledOnValidThread());
196 // If Push() fails, |dictionary_url| has already been fetched or scheduled to
197 // be fetched.
198 if (!fetch_queue_->Push(FetchInfo(dictionary_url, reload, callback))) {
199 // TODO(rdsmith): Log this error to the net log. In the case of a
200 // normal fetch, this can be through the URLRequest
201 // initiating this fetch (once the URLRequest is passed to the fetcher);
202 // in the case of a reload, it's more complicated.
203 SdchManager::SdchErrorRecovery(
204 SDCH_DICTIONARY_PREVIOUSLY_SCHEDULED_TO_DOWNLOAD);
205 return false;
208 // If the loop is already processing, it'll pick up the above in the
209 // normal course of events.
210 if (next_state_ != STATE_NONE)
211 return true;
213 next_state_ = STATE_SEND_REQUEST;
215 // There are no callbacks to user code from the dictionary fetcher,
216 // and Schedule() is only called from user code, so this call to DoLoop()
217 // does not require an |if (in_loop_) return;| guard.
218 DoLoop(OK);
219 return true;
222 void SdchDictionaryFetcher::ResetRequest() {
223 current_request_.reset();
224 buffer_ = nullptr;
225 current_callback_.Reset();
226 dictionary_.clear();
227 return;
230 int SdchDictionaryFetcher::DoLoop(int rv) {
231 DCHECK(!in_loop_);
232 base::AutoReset<bool> auto_reset_in_loop(&in_loop_, true);
234 do {
235 State state = next_state_;
236 next_state_ = STATE_NONE;
237 switch (state) {
238 case STATE_SEND_REQUEST:
239 rv = DoSendRequest(rv);
240 break;
241 case STATE_SEND_REQUEST_COMPLETE:
242 rv = DoSendRequestComplete(rv);
243 break;
244 case STATE_READ_BODY:
245 rv = DoReadBody(rv);
246 break;
247 case STATE_READ_BODY_COMPLETE:
248 rv = DoReadBodyComplete(rv);
249 break;
250 case STATE_REQUEST_COMPLETE:
251 rv = DoCompleteRequest(rv);
252 break;
253 case STATE_NONE:
254 NOTREACHED();
256 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
258 return rv;
261 int SdchDictionaryFetcher::DoSendRequest(int rv) {
262 DCHECK(CalledOnValidThread());
264 // |rv| is ignored, as the result from the previous request doesn't
265 // affect the next request.
267 if (fetch_queue_->IsEmpty() || current_request_.get()) {
268 next_state_ = STATE_NONE;
269 return OK;
272 next_state_ = STATE_SEND_REQUEST_COMPLETE;
274 FetchInfo info;
275 bool success = fetch_queue_->Pop(&info);
276 DCHECK(success);
277 current_request_ = context_->CreateRequest(info.url, IDLE, this);
278 int load_flags = LOAD_DO_NOT_SEND_COOKIES | LOAD_DO_NOT_SAVE_COOKIES;
279 if (info.cache_only)
280 load_flags |= LOAD_ONLY_FROM_CACHE;
281 current_request_->SetLoadFlags(load_flags);
283 buffer_ = new IOBuffer(kBufferSize);
284 current_callback_ = info.callback;
286 current_request_->Start();
287 current_request_->net_log().AddEvent(NetLog::TYPE_SDCH_DICTIONARY_FETCH);
289 return ERR_IO_PENDING;
292 int SdchDictionaryFetcher::DoSendRequestComplete(int rv) {
293 DCHECK(CalledOnValidThread());
295 // If there's been an error, abort the current request.
296 if (rv != OK) {
297 current_request_.reset();
298 buffer_ = NULL;
299 next_state_ = STATE_SEND_REQUEST;
301 return OK;
304 next_state_ = STATE_READ_BODY;
305 return OK;
308 int SdchDictionaryFetcher::DoReadBody(int rv) {
309 DCHECK(CalledOnValidThread());
311 // If there's been an error, abort the current request.
312 if (rv != OK) {
313 ResetRequest();
314 next_state_ = STATE_SEND_REQUEST;
315 return OK;
318 next_state_ = STATE_READ_BODY_COMPLETE;
319 int bytes_read = 0;
320 current_request_->Read(buffer_.get(), kBufferSize, &bytes_read);
321 if (current_request_->status().is_io_pending())
322 return ERR_IO_PENDING;
324 return GetReadResult(bytes_read, current_request_.get());
327 int SdchDictionaryFetcher::DoReadBodyComplete(int rv) {
328 DCHECK(CalledOnValidThread());
330 // An error; abort the current request.
331 if (rv < 0) {
332 current_request_.reset();
333 buffer_ = NULL;
334 next_state_ = STATE_SEND_REQUEST;
335 return OK;
338 DCHECK(current_request_->status().is_success());
340 // Data; append to the dictionary and look for more data.
341 if (rv > 0) {
342 dictionary_.append(buffer_->data(), rv);
343 next_state_ = STATE_READ_BODY;
344 return OK;
347 // End of file; complete the request.
348 next_state_ = STATE_REQUEST_COMPLETE;
349 return OK;
352 int SdchDictionaryFetcher::DoCompleteRequest(int rv) {
353 DCHECK(CalledOnValidThread());
355 // If the dictionary was successfully fetched, add it to the manager.
356 if (rv == OK) {
357 current_callback_.Run(dictionary_, current_request_->url(),
358 current_request_->net_log(),
359 current_request_->was_cached());
362 ResetRequest();
363 next_state_ = STATE_SEND_REQUEST;
364 return OK;
367 } // namespace net