url_request_file_dir_job.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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 "net/url_request/url_request_file_dir_job.h" 6 7#include "base/bind.h" 8#include "base/compiler_specific.h" 9#include "base/message_loop.h" 10#include "base/strings/sys_string_conversions.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/time/time.h" 13#include "net/base/io_buffer.h" 14#include "net/base/net_errors.h" 15#include "net/base/net_util.h" 16#include "net/url_request/url_request_status.h" 17#include "url/gurl.h" 18 19#if defined(OS_POSIX) 20#include <sys/stat.h> 21#endif 22 23namespace net { 24 25URLRequestFileDirJob::URLRequestFileDirJob(URLRequest* request, 26 NetworkDelegate* network_delegate, 27 const base::FilePath& dir_path) 28 : URLRequestJob(request, network_delegate), 29 lister_(dir_path, this), 30 dir_path_(dir_path), 31 canceled_(false), 32 list_complete_(false), 33 wrote_header_(false), 34 read_pending_(false), 35 read_buffer_length_(0), 36 weak_factory_(this) { 37} 38 39void URLRequestFileDirJob::StartAsync() { 40 lister_.Start(); 41 42 NotifyHeadersComplete(); 43} 44 45void URLRequestFileDirJob::Start() { 46 // Start reading asynchronously so that all error reporting and data 47 // callbacks happen as they would for network requests. 48 base::MessageLoop::current()->PostTask( 49 FROM_HERE, 50 base::Bind(&URLRequestFileDirJob::StartAsync, 51 weak_factory_.GetWeakPtr())); 52} 53 54void URLRequestFileDirJob::Kill() { 55 if (canceled_) 56 return; 57 58 canceled_ = true; 59 60 if (!list_complete_) 61 lister_.Cancel(); 62 63 URLRequestJob::Kill(); 64 65 weak_factory_.InvalidateWeakPtrs(); 66} 67 68bool URLRequestFileDirJob::ReadRawData(IOBuffer* buf, int buf_size, 69 int* bytes_read) { 70 DCHECK(bytes_read); 71 *bytes_read = 0; 72 73 if (is_done()) 74 return true; 75 76 if (FillReadBuffer(buf->data(), buf_size, bytes_read)) 77 return true; 78 79 // We are waiting for more data 80 read_pending_ = true; 81 read_buffer_ = buf; 82 read_buffer_length_ = buf_size; 83 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); 84 return false; 85} 86 87bool URLRequestFileDirJob::GetMimeType(std::string* mime_type) const { 88 *mime_type = "text/html"; 89 return true; 90} 91 92bool URLRequestFileDirJob::GetCharset(std::string* charset) { 93 // All the filenames are converted to UTF-8 before being added. 94 *charset = "utf-8"; 95 return true; 96} 97 98void URLRequestFileDirJob::OnListFile( 99 const DirectoryLister::DirectoryListerData& data) { 100 // We wait to write out the header until we get the first file, so that we 101 // can catch errors from DirectoryLister and show an error page. 102 if (!wrote_header_) { 103#if defined(OS_WIN) 104 const base::string16& title = dir_path_.value(); 105#elif defined(OS_POSIX) 106 // TODO(jungshik): Add SysNativeMBToUTF16 to sys_string_conversions. 107 // On Mac, need to add NFKC->NFC conversion either here or in file_path. 108 // On Linux, the file system encoding is not defined, but we assume that 109 // SysNativeMBToWide takes care of it at least for now. We can try something 110 // more sophisticated if necessary later. 111 const base::string16& title = WideToUTF16( 112 base::SysNativeMBToWide(dir_path_.value())); 113#endif 114 data_.append(GetDirectoryListingHeader(title)); 115 wrote_header_ = true; 116 } 117 118#if defined(OS_WIN) 119 std::string raw_bytes; // Empty on Windows means UTF-8 encoded name. 120#elif defined(OS_POSIX) 121 // TOOD(jungshik): The same issue as for the directory name. 122 const std::string& raw_bytes = data.info.GetName().value(); 123#endif 124 data_.append(GetDirectoryListingEntry( 125 data.info.GetName().LossyDisplayName(), 126 raw_bytes, 127 data.info.IsDirectory(), 128 data.info.GetSize(), 129 data.info.GetLastModifiedTime())); 130 131 // TODO(darin): coalesce more? 132 CompleteRead(); 133} 134 135void URLRequestFileDirJob::OnListDone(int error) { 136 DCHECK(!canceled_); 137 if (error != OK) { 138 read_pending_ = false; 139 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, error)); 140 } else { 141 list_complete_ = true; 142 CompleteRead(); 143 } 144} 145 146URLRequestFileDirJob::~URLRequestFileDirJob() {} 147 148void URLRequestFileDirJob::CompleteRead() { 149 if (read_pending_) { 150 int bytes_read; 151 if (FillReadBuffer(read_buffer_->data(), read_buffer_length_, 152 &bytes_read)) { 153 // We completed the read, so reset the read buffer. 154 read_pending_ = false; 155 read_buffer_ = NULL; 156 read_buffer_length_ = 0; 157 158 SetStatus(URLRequestStatus()); 159 NotifyReadComplete(bytes_read); 160 } else { 161 NOTREACHED(); 162 // TODO: Better error code. 163 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 0)); 164 } 165 } 166} 167 168bool URLRequestFileDirJob::FillReadBuffer(char* buf, int buf_size, 169 int* bytes_read) { 170 DCHECK(bytes_read); 171 172 *bytes_read = 0; 173 174 int count = std::min(buf_size, static_cast<int>(data_.size())); 175 if (count) { 176 memcpy(buf, &data_[0], count); 177 data_.erase(0, count); 178 *bytes_read = count; 179 return true; 180 } else if (list_complete_) { 181 // EOF 182 return true; 183 } 184 return false; 185} 186 187} // namespace net 188