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/thread_task_runner_handle.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/load_flags.h"
17 #include "net/base/sdch_net_log_params.h"
18 #include "net/http/http_response_headers.h"
19 #include "net/log/net_log.h"
20 #include "net/url_request/url_request_context.h"
21 #include "net/url_request/url_request_status.h"
22 #include "net/url_request/url_request_throttler_manager.h"
28 const int kBufferSize
= 4096;
30 // Map the bytes_read result from a read attempt and a URLRequest's
31 // status into a single net return value.
32 int GetReadResult(int bytes_read
, const URLRequest
* request
) {
33 int rv
= request
->status().error();
34 if (request
->status().is_success() && bytes_read
< 0) {
36 request
->net_log().AddEventWithNetErrorCode(
37 NetLog::TYPE_SDCH_DICTIONARY_FETCH_IMPLIED_ERROR
, rv
);
47 FetchInfo(const GURL
& url
,
49 const SdchDictionaryFetcher::OnDictionaryFetchedCallback
& callback
)
50 : url(url
), cache_only(cache_only
), callback(callback
) {}
55 SdchDictionaryFetcher::OnDictionaryFetchedCallback callback
;
60 // A UniqueFetchQueue is used to queue outgoing requests, which are either cache
61 // requests or network requests (which *may* still be served from cache).
62 // The UniqueFetchQueue enforces that a URL can only be queued for network fetch
63 // at most once. Calling Clear() resets UniqueFetchQueue's memory of which URLs
65 class SdchDictionaryFetcher::UniqueFetchQueue
{
70 bool Push(const FetchInfo
& info
);
71 bool Pop(FetchInfo
* info
);
76 std::queue
<FetchInfo
> queue_
;
77 std::set
<GURL
> ever_network_queued_
;
79 DISALLOW_COPY_AND_ASSIGN(UniqueFetchQueue
);
82 SdchDictionaryFetcher::UniqueFetchQueue::UniqueFetchQueue() {}
83 SdchDictionaryFetcher::UniqueFetchQueue::~UniqueFetchQueue() {}
85 bool SdchDictionaryFetcher::UniqueFetchQueue::Push(const FetchInfo
& info
) {
86 if (ever_network_queued_
.count(info
.url
) != 0)
89 ever_network_queued_
.insert(info
.url
);
94 bool SdchDictionaryFetcher::UniqueFetchQueue::Pop(FetchInfo
* info
) {
97 *info
= queue_
.front();
102 bool SdchDictionaryFetcher::UniqueFetchQueue::IsEmpty() const {
103 return queue_
.empty();
106 void SdchDictionaryFetcher::UniqueFetchQueue::Clear() {
107 ever_network_queued_
.clear();
108 while (!queue_
.empty())
112 SdchDictionaryFetcher::SdchDictionaryFetcher(URLRequestContext
* context
)
113 : next_state_(STATE_NONE
),
115 fetch_queue_(new UniqueFetchQueue()),
117 DCHECK(CalledOnValidThread());
121 SdchDictionaryFetcher::~SdchDictionaryFetcher() {
124 bool SdchDictionaryFetcher::Schedule(
125 const GURL
& dictionary_url
,
126 const OnDictionaryFetchedCallback
& callback
) {
127 return ScheduleInternal(dictionary_url
, false, callback
);
130 bool SdchDictionaryFetcher::ScheduleReload(
131 const GURL
& dictionary_url
,
132 const OnDictionaryFetchedCallback
& callback
) {
133 return ScheduleInternal(dictionary_url
, true, callback
);
136 void SdchDictionaryFetcher::Cancel() {
137 DCHECK(CalledOnValidThread());
140 next_state_
= STATE_NONE
;
142 fetch_queue_
->Clear();
145 void SdchDictionaryFetcher::OnResponseStarted(URLRequest
* request
) {
146 DCHECK(CalledOnValidThread());
147 DCHECK_EQ(request
, current_request_
.get());
148 DCHECK_EQ(next_state_
, STATE_SEND_REQUEST_COMPLETE
);
151 // Confirm that the response isn't a stale read from the cache (as
152 // may happen in the reload case). If the response was not retrieved over
153 // HTTP, it is presumed to be fresh.
154 HttpResponseHeaders
* response_headers
= request
->response_headers();
155 int result
= request
->status().error();
156 if (result
== OK
&& response_headers
) {
157 ValidationType validation_type
= response_headers
->RequiresValidation(
158 request
->response_info().request_time
,
159 request
->response_info().response_time
, base::Time::Now());
160 // TODO(rdsmith): Maybe handle VALIDATION_ASYNCHRONOUS by queueing
161 // a non-reload request for the dictionary.
162 if (validation_type
!= VALIDATION_NONE
)
169 void SdchDictionaryFetcher::OnReadCompleted(URLRequest
* request
,
171 DCHECK(CalledOnValidThread());
172 DCHECK_EQ(request
, current_request_
.get());
173 DCHECK_EQ(next_state_
, STATE_READ_BODY_COMPLETE
);
176 DoLoop(GetReadResult(bytes_read
, current_request_
.get()));
179 bool SdchDictionaryFetcher::ScheduleInternal(
180 const GURL
& dictionary_url
,
182 const OnDictionaryFetchedCallback
& callback
) {
183 DCHECK(CalledOnValidThread());
185 // If Push() fails, |dictionary_url| has already been fetched or scheduled to
187 if (!fetch_queue_
->Push(FetchInfo(dictionary_url
, reload
, callback
))) {
188 // TODO(rdsmith): Log this error to the net log. In the case of a
189 // normal fetch, this can be through the URLRequest
190 // initiating this fetch (once the URLRequest is passed to the fetcher);
191 // in the case of a reload, it's more complicated.
192 SdchManager::SdchErrorRecovery(
193 SDCH_DICTIONARY_PREVIOUSLY_SCHEDULED_TO_DOWNLOAD
);
197 // If the loop is already processing, it'll pick up the above in the
198 // normal course of events.
199 if (next_state_
!= STATE_NONE
)
202 next_state_
= STATE_SEND_REQUEST
;
204 // There are no callbacks to user code from the dictionary fetcher,
205 // and Schedule() is only called from user code, so this call to DoLoop()
206 // does not require an |if (in_loop_) return;| guard.
211 void SdchDictionaryFetcher::ResetRequest() {
212 current_request_
.reset();
214 current_callback_
.Reset();
219 int SdchDictionaryFetcher::DoLoop(int rv
) {
221 base::AutoReset
<bool> auto_reset_in_loop(&in_loop_
, true);
224 State state
= next_state_
;
225 next_state_
= STATE_NONE
;
227 case STATE_SEND_REQUEST
:
228 rv
= DoSendRequest(rv
);
230 case STATE_SEND_REQUEST_COMPLETE
:
231 rv
= DoSendRequestComplete(rv
);
233 case STATE_READ_BODY
:
236 case STATE_READ_BODY_COMPLETE
:
237 rv
= DoReadBodyComplete(rv
);
239 case STATE_REQUEST_COMPLETE
:
240 rv
= DoCompleteRequest(rv
);
245 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
250 int SdchDictionaryFetcher::DoSendRequest(int rv
) {
251 DCHECK(CalledOnValidThread());
253 // |rv| is ignored, as the result from the previous request doesn't
254 // affect the next request.
256 if (fetch_queue_
->IsEmpty() || current_request_
.get()) {
257 next_state_
= STATE_NONE
;
261 next_state_
= STATE_SEND_REQUEST_COMPLETE
;
264 bool success
= fetch_queue_
->Pop(&info
);
266 current_request_
= context_
->CreateRequest(info
.url
, IDLE
, this);
267 int load_flags
= LOAD_DO_NOT_SEND_COOKIES
| LOAD_DO_NOT_SAVE_COOKIES
;
269 load_flags
|= LOAD_ONLY_FROM_CACHE
;
270 current_request_
->SetLoadFlags(load_flags
);
272 buffer_
= new IOBuffer(kBufferSize
);
273 current_callback_
= info
.callback
;
275 current_request_
->Start();
276 current_request_
->net_log().AddEvent(NetLog::TYPE_SDCH_DICTIONARY_FETCH
);
278 return ERR_IO_PENDING
;
281 int SdchDictionaryFetcher::DoSendRequestComplete(int rv
) {
282 DCHECK(CalledOnValidThread());
284 // If there's been an error, abort the current request.
286 current_request_
.reset();
288 next_state_
= STATE_SEND_REQUEST
;
293 next_state_
= STATE_READ_BODY
;
297 int SdchDictionaryFetcher::DoReadBody(int rv
) {
298 DCHECK(CalledOnValidThread());
300 // If there's been an error, abort the current request.
303 next_state_
= STATE_SEND_REQUEST
;
307 next_state_
= STATE_READ_BODY_COMPLETE
;
309 current_request_
->Read(buffer_
.get(), kBufferSize
, &bytes_read
);
310 if (current_request_
->status().is_io_pending())
311 return ERR_IO_PENDING
;
313 return GetReadResult(bytes_read
, current_request_
.get());
316 int SdchDictionaryFetcher::DoReadBodyComplete(int rv
) {
317 DCHECK(CalledOnValidThread());
319 // An error; abort the current request.
321 current_request_
.reset();
323 next_state_
= STATE_SEND_REQUEST
;
327 DCHECK(current_request_
->status().is_success());
329 // Data; append to the dictionary and look for more data.
331 dictionary_
.append(buffer_
->data(), rv
);
332 next_state_
= STATE_READ_BODY
;
336 // End of file; complete the request.
337 next_state_
= STATE_REQUEST_COMPLETE
;
341 int SdchDictionaryFetcher::DoCompleteRequest(int rv
) {
342 DCHECK(CalledOnValidThread());
344 // If the dictionary was successfully fetched, add it to the manager.
346 current_callback_
.Run(dictionary_
, current_request_
->url(),
347 current_request_
->net_log(),
348 current_request_
->was_cached());
352 next_state_
= STATE_SEND_REQUEST
;