PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/http/multipart.cpp

http://github.com/mozy/mordor
C++ | 231 lines | 207 code | 22 blank | 2 comment | 41 complexity | 698c423218a288d7b2f19a5c5f66b44a MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "multipart.h"
  3. #include <boost/bind.hpp>
  4. #include "mordor/assert.h"
  5. #include "mordor/streams/buffer.h"
  6. #include "mordor/streams/buffered.h"
  7. #include "mordor/streams/notify.h"
  8. #include "mordor/streams/null.h"
  9. #include "mordor/streams/transfer.h"
  10. #include "parser.h"
  11. namespace Mordor {
  12. static const char allowedBoundaryChars[] =
  13. "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'()+_,-./:=?";
  14. std::string
  15. Multipart::randomBoundary()
  16. {
  17. std::string result;
  18. result.resize(40);
  19. for (size_t i = 0; i < 40; ++i)
  20. result[i] = allowedBoundaryChars[rand() % 74];
  21. return result;
  22. }
  23. Multipart::Multipart(Stream::ptr stream, std::string boundary)
  24. : m_stream(stream),
  25. m_boundary(boundary),
  26. m_finished(false),
  27. m_firstPart(true)
  28. {
  29. MORDOR_ASSERT(m_stream);
  30. MORDOR_ASSERT(m_stream->supportsRead() || m_stream->supportsWrite());
  31. MORDOR_ASSERT(!(m_stream->supportsRead() && m_stream->supportsWrite()));
  32. while (!m_boundary.empty() && m_boundary[m_boundary.size() - 1] == ' ')
  33. m_boundary.resize(m_boundary.size() - 1);
  34. MORDOR_ASSERT(!m_boundary.empty());
  35. MORDOR_ASSERT(m_boundary.size() <= 70);
  36. if (m_boundary.find_first_not_of(allowedBoundaryChars) != std::string::npos) {
  37. if (stream->supportsWrite()) {
  38. MORDOR_NOTREACHED();
  39. } else {
  40. MORDOR_THROW_EXCEPTION(InvalidMultipartBoundaryException());
  41. }
  42. }
  43. m_boundary = "--" + m_boundary;
  44. if (m_stream->supportsRead()) {
  45. if (!m_stream->supportsFind())
  46. m_stream.reset(new BufferedStream(m_stream));
  47. MORDOR_ASSERT(m_stream->supportsFind());
  48. MORDOR_ASSERT(m_stream->supportsUnread());
  49. }
  50. }
  51. BodyPart::ptr
  52. Multipart::nextPart()
  53. {
  54. if (m_stream->supportsWrite()) {
  55. MORDOR_ASSERT(!m_finished);
  56. MORDOR_ASSERT(!m_currentPart);
  57. m_currentPart.reset(new BodyPart(shared_from_this()));
  58. std::string boundary = m_boundary + "\r\n";
  59. m_stream->write(boundary.c_str(), boundary.size());
  60. if (m_firstPart) {
  61. m_firstPart = false;
  62. m_boundary = "\r\n" + m_boundary;
  63. }
  64. return m_currentPart;
  65. } else {
  66. if (m_finished) {
  67. MORDOR_ASSERT(!m_currentPart);
  68. return m_currentPart;
  69. }
  70. if (m_currentPart) {
  71. transferStream(m_currentPart->stream(), NullStream::get());
  72. // Changed by the notification callback
  73. MORDOR_ASSERT(!m_currentPart);
  74. }
  75. size_t offsetToBoundary = m_stream->find(m_boundary);
  76. Buffer b;
  77. MORDOR_VERIFY(m_stream->read(b, offsetToBoundary + m_boundary.size()) ==
  78. offsetToBoundary + m_boundary.size());
  79. if (m_firstPart) {
  80. m_firstPart = false;
  81. m_boundary = "\r\n" + m_boundary;
  82. }
  83. b.clear();
  84. m_stream->read(b, 2);
  85. if (b == "--") {
  86. m_finished = true;
  87. }
  88. if (b == "\n") {
  89. m_stream->unread(b, 1);
  90. }
  91. if (b != "\r\n") {
  92. std::string restOfLine = m_stream->getDelimited();
  93. MORDOR_ASSERT(!restOfLine.empty());
  94. restOfLine.resize(restOfLine.size() - 1);
  95. if (restOfLine.find_first_not_of(" \r\t") != std::string::npos) {
  96. MORDOR_THROW_EXCEPTION(InvalidMultipartBoundaryException());
  97. }
  98. }
  99. if (m_finished) {
  100. if (multipartFinished) {
  101. multipartFinished();
  102. multipartFinished = NULL;
  103. }
  104. return m_currentPart;
  105. }
  106. m_currentPart.reset(new BodyPart(shared_from_this()));
  107. return m_currentPart;
  108. }
  109. }
  110. void
  111. Multipart::finish()
  112. {
  113. MORDOR_ASSERT(m_stream->supportsWrite());
  114. MORDOR_ASSERT(!m_finished);
  115. std::string finalBoundary = m_boundary + "--\r\n";
  116. m_stream->write(finalBoundary.c_str(), finalBoundary.size());
  117. m_finished = true;
  118. if (multipartFinished) {
  119. multipartFinished();
  120. multipartFinished = NULL;
  121. }
  122. }
  123. void
  124. Multipart::partDone()
  125. {
  126. m_currentPart.reset();
  127. }
  128. class BodyPartStream : public MutatingFilterStream
  129. {
  130. public:
  131. BodyPartStream(Stream::ptr parent, std::string boundary)
  132. : MutatingFilterStream(parent),
  133. m_boundary(boundary)
  134. {}
  135. using MutatingFilterStream::read;
  136. size_t read(Buffer &b, size_t len)
  137. {
  138. ptrdiff_t boundary = parent()->find(m_boundary, len, false);
  139. if (boundary >= 0)
  140. len = (std::min)((size_t)boundary, len);
  141. return parent()->read(b, len);
  142. }
  143. private:
  144. std::string m_boundary;
  145. };
  146. BodyPart::BodyPart(Multipart::ptr multipart)
  147. : m_multipart(multipart)
  148. {
  149. if (m_multipart->m_stream->supportsRead()) {
  150. HTTP::TrailerParser parser(m_headers);
  151. parser.run(m_multipart->m_stream);
  152. if (parser.error())
  153. MORDOR_THROW_EXCEPTION(HTTP::BadMessageHeaderException());
  154. if (!parser.complete())
  155. MORDOR_THROW_EXCEPTION(HTTP::IncompleteMessageHeaderException());
  156. m_stream.reset(new BodyPartStream(m_multipart->m_stream, m_multipart->m_boundary));
  157. NotifyStream *notify = new NotifyStream(m_stream);
  158. notify->notifyOnEof = boost::bind(&Multipart::partDone, m_multipart);
  159. m_stream.reset(notify);
  160. }
  161. }
  162. HTTP::EntityHeaders &
  163. BodyPart::headers()
  164. {
  165. return m_headers;
  166. }
  167. Stream::ptr
  168. BodyPart::stream()
  169. {
  170. if (m_multipart->m_stream->supportsWrite()) {
  171. MORDOR_ASSERT(m_headers.contentType.type != "multipart");
  172. }
  173. if (!m_stream) {
  174. MORDOR_ASSERT(m_multipart->m_stream->supportsWrite());
  175. std::ostringstream os;
  176. os << m_headers << "\r\n";
  177. std::string headers = os.str();
  178. m_multipart->m_stream->write(headers.c_str(), headers.size());
  179. NotifyStream *notify = new NotifyStream(m_multipart->m_stream, false);
  180. notify->notifyOnClose(boost::bind(&Multipart::partDone, m_multipart));
  181. m_stream.reset(notify);
  182. }
  183. return m_stream;
  184. }
  185. Multipart::ptr
  186. BodyPart::multipart()
  187. {
  188. if (m_childMultipart)
  189. return m_childMultipart;
  190. MORDOR_ASSERT(m_headers.contentType.type == "multipart");
  191. if (!m_stream) {
  192. MORDOR_ASSERT(m_multipart->m_stream->supportsWrite());
  193. std::ostringstream os;
  194. os << m_headers;
  195. std::string headers = os.str();
  196. m_multipart->m_stream->write(headers.c_str(), headers.size());
  197. NotifyStream *notify = new NotifyStream(m_multipart->m_stream, false);
  198. notify->notifyOnClose(boost::bind(&Multipart::partDone, m_multipart));
  199. m_stream.reset(notify);
  200. }
  201. HTTP::StringMap::const_iterator it = m_headers.contentType.parameters.find("boundary");
  202. if (it == m_headers.contentType.parameters.end()) {
  203. MORDOR_ASSERT(!m_multipart->m_stream->supportsWrite());
  204. MORDOR_THROW_EXCEPTION(InvalidMultipartBoundaryException());
  205. }
  206. m_childMultipart.reset(new Multipart(m_stream, it->second));
  207. return m_childMultipart;
  208. }
  209. }