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.h"
6
7 #include "base/file_util.h"
8 #include "base/logging.h"
9 #include "base/string_util.h"
10 #include "net/base/file_stream.h"
11 #include "net/base/net_errors.h"
12
13 #ifdef ANDROID
14 #include "android/jni/platform_file_jni.h"
15 #endif
16
17 namespace net {
18
Element()19 UploadData::Element::Element()
20 : type_(TYPE_BYTES),
21 file_range_offset_(0),
22 file_range_length_(kuint64max),
23 is_last_chunk_(false),
24 override_content_length_(false),
25 content_length_computed_(false),
26 content_length_(-1),
27 file_stream_(NULL) {
28 }
29
~Element()30 UploadData::Element::~Element() {
31 // In the common case |file__stream_| will be null.
32 delete file_stream_;
33 }
34
SetToChunk(const char * bytes,int bytes_len,bool is_last_chunk)35 void UploadData::Element::SetToChunk(const char* bytes,
36 int bytes_len,
37 bool is_last_chunk) {
38 bytes_.clear();
39 bytes_.insert(bytes_.end(), bytes, bytes + bytes_len);
40 type_ = TYPE_CHUNK;
41 is_last_chunk_ = is_last_chunk;
42 }
43
GetContentLength()44 uint64 UploadData::Element::GetContentLength() {
45 if (override_content_length_ || content_length_computed_)
46 return content_length_;
47
48 if (type_ == TYPE_BYTES || type_ == TYPE_CHUNK)
49 return static_cast<uint64>(bytes_.size());
50 else if (type_ == TYPE_BLOB)
51 // The blob reference will be resolved later.
52 return 0;
53
54 DCHECK_EQ(TYPE_FILE, type_);
55 DCHECK(!file_stream_);
56
57 // TODO(darin): This size calculation could be out of sync with the state of
58 // the file when we get around to reading it. We should probably find a way
59 // to lock the file or somehow protect against this error condition.
60
61 content_length_computed_ = true;
62 content_length_ = 0;
63
64 #ifdef ANDROID
65 if (file_path_.value().find("content://") == 0) {
66 content_length_computed_ = true;
67 content_length_ = android::contentUrlSize(file_path_);
68 return content_length_;
69 }
70 #endif
71
72 // We need to open the file here to decide if we should report the file's
73 // size or zero. We cache the open file, so that we can still read it when
74 // it comes time to.
75 file_stream_ = NewFileStreamForReading();
76 if (!file_stream_)
77 return 0;
78
79 int64 length = 0;
80 if (!file_util::GetFileSize(file_path_, &length))
81 return 0;
82
83 if (file_range_offset_ >= static_cast<uint64>(length))
84 return 0; // range is beyond eof
85
86 // compensate for the offset and clip file_range_length_ to eof
87 content_length_ = std::min(length - file_range_offset_, file_range_length_);
88 return content_length_;
89 }
90
NewFileStreamForReading()91 FileStream* UploadData::Element::NewFileStreamForReading() {
92 // In common usage GetContentLength() will call this first and store the
93 // result into |file_| and a subsequent call (from UploadDataStream) will
94 // get the cached open FileStream.
95 if (file_stream_) {
96 FileStream* file = file_stream_;
97 file_stream_ = NULL;
98 return file;
99 }
100
101 scoped_ptr<FileStream> file(new FileStream());
102 int64 rv = file->Open(file_path_,
103 base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
104 if (rv != OK) {
105 // If the file can't be opened, we'll just upload an empty file.
106 DLOG(WARNING) << "Failed to open \"" << file_path_.value()
107 << "\" for reading: " << rv;
108 return NULL;
109 }
110 if (file_range_offset_) {
111 rv = file->Seek(FROM_BEGIN, file_range_offset_);
112 if (rv < 0) {
113 DLOG(WARNING) << "Failed to seek \"" << file_path_.value()
114 << "\" to offset: " << file_range_offset_ << " (" << rv
115 << ")";
116 return NULL;
117 }
118 }
119
120 return file.release();
121 }
122
UploadData()123 UploadData::UploadData()
124 : identifier_(0),
125 chunk_callback_(NULL),
126 is_chunked_(false) {
127 }
128
AppendBytes(const char * bytes,int bytes_len)129 void UploadData::AppendBytes(const char* bytes, int bytes_len) {
130 DCHECK(!is_chunked_);
131 if (bytes_len > 0) {
132 elements_.push_back(Element());
133 elements_.back().SetToBytes(bytes, bytes_len);
134 }
135 }
136
AppendFile(const FilePath & file_path)137 void UploadData::AppendFile(const FilePath& file_path) {
138 DCHECK(!is_chunked_);
139 elements_.push_back(Element());
140 elements_.back().SetToFilePath(file_path);
141 }
142
AppendFileRange(const FilePath & file_path,uint64 offset,uint64 length,const base::Time & expected_modification_time)143 void UploadData::AppendFileRange(const FilePath& file_path,
144 uint64 offset, uint64 length,
145 const base::Time& expected_modification_time) {
146 DCHECK(!is_chunked_);
147 elements_.push_back(Element());
148 elements_.back().SetToFilePathRange(file_path, offset, length,
149 expected_modification_time);
150 }
151
AppendBlob(const GURL & blob_url)152 void UploadData::AppendBlob(const GURL& blob_url) {
153 DCHECK(!is_chunked_);
154 elements_.push_back(Element());
155 elements_.back().SetToBlobUrl(blob_url);
156 }
157
AppendChunk(const char * bytes,int bytes_len,bool is_last_chunk)158 void UploadData::AppendChunk(const char* bytes,
159 int bytes_len,
160 bool is_last_chunk) {
161 DCHECK(is_chunked_);
162 elements_.push_back(Element());
163 elements_.back().SetToChunk(bytes, bytes_len, is_last_chunk);
164 if (chunk_callback_)
165 chunk_callback_->OnChunkAvailable();
166 }
167
set_chunk_callback(ChunkCallback * callback)168 void UploadData::set_chunk_callback(ChunkCallback* callback) {
169 chunk_callback_ = callback;
170 }
171
GetContentLength()172 uint64 UploadData::GetContentLength() {
173 uint64 len = 0;
174 std::vector<Element>::iterator it = elements_.begin();
175 for (; it != elements_.end(); ++it)
176 len += (*it).GetContentLength();
177 return len;
178 }
179
SetElements(const std::vector<Element> & elements)180 void UploadData::SetElements(const std::vector<Element>& elements) {
181 elements_ = elements;
182 }
183
~UploadData()184 UploadData::~UploadData() {
185 }
186
187 } // namespace net
188