blob: d280ff33d1e028fff0cdf5c47c08a7493fb5bd37 [file] [log] [blame]
henrike@webrtc.org0e118e72013-07-10 00:45:36 +00001// libjingle
2// Copyright 2004--2010, Google Inc.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7// 1. Redistributions of source code must retain the above copyright notice,
8// this list of conditions and the following disclaimer.
9// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12// 3. The name of the author may not be used to endorse or promote products
13// derived from this software without specific prior written permission.
14//
15// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26
27#include "talk/base/common.h"
28#include "talk/base/httpcommon.h"
29#include "talk/base/multipart.h"
30
31namespace talk_base {
32
33///////////////////////////////////////////////////////////////////////////////
34// MultipartStream
35///////////////////////////////////////////////////////////////////////////////
36
37MultipartStream::MultipartStream(const std::string& type,
38 const std::string& boundary)
39 : type_(type),
40 boundary_(boundary),
41 adding_(true),
42 current_(0),
43 position_(0) {
44 // The content type should be multipart/*.
45 ASSERT(0 == strncmp(type_.c_str(), "multipart/", 10));
46}
47
48MultipartStream::~MultipartStream() {
49 Close();
50}
51
52void MultipartStream::GetContentType(std::string* content_type) {
53 ASSERT(NULL != content_type);
54 content_type->assign(type_);
55 content_type->append("; boundary=");
56 content_type->append(boundary_);
57}
58
59bool MultipartStream::AddPart(StreamInterface* data_stream,
60 const std::string& content_disposition,
61 const std::string& content_type) {
62 if (!AddPart("", content_disposition, content_type))
63 return false;
64 parts_.push_back(data_stream);
65 data_stream->SignalEvent.connect(this, &MultipartStream::OnEvent);
66 return true;
67}
68
69bool MultipartStream::AddPart(const std::string& data,
70 const std::string& content_disposition,
71 const std::string& content_type) {
72 ASSERT(adding_);
73 if (!adding_)
74 return false;
75 std::stringstream ss;
76 if (!parts_.empty()) {
77 ss << "\r\n";
78 }
79 ss << "--" << boundary_ << "\r\n";
80 if (!content_disposition.empty()) {
81 ss << ToString(HH_CONTENT_DISPOSITION) << ": "
82 << content_disposition << "\r\n";
83 }
84 if (!content_type.empty()) {
85 ss << ToString(HH_CONTENT_TYPE) << ": "
86 << content_type << "\r\n";
87 }
88 ss << "\r\n" << data;
89 parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size()));
90 return true;
91}
92
93void MultipartStream::EndParts() {
94 ASSERT(adding_);
95 if (!adding_)
96 return;
97
98 std::stringstream ss;
99 if (!parts_.empty()) {
100 ss << "\r\n";
101 }
102 ss << "--" << boundary_ << "--" << "\r\n";
103 parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size()));
104
105 ASSERT(0 == current_);
106 ASSERT(0 == position_);
107 adding_ = false;
108 SignalEvent(this, SE_OPEN | SE_READ, 0);
109}
110
111size_t MultipartStream::GetPartSize(const std::string& data,
112 const std::string& content_disposition,
113 const std::string& content_type) const {
114 size_t size = 0;
115 if (!parts_.empty()) {
116 size += 2; // for "\r\n";
117 }
118 size += boundary_.size() + 4; // for "--boundary_\r\n";
119 if (!content_disposition.empty()) {
120 // for ToString(HH_CONTENT_DISPOSITION): content_disposition\r\n
121 size += std::string(ToString(HH_CONTENT_DISPOSITION)).size() + 2 +
122 content_disposition.size() + 2;
123 }
124 if (!content_type.empty()) {
125 // for ToString(HH_CONTENT_TYPE): content_type\r\n
126 size += std::string(ToString(HH_CONTENT_TYPE)).size() + 2 +
127 content_type.size() + 2;
128 }
129 size += 2 + data.size(); // for \r\ndata
130 return size;
131}
132
133size_t MultipartStream::GetEndPartSize() const {
134 size_t size = 0;
135 if (!parts_.empty()) {
136 size += 2; // for "\r\n";
137 }
138 size += boundary_.size() + 6; // for "--boundary_--\r\n";
139 return size;
140}
141
142//
143// StreamInterface
144//
145
146StreamState MultipartStream::GetState() const {
147 if (adding_) {
148 return SS_OPENING;
149 }
150 return (current_ < parts_.size()) ? SS_OPEN : SS_CLOSED;
151}
152
153StreamResult MultipartStream::Read(void* buffer, size_t buffer_len,
154 size_t* read, int* error) {
155 if (adding_) {
156 return SR_BLOCK;
157 }
158 size_t local_read;
159 if (!read) read = &local_read;
160 while (current_ < parts_.size()) {
161 StreamResult result = parts_[current_]->Read(buffer, buffer_len, read,
162 error);
163 if (SR_EOS != result) {
164 if (SR_SUCCESS == result) {
165 position_ += *read;
166 }
167 return result;
168 }
169 ++current_;
170 }
171 return SR_EOS;
172}
173
174StreamResult MultipartStream::Write(const void* data, size_t data_len,
175 size_t* written, int* error) {
176 if (error) {
177 *error = -1;
178 }
179 return SR_ERROR;
180}
181
182void MultipartStream::Close() {
183 for (size_t i = 0; i < parts_.size(); ++i) {
184 delete parts_[i];
185 }
186 parts_.clear();
187 adding_ = false;
188 current_ = 0;
189 position_ = 0;
190}
191
192bool MultipartStream::SetPosition(size_t position) {
193 if (adding_) {
194 return false;
195 }
196 size_t part_size, part_offset = 0;
197 for (size_t i = 0; i < parts_.size(); ++i) {
198 if (!parts_[i]->GetSize(&part_size)) {
199 return false;
200 }
201 if (part_offset + part_size > position) {
202 for (size_t j = i+1; j < _min(parts_.size(), current_+1); ++j) {
203 if (!parts_[j]->Rewind()) {
204 return false;
205 }
206 }
207 if (!parts_[i]->SetPosition(position - part_offset)) {
208 return false;
209 }
210 current_ = i;
211 position_ = position;
212 return true;
213 }
214 part_offset += part_size;
215 }
216 return false;
217}
218
219bool MultipartStream::GetPosition(size_t* position) const {
220 if (position) {
221 *position = position_;
222 }
223 return true;
224}
225
226bool MultipartStream::GetSize(size_t* size) const {
227 size_t part_size, total_size = 0;
228 for (size_t i = 0; i < parts_.size(); ++i) {
229 if (!parts_[i]->GetSize(&part_size)) {
230 return false;
231 }
232 total_size += part_size;
233 }
234 if (size) {
235 *size = total_size;
236 }
237 return true;
238}
239
240bool MultipartStream::GetAvailable(size_t* size) const {
241 if (adding_) {
242 return false;
243 }
244 size_t part_size, total_size = 0;
245 for (size_t i = current_; i < parts_.size(); ++i) {
246 if (!parts_[i]->GetAvailable(&part_size)) {
247 return false;
248 }
249 total_size += part_size;
250 }
251 if (size) {
252 *size = total_size;
253 }
254 return true;
255}
256
257//
258// StreamInterface Slots
259//
260
261void MultipartStream::OnEvent(StreamInterface* stream, int events, int error) {
262 if (adding_ || (current_ >= parts_.size()) || (parts_[current_] != stream)) {
263 return;
264 }
265 SignalEvent(this, events, error);
266}
267
268} // namespace talk_base