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