Add the first GYP-based bots to MB (blink android trybots).
[chromium-blink-merge.git] / net / url_request / sdch_dictionary_fetcher.cc
blob25ae1f21a83a2d3c0444b50470f594a87e1387e7
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"
7 #include <stdint.h>
8 #include <queue>
9 #include <set>
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"
24 namespace net {
26 namespace {
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) {
35 rv = ERR_FAILED;
36 request->net_log().AddEventWithNetErrorCode(
37 NetLog::TYPE_SDCH_DICTIONARY_FETCH_IMPLIED_ERROR, rv);
40 if (rv == OK)
41 rv = bytes_read;
43 return rv;
46 struct FetchInfo {
47 FetchInfo(const GURL& url,
48 bool cache_only,
49 const SdchDictionaryFetcher::OnDictionaryFetchedCallback& callback)
50 : url(url), cache_only(cache_only), callback(callback) {}
51 FetchInfo() {}
53 GURL url;
54 bool cache_only;
55 SdchDictionaryFetcher::OnDictionaryFetchedCallback callback;
58 } // namespace
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
64 // have been queued.
65 class SdchDictionaryFetcher::UniqueFetchQueue {
66 public:
67 UniqueFetchQueue();
68 ~UniqueFetchQueue();
70 bool Push(const FetchInfo& info);
71 bool Pop(FetchInfo* info);
72 bool IsEmpty() const;
73 void Clear();
75 private:
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)
87 return false;
88 if (!info.cache_only)
89 ever_network_queued_.insert(info.url);
90 queue_.push(info);
91 return true;
94 bool SdchDictionaryFetcher::UniqueFetchQueue::Pop(FetchInfo* info) {
95 if (IsEmpty())
96 return false;
97 *info = queue_.front();
98 queue_.pop();
99 return true;
102 bool SdchDictionaryFetcher::UniqueFetchQueue::IsEmpty() const {
103 return queue_.empty();
106 void SdchDictionaryFetcher::UniqueFetchQueue::Clear() {
107 ever_network_queued_.clear();
108 while (!queue_.empty())
109 queue_.pop();
112 SdchDictionaryFetcher::SdchDictionaryFetcher(URLRequestContext* context)
113 : next_state_(STATE_NONE),
114 in_loop_(false),
115 fetch_queue_(new UniqueFetchQueue()),
116 context_(context) {
117 DCHECK(CalledOnValidThread());
118 DCHECK(context);
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());
139 ResetRequest();
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);
149 DCHECK(!in_loop_);
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)
163 result = ERR_FAILED;
166 DoLoop(result);
169 void SdchDictionaryFetcher::OnReadCompleted(URLRequest* request,
170 int bytes_read) {
171 DCHECK(CalledOnValidThread());
172 DCHECK_EQ(request, current_request_.get());
173 DCHECK_EQ(next_state_, STATE_READ_BODY_COMPLETE);
174 DCHECK(!in_loop_);
176 DoLoop(GetReadResult(bytes_read, current_request_.get()));
179 bool SdchDictionaryFetcher::ScheduleInternal(
180 const GURL& dictionary_url,
181 bool reload,
182 const OnDictionaryFetchedCallback& callback) {
183 DCHECK(CalledOnValidThread());
185 // If Push() fails, |dictionary_url| has already been fetched or scheduled to
186 // be fetched.
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);
194 return false;
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)
200 return true;
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.
207 DoLoop(OK);
208 return true;
211 void SdchDictionaryFetcher::ResetRequest() {
212 current_request_.reset();
213 buffer_ = nullptr;
214 current_callback_.Reset();
215 dictionary_.clear();
216 return;
219 int SdchDictionaryFetcher::DoLoop(int rv) {
220 DCHECK(!in_loop_);
221 base::AutoReset<bool> auto_reset_in_loop(&in_loop_, true);
223 do {
224 State state = next_state_;
225 next_state_ = STATE_NONE;
226 switch (state) {
227 case STATE_SEND_REQUEST:
228 rv = DoSendRequest(rv);
229 break;
230 case STATE_SEND_REQUEST_COMPLETE:
231 rv = DoSendRequestComplete(rv);
232 break;
233 case STATE_READ_BODY:
234 rv = DoReadBody(rv);
235 break;
236 case STATE_READ_BODY_COMPLETE:
237 rv = DoReadBodyComplete(rv);
238 break;
239 case STATE_REQUEST_COMPLETE:
240 rv = DoCompleteRequest(rv);
241 break;
242 case STATE_NONE:
243 NOTREACHED();
245 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
247 return rv;
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;
258 return OK;
261 next_state_ = STATE_SEND_REQUEST_COMPLETE;
263 FetchInfo info;
264 bool success = fetch_queue_->Pop(&info);
265 DCHECK(success);
266 current_request_ = context_->CreateRequest(info.url, IDLE, this);
267 int load_flags = LOAD_DO_NOT_SEND_COOKIES | LOAD_DO_NOT_SAVE_COOKIES;
268 if (info.cache_only)
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.
285 if (rv != OK) {
286 current_request_.reset();
287 buffer_ = NULL;
288 next_state_ = STATE_SEND_REQUEST;
290 return OK;
293 next_state_ = STATE_READ_BODY;
294 return OK;
297 int SdchDictionaryFetcher::DoReadBody(int rv) {
298 DCHECK(CalledOnValidThread());
300 // If there's been an error, abort the current request.
301 if (rv != OK) {
302 ResetRequest();
303 next_state_ = STATE_SEND_REQUEST;
304 return OK;
307 next_state_ = STATE_READ_BODY_COMPLETE;
308 int bytes_read = 0;
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.
320 if (rv < 0) {
321 current_request_.reset();
322 buffer_ = NULL;
323 next_state_ = STATE_SEND_REQUEST;
324 return OK;
327 DCHECK(current_request_->status().is_success());
329 // Data; append to the dictionary and look for more data.
330 if (rv > 0) {
331 dictionary_.append(buffer_->data(), rv);
332 next_state_ = STATE_READ_BODY;
333 return OK;
336 // End of file; complete the request.
337 next_state_ = STATE_REQUEST_COMPLETE;
338 return OK;
341 int SdchDictionaryFetcher::DoCompleteRequest(int rv) {
342 DCHECK(CalledOnValidThread());
344 // If the dictionary was successfully fetched, add it to the manager.
345 if (rv == OK) {
346 current_callback_.Run(dictionary_, current_request_->url(),
347 current_request_->net_log(),
348 current_request_->was_cached());
351 ResetRequest();
352 next_state_ = STATE_SEND_REQUEST;
353 return OK;
356 } // namespace net