• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Start()29 void URLRequestSlowDownloadJob::Start() {
30   MessageLoop::current()->PostTask(
31       FROM_HERE,
32       method_factory_.NewRunnableMethod(
33           &URLRequestSlowDownloadJob::StartAsync));
34 }
35 
36 // static
AddUrlHandler()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 */
Factory(net::URLRequest * request,const std::string & scheme)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 */
FinishPendingRequests()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 
URLRequestSlowDownloadJob(net::URLRequest * request)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 
StartAsync()74 void URLRequestSlowDownloadJob::StartAsync() {
75   if (LowerCaseEqualsASCII(kFinishDownloadUrl, request_->url().spec().c_str()))
76     URLRequestSlowDownloadJob::FinishPendingRequests();
77 
78   NotifyHeadersComplete();
79 }
80 
ReadRawData(net::IOBuffer * buf,int buf_size,int * bytes_read)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 
CheckDoneStatus()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.
GetResponseInfo(net::HttpResponseInfo * info)144 void URLRequestSlowDownloadJob::GetResponseInfo(net::HttpResponseInfo* info) {
145   // Forward to private const version.
146   GetResponseInfoConst(info);
147 }
148 
~URLRequestSlowDownloadJob()149 URLRequestSlowDownloadJob::~URLRequestSlowDownloadJob() {}
150 
151 // Private const version.
GetResponseInfoConst(net::HttpResponseInfo * info) const152 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 
GetMimeType(std::string * mime_type) const178 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