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

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

Issue 881413003: Make SDCH dictionaries persistent across browser restart. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 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 <stdint.h> 7 #include <stdint.h>
8 8
9 #include "base/auto_reset.h" 9 #include "base/auto_reset.h"
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/compiler_specific.h" 11 #include "base/compiler_specific.h"
12 #include "base/profiler/scoped_tracker.h" 12 #include "base/profiler/scoped_tracker.h"
13 #include "base/thread_task_runner_handle.h" 13 #include "base/thread_task_runner_handle.h"
14 #include "net/base/io_buffer.h" 14 #include "net/base/io_buffer.h"
15 #include "net/base/load_flags.h" 15 #include "net/base/load_flags.h"
16 #include "net/base/sdch_net_log_params.h" 16 #include "net/base/sdch_net_log_params.h"
17 #include "net/http/http_response_headers.h"
17 #include "net/url_request/url_request_context.h" 18 #include "net/url_request/url_request_context.h"
18 #include "net/url_request/url_request_status.h" 19 #include "net/url_request/url_request_status.h"
19 #include "net/url_request/url_request_throttler_manager.h" 20 #include "net/url_request/url_request_throttler_manager.h"
20 21
21 namespace { 22 namespace {
22 23
23 const int kBufferSize = 4096; 24 const int kBufferSize = 4096;
24 25
25 } // namespace 26 } // namespace
26 27
27 namespace net { 28 namespace net {
28 29
29 SdchDictionaryFetcher::SdchDictionaryFetcher( 30 SdchDictionaryFetcher::SdchDictionaryFetcher(
30 URLRequestContext* context, 31 URLRequestContext* context,
31 const OnDictionaryFetchedCallback& callback) 32 const OnDictionaryFetchedCallback& callback)
32 : next_state_(STATE_NONE), 33 : next_state_(STATE_NONE),
33 in_loop_(false), 34 in_loop_(false),
34 context_(context), 35 context_(context),
35 dictionary_fetched_callback_(callback), 36 dictionary_fetched_callback_(callback),
36 weak_factory_(this) { 37 weak_factory_(this) {
37 DCHECK(CalledOnValidThread()); 38 DCHECK(CalledOnValidThread());
38 DCHECK(context); 39 DCHECK(context);
39 } 40 }
40 41
41 SdchDictionaryFetcher::~SdchDictionaryFetcher() { 42 SdchDictionaryFetcher::~SdchDictionaryFetcher() {
42 DCHECK(CalledOnValidThread()); 43 DCHECK(CalledOnValidThread());
44
45 while (!fetch_queue_.empty()) {
Bernhard Bauer 2015/01/29 17:42:24 Maybe extract this into a method EmptyQueue()?
Randy Smith (Not in Mondays) 2015/01/30 20:11:10 I hit the code duplication with a slightly bigger
Bernhard Bauer 2015/02/02 14:39:46 Acknowledged.
46 delete fetch_queue_.front().extra_data;
47
48 fetch_queue_.pop();
49 }
43 } 50 }
44 51
45 void SdchDictionaryFetcher::Schedule(const GURL& dictionary_url) { 52 void SdchDictionaryFetcher::Schedule(const GURL& dictionary_url,
46 DCHECK(CalledOnValidThread()); 53 scoped_ptr<Data> extra_data) {
54 ScheduleInternal(dictionary_url, false, extra_data.Pass());
55 }
47 56
48 // Avoid pushing duplicate copy onto queue. We may fetch this url again later 57 void SdchDictionaryFetcher::ScheduleReload(const GURL& dictionary_url,
49 // and get a different dictionary, but there is no reason to have it in the 58 scoped_ptr<Data> extra_data) {
50 // queue twice at one time. 59 ScheduleInternal(dictionary_url, true, extra_data.Pass());
51 if ((!fetch_queue_.empty() && fetch_queue_.back() == dictionary_url) ||
52 attempted_load_.find(dictionary_url) != attempted_load_.end()) {
53 // TODO(rdsmith): log this error to the net log of the URLRequest
54 // initiating this fetch, once URLRequest will be passed here.
55 SdchManager::SdchErrorRecovery(
56 SDCH_DICTIONARY_PREVIOUSLY_SCHEDULED_TO_DOWNLOAD);
57 return;
58 }
59
60 attempted_load_.insert(dictionary_url);
61 fetch_queue_.push(dictionary_url);
62
63 // If the loop is already processing, it'll pick up the above in the
64 // normal course of events.
65 if (next_state_ != STATE_NONE)
66 return;
67
68 next_state_ = STATE_IDLE;
69
70 // There are no callbacks to user code from the dictionary fetcher,
71 // and Schedule() is only called from user code, so this call to DoLoop()
72 // does not require an |if (in_loop_) return;| guard.
73 DoLoop(OK);
74 } 60 }
75 61
76 void SdchDictionaryFetcher::Cancel() { 62 void SdchDictionaryFetcher::Cancel() {
77 DCHECK(CalledOnValidThread()); 63 DCHECK(CalledOnValidThread());
78 64
79 next_state_ = STATE_NONE; 65 next_state_ = STATE_NONE;
66 current_request_.reset();
67 buffer_ = nullptr;
68 current_extra_data_.reset();
80 69
81 while (!fetch_queue_.empty()) 70 while (!fetch_queue_.empty()) {
71 delete fetch_queue_.front().extra_data;
72
82 fetch_queue_.pop(); 73 fetch_queue_.pop();
74 }
83 attempted_load_.clear(); 75 attempted_load_.clear();
84 weak_factory_.InvalidateWeakPtrs(); 76 weak_factory_.InvalidateWeakPtrs();
85 current_request_.reset(NULL);
86 buffer_ = NULL;
87 dictionary_.clear(); 77 dictionary_.clear();
88 } 78 }
89 79
90 void SdchDictionaryFetcher::OnResponseStarted(URLRequest* request) { 80 void SdchDictionaryFetcher::OnResponseStarted(URLRequest* request) {
91 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. 81 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
92 tracked_objects::ScopedTracker tracking_profile( 82 tracked_objects::ScopedTracker tracking_profile(
93 FROM_HERE_WITH_EXPLICIT_FUNCTION( 83 FROM_HERE_WITH_EXPLICIT_FUNCTION(
94 "423948 SdchDictionaryFetcher::OnResponseStarted")); 84 "423948 SdchDictionaryFetcher::OnResponseStarted"));
95 85
96 DCHECK(CalledOnValidThread()); 86 DCHECK(CalledOnValidThread());
97 DCHECK_EQ(request, current_request_.get()); 87 DCHECK_EQ(request, current_request_.get());
98 DCHECK_EQ(next_state_, STATE_REQUEST_STARTED); 88 DCHECK_EQ(next_state_, STATE_REQUEST_STARTED);
99 89
90 // Confirm that the response isn't a stale read from the cache (as
91 // may happen in the reload case). If the response was not retrieved over
92 // HTTP, it is presumed to be fresh.
93 HttpResponseHeaders* response_headers = request->response_headers();
94 if (response_headers) {
95 ValidationType validation_type = response_headers->RequiresValidation(
96 request->response_info().request_time,
97 request->response_info().response_time, base::Time::Now());
98 // TODO(rdsmith): Maybe handle VALIDATION_ASYNCHRONOUS by queueing
99 // a non-reload request for the dictionary.
100 if (validation_type != VALIDATION_NONE) {
101 // Stale entry; drop it on the floor.
102 ResetRequest();
103 return;
104 }
105 }
106
100 // The response has started, so the stream can be read from. 107 // The response has started, so the stream can be read from.
101 next_state_ = STATE_REQUEST_READING; 108 next_state_ = STATE_REQUEST_READING;
102 109
103 // If this function was synchronously called, the containing 110 // If this function was synchronously called, the containing
104 // state machine loop will handle the state transition. Otherwise, 111 // state machine loop will handle the state transition. Otherwise,
105 // restart the state machine loop. 112 // restart the state machine loop.
106 if (in_loop_) 113 if (in_loop_)
107 return; 114 return;
108 115
109 DoLoop(request->status().error()); 116 DoLoop(request->status().error());
(...skipping 18 matching lines...) Expand all
128 135
129 // If this function was synchronously called, the containing 136 // If this function was synchronously called, the containing
130 // state machine loop will handle the state transition. Otherwise, 137 // state machine loop will handle the state transition. Otherwise,
131 // restart the state machine loop. 138 // restart the state machine loop.
132 if (in_loop_) 139 if (in_loop_)
133 return; 140 return;
134 141
135 DoLoop(request->status().error()); 142 DoLoop(request->status().error());
136 } 143 }
137 144
145 SdchDictionaryFetcher::QueuedInfo::QueuedInfo(const GURL& url,
146 bool download_only_from_cache,
147 scoped_ptr<Data> extra_data)
148 : url(url),
149 download_only_from_cache(download_only_from_cache),
150 // The QueuedInfo structure takes ownership of the data. A
151 // scoped_ptr<> cannot be used because structures used in std::queue<>
152 // need to be copyable.
153 extra_data(extra_data.release()) {
154 }
155
156 void SdchDictionaryFetcher::ScheduleInternal(const GURL& dictionary_url,
157 bool reload,
158 scoped_ptr<Data> extra_data) {
159 DCHECK(CalledOnValidThread());
160
161 // Avoid pushing duplicate copy onto queue. We may fetch this url again later
162 // and get a different dictionary, but there is no reason to have it in the
163 // queue twice at one time.
164 if ((!fetch_queue_.empty() && fetch_queue_.back().url == dictionary_url) ||
165 attempted_load_.find(dictionary_url) != attempted_load_.end()) {
166 // TODO(rdsmith): Log this error to the net log. In the case of a
167 // normal fetch, this can be through the URLRequest
168 // initiating this fetch (once the URLRequest is passed to the fetcher);
169 // in the case of a reload, it's more complicated.
170 SdchManager::SdchErrorRecovery(
171 SDCH_DICTIONARY_PREVIOUSLY_SCHEDULED_TO_DOWNLOAD);
172 return;
173 }
174
175 if (!reload)
176 attempted_load_.insert(dictionary_url);
177 fetch_queue_.push(QueuedInfo(dictionary_url, reload, extra_data.Pass()));
178
179 // If the loop is already processing, it'll pick up the above in the
180 // normal course of events.
181 if (next_state_ != STATE_NONE)
182 return;
183
184 next_state_ = STATE_IDLE;
185
186 // There are no callbacks to user code from the dictionary fetcher,
187 // and Schedule() is only called from user code, so this call to DoLoop()
188 // does not require an |if (in_loop_) return;| guard.
189 DoLoop(OK);
190 }
191
192 void SdchDictionaryFetcher::ResetRequest() {
193 current_request_.reset();
194 buffer_ = nullptr;
195 current_extra_data_.reset();
196 next_state_ = STATE_IDLE;
197 dictionary_.clear();
198
199 if (!in_loop_)
200 DoLoop(OK);
201 return;
202 }
203
138 int SdchDictionaryFetcher::DoLoop(int rv) { 204 int SdchDictionaryFetcher::DoLoop(int rv) {
139 DCHECK(!in_loop_); 205 DCHECK(!in_loop_);
140 base::AutoReset<bool> auto_reset_in_loop(&in_loop_, true); 206 base::AutoReset<bool> auto_reset_in_loop(&in_loop_, true);
141 207
142 do { 208 do {
143 State state = next_state_; 209 State state = next_state_;
144 next_state_ = STATE_NONE; 210 next_state_ = STATE_NONE;
145 switch (state) { 211 switch (state) {
146 case STATE_IDLE: 212 case STATE_IDLE:
147 rv = DoDispatchRequest(rv); 213 rv = DoDispatchRequest(rv);
(...skipping 20 matching lines...) Expand all
168 234
169 // |rv| is ignored, as the result from the previous request doesn't 235 // |rv| is ignored, as the result from the previous request doesn't
170 // affect the next request. 236 // affect the next request.
171 237
172 if (fetch_queue_.empty() || current_request_.get()) { 238 if (fetch_queue_.empty() || current_request_.get()) {
173 next_state_ = STATE_NONE; 239 next_state_ = STATE_NONE;
174 return OK; 240 return OK;
175 } 241 }
176 242
177 current_request_ = 243 current_request_ =
178 context_->CreateRequest(fetch_queue_.front(), IDLE, this, NULL); 244 context_->CreateRequest(fetch_queue_.front().url, IDLE, this, NULL);
179 current_request_->SetLoadFlags(LOAD_DO_NOT_SEND_COOKIES | 245 current_request_->SetLoadFlags(
180 LOAD_DO_NOT_SAVE_COOKIES); 246 LOAD_DO_NOT_SEND_COOKIES | LOAD_DO_NOT_SAVE_COOKIES |
247 (fetch_queue_.front().download_only_from_cache ? LOAD_ONLY_FROM_CACHE
248 : 0));
249
181 buffer_ = new IOBuffer(kBufferSize); 250 buffer_ = new IOBuffer(kBufferSize);
251 current_extra_data_.reset(fetch_queue_.front().extra_data);
182 fetch_queue_.pop(); 252 fetch_queue_.pop();
183 253
184 next_state_ = STATE_REQUEST_STARTED; 254 next_state_ = STATE_REQUEST_STARTED;
185 current_request_->Start(); 255 current_request_->Start();
186 current_request_->net_log().AddEvent(NetLog::TYPE_SDCH_DICTIONARY_FETCH); 256 current_request_->net_log().AddEvent(NetLog::TYPE_SDCH_DICTIONARY_FETCH);
187 257
188 return OK; 258 return OK;
189 } 259 }
190 260
191 int SdchDictionaryFetcher::DoRequestStarted(int rv) { 261 int SdchDictionaryFetcher::DoRequestStarted(int rv) {
192 DCHECK(CalledOnValidThread()); 262 DCHECK(CalledOnValidThread());
193 DCHECK_EQ(rv, OK); // Can only come straight from above function. 263 DCHECK_EQ(rv, OK); // Can only come straight from above function.
194 264
195 // The transition to STATE_REQUEST_READING occurs in the 265 // The transition to STATE_REQUEST_READING occurs in the
196 // OnResponseStarted() callback triggered by URLRequest::Start() 266 // OnResponseStarted() callback triggered by URLRequest::Start()
197 // (called in DoDispatchRequest(), above). If that callback did not 267 // (called in DoDispatchRequest(), above). If that callback did not
198 // occur synchronously, this routine is executed; it returns ERR_IO_PENDING, 268 // occur synchronously, this routine is executed; it returns ERR_IO_PENDING,
199 // indicating to the controlling loop that no further work should be done 269 // indicating to the controlling loop that no further work should be done
200 // until the callback occurs (which will re-invoke DoLoop()). 270 // until the callback occurs (which will re-invoke DoLoop()).
201 next_state_ = STATE_REQUEST_STARTED; 271 next_state_ = STATE_REQUEST_STARTED;
202 return ERR_IO_PENDING; 272 return ERR_IO_PENDING;
203 } 273 }
204 274
205 int SdchDictionaryFetcher::DoRead(int rv) { 275 int SdchDictionaryFetcher::DoRead(int rv) {
206 DCHECK(CalledOnValidThread()); 276 DCHECK(CalledOnValidThread());
207 277
208 // If there's been an error, abort the current request. 278 // If there's been an error, abort the current request.
209 if (rv != OK) { 279 if (rv != OK) {
210 current_request_.reset(); 280 ResetRequest();
211 buffer_ = NULL;
212 next_state_ = STATE_IDLE;
213
214 return OK; 281 return OK;
215 } 282 }
216 283
217 next_state_ = STATE_REQUEST_READING; 284 next_state_ = STATE_REQUEST_READING;
218 int bytes_read = 0; 285 int bytes_read = 0;
219 current_request_->Read(buffer_.get(), kBufferSize, &bytes_read); 286 current_request_->Read(buffer_.get(), kBufferSize, &bytes_read);
220 if (current_request_->status().is_io_pending()) 287 if (current_request_->status().is_io_pending())
221 return ERR_IO_PENDING; 288 return ERR_IO_PENDING;
222 289
223 if (bytes_read < 0 || !current_request_->status().is_success()) { 290 if (bytes_read < 0 || !current_request_->status().is_success()) {
(...skipping 18 matching lines...) Expand all
242 309
243 return OK; 310 return OK;
244 } 311 }
245 312
246 int SdchDictionaryFetcher::DoCompleteRequest(int rv) { 313 int SdchDictionaryFetcher::DoCompleteRequest(int rv) {
247 DCHECK(CalledOnValidThread()); 314 DCHECK(CalledOnValidThread());
248 315
249 // If the dictionary was successfully fetched, add it to the manager. 316 // If the dictionary was successfully fetched, add it to the manager.
250 if (rv == OK) { 317 if (rv == OK) {
251 dictionary_fetched_callback_.Run(dictionary_, current_request_->url(), 318 dictionary_fetched_callback_.Run(dictionary_, current_request_->url(),
319 current_extra_data_.Pass(),
252 current_request_->net_log()); 320 current_request_->net_log());
253 } 321 }
254 322
255 current_request_.reset(); 323 ResetRequest();
256 buffer_ = NULL;
257 dictionary_.clear();
258
259 next_state_ = STATE_IDLE;
260
261 return OK; 324 return OK;
262 } 325 }
263 326
264 } // namespace net 327 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698