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