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"
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"
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) {
37 request
->net_log().AddEventWithNetErrorCode(
38 NetLog::TYPE_SDCH_DICTIONARY_FETCH_IMPLIED_ERROR
, rv
);
48 FetchInfo(const GURL
& url
,
50 const SdchDictionaryFetcher::OnDictionaryFetchedCallback
& callback
)
51 : url(url
), cache_only(cache_only
), callback(callback
) {}
56 SdchDictionaryFetcher::OnDictionaryFetchedCallback callback
;
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
66 class SdchDictionaryFetcher::UniqueFetchQueue
{
71 bool Push(const FetchInfo
& info
);
72 bool Pop(FetchInfo
* info
);
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)
90 ever_network_queued_
.insert(info
.url
);
95 bool SdchDictionaryFetcher::UniqueFetchQueue::Pop(FetchInfo
* info
) {
98 *info
= queue_
.front();
103 bool SdchDictionaryFetcher::UniqueFetchQueue::IsEmpty() const {
104 return queue_
.empty();
107 void SdchDictionaryFetcher::UniqueFetchQueue::Clear() {
108 ever_network_queued_
.clear();
109 while (!queue_
.empty())
113 SdchDictionaryFetcher::SdchDictionaryFetcher(URLRequestContext
* context
)
114 : next_state_(STATE_NONE
),
116 fetch_queue_(new UniqueFetchQueue()),
118 DCHECK(CalledOnValidThread());
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());
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
);
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
)
175 void SdchDictionaryFetcher::OnReadCompleted(URLRequest
* request
,
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
);
187 DoLoop(GetReadResult(bytes_read
, current_request_
.get()));
190 bool SdchDictionaryFetcher::ScheduleInternal(
191 const GURL
& dictionary_url
,
193 const OnDictionaryFetchedCallback
& callback
) {
194 DCHECK(CalledOnValidThread());
196 // If Push() fails, |dictionary_url| has already been fetched or scheduled to
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
);
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
)
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.
222 void SdchDictionaryFetcher::ResetRequest() {
223 current_request_
.reset();
225 current_callback_
.Reset();
230 int SdchDictionaryFetcher::DoLoop(int rv
) {
232 base::AutoReset
<bool> auto_reset_in_loop(&in_loop_
, true);
235 State state
= next_state_
;
236 next_state_
= STATE_NONE
;
238 case STATE_SEND_REQUEST
:
239 rv
= DoSendRequest(rv
);
241 case STATE_SEND_REQUEST_COMPLETE
:
242 rv
= DoSendRequestComplete(rv
);
244 case STATE_READ_BODY
:
247 case STATE_READ_BODY_COMPLETE
:
248 rv
= DoReadBodyComplete(rv
);
250 case STATE_REQUEST_COMPLETE
:
251 rv
= DoCompleteRequest(rv
);
256 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
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
;
272 next_state_
= STATE_SEND_REQUEST_COMPLETE
;
275 bool success
= fetch_queue_
->Pop(&info
);
277 current_request_
= context_
->CreateRequest(info
.url
, IDLE
, this);
278 int load_flags
= LOAD_DO_NOT_SEND_COOKIES
| LOAD_DO_NOT_SAVE_COOKIES
;
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.
297 current_request_
.reset();
299 next_state_
= STATE_SEND_REQUEST
;
304 next_state_
= STATE_READ_BODY
;
308 int SdchDictionaryFetcher::DoReadBody(int rv
) {
309 DCHECK(CalledOnValidThread());
311 // If there's been an error, abort the current request.
314 next_state_
= STATE_SEND_REQUEST
;
318 next_state_
= STATE_READ_BODY_COMPLETE
;
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.
332 current_request_
.reset();
334 next_state_
= STATE_SEND_REQUEST
;
338 DCHECK(current_request_
->status().is_success());
340 // Data; append to the dictionary and look for more data.
342 dictionary_
.append(buffer_
->data(), rv
);
343 next_state_
= STATE_READ_BODY
;
347 // End of file; complete the request.
348 next_state_
= STATE_REQUEST_COMPLETE
;
352 int SdchDictionaryFetcher::DoCompleteRequest(int rv
) {
353 DCHECK(CalledOnValidThread());
355 // If the dictionary was successfully fetched, add it to the manager.
357 current_callback_
.Run(dictionary_
, current_request_
->url(),
358 current_request_
->net_log(),
359 current_request_
->was_cached());
363 next_state_
= STATE_SEND_REQUEST
;