Home | History | Annotate | Download | only in url_request
      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/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 
     23 namespace net {
     24 
     25 URLRequestFileDirJob::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 
     39 void URLRequestFileDirJob::StartAsync() {
     40   lister_.Start();
     41 
     42   NotifyHeadersComplete();
     43 }
     44 
     45 void 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 
     54 void 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 
     68 bool 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 
     87 bool URLRequestFileDirJob::GetMimeType(std::string* mime_type) const {
     88   *mime_type = "text/html";
     89   return true;
     90 }
     91 
     92 bool 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 
     98 void 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 
    135 void 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 
    146 URLRequestFileDirJob::~URLRequestFileDirJob() {}
    147 
    148 void 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 
    168 bool 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