 28e2075280
			
		
	
	28e2075280
	
	
	
		
			
			trunk/talk git-svn-id: http://webrtc.googlecode.com/svn/trunk@4318 4adac7df-926f-26a2-2b94-8c16560cd09d
		
			
				
	
	
		
			269 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // libjingle
 | |
| // Copyright 2004--2010, Google Inc.
 | |
| //
 | |
| // Redistribution and use in source and binary forms, with or without
 | |
| // modification, are permitted provided that the following conditions are met:
 | |
| //
 | |
| //  1. Redistributions of source code must retain the above copyright notice,
 | |
| //     this list of conditions and the following disclaimer.
 | |
| //  2. Redistributions in binary form must reproduce the above copyright notice,
 | |
| //     this list of conditions and the following disclaimer in the documentation
 | |
| //     and/or other materials provided with the distribution.
 | |
| //  3. The name of the author may not be used to endorse or promote products
 | |
| //     derived from this software without specific prior written permission.
 | |
| //
 | |
| // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 | |
| // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | |
| // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 | |
| // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
| // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 | |
| // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 | |
| // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 | |
| // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 | |
| // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
| 
 | |
| 
 | |
| #include "talk/base/common.h"
 | |
| #include "talk/base/httpcommon.h"
 | |
| #include "talk/base/multipart.h"
 | |
| 
 | |
| namespace talk_base {
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////
 | |
| // 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 < _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 talk_base
 |