| OLD | NEW |
| (Empty) |
| 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 storage { | |
| 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 storage | |
| OLD | NEW |