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