Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/base/nsMIMEInputStream.cpp
Line
Count
Source (jump to first uncovered line)
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
/**
7
 * The MIME stream separates headers and a datastream. It also allows
8
 * automatic creation of the content-length header.
9
 */
10
11
#include "ipc/IPCMessageUtils.h"
12
13
#include "nsCOMPtr.h"
14
#include "nsComponentManagerUtils.h"
15
#include "nsIAsyncInputStream.h"
16
#include "nsIInputStreamLength.h"
17
#include "nsIHttpHeaderVisitor.h"
18
#include "nsIMIMEInputStream.h"
19
#include "nsISeekableStream.h"
20
#include "nsString.h"
21
#include "nsMIMEInputStream.h"
22
#include "nsIClassInfoImpl.h"
23
#include "nsIIPCSerializableInputStream.h"
24
#include "mozilla/Move.h"
25
#include "mozilla/Mutex.h"
26
#include "mozilla/ipc/InputStreamUtils.h"
27
28
using namespace mozilla::ipc;
29
using mozilla::Maybe;
30
31
class nsMIMEInputStream : public nsIMIMEInputStream,
32
                          public nsISeekableStream,
33
                          public nsIIPCSerializableInputStream,
34
                          public nsIAsyncInputStream,
35
                          public nsIInputStreamCallback,
36
                          public nsIInputStreamLength,
37
                          public nsIAsyncInputStreamLength,
38
                          public nsIInputStreamLengthCallback
39
{
40
0
    virtual ~nsMIMEInputStream() = default;
41
42
public:
43
    nsMIMEInputStream();
44
45
    NS_DECL_THREADSAFE_ISUPPORTS
46
    NS_DECL_NSIINPUTSTREAM
47
    NS_DECL_NSIMIMEINPUTSTREAM
48
    NS_DECL_NSISEEKABLESTREAM
49
    NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
50
    NS_DECL_NSIASYNCINPUTSTREAM
51
    NS_DECL_NSIINPUTSTREAMCALLBACK
52
    NS_DECL_NSIINPUTSTREAMLENGTH
53
    NS_DECL_NSIASYNCINPUTSTREAMLENGTH
54
    NS_DECL_NSIINPUTSTREAMLENGTHCALLBACK
55
56
private:
57
58
    void InitStreams();
59
60
    struct MOZ_STACK_CLASS ReadSegmentsState {
61
        nsCOMPtr<nsIInputStream> mThisStream;
62
        nsWriteSegmentFun mWriter;
63
        void* mClosure;
64
    };
65
    static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure,
66
                              const char* aFromRawSegment, uint32_t aToOffset,
67
                              uint32_t aCount, uint32_t *aWriteCount);
68
69
    bool IsAsyncInputStream() const;
70
    bool IsIPCSerializable() const;
71
    bool IsInputStreamLength() const;
72
    bool IsAsyncInputStreamLength() const;
73
74
    nsTArray<HeaderEntry> mHeaders;
75
76
    nsCOMPtr<nsIInputStream> mStream;
77
    bool mStartedReading;
78
79
    mozilla::Mutex mMutex;
80
81
    // This is protected by mutex.
82
    nsCOMPtr<nsIInputStreamCallback> mAsyncWaitCallback;
83
84
    // This is protected by mutex.
85
    nsCOMPtr<nsIInputStreamLengthCallback> mAsyncInputStreamLengthCallback;
86
};
87
88
NS_IMPL_ADDREF(nsMIMEInputStream)
89
NS_IMPL_RELEASE(nsMIMEInputStream)
90
91
NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE,
92
                  NS_MIMEINPUTSTREAM_CID)
93
94
0
NS_INTERFACE_MAP_BEGIN(nsMIMEInputStream)
95
0
  NS_INTERFACE_MAP_ENTRY(nsIMIMEInputStream)
96
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIMIMEInputStream)
97
0
  NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
98
0
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
99
0
                                     IsIPCSerializable())
100
0
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
101
0
                                     IsAsyncInputStream())
102
0
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
103
0
                                     IsAsyncInputStream())
104
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMIMEInputStream)
105
0
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength,
106
0
                                     IsInputStreamLength())
107
0
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength,
108
0
                                     IsAsyncInputStreamLength())
109
0
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLengthCallback,
110
0
                                     IsAsyncInputStreamLength())
111
0
  NS_IMPL_QUERY_CLASSINFO(nsMIMEInputStream)
