nsMIMEInputStream.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /**
  6. * The MIME stream separates headers and a datastream. It also allows
  7. * automatic creation of the content-length header.
  8. */
  9. #include "ipc/IPCMessageUtils.h"
  10. #include "nsCOMPtr.h"
  11. #include "nsComponentManagerUtils.h"
  12. #include "nsIMultiplexInputStream.h"
  13. #include "nsIMIMEInputStream.h"
  14. #include "nsISeekableStream.h"
  15. #include "nsIStringStream.h"
  16. #include "nsString.h"
  17. #include "nsMIMEInputStream.h"
  18. #include "nsIClassInfoImpl.h"
  19. #include "nsIIPCSerializableInputStream.h"
  20. #include "mozilla/ipc/InputStreamUtils.h"
  21. using namespace mozilla::ipc;
  22. using mozilla::Maybe;
  23. using mozilla::Nothing;
  24. class nsMIMEInputStream : public nsIMIMEInputStream,
  25. public nsISeekableStream,
  26. public nsIIPCSerializableInputStream
  27. {
  28. virtual ~nsMIMEInputStream();
  29. public:
  30. nsMIMEInputStream();
  31. NS_DECL_THREADSAFE_ISUPPORTS
  32. NS_DECL_NSIINPUTSTREAM
  33. NS_DECL_NSIMIMEINPUTSTREAM
  34. NS_DECL_NSISEEKABLESTREAM
  35. NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
  36. nsresult Init();
  37. private:
  38. void InitStreams();
  39. struct MOZ_STACK_CLASS ReadSegmentsState {
  40. nsCOMPtr<nsIInputStream> mThisStream;
  41. nsWriteSegmentFun mWriter;
  42. void* mClosure;
  43. };
  44. static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure,
  45. const char* aFromRawSegment, uint32_t aToOffset,
  46. uint32_t aCount, uint32_t *aWriteCount);
  47. nsCString mHeaders;
  48. nsCOMPtr<nsIStringInputStream> mHeaderStream;
  49. nsCString mContentLength;
  50. nsCOMPtr<nsIStringInputStream> mCLStream;
  51. nsCOMPtr<nsIInputStream> mData;
  52. nsCOMPtr<nsIMultiplexInputStream> mStream;
  53. bool mAddContentLength;
  54. bool mStartedReading;
  55. };
  56. NS_IMPL_ADDREF(nsMIMEInputStream)
  57. NS_IMPL_RELEASE(nsMIMEInputStream)
  58. NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE,
  59. NS_MIMEINPUTSTREAM_CID)
  60. NS_IMPL_QUERY_INTERFACE_CI(nsMIMEInputStream,
  61. nsIMIMEInputStream,
  62. nsIInputStream,
  63. nsISeekableStream,
  64. nsIIPCSerializableInputStream)
  65. NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream,
  66. nsIMIMEInputStream,
  67. nsIInputStream,
  68. nsISeekableStream)
  69. nsMIMEInputStream::nsMIMEInputStream() : mAddContentLength(false),
  70. mStartedReading(false)
  71. {
  72. }
  73. nsMIMEInputStream::~nsMIMEInputStream()
  74. {
  75. }
  76. nsresult nsMIMEInputStream::Init()
  77. {
  78. nsresult rv = NS_OK;
  79. mStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1",
  80. &rv);
  81. NS_ENSURE_SUCCESS(rv, rv);
  82. mHeaderStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1",
  83. &rv);
  84. NS_ENSURE_SUCCESS(rv, rv);
  85. mCLStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
  86. NS_ENSURE_SUCCESS(rv, rv);
  87. rv = mStream->AppendStream(mHeaderStream);
  88. NS_ENSURE_SUCCESS(rv, rv);
  89. rv = mStream->AppendStream(mCLStream);
  90. NS_ENSURE_SUCCESS(rv, rv);
  91. return NS_OK;
  92. }
  93. NS_IMETHODIMP
  94. nsMIMEInputStream::GetAddContentLength(bool *aAddContentLength)
  95. {
  96. *aAddContentLength = mAddContentLength;
  97. return NS_OK;
  98. }
  99. NS_IMETHODIMP
  100. nsMIMEInputStream::SetAddContentLength(bool aAddContentLength)
  101. {
  102. NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
  103. mAddContentLength = aAddContentLength;
  104. return NS_OK;
  105. }
  106. NS_IMETHODIMP
  107. nsMIMEInputStream::AddHeader(const char *aName, const char *aValue)
  108. {
  109. NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
  110. mHeaders.Append(aName);
  111. mHeaders.AppendLiteral(": ");
  112. mHeaders.Append(aValue);
  113. mHeaders.AppendLiteral("\r\n");
  114. // Just in case someone somehow uses our stream, lets at least
  115. // let the stream have a valid pointer. The stream will be properly
  116. // initialized in nsMIMEInputStream::InitStreams
  117. mHeaderStream->ShareData(mHeaders.get(), 0);
  118. return NS_OK;
  119. }
  120. NS_IMETHODIMP
  121. nsMIMEInputStream::SetData(nsIInputStream *aStream)
  122. {
  123. NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
  124. // Remove the old stream if there is one
  125. if (mData)
  126. mStream->RemoveStream(2);
  127. mData = aStream;
  128. if (aStream)
  129. mStream->AppendStream(mData);
  130. return NS_OK;
  131. }
  132. NS_IMETHODIMP
  133. nsMIMEInputStream::GetData(nsIInputStream **aStream)
  134. {
  135. NS_ENSURE_ARG_POINTER(aStream);
  136. *aStream = mData;
  137. NS_IF_ADDREF(*aStream);
  138. return NS_OK;
  139. }
  140. // set up the internal streams
  141. void nsMIMEInputStream::InitStreams()
  142. {
  143. NS_ASSERTION(!mStartedReading,
  144. "Don't call initStreams twice without rewinding");
  145. mStartedReading = true;
  146. // We'll use the content-length stream to add the final \r\n
  147. if (mAddContentLength) {
  148. uint64_t cl = 0;
  149. if (mData) {
  150. mData->Available(&cl);
  151. }
  152. mContentLength.AssignLiteral("Content-Length: ");
  153. mContentLength.AppendInt(cl);
  154. mContentLength.AppendLiteral("\r\n\r\n");
  155. }
  156. else {
  157. mContentLength.AssignLiteral("\r\n");
  158. }
  159. mCLStream->ShareData(mContentLength.get(), -1);
  160. mHeaderStream->ShareData(mHeaders.get(), -1);
  161. }
  162. #define INITSTREAMS \
  163. if (!mStartedReading) { \
  164. InitStreams(); \
  165. }
  166. // Reset mStartedReading when Seek-ing to start
  167. NS_IMETHODIMP
  168. nsMIMEInputStream::Seek(int32_t whence, int64_t offset)
  169. {
  170. nsresult rv;
  171. nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
  172. if (whence == NS_SEEK_SET && offset == 0) {
  173. rv = stream->Seek(whence, offset);
  174. if (NS_SUCCEEDED(rv))
  175. mStartedReading = false;
  176. }
  177. else {
  178. INITSTREAMS;
  179. rv = stream->Seek(whence, offset);
  180. }
  181. return rv;
  182. }
  183. // Proxy ReadSegments since we need to be a good little nsIInputStream
  184. NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter,
  185. void *aClosure, uint32_t aCount,
  186. uint32_t *_retval)
  187. {
  188. INITSTREAMS;
  189. ReadSegmentsState state;
  190. state.mThisStream = this;
  191. state.mWriter = aWriter;
  192. state.mClosure = aClosure;
  193. return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval);
  194. }
  195. nsresult
  196. nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
  197. const char* aFromRawSegment,
  198. uint32_t aToOffset, uint32_t aCount,
  199. uint32_t *aWriteCount)
  200. {
  201. ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
  202. return (state->mWriter)(state->mThisStream,
  203. state->mClosure,
  204. aFromRawSegment,
  205. aToOffset,
  206. aCount,
  207. aWriteCount);
  208. }
  209. /**
  210. * Forward everything else to the mStream after calling InitStreams()
  211. */
  212. // nsIInputStream
  213. NS_IMETHODIMP nsMIMEInputStream::Close(void) { INITSTREAMS; return mStream->Close(); }
  214. NS_IMETHODIMP nsMIMEInputStream::Available(uint64_t *_retval) { INITSTREAMS; return mStream->Available(_retval); }
  215. NS_IMETHODIMP nsMIMEInputStream::Read(char * buf, uint32_t count, uint32_t *_retval) { INITSTREAMS; return mStream->Read(buf, count, _retval); }
  216. NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(bool *aNonBlocking) { INITSTREAMS; return mStream->IsNonBlocking(aNonBlocking); }
  217. // nsISeekableStream
  218. NS_IMETHODIMP nsMIMEInputStream::Tell(int64_t *_retval)
  219. {
  220. INITSTREAMS;
  221. nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
  222. return stream->Tell(_retval);
  223. }
  224. NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) {
  225. INITSTREAMS;
  226. nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
  227. return stream->SetEOF();
  228. }
  229. /**
  230. * Factory method used by do_CreateInstance
  231. */
  232. nsresult
  233. nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
  234. {
  235. *result = nullptr;
  236. if (outer)
  237. return NS_ERROR_NO_AGGREGATION;
  238. nsMIMEInputStream *inst = new nsMIMEInputStream();
  239. if (!inst)
  240. return NS_ERROR_OUT_OF_MEMORY;
  241. NS_ADDREF(inst);
  242. nsresult rv = inst->Init();
  243. if (NS_FAILED(rv)) {
  244. NS_RELEASE(inst);
  245. return rv;
  246. }
  247. rv = inst->QueryInterface(iid, result);
  248. NS_RELEASE(inst);
  249. return rv;
  250. }
  251. void
  252. nsMIMEInputStream::Serialize(InputStreamParams& aParams,
  253. FileDescriptorArray& aFileDescriptors)
  254. {
  255. MIMEInputStreamParams params;
  256. if (mData) {
  257. nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mData);
  258. MOZ_ASSERT(stream);
  259. InputStreamParams wrappedParams;
  260. SerializeInputStream(stream, wrappedParams, aFileDescriptors);
  261. NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
  262. "Wrapped stream failed to serialize!");
  263. params.optionalStream() = wrappedParams;
  264. }
  265. else {
  266. params.optionalStream() = mozilla::void_t();
  267. }
  268. params.headers() = mHeaders;
  269. params.contentLength() = mContentLength;
  270. params.startedReading() = mStartedReading;
  271. params.addContentLength() = mAddContentLength;
  272. aParams = params;
  273. }
  274. bool
  275. nsMIMEInputStream::Deserialize(const InputStreamParams& aParams,
  276. const FileDescriptorArray& aFileDescriptors)
  277. {
  278. if (aParams.type() != InputStreamParams::TMIMEInputStreamParams) {
  279. NS_ERROR("Received unknown parameters from the other process!");
  280. return false;
  281. }
  282. const MIMEInputStreamParams& params =
  283. aParams.get_MIMEInputStreamParams();
  284. const OptionalInputStreamParams& wrappedParams = params.optionalStream();
  285. mHeaders = params.headers();
  286. mContentLength = params.contentLength();
  287. mStartedReading = params.startedReading();
  288. // nsMIMEInputStream::Init() already appended mHeaderStream & mCLStream
  289. mHeaderStream->ShareData(mHeaders.get(),
  290. mStartedReading ? mHeaders.Length() : 0);
  291. mCLStream->ShareData(mContentLength.get(),
  292. mStartedReading ? mContentLength.Length() : 0);
  293. nsCOMPtr<nsIInputStream> stream;
  294. if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
  295. stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(),
  296. aFileDescriptors);
  297. if (!stream) {
  298. NS_WARNING("Failed to deserialize wrapped stream!");
  299. return false;
  300. }
  301. mData = stream;
  302. if (NS_FAILED(mStream->AppendStream(mData))) {
  303. NS_WARNING("Failed to append stream!");
  304. return false;
  305. }
  306. }
  307. else {
  308. NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
  309. "Unknown type for OptionalInputStreamParams!");
  310. }
  311. mAddContentLength = params.addContentLength();
  312. return true;
  313. }
  314. Maybe<uint64_t>
  315. nsMIMEInputStream::ExpectedSerializedLength()
  316. {
  317. nsCOMPtr<nsIIPCSerializableInputStream> serializable = do_QueryInterface(mStream);
  318. return serializable ? serializable->ExpectedSerializedLength() : Nothing();
  319. }