Home | History | Annotate | Download | only in net
      1 // Copyright (c) 2011 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 "chrome/browser/net/url_request_slow_download_job.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/message_loop.h"
      9 #include "base/string_util.h"
     10 #include "googleurl/src/gurl.h"
     11 #include "net/base/io_buffer.h"
     12 #include "net/http/http_response_headers.h"
     13 #include "net/url_request/url_request.h"
     14 #include "net/url_request/url_request_filter.h"
     15 
     16 const int kFirstDownloadSize = 1024 * 35;
     17 const int kSecondDownloadSize = 1024 * 10;
     18 
     19 const char URLRequestSlowDownloadJob::kUnknownSizeUrl[] =
     20   "http://url.handled.by.slow.download/download-unknown-size";
     21 const char URLRequestSlowDownloadJob::kKnownSizeUrl[] =
     22   "http://url.handled.by.slow.download/download-known-size";
     23 const char URLRequestSlowDownloadJob::kFinishDownloadUrl[] =
     24   "http://url.handled.by.slow.download/download-finish";
     25 
     26 std::vector<URLRequestSlowDownloadJob*>
     27     URLRequestSlowDownloadJob::kPendingRequests;
     28 
     29 void URLRequestSlowDownloadJob::Start() {
     30   MessageLoop::current()->PostTask(
     31       FROM_HERE,
     32       method_factory_.NewRunnableMethod(
     33           &URLRequestSlowDownloadJob::StartAsync));
     34 }
     35 
     36 // static
     37 void URLRequestSlowDownloadJob::AddUrlHandler() {
     38   net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
     39   filter->AddUrlHandler(GURL(kUnknownSizeUrl),
     40                         &URLRequestSlowDownloadJob::Factory);
     41   filter->AddUrlHandler(GURL(kKnownSizeUrl),
     42                         &URLRequestSlowDownloadJob::Factory);
     43   filter->AddUrlHandler(GURL(kFinishDownloadUrl),
     44                         &URLRequestSlowDownloadJob::Factory);
     45 }
     46 
     47 /*static */
     48 net::URLRequestJob* URLRequestSlowDownloadJob::Factory(
     49     net::URLRequest* request,
     50     const std::string& scheme) {
     51   URLRequestSlowDownloadJob* job = new URLRequestSlowDownloadJob(request);
     52   if (request->url().spec() != kFinishDownloadUrl)
     53     URLRequestSlowDownloadJob::kPendingRequests.push_back(job);
     54   return job;
     55 }
     56 
     57 /* static */
     58 void URLRequestSlowDownloadJob::FinishPendingRequests() {
     59   typedef std::vector<URLRequestSlowDownloadJob*> JobList;
     60   for (JobList::iterator it = kPendingRequests.begin(); it !=
     61        kPendingRequests.end(); ++it) {
     62     (*it)->set_should_finish_download();
     63   }
     64   kPendingRequests.clear();
     65 }
     66 
     67 URLRequestSlowDownloadJob::URLRequestSlowDownloadJob(net::URLRequest* request)
     68     : net::URLRequestJob(request),
     69       first_download_size_remaining_(kFirstDownloadSize),
     70       should_finish_download_(false),
     71       should_send_second_chunk_(false),
     72       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {}
     73 
     74 void URLRequestSlowDownloadJob::StartAsync() {
     75   if (LowerCaseEqualsASCII(kFinishDownloadUrl, request_->url().spec().c_str()))
     76     URLRequestSlowDownloadJob::FinishPendingRequests();
     77 
     78   NotifyHeadersComplete();
     79 }
     80 
     81 bool URLRequestSlowDownloadJob::ReadRawData(net::IOBuffer* buf, int buf_size,
     82                                             int *bytes_read) {
     83   if (LowerCaseEqualsASCII(kFinishDownloadUrl,
     84                            request_->url().spec().c_str())) {
     85     *bytes_read = 0;
     86     return true;
     87   }
     88 
     89   if (should_send_second_chunk_) {
     90     DCHECK(buf_size > kSecondDownloadSize);
     91     for (int i = 0; i < kSecondDownloadSize; ++i) {
     92       buf->data()[i] = '*';
     93     }
     94     *bytes_read = kSecondDownloadSize;
     95     should_send_second_chunk_ = false;
     96     return true;
     97   }
     98 
     99   if (first_download_size_remaining_ > 0) {
    100     int send_size = std::min(first_download_size_remaining_, buf_size);
    101     for (int i = 0; i < send_size; ++i) {
    102       buf->data()[i] = '*';
    103     }
    104     *bytes_read = send_size;
    105     first_download_size_remaining_ -= send_size;
    106 
    107     DCHECK(!is_done());
    108     return true;
    109   }
    110 
    111   if (should_finish_download_) {
    112     *bytes_read = 0;
    113     return true;
    114   }
    115 
    116   // If we make it here, the first chunk has been sent and we need to wait
    117   // until a request is made for kFinishDownloadUrl.
    118   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
    119   MessageLoop::current()->PostDelayedTask(
    120       FROM_HERE,
    121       method_factory_.NewRunnableMethod(
    122           &URLRequestSlowDownloadJob::CheckDoneStatus),
    123       100);
    124 
    125   // Return false to signal there is pending data.
    126   return false;
    127 }
    128 
    129 void URLRequestSlowDownloadJob::CheckDoneStatus() {
    130   if (should_finish_download_) {
    131     should_send_second_chunk_ = true;
    132     SetStatus(net::URLRequestStatus());
    133     NotifyReadComplete(kSecondDownloadSize);
    134   } else {
    135     MessageLoop::current()->PostDelayedTask(
    136         FROM_HERE,
    137         method_factory_.NewRunnableMethod(
    138             &URLRequestSlowDownloadJob::CheckDoneStatus),
    139         100);
    140   }
    141 }
    142 
    143 // Public virtual version.
    144 void URLRequestSlowDownloadJob::GetResponseInfo(net::HttpResponseInfo* info) {
    145   // Forward to private const version.
    146   GetResponseInfoConst(info);
    147 }
    148 
    149 URLRequestSlowDownloadJob::~URLRequestSlowDownloadJob() {}
    150 
    151 // Private const version.
    152 void URLRequestSlowDownloadJob::GetResponseInfoConst(
    153     net::HttpResponseInfo* info) const {
    154   // Send back mock headers.
    155   std::string raw_headers;
    156   if (LowerCaseEqualsASCII(kFinishDownloadUrl,
    157                            request_->url().spec().c_str())) {
    158     raw_headers.append(
    159       "HTTP/1.1 200 OK\n"
    160       "Content-type: text/plain\n");
    161   } else {
    162     raw_headers.append(
    163       "HTTP/1.1 200 OK\n"
    164       "Content-type: application/octet-stream\n"
    165       "Cache-Control: max-age=0\n");
    166 
    167     if (LowerCaseEqualsASCII(kKnownSizeUrl, request_->url().spec().c_str())) {
    168       raw_headers.append(StringPrintf("Content-Length: %d\n",
    169           kFirstDownloadSize + kSecondDownloadSize));
    170     }
    171   }
    172 
    173   // ParseRawHeaders expects \0 to end each header line.
    174   ReplaceSubstringsAfterOffset(&raw_headers, 0, "\n", std::string("\0", 1));
    175   info->headers = new net::HttpResponseHeaders(raw_headers);
    176 }
    177 
    178 bool URLRequestSlowDownloadJob::GetMimeType(std::string* mime_type) const {
    179   net::HttpResponseInfo info;
    180   GetResponseInfoConst(&info);
    181   return info.headers && info.headers->GetMimeType(mime_type);
    182 }
    183