112
0
NS_INTERFACE_MAP_END
113
114
NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream,
115
                            nsIMIMEInputStream,
116
                            nsIAsyncInputStream,
117
                            nsIInputStream,
118
                            nsISeekableStream)
119
120
nsMIMEInputStream::nsMIMEInputStream()
121
  : mStartedReading(false)
122
  , mMutex("nsMIMEInputStream::mMutex")
123
0
{
124
0
}
125
126
NS_IMETHODIMP
127
nsMIMEInputStream::AddHeader(const char *aName, const char *aValue)
128
0
{
129
0
    NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
130
0
131
0
    HeaderEntry* entry = mHeaders.AppendElement();
132
0
    entry->name().Append(aName);
133
0
    entry->value().Append(aValue);
134
0
135
0
    return NS_OK;
136
0
}
137
138
NS_IMETHODIMP
139
nsMIMEInputStream::VisitHeaders(nsIHttpHeaderVisitor *visitor)
140
0
{
141
0
  nsresult rv;
142
0
143
0
  for (auto& header : mHeaders) {
144
0
    rv = visitor->VisitHeader(header.name(), header.value());
145
0
    if (NS_FAILED(rv)) {
146
0
      return rv;
147
0
    }
148
0
  }
149
0
  return NS_OK;
150
0
}
151
152
NS_IMETHODIMP
153
nsMIMEInputStream::SetData(nsIInputStream *aStream)
154
0
{
155
0
    NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
156
0
157
0
    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aStream);
158
0
    if (!seekable) {
159
0
      return NS_ERROR_INVALID_ARG;
160
0
    }
161
0
162
0
    mStream = aStream;
163
0
    return NS_OK;
164
0
}
165
166
NS_IMETHODIMP
167
nsMIMEInputStream::GetData(nsIInputStream **aStream)
168
0
{
169
0
  NS_ENSURE_ARG_POINTER(aStream);
170
0
  *aStream = mStream;
171
0
  NS_IF_ADDREF(*aStream);
172
0
  return NS_OK;
173
0
}
174
175
// set up the internal streams
176
void nsMIMEInputStream::InitStreams()
177
0
{
178
0
    NS_ASSERTION(!mStartedReading,
179
0
                 "Don't call initStreams twice without rewinding");
180
0
181
0
    mStartedReading = true;
182
0
}
183
184
185
186
0
#define INITSTREAMS         \
187
0
if (!mStartedReading) {     \
188
0
    NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED); \
189
0
    InitStreams();          \
190
0
}
191
192
// Reset mStartedReading when Seek-ing to start
193
NS_IMETHODIMP
194
nsMIMEInputStream::Seek(int32_t whence, int64_t offset)
195
0
{
196
0
    NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED);
197
0
198
0
    nsresult rv;
199
0
    nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
200
0
201
0
    if (whence == NS_SEEK_SET && offset == 0) {
202
0
        rv = stream->Seek(whence, offset);
203
0
        if (NS_SUCCEEDED(rv))
204
0
            mStartedReading = false;
205
0
    }
206
0
    else {
207
0
        INITSTREAMS;
208
0
        rv = stream->Seek(whence, offset);
209
0
    }
210
0
211
0
    return rv;
212
0
}
213
214
// Proxy ReadSegments since we need to be a good little nsIInputStream
215
NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter,
216
                                              void *aClosure, uint32_t aCount,
217
                                              uint32_t *_retval)
218
0
{
219
0
    INITSTREAMS;
220
0
    ReadSegmentsState state;
221
0
    // Disambiguate ambiguous nsIInputStream.
222
0
    state.mThisStream = static_cast<nsIInputStream*>(
223
0
                          static_cast<nsIMIMEInputStream*>(this));
224
0
    state.mWriter = aWriter;
225
0
    state.mClosure = aClosure;
226
0
    return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval);
227
0
}
228
229
nsresult
230
nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
231
                             const char* aFromRawSegment,
232
                             uint32_t aToOffset, uint32_t aCount,
233
                             uint32_t *aWriteCount)
234
0
{
235
0
    ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
236
0
    return  (state->mWriter)(state->mThisStream,
237
0
                             state->mClosure,
238
0
                             aFromRawSegment,
239
0
                             aToOffset,
240
0
                             aCount,
241
0
                             aWriteCount);
242
0
}
243
244
/**
245
 * Forward everything else to the mStream after calling InitStreams()
246
 */
