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 #include "net/base/upload_data_stream.h" 6 7 #include "base/file_util.h" 8 #include "base/logging.h" 9 #include "net/base/file_stream.h" 10 #include "net/base/io_buffer.h" 11 #include "net/base/net_errors.h" 12 13 namespace net { 14 15 bool UploadDataStream::merge_chunks_ = true; 16 17 UploadDataStream::~UploadDataStream() { 18 } 19 20 UploadDataStream* UploadDataStream::Create(UploadData* data, int* error_code) { 21 scoped_ptr<UploadDataStream> stream(new UploadDataStream(data)); 22 int rv = stream->FillBuf(); 23 if (error_code) 24 *error_code = rv; 25 if (rv != OK) 26 return NULL; 27 28 return stream.release(); 29 } 30 31 void UploadDataStream::MarkConsumedAndFillBuffer(size_t num_bytes) { 32 DCHECK_LE(num_bytes, buf_len_); 33 DCHECK(!eof_); 34 35 if (num_bytes) { 36 buf_len_ -= num_bytes; 37 if (buf_len_) 38 memmove(buf_->data(), buf_->data() + num_bytes, buf_len_); 39 } 40 41 FillBuf(); 42 43 current_position_ += num_bytes; 44 } 45 46 UploadDataStream::UploadDataStream(UploadData* data) 47 : data_(data), 48 buf_(new IOBuffer(kBufSize)), 49 buf_len_(0), 50 next_element_(0), 51 next_element_offset_(0), 52 next_element_remaining_(0), 53 total_size_(data->is_chunked() ? 0 : data->GetContentLength()), 54 current_position_(0), 55 eof_(false) { 56 } 57 58 int UploadDataStream::FillBuf() { 59 std::vector<UploadData::Element>& elements = *data_->elements(); 60 61 while (buf_len_ < kBufSize && next_element_ < elements.size()) { 62 bool advance_to_next_element = false; 63 64 UploadData::Element& element = elements[next_element_]; 65 66 size_t size_remaining = kBufSize - buf_len_; 67 if (element.type() == UploadData::TYPE_BYTES || 68 element.type() == UploadData::TYPE_CHUNK) { 69 const std::vector<char>& d = element.bytes(); 70 size_t count = d.size() - next_element_offset_; 71 72 size_t bytes_copied = std::min(count, size_remaining); 73 74 // Check if we have anything to copy first, because we are getting the 75 // address of an element in |d| and that will throw an exception if |d| 76 // is an empty vector. 77 if (bytes_copied) { 78 memcpy(buf_->data() + buf_len_, &d[next_element_offset_], bytes_copied); 79 buf_len_ += bytes_copied; 80 } 81 82 if (bytes_copied == count) { 83 advance_to_next_element = true; 84 } else { 85 next_element_offset_ += bytes_copied; 86 } 87 } else { 88 DCHECK(element.type() == UploadData::TYPE_FILE); 89 90 if (!next_element_remaining_) { 91 // If the underlying file has been changed, treat it as error. 92 // Note that the expected modification time from WebKit is based on 93 // time_t precision. So we have to convert both to time_t to compare. 94 if (!element.expected_file_modification_time().is_null()) { 95 base::PlatformFileInfo info; 96 if (file_util::GetFileInfo(element.file_path(), &info) && 97 element.expected_file_modification_time().ToTimeT() != 98 info.last_modified.ToTimeT()) { 99 return ERR_UPLOAD_FILE_CHANGED; 100 } 101 } 102 next_element_remaining_ = element.GetContentLength(); 103 #ifdef ANDROID 104 if (element.file_path().value().find("content://") == 0) { 105 next_element_java_stream_.reset( 106 new android::JavaISWrapper(element.file_path())); 107 } else 108 #endif 109 next_element_stream_.reset(element.NewFileStreamForReading()); 110 } 111 112 int rv = 0; 113 int count = 114 static_cast<int>(std::min(next_element_remaining_, 115 static_cast<uint64>(size_remaining))); 116 if (count > 0) { 117 #ifdef ANDROID 118 if (next_element_java_stream_.get()) 119 rv = next_element_java_stream_->Read(buf_->data() + buf_len_, count); 120 else { 121 #endif 122 if (next_element_stream_.get()) 123 rv = next_element_stream_->Read(buf_->data() + buf_len_, count, NULL); 124 #ifdef ANDROID 125 } 126 #endif 127 if (rv <= 0) { 128 // If there's less data to read than we initially observed, then 129 // pad with zero. Otherwise the server will hang waiting for the 130 // rest of the data. 131 memset(buf_->data() + buf_len_, 0, count); 132 rv = count; 133 } 134 buf_len_ += rv; 135 } 136 137 if (static_cast<int>(next_element_remaining_) == rv) { 138 advance_to_next_element = true; 139 } else { 140 next_element_remaining_ -= rv; 141 } 142 } 143 144 if (advance_to_next_element) { 145 ++next_element_; 146 next_element_offset_ = 0; 147 next_element_remaining_ = 0; 148 next_element_stream_.reset(); 149 } 150 151 if (is_chunked() && !merge_chunks_) 152 break; 153 } 154 155 if (next_element_ == elements.size() && !buf_len_) { 156 if (!data_->is_chunked() || 157 (!elements.empty() && elements.back().is_last_chunk())) { 158 eof_ = true; 159 } 160 } 161 162 return OK; 163 } 164 165 bool UploadDataStream::IsOnLastChunk() const { 166 const std::vector<UploadData::Element>& elements = *data_->elements(); 167 DCHECK(data_->is_chunked()); 168 return (eof_ || 169 (!elements.empty() && 170 next_element_ == elements.size() && 171 elements.back().is_last_chunk())); 172 } 173 174 } // namespace net 175