upload_data_stream.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 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
6
7#include "net/base/upload_data_stream.h"
8
9#include "base/file_util.h"
10#include "base/logging.h"
11#include "net/base/file_stream.h"
12#include "net/base/io_buffer.h"
13#include "net/base/net_errors.h"
14
15namespace net {
16
17UploadDataStream* UploadDataStream::Create(UploadData* data, int* error_code) {
18  scoped_ptr<UploadDataStream> stream(new UploadDataStream(data));
19  int rv = stream->FillBuf();
20  if (error_code)
21    *error_code = rv;
22  if (rv != OK)
23    return NULL;
24
25  return stream.release();
26}
27
28UploadDataStream::UploadDataStream(UploadData* data)
29    : data_(data),
30      buf_(new IOBuffer(kBufSize)),
31      buf_len_(0),
32      next_element_(data->elements()->begin()),
33      next_element_offset_(0),
34      next_element_remaining_(0),
35      total_size_(data->GetContentLength()),
36      current_position_(0),
37      eof_(false) {
38}
39
40UploadDataStream::~UploadDataStream() {
41}
42
43void UploadDataStream::DidConsume(size_t num_bytes) {
44  DCHECK_LE(num_bytes, buf_len_);
45  DCHECK(!eof_);
46
47  buf_len_ -= num_bytes;
48  if (buf_len_)
49    memmove(buf_->data(), buf_->data() + num_bytes, buf_len_);
50
51  FillBuf();
52
53  current_position_ += num_bytes;
54}
55
56int UploadDataStream::FillBuf() {
57  std::vector<UploadData::Element>::iterator end =
58      data_->elements()->end();
59
60  while (buf_len_ < kBufSize && next_element_ != end) {
61    bool advance_to_next_element = false;
62
63    UploadData::Element& element = *next_element_;
64
65    size_t size_remaining = kBufSize - buf_len_;
66    if (element.type() == UploadData::TYPE_BYTES) {
67      const std::vector<char>& d = element.bytes();
68      size_t count = d.size() - next_element_offset_;
69
70      size_t bytes_copied = std::min(count, size_remaining);
71
72      memcpy(buf_->data() + buf_len_, &d[next_element_offset_], bytes_copied);
73      buf_len_ += bytes_copied;
74
75      if (bytes_copied == count) {
76        advance_to_next_element = true;
77      } else {
78        next_element_offset_ += bytes_copied;
79      }
80    } else {
81      DCHECK(element.type() == UploadData::TYPE_FILE);
82
83      if (!next_element_remaining_) {
84        // If the underlying file has been changed, treat it as error.
85        // Note that the expected modification time from WebKit is based on
86        // time_t precision. So we have to convert both to time_t to compare.
87        if (!element.expected_file_modification_time().is_null()) {
88          base::PlatformFileInfo info;
89          if (file_util::GetFileInfo(element.file_path(), &info) &&
90              element.expected_file_modification_time().ToTimeT() !=
91                  info.last_modified.ToTimeT()) {
92            return ERR_UPLOAD_FILE_CHANGED;
93          }
94        }
95        next_element_remaining_ = element.GetContentLength();
96        next_element_stream_.reset(element.NewFileStreamForReading());
97      }
98
99      int rv = 0;
100      int count =
101          static_cast<int>(std::min(next_element_remaining_,
102                                    static_cast<uint64>(size_remaining)));
103      if (count > 0) {
104        if (next_element_stream_.get())
105          rv = next_element_stream_->Read(buf_->data() + buf_len_, count, NULL);
106        if (rv <= 0) {
107          // If there's less data to read than we initially observed, then
108          // pad with zero.  Otherwise the server will hang waiting for the
109          // rest of the data.
110          memset(buf_->data() + buf_len_, 0, count);
111          rv = count;
112        }
113        buf_len_ += rv;
114      }
115
116      if (static_cast<int>(next_element_remaining_) == rv) {
117        advance_to_next_element = true;
118      } else {
119        next_element_remaining_ -= rv;
120      }
121    }
122
123    if (advance_to_next_element) {
124      ++next_element_;
125      next_element_offset_ = 0;
126      next_element_remaining_ = 0;
127      next_element_stream_.reset();
128    }
129  }
130
131  if (next_element_ == end && !buf_len_)
132    eof_ = true;
133
134  return OK;
135}
136
137}  // namespace net
138