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/. */
7 * The MIME stream separates headers and a datastream. It also allows
8 * automatic creation of the content-length header.
11 #include "ipc/IPCMessageUtils.h"
14 #include "nsComponentManagerUtils.h"
15 #include "nsIMultiplexInputStream.h"
16 #include "nsIMIMEInputStream.h"
17 #include "nsISeekableStream.h"
18 #include "nsIStringStream.h"
20 #include "nsMIMEInputStream.h"
21 #include "nsIClassInfoImpl.h"
22 #include "nsIIPCSerializableInputStream.h"
23 #include "mozilla/ipc/InputStreamUtils.h"
25 using namespace mozilla::ipc
;
27 class nsMIMEInputStream
: public nsIMIMEInputStream
,
28 public nsISeekableStream
,
29 public nsIIPCSerializableInputStream
33 virtual ~nsMIMEInputStream();
36 NS_DECL_NSIINPUTSTREAM
37 NS_DECL_NSIMIMEINPUTSTREAM
38 NS_DECL_NSISEEKABLESTREAM
39 NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
47 struct ReadSegmentsState
{
48 nsIInputStream
* mThisStream
;
49 nsWriteSegmentFun mWriter
;
52 static NS_METHOD
ReadSegCb(nsIInputStream
* aIn
, void* aClosure
,
53 const char* aFromRawSegment
, uint32_t aToOffset
,
54 uint32_t aCount
, uint32_t *aWriteCount
);
57 nsCOMPtr
<nsIStringInputStream
> mHeaderStream
;
59 nsCString mContentLength
;
60 nsCOMPtr
<nsIStringInputStream
> mCLStream
;
62 nsCOMPtr
<nsIInputStream
> mData
;
63 nsCOMPtr
<nsIMultiplexInputStream
> mStream
;
64 bool mAddContentLength
;
68 NS_IMPL_THREADSAFE_ADDREF(nsMIMEInputStream
)
69 NS_IMPL_THREADSAFE_RELEASE(nsMIMEInputStream
)
71 NS_IMPL_CLASSINFO(nsMIMEInputStream
, NULL
, nsIClassInfo::THREADSAFE
,
72 NS_MIMEINPUTSTREAM_CID
)
74 NS_IMPL_QUERY_INTERFACE4_CI(nsMIMEInputStream
,
78 nsIIPCSerializableInputStream
)
79 NS_IMPL_CI_INTERFACE_GETTER3(nsMIMEInputStream
,
84 nsMIMEInputStream::nsMIMEInputStream() : mAddContentLength(false),
85 mStartedReading(false)
89 nsMIMEInputStream::~nsMIMEInputStream()
93 NS_METHOD
nsMIMEInputStream::Init()
96 mStream
= do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1",
98 NS_ENSURE_SUCCESS(rv
, rv
);
100 mHeaderStream
= do_CreateInstance("@mozilla.org/io/string-input-stream;1",
102 NS_ENSURE_SUCCESS(rv
, rv
);
103 mCLStream
= do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv
);
104 NS_ENSURE_SUCCESS(rv
, rv
);
106 rv
= mStream
->AppendStream(mHeaderStream
);
107 NS_ENSURE_SUCCESS(rv
, rv
);
109 rv
= mStream
->AppendStream(mCLStream
);
110 NS_ENSURE_SUCCESS(rv
, rv
);
116 /* attribute boolean addContentLength; */
118 nsMIMEInputStream::GetAddContentLength(bool *aAddContentLength
)
120 *aAddContentLength
= mAddContentLength
;
124 nsMIMEInputStream::SetAddContentLength(bool aAddContentLength
)
126 NS_ENSURE_FALSE(mStartedReading
, NS_ERROR_FAILURE
);
127 mAddContentLength
= aAddContentLength
;
131 /* void addHeader ([const] in string name, [const] in string value); */
133 nsMIMEInputStream::AddHeader(const char *aName
, const char *aValue
)
135 NS_ENSURE_FALSE(mStartedReading
, NS_ERROR_FAILURE
);
136 mHeaders
.Append(aName
);
137 mHeaders
.AppendLiteral(": ");
138 mHeaders
.Append(aValue
);
139 mHeaders
.AppendLiteral("\r\n");
141 // Just in case someone somehow uses our stream, lets at least
142 // let the stream have a valid pointer. The stream will be properly
143 // initialized in nsMIMEInputStream::InitStreams
144 mHeaderStream
->ShareData(mHeaders
.get(), 0);
149 /* void setData (in nsIInputStream stream); */
151 nsMIMEInputStream::SetData(nsIInputStream
*aStream
)
153 NS_ENSURE_FALSE(mStartedReading
, NS_ERROR_FAILURE
);
154 // Remove the old stream if there is one
156 mStream
->RemoveStream(2);
160 mStream
->AppendStream(mData
);
164 // set up the internal streams
165 void nsMIMEInputStream::InitStreams()
167 NS_ASSERTION(!mStartedReading
,
168 "Don't call initStreams twice without rewinding");
170 mStartedReading
= true;
172 // We'll use the content-length stream to add the final \r\n
173 if (mAddContentLength
) {
176 mData
->Available(&cl
);
178 mContentLength
.AssignLiteral("Content-Length: ");
179 mContentLength
.AppendInt(cl
);
180 mContentLength
.AppendLiteral("\r\n\r\n");
183 mContentLength
.AssignLiteral("\r\n");
185 mCLStream
->ShareData(mContentLength
.get(), -1);
186 mHeaderStream
->ShareData(mHeaders
.get(), -1);
191 #define INITSTREAMS \
192 if (!mStartedReading) { \
196 // Reset mStartedReading when Seek-ing to start
198 nsMIMEInputStream::Seek(int32_t whence
, int64_t offset
)
201 nsCOMPtr
<nsISeekableStream
> stream
= do_QueryInterface(mStream
);
202 if (whence
== NS_SEEK_SET
&& LL_EQ(offset
, LL_Zero())) {
203 rv
= stream
->Seek(whence
, offset
);
204 if (NS_SUCCEEDED(rv
))
205 mStartedReading
= false;
209 rv
= stream
->Seek(whence
, offset
);
215 // Proxy ReadSegments since we need to be a good little nsIInputStream
216 NS_IMETHODIMP
nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter
,
217 void *aClosure
, uint32_t aCount
,
221 ReadSegmentsState state
;
222 state
.mThisStream
= this;
223 state
.mWriter
= aWriter
;
224 state
.mClosure
= aClosure
;
225 return mStream
->ReadSegments(ReadSegCb
, &state
, aCount
, _retval
);
229 nsMIMEInputStream::ReadSegCb(nsIInputStream
* aIn
, void* aClosure
,
230 const char* aFromRawSegment
,
231 uint32_t aToOffset
, uint32_t aCount
,
232 uint32_t *aWriteCount
)
234 ReadSegmentsState
* state
= (ReadSegmentsState
*)aClosure
;
235 return (state
->mWriter
)(state
->mThisStream
,
244 * Forward everything else to the mStream after calling InitStreams()
248 NS_IMETHODIMP
nsMIMEInputStream::Close(void) { INITSTREAMS
; return mStream
->Close(); }
249 NS_IMETHODIMP
nsMIMEInputStream::Available(uint64_t *_retval
) { INITSTREAMS
; return mStream
->Available(_retval
); }
250 NS_IMETHODIMP
nsMIMEInputStream::Read(char * buf
, uint32_t count
, uint32_t *_retval
) { INITSTREAMS
; return mStream
->Read(buf
, count
, _retval
); }
251 NS_IMETHODIMP
nsMIMEInputStream::IsNonBlocking(bool *aNonBlocking
) { INITSTREAMS
; return mStream
->IsNonBlocking(aNonBlocking
); }
254 NS_IMETHODIMP
nsMIMEInputStream::Tell(int64_t *_retval
)
257 nsCOMPtr
<nsISeekableStream
> stream
= do_QueryInterface(mStream
);
258 return stream
->Tell(_retval
);
260 NS_IMETHODIMP
nsMIMEInputStream::SetEOF(void) {
262 nsCOMPtr
<nsISeekableStream
> stream
= do_QueryInterface(mStream
);
263 return stream
->SetEOF();
268 * Factory method used by do_CreateInstance
272 nsMIMEInputStreamConstructor(nsISupports
*outer
, REFNSIID iid
, void **result
)
277 return NS_ERROR_NO_AGGREGATION
;
279 nsMIMEInputStream
*inst
= new nsMIMEInputStream();
281 return NS_ERROR_OUT_OF_MEMORY
;
285 nsresult rv
= inst
->Init();
291 rv
= inst
->QueryInterface(iid
, result
);
298 nsMIMEInputStream::Serialize(InputStreamParams
& aParams
)
300 MIMEInputStreamParams params
;
303 nsCOMPtr
<nsIIPCSerializableInputStream
> stream
=
304 do_QueryInterface(mData
);
305 NS_ASSERTION(stream
, "Wrapped stream is not serializable!");
307 InputStreamParams wrappedParams
;
308 stream
->Serialize(wrappedParams
);
310 NS_ASSERTION(wrappedParams
.type() != InputStreamParams::T__None
,
311 "Wrapped stream failed to serialize!");
313 params
.optionalStream() = wrappedParams
;
316 params
.optionalStream() = mozilla::void_t();
319 params
.headers() = mHeaders
;
320 params
.contentLength() = mContentLength
;
321 params
.startedReading() = mStartedReading
;
322 params
.addContentLength() = mAddContentLength
;
328 nsMIMEInputStream::Deserialize(const InputStreamParams
& aParams
)
330 if (aParams
.type() != InputStreamParams::TMIMEInputStreamParams
) {
331 NS_ERROR("Received unknown parameters from the other process!");
335 const MIMEInputStreamParams
& params
=
336 aParams
.get_MIMEInputStreamParams();
337 const OptionalInputStreamParams
& wrappedParams
= params
.optionalStream();
339 mHeaders
= params
.headers();
340 mContentLength
= params
.contentLength();
341 mStartedReading
= params
.startedReading();
343 // nsMIMEInputStream::Init() already appended mHeaderStream & mCLStream
344 mHeaderStream
->ShareData(mHeaders
.get(),
345 mStartedReading
? mHeaders
.Length() : 0);
346 mCLStream
->ShareData(mContentLength
.get(),
347 mStartedReading
? mContentLength
.Length() : 0);
349 nsCOMPtr
<nsIInputStream
> stream
;
350 if (wrappedParams
.type() == OptionalInputStreamParams::TInputStreamParams
) {
351 stream
= DeserializeInputStream(wrappedParams
.get_InputStreamParams());
353 NS_WARNING("Failed to deserialize wrapped stream!");
359 if (NS_FAILED(mStream
->AppendStream(mData
))) {
360 NS_WARNING("Failed to append stream!");
365 NS_ASSERTION(wrappedParams
.type() == OptionalInputStreamParams::Tvoid_t
,
366 "Unknown type for OptionalInputStreamParams!");
369 mAddContentLength
= params
.addContentLength();