247
248
// nsIInputStream
249
0
NS_IMETHODIMP nsMIMEInputStream::Close(void) { INITSTREAMS; return mStream->Close(); }
250
0
NS_IMETHODIMP nsMIMEInputStream::Available(uint64_t *_retval) { INITSTREAMS; return mStream->Available(_retval); }
251
0
NS_IMETHODIMP nsMIMEInputStream::Read(char * buf, uint32_t count, uint32_t *_retval) { INITSTREAMS; return mStream->Read(buf, count, _retval); }
252
0
NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(bool *aNonBlocking) { INITSTREAMS; return mStream->IsNonBlocking(aNonBlocking); }
253
254
// nsIAsyncInputStream
255
NS_IMETHODIMP
256
nsMIMEInputStream::CloseWithStatus(nsresult aStatus)
257
0
{
258
0
    INITSTREAMS;
259
0
    nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
260
0
    return asyncStream->CloseWithStatus(aStatus);
261
0
}
262
263
NS_IMETHODIMP
264
nsMIMEInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
265
                             uint32_t aFlags, uint32_t aRequestedCount,
266
                             nsIEventTarget* aEventTarget)
267
0
{
268
0
    INITSTREAMS;
269
0
    nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
270
0
    if (NS_WARN_IF(!asyncStream)) {
271
0
      return NS_ERROR_FAILURE;
272
0
    }
273
0
274
0
    nsCOMPtr<nsIInputStreamCallback> callback = aCallback ? this : nullptr;
275
0
    {
276
0
        MutexAutoLock lock(mMutex);
277
0
        if (mAsyncWaitCallback && aCallback) {
278
0
            return NS_ERROR_FAILURE;
279
0
        }
280
0
281
0
        mAsyncWaitCallback = aCallback;
282
0
    }
283
0
284
0
    return asyncStream->AsyncWait(callback, aFlags, aRequestedCount,
285
0
                                  aEventTarget);
286
0
}
287
288
// nsIInputStreamCallback
289
290
NS_IMETHODIMP
291
nsMIMEInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
292
0
{
293
0
    nsCOMPtr<nsIInputStreamCallback> callback;
294
0
295
0
    {
296
0
        MutexAutoLock lock(mMutex);
297
0
298
0
        // We have been canceled in the meanwhile.
299
0
        if (!mAsyncWaitCallback) {
300
0
            return NS_OK;
301
0
        }
302
0
303
0
        callback.swap(mAsyncWaitCallback);
304
0
  }
305
0
306
0
  MOZ_ASSERT(callback);
307
0
  return callback->OnInputStreamReady(this);
308
0
}
309
310
// nsISeekableStream
311
NS_IMETHODIMP nsMIMEInputStream::Tell(int64_t *_retval)
312
0
{
313
0
    INITSTREAMS;
314
0
    nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
315
0
    return stream->Tell(_retval);
316
0
}
317
0
NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) {
318
0
    INITSTREAMS;
319
0
    nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
320
0
    return stream->SetEOF();
321
0
}
322
323
324
/**
325
 * Factory method used by do_CreateInstance
326
 */
327
328
nsresult
329
nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
330
0
{
331
0
    *result = nullptr;
332
0
333
0
    if (outer)
334
0
        return NS_ERROR_NO_AGGREGATION;
335
0
336
0
    RefPtr<nsMIMEInputStream> inst = new nsMIMEInputStream();
337
0
    if (!inst)
338
0
        return NS_ERROR_OUT_OF_MEMORY;
339
0
340
0
    return inst->QueryInterface(iid, result);
341
0
}
342
343
void
344
nsMIMEInputStream::Serialize(InputStreamParams& aParams,
345
                             FileDescriptorArray& aFileDescriptors)
346
0
{
347
0
    MIMEInputStreamParams params;
348
0
349
0
    if (mStream) {
350
0
        InputStreamParams wrappedParams;
351
0
        InputStreamHelper::SerializeInputStream(mStream, wrappedParams,
352
0
                                                aFileDescriptors);
353
0
354
0
        NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
355
0
                     "Wrapped stream failed to serialize!");
356
0
357
0
        params.optionalStream() = wrappedParams;
358
0
    }
359
0
    else {
360
0
        params.optionalStream() = mozilla::void_t();
361
0
    }
362
0
363
0
    params.headers() = mHeaders;
364
0
    params.startedReading() = mStartedReading;
365
0
366
0
    aParams = params;
