blob: 0d73880e46f9680045e7314ac14df4a7639dc627 [file] [log] [blame]
henrike@webrtc.orgf7795df2014-05-13 18:00:26 +00001/*
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
12#include "webrtc/base/common.h"
13#include "webrtc/base/httpcommon.h"
14#include "webrtc/base/multipart.h"
15
16namespace rtc {
17
18///////////////////////////////////////////////////////////////////////////////
19// MultipartStream
20///////////////////////////////////////////////////////////////////////////////
21
22MultipartStream::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
33MultipartStream::~MultipartStream() {
34 Close();
35}
36
37void 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
44bool 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
54bool 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
78void 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
96size_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
118size_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
131StreamState MultipartStream::GetState() const {
132 if (adding_) {
133 return SS_OPENING;
134 }
135 return (current_ < parts_.size()) ? SS_OPEN : SS_CLOSED;
136}
137
138StreamResult 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
159StreamResult 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
167void 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
177bool 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 < _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
204bool MultipartStream::GetPosition(size_t* position) const {
205 if (position) {
206 *position = position_;
207 }
208 return true;
209}
210
211bool 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
225bool 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
246void 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