|  | /* | 
|  | *  Copyright 2004 The WebRTC Project Authors. All rights reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include <algorithm> | 
|  | #include "webrtc/base/common.h" | 
|  | #include "webrtc/base/httpcommon.h" | 
|  | #include "webrtc/base/multipart.h" | 
|  |  | 
|  | namespace rtc { | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  | // MultipartStream | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | MultipartStream::MultipartStream(const std::string& type, | 
|  | const std::string& boundary) | 
|  | : type_(type), | 
|  | boundary_(boundary), | 
|  | adding_(true), | 
|  | current_(0), | 
|  | position_(0) { | 
|  | // The content type should be multipart/*. | 
|  | ASSERT(0 == strncmp(type_.c_str(), "multipart/", 10)); | 
|  | } | 
|  |  | 
|  | MultipartStream::~MultipartStream() { | 
|  | Close(); | 
|  | } | 
|  |  | 
|  | void MultipartStream::GetContentType(std::string* content_type) { | 
|  | ASSERT(NULL != content_type); | 
|  | content_type->assign(type_); | 
|  | content_type->append("; boundary="); | 
|  | content_type->append(boundary_); | 
|  | } | 
|  |  | 
|  | bool MultipartStream::AddPart(StreamInterface* data_stream, | 
|  | const std::string& content_disposition, | 
|  | const std::string& content_type) { | 
|  | if (!AddPart("", content_disposition, content_type)) | 
|  | return false; | 
|  | parts_.push_back(data_stream); | 
|  | data_stream->SignalEvent.connect(this, &MultipartStream::OnEvent); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MultipartStream::AddPart(const std::string& data, | 
|  | const std::string& content_disposition, | 
|  | const std::string& content_type) { | 
|  | ASSERT(adding_); | 
|  | if (!adding_) | 
|  | return false; | 
|  | std::stringstream ss; | 
|  | if (!parts_.empty()) { | 
|  | ss << "\r\n"; | 
|  | } | 
|  | ss << "--" << boundary_ << "\r\n"; | 
|  | if (!content_disposition.empty()) { | 
|  | ss << ToString(HH_CONTENT_DISPOSITION) << ": " | 
|  | << content_disposition << "\r\n"; | 
|  | } | 
|  | if (!content_type.empty()) { | 
|  | ss << ToString(HH_CONTENT_TYPE) << ": " | 
|  | << content_type << "\r\n"; | 
|  | } | 
|  | ss << "\r\n" << data; | 
|  | parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void MultipartStream::EndParts() { | 
|  | ASSERT(adding_); | 
|  | if (!adding_) | 
|  | return; | 
|  |  | 
|  | std::stringstream ss; | 
|  | if (!parts_.empty()) { | 
|  | ss << "\r\n"; | 
|  | } | 
|  | ss << "--" << boundary_ << "--" << "\r\n"; | 
|  | parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size())); | 
|  |  | 
|  | ASSERT(0 == current_); | 
|  | ASSERT(0 == position_); | 
|  | adding_ = false; | 
|  | SignalEvent(this, SE_OPEN | SE_READ, 0); | 
|  | } | 
|  |  | 
|  | size_t MultipartStream::GetPartSize(const std::string& data, | 
|  | const std::string& content_disposition, | 
|  | const std::string& content_type) const { | 
|  | size_t size = 0; | 
|  | if (!parts_.empty()) { | 
|  | size += 2;  // for "\r\n"; | 
|  | } | 
|  | size += boundary_.size() + 4;  // for "--boundary_\r\n"; | 
|  | if (!content_disposition.empty()) { | 
|  | // for ToString(HH_CONTENT_DISPOSITION): content_disposition\r\n | 
|  | size += std::string(ToString(HH_CONTENT_DISPOSITION)).size() + 2 + | 
|  | content_disposition.size() + 2; | 
|  | } | 
|  | if (!content_type.empty()) { | 
|  | // for ToString(HH_CONTENT_TYPE): content_type\r\n | 
|  | size += std::string(ToString(HH_CONTENT_TYPE)).size() + 2 + | 
|  | content_type.size() + 2; | 
|  | } | 
|  | size += 2 + data.size();  // for \r\ndata | 
|  | return size; | 
|  | } | 
|  |  | 
|  | size_t MultipartStream::GetEndPartSize() const { | 
|  | size_t size = 0; | 
|  | if (!parts_.empty()) { | 
|  | size += 2;  // for "\r\n"; | 
|  | } | 
|  | size += boundary_.size() + 6;  // for "--boundary_--\r\n"; | 
|  | return size; | 
|  | } | 
|  |  | 
|  | // | 
|  | // StreamInterface | 
|  | // | 
|  |  | 
|  | StreamState MultipartStream::GetState() const { | 
|  | if (adding_) { | 
|  | return SS_OPENING; | 
|  | } | 
|  | return (current_ < parts_.size()) ? SS_OPEN : SS_CLOSED; | 
|  | } | 
|  |  | 
|  | StreamResult MultipartStream::Read(void* buffer, size_t buffer_len, | 
|  | size_t* read, int* error) { | 
|  | if (adding_) { | 
|  | return SR_BLOCK; | 
|  | } | 
|  | size_t local_read; | 
|  | if (!read) read = &local_read; | 
|  | while (current_ < parts_.size()) { | 
|  | StreamResult result = parts_[current_]->Read(buffer, buffer_len, read, | 
|  | error); | 
|  | if (SR_EOS != result) { | 
|  | if (SR_SUCCESS == result) { | 
|  | position_ += *read; | 
|  | } | 
|  | return result; | 
|  | } | 
|  | ++current_; | 
|  | } | 
|  | return SR_EOS; | 
|  | } | 
|  |  | 
|  | StreamResult MultipartStream::Write(const void* data, size_t data_len, | 
|  | size_t* written, int* error) { | 
|  | if (error) { | 
|  | *error = -1; | 
|  | } | 
|  | return SR_ERROR; | 
|  | } | 
|  |  | 
|  | void MultipartStream::Close() { | 
|  | for (size_t i = 0; i < parts_.size(); ++i) { | 
|  | delete parts_[i]; | 
|  | } | 
|  | parts_.clear(); | 
|  | adding_ = false; | 
|  | current_ = 0; | 
|  | position_ = 0; | 
|  | } | 
|  |  | 
|  | bool MultipartStream::SetPosition(size_t position) { | 
|  | if (adding_) { | 
|  | return false; | 
|  | } | 
|  | size_t part_size, part_offset = 0; | 
|  | for (size_t i = 0; i < parts_.size(); ++i) { | 
|  | if (!parts_[i]->GetSize(&part_size)) { | 
|  | return false; | 
|  | } | 
|  | if (part_offset + part_size > position) { | 
|  | for (size_t j = i + 1; j < std::min(parts_.size(), current_ + 1); ++j) { | 
|  | if (!parts_[j]->Rewind()) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | if (!parts_[i]->SetPosition(position - part_offset)) { | 
|  | return false; | 
|  | } | 
|  | current_ = i; | 
|  | position_ = position; | 
|  | return true; | 
|  | } | 
|  | part_offset += part_size; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool MultipartStream::GetPosition(size_t* position) const { | 
|  | if (position) { | 
|  | *position = position_; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MultipartStream::GetSize(size_t* size) const { | 
|  | size_t part_size, total_size = 0; | 
|  | for (size_t i = 0; i < parts_.size(); ++i) { | 
|  | if (!parts_[i]->GetSize(&part_size)) { | 
|  | return false; | 
|  | } | 
|  | total_size += part_size; | 
|  | } | 
|  | if (size) { | 
|  | *size = total_size; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MultipartStream::GetAvailable(size_t* size) const { | 
|  | if (adding_) { | 
|  | return false; | 
|  | } | 
|  | size_t part_size, total_size = 0; | 
|  | for (size_t i = current_; i < parts_.size(); ++i) { | 
|  | if (!parts_[i]->GetAvailable(&part_size)) { | 
|  | return false; | 
|  | } | 
|  | total_size += part_size; | 
|  | } | 
|  | if (size) { | 
|  | *size = total_size; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // | 
|  | // StreamInterface Slots | 
|  | // | 
|  |  | 
|  | void MultipartStream::OnEvent(StreamInterface* stream, int events, int error) { | 
|  | if (adding_ || (current_ >= parts_.size()) || (parts_[current_] != stream)) { | 
|  | return; | 
|  | } | 
|  | SignalEvent(this, events, error); | 
|  | } | 
|  |  | 
|  | }  // namespace rtc |