url_request_file_dir_job.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/utf_string_conversions.h" 12#include "base/time.h" 13#include "googleurl/src/gurl.h" 14#include "net/base/io_buffer.h" 15#include "net/base/net_errors.h" 16#include "net/base/net_util.h" 17#include "net/url_request/url_request_status.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 int64 size = (static_cast<unsigned __int64>(data.info.nFileSizeHigh) << 32) | 120 data.info.nFileSizeLow; 121 122 // Note that we should not convert ftLastWriteTime to the local time because 123 // ICU's datetime formatting APIs expect time in UTC and take into account 124 // the timezone before formatting. 125 data_.append(GetDirectoryListingEntry( 126 data.info.cFileName, 127 std::string(), 128 ((data.info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0), 129 size, 130 base::Time::FromFileTime(data.info.ftLastWriteTime))); 131#elif defined(OS_POSIX) 132 // TOOD(jungshik): The same issue as for the directory name. 133 data_.append(GetDirectoryListingEntry( 134 WideToUTF16(base::SysNativeMBToWide(data.info.filename)), 135 data.info.filename, 136 S_ISDIR(data.info.stat.st_mode), 137 data.info.stat.st_size, 138 base::Time::FromTimeT(data.info.stat.st_mtime))); 139#endif 140 141 // TODO(darin): coalesce more? 142 CompleteRead(); 143} 144 145void URLRequestFileDirJob::OnListDone(int error) { 146 DCHECK(!canceled_); 147 if (error != OK) { 148 read_pending_ = false; 149 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, error)); 150 } else { 151 list_complete_ = true; 152 CompleteRead(); 153 } 154} 155 156URLRequestFileDirJob::~URLRequestFileDirJob() {} 157 158void URLRequestFileDirJob::CompleteRead() { 159 if (read_pending_) { 160 int bytes_read; 161 if (FillReadBuffer(read_buffer_->data(), read_buffer_length_, 162 &bytes_read)) { 163 // We completed the read, so reset the read buffer. 164 read_pending_ = false; 165 read_buffer_ = NULL; 166 read_buffer_length_ = 0; 167 168 SetStatus(URLRequestStatus()); 169 NotifyReadComplete(bytes_read); 170 } else { 171 NOTREACHED(); 172 // TODO: Better error code. 173 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 0)); 174 } 175 } 176} 177 178bool URLRequestFileDirJob::FillReadBuffer(char* buf, int buf_size, 179 int* bytes_read) { 180 DCHECK(bytes_read); 181 182 *bytes_read = 0; 183 184 int count = std::min(buf_size, static_cast<int>(data_.size())); 185 if (count) { 186 memcpy(buf, &data_[0], count); 187 data_.erase(0, count); 188 *bytes_read = count; 189 return true; 190 } else if (list_complete_) { 191 // EOF 192 return true; 193 } 194 return false; 195} 196 197} // namespace net 198