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"
9 #include "base/auto_reset.h"
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/load_flags.h"
16 #include "net/base/sdch_net_log_params.h"
17 #include "net/url_request/url_request_context.h"
18 #include "net/url_request/url_request_status.h"
19 #include "net/url_request/url_request_throttler_manager.h"
23 const int kBufferSize
= 4096;
29 SdchDictionaryFetcher::SdchDictionaryFetcher(
30 URLRequestContext
* context
,
31 const OnDictionaryFetchedCallback
& callback
)
32 : next_state_(STATE_NONE
),
35 dictionary_fetched_callback_(callback
),
37 DCHECK(CalledOnValidThread());
41 SdchDictionaryFetcher::~SdchDictionaryFetcher() {
42 DCHECK(CalledOnValidThread());
45 void SdchDictionaryFetcher::Schedule(const GURL
& dictionary_url
) {
46 DCHECK(CalledOnValidThread());
48 // Avoid pushing duplicate copy onto queue. We may fetch this url again later
49 // and get a different dictionary, but there is no reason to have it in the
50 // queue twice at one time.
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
);
60 attempted_load_
.insert(dictionary_url
);
61 fetch_queue_
.push(dictionary_url
);
63 next_state_
= STATE_IDLE
;
65 // There are no callbacks to user code from the dictionary fetcher,
66 // and Schedule() is only called from user code, so this call to DoLoop()
67 // does not require an |if (in_loop_) return;| guard.
71 void SdchDictionaryFetcher::Cancel() {
72 DCHECK(CalledOnValidThread());
74 next_state_
= STATE_NONE
;
76 while (!fetch_queue_
.empty())
78 attempted_load_
.clear();
79 weak_factory_
.InvalidateWeakPtrs();
80 current_request_
.reset(NULL
);
85 void SdchDictionaryFetcher::OnResponseStarted(URLRequest
* request
) {
86 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
87 tracked_objects::ScopedTracker
tracking_profile(
88 FROM_HERE_WITH_EXPLICIT_FUNCTION(
89 "423948 SdchDictionaryFetcher::OnResponseStarted"));
91 DCHECK(CalledOnValidThread());
92 DCHECK_EQ(request
, current_request_
.get());
93 DCHECK_EQ(next_state_
, STATE_REQUEST_STARTED
);
95 // The response has started, so the stream can be read from.
96 next_state_
= STATE_REQUEST_READING
;
98 // If this function was synchronously called, the containing
99 // state machine loop will handle the state transition. Otherwise,
100 // restart the state machine loop.
104 DoLoop(request
->status().error());
107 void SdchDictionaryFetcher::OnReadCompleted(URLRequest
* request
,
109 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
110 tracked_objects::ScopedTracker
tracking_profile(
111 FROM_HERE_WITH_EXPLICIT_FUNCTION(
112 "423948 SdchDictionaryFetcher::OnReadCompleted"));
114 DCHECK(CalledOnValidThread());
115 DCHECK_EQ(request
, current_request_
.get());
116 DCHECK_EQ(next_state_
, STATE_REQUEST_READING
);
118 // No state transition is required in this function; the
119 // completion of the request is detected in DoRead().
121 if (request
->status().is_success())
122 dictionary_
.append(buffer_
->data(), bytes_read
);
124 // If this function was synchronously called, the containing
125 // state machine loop will handle the state transition. Otherwise,
126 // restart the state machine loop.
130 DoLoop(request
->status().error());
133 int SdchDictionaryFetcher::DoLoop(int rv
) {
135 base::AutoReset
<bool> auto_reset_in_loop(&in_loop_
, true);
138 State state
= next_state_
;
139 next_state_
= STATE_NONE
;
142 rv
= DoDispatchRequest(rv
);
144 case STATE_REQUEST_STARTED
:
145 rv
= DoRequestStarted(rv
);
147 case STATE_REQUEST_READING
:
150 case STATE_REQUEST_COMPLETE
:
151 rv
= DoCompleteRequest(rv
);
156 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
161 int SdchDictionaryFetcher::DoDispatchRequest(int rv
) {
162 DCHECK(CalledOnValidThread());
164 // |rv| is ignored, as the result from the previous request doesn't
165 // affect the next request.
167 if (fetch_queue_
.empty() || current_request_
.get()) {
168 next_state_
= STATE_NONE
;
173 context_
->CreateRequest(fetch_queue_
.front(), IDLE
, this, NULL
);
174 current_request_
->SetLoadFlags(LOAD_DO_NOT_SEND_COOKIES
|
175 LOAD_DO_NOT_SAVE_COOKIES
);
176 buffer_
= new IOBuffer(kBufferSize
);
179 next_state_
= STATE_REQUEST_STARTED
;
180 current_request_
->Start();
181 current_request_
->net_log().AddEvent(NetLog::TYPE_SDCH_DICTIONARY_FETCH
);
186 int SdchDictionaryFetcher::DoRequestStarted(int rv
) {
187 DCHECK(CalledOnValidThread());
188 DCHECK_EQ(rv
, OK
); // Can only come straight from above function.
190 // The transition to STATE_REQUEST_READING occurs in the
191 // OnResponseStarted() callback triggered by URLRequest::Start()
192 // (called in DoDispatchRequest(), above). If that callback did not
193 // occur synchronously, this routine is executed; it returns ERR_IO_PENDING,
194 // indicating to the controlling loop that no further work should be done
195 // until the callback occurs (which will re-invoke DoLoop()).
196 next_state_
= STATE_REQUEST_STARTED
;
197 return ERR_IO_PENDING
;
200 int SdchDictionaryFetcher::DoRead(int rv
) {
201 DCHECK(CalledOnValidThread());
203 // If there's been an error, abort the current request.
205 current_request_
.reset();
207 next_state_
= STATE_IDLE
;
212 next_state_
= STATE_REQUEST_READING
;
214 current_request_
->Read(buffer_
.get(), kBufferSize
, &bytes_read
);
215 if (current_request_
->status().is_io_pending())
216 return ERR_IO_PENDING
;
218 if (bytes_read
< 0 || !current_request_
->status().is_success()) {
219 if (current_request_
->status().error() != OK
)
220 return current_request_
->status().error();
222 // An error with request status of OK should not happen,
223 // but there's enough machinery underneath URLRequest::Read()
224 // that this routine checks for that case.
226 current_request_
->status().status() == URLRequestStatus::CANCELED
?
227 ERR_ABORTED
: ERR_FAILED
;
228 current_request_
->net_log().AddEventWithNetErrorCode(
229 NetLog::TYPE_SDCH_DICTIONARY_FETCH_IMPLIED_ERROR
, error
);
234 next_state_
= STATE_REQUEST_COMPLETE
;
236 dictionary_
.append(buffer_
->data(), bytes_read
);
241 int SdchDictionaryFetcher::DoCompleteRequest(int rv
) {
242 DCHECK(CalledOnValidThread());
244 // If the dictionary was successfully fetched, add it to the manager.
246 dictionary_fetched_callback_
.Run(dictionary_
, current_request_
->url(),
247 current_request_
->net_log());
250 current_request_
.reset();
254 next_state_
= STATE_IDLE
;