1 /*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include <algorithm>
12 #include "webrtc/base/common.h"
13 #include "webrtc/base/httpcommon.h"
14 #include "webrtc/base/multipart.h"
15
16 namespace rtc {
17
18 ///////////////////////////////////////////////////////////////////////////////
19 // MultipartStream
20 ///////////////////////////////////////////////////////////////////////////////
21
MultipartStream(const std::string & type,const std::string & boundary)22 MultipartStream::MultipartStream(const std::string& type,
23 const std::string& boundary)
24 : type_(type),
25 boundary_(boundary),
26 adding_(true),
27 current_(0),
28 position_(0) {
29 // The content type should be multipart/*.
30 ASSERT(0 == strncmp(type_.c_str(), "multipart/", 10));
31 }
32
~MultipartStream()33 MultipartStream::~MultipartStream() {
34 Close();
35 }
36
GetContentType(std::string * content_type)37 void MultipartStream::GetContentType(std::string* content_type) {
38 ASSERT(NULL != content_type);
39 content_type->assign(type_);
40 content_type->append("; boundary=");
41 content_type->append(boundary_);
42 }
43
AddPart(StreamInterface * data_stream,const std::string & content_disposition,const std::string & content_type)44 bool MultipartStream::AddPart(StreamInterface* data_stream,
45 const std::string& content_disposition,
46 const std::string& content_type) {
47 if (!AddPart("", content_disposition, content_type))
48 return false;
49 parts_.push_back(data_stream);
50 data_stream->SignalEvent.connect(this, &MultipartStream::OnEvent);
51 return true;
52 }
53
AddPart(const std::string & data,const std::string & content_disposition,const std::string & content_type)54 bool MultipartStream::AddPart(const std::string& data,
55 const std::string& content_disposition,
56 const std::string& content_type) {
57 ASSERT(adding_);
58 if (!adding_)
59 return false;
60 std::stringstream ss;
61 if (!parts_.empty()) {
62 ss << "\r\n";
63 }
64 ss << "--" << boundary_ << "\r\n";
65 if (!content_disposition.empty()) {
66 ss << ToString(HH_CONTENT_DISPOSITION) << ": "
67 << content_disposition << "\r\n";
68 }
69 if (!content_type.empty()) {
70 ss << ToString(HH_CONTENT_TYPE) << ": "
71 << content_type << "\r\n";
72 }
73 ss << "\r\n" << data;
74 parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size()));
75 return true;
76 }
77
EndParts()78 void MultipartStream::EndParts() {
79 ASSERT(adding_);
80 if (!adding_)
81 return;
82
83 std::stringstream ss;
84 if (!parts_.empty()) {
85 ss << "\r\n";
86 }
87 ss << "--" << boundary_ << "--" << "\r\n";
88 parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size()));
89
90 ASSERT(0 == current_);
91 ASSERT(0 == position_);
92 adding_ = false;
93 SignalEvent(this, SE_OPEN | SE_READ, 0);
94 }
95
GetPartSize(const std::string & data,const std::string & content_disposition,const std::string & content_type) const96 size_t MultipartStream::GetPartSize(const std::string& data,
97 const std::string& content_disposition,
98 const std::string& content_type) const {
99 size_t size = 0;
100 if (!parts_.empty()) {
101 size += 2; // for "\r\n";
102 }
103 size += boundary_.size() + 4; // for "--boundary_\r\n";
104 if (!content_disposition.empty()) {
105 // for ToString(HH_CONTENT_DISPOSITION): content_disposition\r\n
106 size += std::string(ToString(HH_CONTENT_DISPOSITION)).size() + 2 +
107 content_disposition.size() + 2;
108 }
109 if (!content_type.empty()) {
110 // for ToString(HH_CONTENT_TYPE): content_type\r\n
111 size += std::string(ToString(HH_CONTENT_TYPE)).size() + 2 +
112 content_type.size() + 2;
113 }
114 size += 2 + data.size(); // for \r\ndata
115 return size;
116 }
117
GetEndPartSize() const118 size_t MultipartStream::GetEndPartSize() const {
119 size_t size = 0;
120 if (!parts_.empty()) {
121 size += 2; // for "\r\n";
122 }
123 size += boundary_.size() + 6; // for "--boundary_--\r\n";
124 return size;
125 }
126
127 //
128 // StreamInterface
129 //
130
GetState() const131 StreamState MultipartStream::GetState() const {
132 if (adding_) {
133 return SS_OPENING;
134 }
135 return (current_ < parts_.size()) ? SS_OPEN : SS_CLOSED;
136 }
137
Read(void * buffer,size_t buffer_len,size_t * read,int * error)138 StreamResult MultipartStream::Read(void* buffer, size_t buffer_len,
139 size_t* read, int* error) {
140 if (adding_) {
141 return SR_BLOCK;
142 }
143 size_t local_read;
144 if (!read) read = &local_read;
145 while (current_ < parts_.size()) {
146 StreamResult result = parts_[current_]->Read(buffer, buffer_len, read,
147 error);
148 if (SR_EOS != result) {
149 if (SR_SUCCESS == result) {
150 position_ += *read;
151 }
152 return result;
153 }
154 ++current_;
155 }
156 return SR_EOS;
157 }
158
Write(const void * data,size_t data_len,size_t * written,int * error)159 StreamResult MultipartStream::Write(const void* data, size_t data_len,
160 size_t* written, int* error) {
161 if (error) {
162 *error = -1;
163 }
164 return SR_ERROR;
165 }
166
Close()167 void MultipartStream::Close() {
168 for (size_t i = 0; i < parts_.size(); ++i) {
169 delete parts_[i];
170 }
171 parts_.clear();
172 adding_ = false;
173 current_ = 0;
174 position_ = 0;
175 }
176
SetPosition(size_t position)177 bool MultipartStream::SetPosition(size_t position) {
178 if (adding_) {
179 return false;
180 }
181 size_t part_size, part_offset = 0;
182 for (size_t i = 0; i < parts_.size(); ++i) {
183 if (!parts_[i]->GetSize(&part_size)) {
184 return false;
185 }
186 if (part_offset + part_size > position) {
187 for (size_t j = i + 1; j < std::min(parts_.size(), current_ + 1); ++j) {
188 if (!parts_[j]->Rewind()) {
189 return false;
190 }
191 }
192 if (!parts_[i]->SetPosition(position - part_offset)) {
193 return false;
194 }
195 current_ = i;
196 position_ = position;
197 return true;
198 }
199 part_offset += part_size;
200 }
201 return false;
202 }
203
GetPosition(size_t * position) const204 bool MultipartStream::GetPosition(size_t* position) const {
205 if (position) {
206 *position = position_;
207 }
208 return true;
209 }
210
GetSize(size_t * size) const211 bool MultipartStream::GetSize(size_t* size) const {
212 size_t part_size, total_size = 0;
213 for (size_t i = 0; i < parts_.size(); ++i) {
214 if (!parts_[i]->GetSize(&part_size)) {
215 return false;
216 }
217 total_size += part_size;
218 }
219 if (size) {
220 *size = total_size;
221 }
222 return true;
223 }
224
GetAvailable(size_t * size) const225 bool MultipartStream::GetAvailable(size_t* size) const {
226 if (adding_) {
227 return false;
228 }
229 size_t part_size, total_size = 0;
230 for (size_t i = current_; i < parts_.size(); ++i) {
231 if (!parts_[i]->GetAvailable(&part_size)) {
232 return false;
233 }
234 total_size += part_size;
235 }
236 if (size) {
237 *size = total_size;
238 }
239 return true;
240 }
241
242 //
243 // StreamInterface Slots
244 //
245
OnEvent(StreamInterface * stream,int events,int error)246 void MultipartStream::OnEvent(StreamInterface* stream, int events, int error) {
247 if (adding_ || (current_ >= parts_.size()) || (parts_[current_] != stream)) {
248 return;
249 }
250 SignalEvent(this, events, error);
251 }
252
253 } // namespace rtc
254