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