OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "net/base/sdch_dictionary_fetcher.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include "base/auto_reset.h" | |
10 #include "base/bind.h" | |
11 #include "base/compiler_specific.h" | |
12 #include "base/thread_task_runner_handle.h" | |
13 #include "net/base/load_flags.h" | |
14 #include "net/url_request/url_request_context.h" | |
15 #include "net/url_request/url_request_status.h" | |
16 #include "net/url_request/url_request_throttler_manager.h" | |
17 | |
18 namespace { | |
19 | |
20 const int kBufferSize = 4096; | |
21 | |
22 } // namespace | |
23 | |
24 namespace net { | |
25 | |
26 SdchDictionaryFetcher::SdchDictionaryFetcher( | |
27 SdchFetcher::Delegate* consumer, | |
28 URLRequestContext* context) | |
29 : next_state_(STATE_NONE), | |
30 in_loop_(false), | |
31 consumer_(consumer), | |
32 context_(context), | |
33 weak_factory_(this) { | |
34 DCHECK(CalledOnValidThread()); | |
35 DCHECK(consumer); | |
36 DCHECK(context); | |
37 } | |
38 | |
39 SdchDictionaryFetcher::~SdchDictionaryFetcher() { | |
40 DCHECK(CalledOnValidThread()); | |
41 } | |
42 | |
43 void SdchDictionaryFetcher::Schedule(const GURL& dictionary_url) { | |
44 DCHECK(CalledOnValidThread()); | |
45 | |
46 // Avoid pushing duplicate copy onto queue. We may fetch this url again later | |
47 // and get a different dictionary, but there is no reason to have it in the | |
48 // queue twice at one time. | |
49 if (!fetch_queue_.empty() && fetch_queue_.back() == dictionary_url) { | |
50 SdchManager::SdchErrorRecovery( | |
51 SdchManager::DICTIONARY_ALREADY_SCHEDULED_TO_DOWNLOAD); | |
52 return; | |
53 } | |
54 if (attempted_load_.find(dictionary_url) != attempted_load_.end()) { | |
55 SdchManager::SdchErrorRecovery( | |
56 SdchManager::DICTIONARY_ALREADY_TRIED_TO_DOWNLOAD); | |
57 return; | |
58 } | |
59 attempted_load_.insert(dictionary_url); | |
60 fetch_queue_.push(dictionary_url); | |
61 | |
62 next_state_ = STATE_IDLE; | |
63 | |
64 // There are no callbacks to user code from the dictionary fetcher, | |
65 // and Schedule() is only called from user code, so this call to DoLoop() | |
66 // does not require an |if (in_loop_) return;| guard. | |
67 DoLoop(OK); | |
68 } | |
69 | |
70 void SdchDictionaryFetcher::Cancel() { | |
71 DCHECK(CalledOnValidThread()); | |
72 | |
73 next_state_ = STATE_NONE; | |
74 | |
75 while (!fetch_queue_.empty()) | |
76 fetch_queue_.pop(); | |
77 attempted_load_.clear(); | |
78 weak_factory_.InvalidateWeakPtrs(); | |
79 current_request_.reset(NULL); | |
80 buffer_ = NULL; | |
81 dictionary_.clear(); | |
82 } | |
83 | |
84 void SdchDictionaryFetcher::OnResponseStarted(URLRequest* request) { | |
85 DCHECK(CalledOnValidThread()); | |
86 DCHECK_EQ(request, current_request_.get()); | |
87 DCHECK_EQ(next_state_, STATE_REQUEST_STARTED); | |
88 | |
89 // The response has started, so the stream can be read from. | |
90 next_state_ = STATE_REQUEST_READING; | |
91 | |
92 // If this function was synchronously called, the containing | |
93 // state machine loop will handle the state transition. Otherwise, | |
94 // restart the state machine loop. | |
95 if (in_loop_) | |
96 return; | |
97 | |
98 DoLoop(request->status().error()); | |
99 } | |
100 | |
101 void SdchDictionaryFetcher::OnReadCompleted(URLRequest* request, | |
102 int bytes_read) { | |
103 DCHECK(CalledOnValidThread()); | |
104 DCHECK_EQ(request, current_request_.get()); | |
105 DCHECK_EQ(next_state_, STATE_REQUEST_READING); | |
106 | |
107 // No state transition is required in this function; the | |
108 // completion of the request is detected in DoRead(). | |
109 | |
110 if (request->status().is_success()) | |
111 dictionary_.append(buffer_->data(), bytes_read); | |
112 | |
113 // If this function was synchronously called, the containing | |
114 // state machine loop will handle the state transition. Otherwise, | |
115 // restart the state machine loop. | |
116 if (in_loop_) | |
117 return; | |
118 | |
119 DoLoop(request->status().error()); | |
120 } | |
121 | |
122 int SdchDictionaryFetcher::DoLoop(int rv) { | |
123 DCHECK(!in_loop_); | |
124 base::AutoReset<bool> auto_reset_in_loop(&in_loop_, true); | |
125 | |
126 do { | |
127 State state = next_state_; | |
128 next_state_ = STATE_NONE; | |
129 switch (state) { | |
130 case STATE_IDLE: | |
131 rv = DoDispatchRequest(rv); | |
132 break; | |
133 case STATE_REQUEST_STARTED: | |
134 rv = DoRequestStarted(rv); | |
135 break; | |
136 case STATE_REQUEST_READING: | |
137 rv = DoRead(rv); | |
138 break; | |
139 case STATE_REQUEST_COMPLETE: | |
140 rv = DoCompleteRequest(rv); | |
141 break; | |
142 case STATE_NONE: | |
143 NOTREACHED(); | |
144 } | |
145 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
146 | |
147 return rv; | |
148 } | |
149 | |
150 int SdchDictionaryFetcher::DoDispatchRequest(int rv) { | |
151 DCHECK(CalledOnValidThread()); | |
152 | |
153 // |rv| is ignored, as the result from the previous request doesn't | |
154 // affect the next request. | |
155 | |
156 if (fetch_queue_.empty() || current_request_.get()) { | |
157 next_state_ = STATE_NONE; | |
158 return OK; | |
159 } | |
160 | |
161 current_request_ = context_->CreateRequest( | |
162 fetch_queue_.front(), IDLE, this, NULL); | |
163 current_request_->SetLoadFlags(LOAD_DO_NOT_SEND_COOKIES | | |
164 LOAD_DO_NOT_SAVE_COOKIES); | |
165 buffer_ = new IOBuffer(kBufferSize); | |
166 fetch_queue_.pop(); | |
167 | |
168 next_state_ = STATE_REQUEST_STARTED; | |
169 current_request_->Start(); | |
170 | |
171 return OK; | |
172 } | |
173 | |
174 int SdchDictionaryFetcher::DoRequestStarted(int rv) { | |
175 DCHECK(CalledOnValidThread()); | |
176 DCHECK_EQ(rv, OK); // Can only come straight from above function. | |
177 | |
178 // The transition to STATE_REQUEST_READING occurs in the | |
179 // OnResponseStarted() callback triggered by URLRequest::Start() | |
180 // (called in DoDispatchRequest(), above). If that callback did not | |
181 // occur synchronously, this routine is executed; it returns ERR_IO_PENDING, | |
182 // indicating to the controlling loop that no further work should be done | |
183 // until the callback occurs (which will re-invoke DoLoop()). | |
184 next_state_ = STATE_REQUEST_STARTED; | |
185 return ERR_IO_PENDING; | |
186 } | |
187 | |
188 int SdchDictionaryFetcher::DoRead(int rv) { | |
189 DCHECK(CalledOnValidThread()); | |
190 | |
191 // If there's been an error, abort the current request. | |
192 if (rv != OK) { | |
193 current_request_.reset(); | |
194 buffer_ = NULL; | |
195 next_state_ = STATE_IDLE; | |
196 | |
197 return OK; | |
198 } | |
199 | |
200 next_state_ = STATE_REQUEST_READING; | |
201 int bytes_read = 0; | |
202 if (!current_request_->Read(buffer_.get(), kBufferSize, &bytes_read)) { | |
203 if (current_request_->status().is_io_pending()) | |
204 return ERR_IO_PENDING; | |
205 | |
206 if (current_request_->status().error() == OK) { | |
207 // This "should never happen", but if it does the result will be | |
208 // an infinite loop. It's not clear how to handle a read failure | |
209 // without a promise to invoke the callback at some point in the future, | |
210 // so the request is failed. | |
211 SdchManager::SdchErrorRecovery(SdchManager::DICTIONARY_FETCH_READ_FAILED); | |
212 DLOG(FATAL) << | |
213 "URLRequest::Read() returned false without IO pending or error!"; | |
214 return ERR_FAILED; | |
215 } | |
216 | |
217 return current_request_->status().error(); | |
218 } | |
219 | |
220 if (bytes_read != 0) | |
221 dictionary_.append(buffer_->data(), bytes_read); | |
222 else | |
223 next_state_ = STATE_REQUEST_COMPLETE; | |
224 | |
225 return OK; | |
226 } | |
227 | |
228 int SdchDictionaryFetcher::DoCompleteRequest(int rv) { | |
229 DCHECK(CalledOnValidThread()); | |
230 | |
231 // If the dictionary was successfully fetched, add it to the manager. | |
232 if (rv == OK) | |
233 consumer_->AddSdchDictionary(dictionary_, current_request_->url()); | |
234 | |
235 current_request_.reset(); | |
236 buffer_ = NULL; | |
237 dictionary_.clear(); | |
238 | |
239 next_state_ = STATE_IDLE; | |
240 | |
241 return OK; | |
242 } | |
243 | |
244 } // namespace net | |
OLD | NEW |