1 // Copyright (c) 2012 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 "webkit/browser/fileapi/file_system_url_request_job.h" 6 7 #include <vector> 8 9 #include "base/bind.h" 10 #include "base/compiler_specific.h" 11 #include "base/files/file_path.h" 12 #include "base/files/file_util_proxy.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/message_loop/message_loop_proxy.h" 15 #include "base/threading/thread_restrictions.h" 16 #include "base/time/time.h" 17 #include "build/build_config.h" 18 #include "net/base/file_stream.h" 19 #include "net/base/io_buffer.h" 20 #include "net/base/mime_util.h" 21 #include "net/base/net_errors.h" 22 #include "net/base/net_util.h" 23 #include "net/http/http_response_headers.h" 24 #include "net/http/http_response_info.h" 25 #include "net/http/http_util.h" 26 #include "net/url_request/url_request.h" 27 #include "url/gurl.h" 28 #include "webkit/browser/blob/file_stream_reader.h" 29 #include "webkit/browser/fileapi/file_system_context.h" 30 #include "webkit/browser/fileapi/file_system_operation_runner.h" 31 #include "webkit/common/fileapi/file_system_util.h" 32 33 using net::NetworkDelegate; 34 using net::URLRequest; 35 using net::URLRequestJob; 36 using net::URLRequestStatus; 37 38 namespace fileapi { 39 40 static net::HttpResponseHeaders* CreateHttpResponseHeaders() { 41 // HttpResponseHeaders expects its input string to be terminated by two NULs. 42 static const char kStatus[] = "HTTP/1.1 200 OK\0"; 43 static const size_t kStatusLen = arraysize(kStatus); 44 45 net::HttpResponseHeaders* headers = 46 new net::HttpResponseHeaders(std::string(kStatus, kStatusLen)); 47 48 // Tell WebKit never to cache this content. 49 std::string cache_control(net::HttpRequestHeaders::kCacheControl); 50 cache_control.append(": no-cache"); 51 headers->AddHeader(cache_control); 52 53 return headers; 54 } 55 56 FileSystemURLRequestJob::FileSystemURLRequestJob( 57 URLRequest* request, 58 NetworkDelegate* network_delegate, 59 const std::string& storage_domain, 60 FileSystemContext* file_system_context) 61 : URLRequestJob(request, network_delegate), 62 storage_domain_(storage_domain), 63 file_system_context_(file_system_context), 64 is_directory_(false), 65 remaining_bytes_(0), 66 weak_factory_(this) { 67 } 68 69 FileSystemURLRequestJob::~FileSystemURLRequestJob() {} 70 71 void FileSystemURLRequestJob::Start() { 72 base::MessageLoop::current()->PostTask( 73 FROM_HERE, 74 base::Bind(&FileSystemURLRequestJob::StartAsync, 75 weak_factory_.GetWeakPtr())); 76 } 77 78 void FileSystemURLRequestJob::Kill() { 79 reader_.reset(); 80 URLRequestJob::Kill(); 81 weak_factory_.InvalidateWeakPtrs(); 82 } 83 84 bool FileSystemURLRequestJob::ReadRawData(net::IOBuffer* dest, int dest_size, 85 int *bytes_read) { 86 DCHECK_NE(dest_size, 0); 87 DCHECK(bytes_read); 88 DCHECK_GE(remaining_bytes_, 0); 89 90 if (reader_.get() == NULL) 91 return false; 92 93 if (remaining_bytes_ < dest_size) 94 dest_size = static_cast<int>(remaining_bytes_); 95 96 if (!dest_size) { 97 *bytes_read = 0; 98 return true; 99 } 100 101 const int rv = reader_->Read(dest, dest_size, 102 base::Bind(&FileSystemURLRequestJob::DidRead, 103 weak_factory_.GetWeakPtr())); 104 if (rv >= 0) { 105 // Data is immediately available. 106 *bytes_read = rv; 107 remaining_bytes_ -= rv; 108 DCHECK_GE(remaining_bytes_, 0); 109 return true; 110 } 111 if (rv == net::ERR_IO_PENDING) 112 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); 113 else 114 NotifyFailed(rv); 115 return false; 116 } 117 118 bool FileSystemURLRequestJob::GetMimeType(std::string* mime_type) const { 119 DCHECK(request_); 120 DCHECK(url_.is_valid()); 121 base::FilePath::StringType extension = url_.path().Extension(); 122 if (!extension.empty()) 123 extension = extension.substr(1); 124 return net::GetWellKnownMimeTypeFromExtension(extension, mime_type); 125 } 126 127 void FileSystemURLRequestJob::SetExtraRequestHeaders( 128 const net::HttpRequestHeaders& headers) { 129 std::string range_header; 130 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) { 131 std::vector<net::HttpByteRange> ranges; 132 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) { 133 if (ranges.size() == 1) { 134 byte_range_ = ranges[0]; 135 } else { 136 // We don't support multiple range requests in one single URL request. 137 // TODO(adamk): decide whether we want to support multiple range 138 // requests. 139 NotifyFailed(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE); 140 } 141 } 142 } 143 } 144 145 void FileSystemURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) { 146 if (response_info_) 147 *info = *response_info_; 148 } 149 150 int FileSystemURLRequestJob::GetResponseCode() const { 151 if (response_info_) 152 return 200; 153 return URLRequestJob::GetResponseCode(); 154 } 155 156 void FileSystemURLRequestJob::StartAsync() { 157 if (!request_) 158 return; 159 DCHECK(!reader_.get()); 160 url_ = file_system_context_->CrackURL(request_->url()); 161 if (!url_.is_valid()) { 162 file_system_context_->AttemptAutoMountForURLRequest( 163 request_, 164 storage_domain_, 165 base::Bind(&FileSystemURLRequestJob::DidAttemptAutoMount, 166 weak_factory_.GetWeakPtr())); 167 return; 168 } 169 if (!file_system_context_->CanServeURLRequest(url_)) { 170 // In incognito mode the API is not usable and there should be no data. 171 NotifyFailed(net::ERR_FILE_NOT_FOUND); 172 return; 173 } 174 file_system_context_->operation_runner()->GetMetadata( 175 url_, 176 base::Bind(&FileSystemURLRequestJob::DidGetMetadata, 177 weak_factory_.GetWeakPtr())); 178 } 179 180 void FileSystemURLRequestJob::DidAttemptAutoMount(base::File::Error result) { 181 if (result >= 0 && 182 file_system_context_->CrackURL(request_->url()).is_valid()) { 183 StartAsync(); 184 } else { 185 NotifyFailed(net::ERR_FILE_NOT_FOUND); 186 } 187 } 188 189 void FileSystemURLRequestJob::DidGetMetadata( 190 base::File::Error error_code, 191 const base::File::Info& file_info) { 192 if (error_code != base::File::FILE_OK) { 193 NotifyFailed(error_code == base::File::FILE_ERROR_INVALID_URL ? 194 net::ERR_INVALID_URL : net::ERR_FILE_NOT_FOUND); 195 return; 196 } 197 198 // We may have been orphaned... 199 if (!request_) 200 return; 201 202 is_directory_ = file_info.is_directory; 203 204 if (!byte_range_.ComputeBounds(file_info.size)) { 205 NotifyFailed(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE); 206 return; 207 } 208 209 if (is_directory_) { 210 NotifyHeadersComplete(); 211 return; 212 } 213 214 remaining_bytes_ = byte_range_.last_byte_position() - 215 byte_range_.first_byte_position() + 1; 216 DCHECK_GE(remaining_bytes_, 0); 217 218 DCHECK(!reader_.get()); 219 reader_ = file_system_context_->CreateFileStreamReader( 220 url_, byte_range_.first_byte_position(), base::Time()); 221 222 set_expected_content_size(remaining_bytes_); 223 response_info_.reset(new net::HttpResponseInfo()); 224 response_info_->headers = CreateHttpResponseHeaders(); 225 NotifyHeadersComplete(); 226 } 227 228 void FileSystemURLRequestJob::DidRead(int result) { 229 if (result > 0) 230 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status 231 else if (result == 0) 232 NotifyDone(URLRequestStatus()); 233 else 234 NotifyFailed(result); 235 236 remaining_bytes_ -= result; 237 DCHECK_GE(remaining_bytes_, 0); 238 239 NotifyReadComplete(result); 240 } 241 242 bool FileSystemURLRequestJob::IsRedirectResponse(GURL* location, 243 int* http_status_code) { 244 if (is_directory_) { 245 // This happens when we discovered the file is a directory, so needs a 246 // slash at the end of the path. 247 std::string new_path = request_->url().path(); 248 new_path.push_back('/'); 249 GURL::Replacements replacements; 250 replacements.SetPathStr(new_path); 251 *location = request_->url().ReplaceComponents(replacements); 252 *http_status_code = 301; // simulate a permanent redirect 253 return true; 254 } 255 256 return false; 257 } 258 259 void FileSystemURLRequestJob::NotifyFailed(int rv) { 260 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); 261 } 262 263 } // namespace fileapi 264