367
0
}
368
369
bool
370
nsMIMEInputStream::Deserialize(const InputStreamParams& aParams,
371
                               const FileDescriptorArray& aFileDescriptors)
372
0
{
373
0
    if (aParams.type() != InputStreamParams::TMIMEInputStreamParams) {
374
0
        NS_ERROR("Received unknown parameters from the other process!");
375
0
        return false;
376
0
    }
377
0
378
0
    const MIMEInputStreamParams& params =
379
0
        aParams.get_MIMEInputStreamParams();
380
0
    const OptionalInputStreamParams& wrappedParams = params.optionalStream();
381
0
382
0
    mHeaders = params.headers();
383
0
    mStartedReading = params.startedReading();
384
0
385
0
    if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
386
0
        nsCOMPtr<nsIInputStream> stream;
387
0
        stream =
388
0
            InputStreamHelper::DeserializeInputStream(wrappedParams.get_InputStreamParams(),
389
0
                                                      aFileDescriptors);
390
0
        if (!stream) {
391
0
            NS_WARNING("Failed to deserialize wrapped stream!");
392
0
            return false;
393
0
        }
394
0
395
0
        mStream = stream;
396
0
    }
397
0
    else {
398
0
        NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
399
0
                     "Unknown type for OptionalInputStreamParams!");
400
0
    }
401
0
402
0
    return true;
403
0
}
404
405
Maybe<uint64_t>
406
nsMIMEInputStream::ExpectedSerializedLength()
407
0
{
408
0
    nsCOMPtr<nsIIPCSerializableInputStream> serializable = do_QueryInterface(mStream);
409
0
    return serializable ? serializable->ExpectedSerializedLength() : Nothing();
410
0
}
411
412
NS_IMETHODIMP
413
nsMIMEInputStream::Length(int64_t* aLength)
414
0
{
415
0
    nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream);
416
0
    if (NS_WARN_IF(!stream)) {
417
0
        return NS_ERROR_FAILURE;
418
0
    }
419
0
420
0
    return stream->Length(aLength);
421
0
}
422
423
NS_IMETHODIMP
424
nsMIMEInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
425
                                   nsIEventTarget* aEventTarget)
426
0
{
427
0
    nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream);
428
0
    if (NS_WARN_IF(!stream)) {
429
0
        return NS_ERROR_FAILURE;
430
0
    }
431
0
432
0
    nsCOMPtr<nsIInputStreamLengthCallback> callback = aCallback ? this : nullptr;
433
0
    {
434
0
        MutexAutoLock lock(mMutex);
435
0
        mAsyncInputStreamLengthCallback = aCallback;
436
0
    }
437
0
438
0
    return stream->AsyncLengthWait(callback, aEventTarget);
439
0
}
440
441
NS_IMETHODIMP
442
nsMIMEInputStream::OnInputStreamLengthReady(nsIAsyncInputStreamLength* aStream,
443
                                            int64_t aLength)
444
0
{
445
0
    nsCOMPtr<nsIInputStreamLengthCallback> callback;
446
0
    {
447
0
        MutexAutoLock lock(mMutex);
448
0
        // We have been canceled in the meanwhile.
449
0
        if (!mAsyncInputStreamLengthCallback) {
450
0
            return NS_OK;
451
0
        }
452
0
453
0
        callback.swap(mAsyncInputStreamLengthCallback);
454
0
    }
455
0
456
0
    MOZ_ASSERT(callback);
457
0
    return callback->OnInputStreamLengthReady(this, aLength);
458
0
}
459
460
bool
461
nsMIMEInputStream::IsAsyncInputStream() const
462
0
{
463
0
    nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
464
0
    return !!asyncStream;
465
0
}
466
467
bool
468
nsMIMEInputStream::IsIPCSerializable() const
469
0
{
470
0
    // If SetData() or Deserialize() has not be called yet, mStream is null.
471
0
    if (!mStream) {
472
0
      return true;
473
0
    }
474
0
475
0
    nsCOMPtr<nsIIPCSerializableInputStream> serializable = do_QueryInterface(mStream);
476
0
    return !!serializable;
477
0
}
478
479
bool
480
nsMIMEInputStream::IsInputStreamLength() const
481
0
{
482
0
    nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream);
483
0
    return !!stream;
484
0
}
485
486
bool
487
nsMIMEInputStream::IsAsyncInputStreamLength() const
488
0
{
489
0
    nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream);
490
0
    return !!stream;
491
0
}