Home | History | Annotate | Download | only in base
      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
    245