1 // Copyright 2014 The Chromium Authors
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/chunked_upload_data_stream.h"
6
7 #include "base/check_op.h"
8 #include "base/memory/ptr_util.h"
9 #include "net/base/io_buffer.h"
10 #include "net/base/net_errors.h"
11
12 namespace net {
13
14 ChunkedUploadDataStream::Writer::~Writer() = default;
15
AppendData(const char * data,int data_len,bool is_done)16 bool ChunkedUploadDataStream::Writer::AppendData(const char* data,
17 int data_len,
18 bool is_done) {
19 if (!upload_data_stream_)
20 return false;
21 upload_data_stream_->AppendData(data, data_len, is_done);
22 return true;
23 }
24
Writer(base::WeakPtr<ChunkedUploadDataStream> upload_data_stream)25 ChunkedUploadDataStream::Writer::Writer(
26 base::WeakPtr<ChunkedUploadDataStream> upload_data_stream)
27 : upload_data_stream_(upload_data_stream) {}
28
ChunkedUploadDataStream(int64_t identifier,bool has_null_source)29 ChunkedUploadDataStream::ChunkedUploadDataStream(int64_t identifier,
30 bool has_null_source)
31 : UploadDataStream(/*is_chunked=*/true, has_null_source, identifier) {}
32
33 ChunkedUploadDataStream::~ChunkedUploadDataStream() = default;
34
35 std::unique_ptr<ChunkedUploadDataStream::Writer>
CreateWriter()36 ChunkedUploadDataStream::CreateWriter() {
37 return base::WrapUnique(new Writer(weak_factory_.GetWeakPtr()));
38 }
39
AppendData(const char * data,int data_len,bool is_done)40 void ChunkedUploadDataStream::AppendData(
41 const char* data, int data_len, bool is_done) {
42 DCHECK(!all_data_appended_);
43 DCHECK(data_len > 0 || is_done);
44 if (data_len > 0) {
45 DCHECK(data);
46 upload_data_.push_back(
47 std::make_unique<std::vector<char>>(data, data + data_len));
48 }
49 all_data_appended_ = is_done;
50
51 if (!read_buffer_.get())
52 return;
53
54 int result = ReadChunk(read_buffer_.get(), read_buffer_len_);
55 // Shouldn't get an error or ERR_IO_PENDING.
56 DCHECK_GE(result, 0);
57 read_buffer_ = nullptr;
58 read_buffer_len_ = 0;
59 OnReadCompleted(result);
60 }
61
InitInternal(const NetLogWithSource & net_log)62 int ChunkedUploadDataStream::InitInternal(const NetLogWithSource& net_log) {
63 // ResetInternal should already have been called.
64 DCHECK(!read_buffer_.get());
65 DCHECK_EQ(0u, read_index_);
66 DCHECK_EQ(0u, read_offset_);
67 return OK;
68 }
69
ReadInternal(IOBuffer * buf,int buf_len)70 int ChunkedUploadDataStream::ReadInternal(IOBuffer* buf, int buf_len) {
71 DCHECK_LT(0, buf_len);
72 DCHECK(!read_buffer_.get());
73
74 int result = ReadChunk(buf, buf_len);
75 if (result == ERR_IO_PENDING) {
76 read_buffer_ = buf;
77 read_buffer_len_ = buf_len;
78 }
79 return result;
80 }
81
ResetInternal()82 void ChunkedUploadDataStream::ResetInternal() {
83 read_buffer_ = nullptr;
84 read_buffer_len_ = 0;
85 read_index_ = 0;
86 read_offset_ = 0;
87 }
88
ReadChunk(IOBuffer * buf,int buf_len)89 int ChunkedUploadDataStream::ReadChunk(IOBuffer* buf, int buf_len) {
90 // Copy as much data as possible from |upload_data_| to |buf|.
91 int bytes_read = 0;
92 while (read_index_ < upload_data_.size() && bytes_read < buf_len) {
93 std::vector<char>* data = upload_data_[read_index_].get();
94 size_t bytes_to_read =
95 std::min(static_cast<size_t>(buf_len - bytes_read),
96 data->size() - read_offset_);
97 memcpy(buf->data() + bytes_read, data->data() + read_offset_,
98 bytes_to_read);
99 bytes_read += bytes_to_read;
100 read_offset_ += bytes_to_read;
101 if (read_offset_ == data->size()) {
102 read_index_++;
103 read_offset_ = 0;
104 }
105 }
106 DCHECK_LE(bytes_read, buf_len);
107
108 // If no data was written, and not all data has been appended, return
109 // ERR_IO_PENDING. The read will be completed in the next call to AppendData.
110 if (bytes_read == 0 && !all_data_appended_)
111 return ERR_IO_PENDING;
112
113 if (read_index_ == upload_data_.size() && all_data_appended_)
114 SetIsFinalChunk();
115 return bytes_read;
116 }
117
118 } // namespace net
119