Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set sw=2 ts=8 et tw=80 : */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsHTTPCompressConv.h"
8
#include "nsMemory.h"
9
#include "plstr.h"
10
#include "nsCOMPtr.h"
11
#include "nsError.h"
12
#include "nsStreamUtils.h"
13
#include "nsStringStream.h"
14
#include "nsComponentManagerUtils.h"
15
#include "nsThreadUtils.h"
16
#include "mozilla/Preferences.h"
17
#include "mozilla/Logging.h"
18
#include "nsIForcePendingChannel.h"
19
#include "nsIRequest.h"
20
21
// brotli headers
22
#include "state.h"
23
#include "brotli/decode.h"
24
25
namespace mozilla {
26
namespace net {
27
28
extern LazyLogModule gHttpLog;
29
0
#define LOG(args) MOZ_LOG(mozilla::net::gHttpLog, mozilla::LogLevel::Debug, args)
30
31
// nsISupports implementation
32
NS_IMPL_ISUPPORTS(nsHTTPCompressConv,
33
                  nsIStreamConverter,
34
                  nsIStreamListener,
35
                  nsIRequestObserver,
36
                  nsICompressConvStats,
37
                  nsIThreadRetargetableStreamListener)
38
39
// nsFTPDirListingConv methods
40
nsHTTPCompressConv::nsHTTPCompressConv()
41
  : mMode(HTTP_COMPRESS_IDENTITY)
42
  , mOutBuffer(nullptr)
43
  , mInpBuffer(nullptr)
44
  , mOutBufferLen(0)
45
  , mInpBufferLen(0)
46
  , mCheckHeaderDone(false)
47
  , mStreamEnded(false)
48
  , mStreamInitialized(false)
49
  , mDummyStreamInitialised(false)
50
  , d_stream{}
51
  , mLen(0)
52
  , hMode(0)
53
  , mSkipCount(0)
54
  , mFlags(0)
55
  , mDecodedDataLength(0)
56
  , mMutex("nsHTTPCompressConv")
57
0
{
58
0
  LOG(("nsHttpCompresssConv %p ctor\n", this));
59
0
  if (NS_IsMainThread()) {
60
0
    mFailUncleanStops =
61
0
      Preferences::GetBool("network.http.enforce-framing.http", false);
62
0
  } else {
63
0
    mFailUncleanStops = false;
64
0
  }
65
0
}
66
67
nsHTTPCompressConv::~nsHTTPCompressConv()
68
0
{
69
0
  LOG(("nsHttpCompresssConv %p dtor\n", this));
70
0
  if (mInpBuffer) {
71
0
    free(mInpBuffer);
72
0
  }
73
0
74
0
  if (mOutBuffer) {
75
0
    free(mOutBuffer);
76
0
  }
77
0
78
0
  // For some reason we are not getting Z_STREAM_END.  But this was also seen
79
0
  //    for mozilla bug 198133.  Need to handle this case.
80
0
  if (mStreamInitialized && !mStreamEnded) {
81
0
    inflateEnd (&d_stream);
82
0
  }
83
0
}
84
85
NS_IMETHODIMP
86
nsHTTPCompressConv::GetDecodedDataLength(uint64_t *aDecodedDataLength)
87
0
{
88
0
    *aDecodedDataLength = mDecodedDataLength;
89
0
    return NS_OK;
90
0
}
91
92
NS_IMETHODIMP
93
nsHTTPCompressConv::AsyncConvertData(const char *aFromType,
94
                                     const char *aToType,
95
                                     nsIStreamListener *aListener,
96
                                     nsISupports *aCtxt)
97
0
{
98
0
  if (!PL_strncasecmp(aFromType, HTTP_COMPRESS_TYPE, sizeof(HTTP_COMPRESS_TYPE)-1) ||
99
0
      !PL_strncasecmp(aFromType, HTTP_X_COMPRESS_TYPE, sizeof(HTTP_X_COMPRESS_TYPE)-1)) {
100
0
    mMode = HTTP_COMPRESS_COMPRESS;
101
0
  } else if (!PL_strncasecmp(aFromType, HTTP_GZIP_TYPE, sizeof(HTTP_GZIP_TYPE)-1) ||
102
0
             !PL_strncasecmp(aFromType, HTTP_X_GZIP_TYPE, sizeof(HTTP_X_GZIP_TYPE)-1)) {
103
0
    mMode = HTTP_COMPRESS_GZIP;
104
0
  } else if (!PL_strncasecmp(aFromType, HTTP_DEFLATE_TYPE, sizeof(HTTP_DEFLATE_TYPE)-1)) {
105
0
    mMode = HTTP_COMPRESS_DEFLATE;
106
0
  } else if (!PL_strncasecmp(aFromType, HTTP_BROTLI_TYPE, sizeof(HTTP_BROTLI_TYPE)-1)) {
107
0
    mMode = HTTP_COMPRESS_BROTLI;
108
0
  }
109
0
  LOG(("nsHttpCompresssConv %p AsyncConvertData %s %s mode %d\n",
110
0
       this, aFromType, aToType, (CompressMode)mMode));
111
0
112
0
  MutexAutoLock lock(mMutex);
113
0
  // hook ourself up with the receiving listener.
114
0
  mListener = aListener;
115
0
116
0
  return NS_OK;
117
0
}
118
119
NS_IMETHODIMP
120
nsHTTPCompressConv::OnStartRequest(nsIRequest* request, nsISupports *aContext)
121
0
{
122
0
  LOG(("nsHttpCompresssConv %p onstart\n", this));
123
0
  nsCOMPtr<nsIStreamListener> listener;
124
0
  {
125
0
    MutexAutoLock lock(mMutex);
126
0
    listener = mListener;
127
0
  }
128
0
  return listener->OnStartRequest(request, aContext);
129
0
}
130
131
NS_IMETHODIMP
132
nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext,
133
                                  nsresult aStatus)
134
0
{
135
0
  nsresult status = aStatus;
136
0
  LOG(("nsHttpCompresssConv %p onstop %" PRIx32 "\n", this, static_cast<uint32_t>(aStatus)));
137
0
138
0
  // Framing integrity is enforced for content-encoding: gzip, but not for
139
0
  // content-encoding: deflate. Note that gzip vs deflate is NOT determined
140
0
  // by content sniffing but only via header.
141
0
  if (!mStreamEnded && NS_SUCCEEDED(status) &&
142
0
      (mFailUncleanStops && (mMode == HTTP_COMPRESS_GZIP)) ) {
143
0
    // This is not a clean end of gzip stream: the transfer is incomplete.
144
0
    status = NS_ERROR_NET_PARTIAL_TRANSFER;
145
0
    LOG(("nsHttpCompresssConv %p onstop partial gzip\n", this));
146
0
  }
147
0
  if (NS_SUCCEEDED(status) && mMode == HTTP_COMPRESS_BROTLI) {
148
0
    nsCOMPtr<nsIForcePendingChannel> fpChannel = do_QueryInterface(request);
149
0
    bool isPending = false;
150
0
    if (request) {
151
0
      request->IsPending(&isPending);
152
0
    }
153
0
    if (fpChannel && !isPending) {
154
0
      fpChannel->ForcePending(true);
155
0
    }
156
0
    if (mBrotli && (mBrotli->mTotalOut == 0) && !mBrotli->mBrotliStateIsStreamEnd) {
157
0
      status = NS_ERROR_INVALID_CONTENT_ENCODING;
158
0
    }
159
0
    LOG(("nsHttpCompresssConv %p onstop brotlihandler rv %" PRIx32 "\n",
160
0
         this, static_cast<uint32_t>(status)));
161
0
    if (fpChannel && !isPending) {
162
0
      fpChannel->ForcePending(false);
163
0
    }
164
0
  }
165
0
166
0
  nsCOMPtr<nsIStreamListener> listener;
167
0
  {
168
0
    MutexAutoLock lock(mMutex);
169
0
    listener = mListener;
170
0
  }
171
0
  return listener->OnStopRequest(request, aContext, status);
172
0
}
173
174
175
/* static */ nsresult
176
nsHTTPCompressConv::BrotliHandler(nsIInputStream *stream, void *closure, const char *dataIn,
177
                                  uint32_t, uint32_t aAvail, uint32_t *countRead)
178
0
{
179
0
  MOZ_ASSERT(stream);
180
0
  nsHTTPCompressConv *self = static_cast<nsHTTPCompressConv *>(closure);
181
0
  *countRead = 0;
182
0
183
0
  const size_t kOutSize = 128 * 1024; // just a chunk size, we call in a loop
184
0
  uint8_t *outPtr;
185
0
  size_t outSize;
186
0
  size_t avail = aAvail;
187
0
  BrotliDecoderResult res;
188
0
189
0
  if (!self->mBrotli) {
190
0
    *countRead = aAvail;
191
0
    return NS_OK;
192
0
  }
193
0
194
0
  auto outBuffer = MakeUniqueFallible<uint8_t[]>(kOutSize);
195
0
  if (outBuffer == nullptr) {
196
0
    self->mBrotli->mStatus = NS_ERROR_OUT_OF_MEMORY;
197
0
    return self->mBrotli->mStatus;
198
0
  }
199
0
200
0
  do {
201
0
    outSize = kOutSize;
202
0
    outPtr = outBuffer.get();
203
0
204
0
    // brotli api is documented in brotli/dec/decode.h and brotli/dec/decode.c
205
0
    LOG(("nsHttpCompresssConv %p brotlihandler decompress %zu\n", self, avail));
206
0
    size_t totalOut = self->mBrotli->mTotalOut;
207
0
    res = ::BrotliDecoderDecompressStream(
208
0
      &self->mBrotli->mState,
209
0
      &avail, reinterpret_cast<const unsigned char **>(&dataIn),
210
0
      &outSize, &outPtr, &totalOut);
211
0
    outSize = kOutSize - outSize;
212
0
    self->mBrotli->mTotalOut = totalOut;
213
0
    self->mBrotli->mBrotliStateIsStreamEnd = BrotliDecoderIsFinished(&self->mBrotli->mState);
214
0
    LOG(("nsHttpCompresssConv %p brotlihandler decompress rv=%" PRIx32 " out=%zu\n",
215
0
         self, static_cast<uint32_t>(res), outSize));
216
0
217
0
    if (res == BROTLI_DECODER_RESULT_ERROR) {
218
0
      LOG(("nsHttpCompressConv %p marking invalid encoding", self));
219
0
      self->mBrotli->mStatus = NS_ERROR_INVALID_CONTENT_ENCODING;
220
0
      return self->mBrotli->mStatus;
221
0
    }
222
0
223
0
    // in 'the current implementation' brotli must consume everything before
224
0
    // asking for more input
225
0
    if (res == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
226
0
      MOZ_ASSERT(!avail);
227
0
      if (avail) {
228
0
        LOG(("nsHttpCompressConv %p did not consume all input", self));
229
0
        self->mBrotli->mStatus = NS_ERROR_UNEXPECTED;
230
0
        return self->mBrotli->mStatus;
231
0
      }
232
0
    }
233
0
    if (outSize > 0) {
234
0
      nsresult rv = self->do_OnDataAvailable(self->mBrotli->mRequest,
235
0
                                             self->mBrotli->mContext,
236
0
                                             self->mBrotli->mSourceOffset,
237
0
                                             reinterpret_cast<const char *>(outBuffer.get()),
238
0
                                             outSize);
239
0
      LOG(("nsHttpCompressConv %p BrotliHandler ODA rv=%" PRIx32, self, static_cast<uint32_t>(rv)));
240
0
      if (NS_FAILED(rv)) {
241
0
        self->mBrotli->mStatus = rv;
242
0
        return self->mBrotli->mStatus;
243
0
      }
244
0
    }
245
0
246
0
    if (res == BROTLI_DECODER_RESULT_SUCCESS ||
247
0
        res == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
248
0
      *countRead = aAvail;
249
0
      return NS_OK;
250
0
    }
251
0
    MOZ_ASSERT (res == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
252
0
  } while (res == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
253
0
254
0
  self->mBrotli->mStatus = NS_ERROR_UNEXPECTED;
255
0
  return self->mBrotli->mStatus;
256
0
}
257
258
NS_IMETHODIMP
259
nsHTTPCompressConv::OnDataAvailable(nsIRequest* request,
260
                                    nsISupports *aContext,
261
                                    nsIInputStream *iStr,
262
                                    uint64_t aSourceOffset,
263
                                    uint32_t aCount)
264
0
{
265
0
  nsresult rv = NS_ERROR_INVALID_CONTENT_ENCODING;
266
0
  uint32_t streamLen = aCount;
267
0
  LOG(("nsHttpCompressConv %p OnDataAvailable %d", this, aCount));
268
0
269
0
  if (streamLen == 0) {
270
0
    NS_ERROR("count of zero passed to OnDataAvailable");
271
0
    return NS_ERROR_UNEXPECTED;
272
0
  }
273
0
274
0
  if (mStreamEnded) {
275
0
    // Hmm... this may just indicate that the data stream is done and that
276
0
    // what's left is either metadata or padding of some sort.... throwing
277
0
    // it out is probably the safe thing to do.
278
0
    uint32_t n;
279
0
    return iStr->ReadSegments(NS_DiscardSegment, nullptr, streamLen, &n);
280
0
  }
281
0
282
0
  switch (mMode) {
283
0
  case HTTP_COMPRESS_GZIP:
284
0
    streamLen = check_header(iStr, streamLen, &rv);
285
0
286
0
    if (rv != NS_OK) {
287
0
      return rv;
288
0
    }
289
0
290
0
    if (streamLen == 0) {
291
0
      return NS_OK;
292
0
    }
293
0
294
0
    MOZ_FALLTHROUGH;
295
0
296
0
  case HTTP_COMPRESS_DEFLATE:
297
0
298
0
    if (mInpBuffer != nullptr && streamLen > mInpBufferLen) {
299
0
      unsigned char* originalInpBuffer = mInpBuffer;
300
0
      if (!(mInpBuffer = (unsigned char *) realloc(originalInpBuffer, mInpBufferLen = streamLen))) {
301
0
        free(originalInpBuffer);
302
0
      }
303
0
304
0
      if (mOutBufferLen < streamLen * 2) {
305
0
        unsigned char* originalOutBuffer = mOutBuffer;
306
0
        if (!(mOutBuffer = (unsigned char *) realloc(mOutBuffer, mOutBufferLen = streamLen * 3))) {
307
0
          free(originalOutBuffer);
308
0
        }
309
0
      }
310
0
311
0
      if (mInpBuffer == nullptr || mOutBuffer == nullptr) {
312
0
        return NS_ERROR_OUT_OF_MEMORY;
313
0
      }
314
0
    }
315
0
316
0
    if (mInpBuffer == nullptr) {
317
0
      mInpBuffer = (unsigned char *) malloc(mInpBufferLen = streamLen);
318
0
    }
319
0
320
0
    if (mOutBuffer == nullptr) {
321
0
      mOutBuffer = (unsigned char *) malloc(mOutBufferLen = streamLen * 3);
322
0
    }
323
0
324
0
    if (mInpBuffer == nullptr || mOutBuffer == nullptr) {
325
0
      return NS_ERROR_OUT_OF_MEMORY;
326
0
    }
327
0
328
0
    uint32_t unused;
329
0
    iStr->Read((char *)mInpBuffer, streamLen, &unused);
330
0
331
0
    if (mMode == HTTP_COMPRESS_DEFLATE) {
332
0
      if (!mStreamInitialized) {
333
0
        memset(&d_stream, 0, sizeof (d_stream));
334
0
335
0
        if (inflateInit(&d_stream) != Z_OK) {
336
0
          return NS_ERROR_FAILURE;
337
0
        }
338
0
339
0
        mStreamInitialized = true;
340
0
      }
341
0
      d_stream.next_in = mInpBuffer;
342
0
      d_stream.avail_in = (uInt)streamLen;
343
0
344
0
      mDummyStreamInitialised = false;
345
0
      for (;;) {
346
0
        d_stream.next_out = mOutBuffer;
347
0
        d_stream.avail_out = (uInt)mOutBufferLen;
348
0
349
0
        int code = inflate(&d_stream, Z_NO_FLUSH);
350
0
        unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out;
351
0
352
0
        if (code == Z_STREAM_END) {
353
0
          if (bytesWritten) {
354
0
            rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
355
0
            if (NS_FAILED (rv)) {
356
0
              return rv;
357
0
            }
358
0
          }
359
0
360
0
          inflateEnd(&d_stream);
361
0
          mStreamEnded = true;
362
0
          break;
363
0
        } else if (code == Z_OK) {
364
0
          if (bytesWritten) {
365
0
            rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
366
0
            if (NS_FAILED (rv)) {
367
0
              return rv;
368
0
            }
369
0
          }
370
0
        } else if (code == Z_BUF_ERROR) {
371
0
          if (bytesWritten) {
372
0
            rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
373
0
            if (NS_FAILED (rv)) {
374
0
              return rv;
375
0
            }
376
0
          }
377
0
          break;
378
0
        } else if (code == Z_DATA_ERROR) {
379
0
          // some servers (notably Apache with mod_deflate) don't generate zlib headers
380
0
          // insert a dummy header and try again
381
0
          static char dummy_head[2] =
382
0
            {
383
0
              0x8 + 0x7 * 0x10,
384
0
              (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
385
0
            };
386
0
          inflateReset(&d_stream);
387
0
          d_stream.next_in = (Bytef*) dummy_head;
388
0
          d_stream.avail_in = sizeof(dummy_head);
389
0
390
0
          code = inflate(&d_stream, Z_NO_FLUSH);
391
0
          if (code != Z_OK) {
392
0
            return NS_ERROR_FAILURE;
393
0
          }
394
0
395
0
          // stop an endless loop caused by non-deflate data being labelled as deflate
396
0
          if (mDummyStreamInitialised) {
397
0
            NS_WARNING("endless loop detected"
398
0
                       " - invalid deflate");
399
0
            return NS_ERROR_INVALID_CONTENT_ENCODING;
400
0
          }
401
0
          mDummyStreamInitialised = true;
402
0
          // reset stream pointers to our original data
403
0
          d_stream.next_in = mInpBuffer;
404
0
          d_stream.avail_in = (uInt)streamLen;
405
0
        } else {
406
0
          return NS_ERROR_INVALID_CONTENT_ENCODING;
407
0
        }
408
0
      } /* for */
409
0
    } else {
410
0
      if (!mStreamInitialized) {
411
0
        memset(&d_stream, 0, sizeof (d_stream));
412
0
413
0
        if (inflateInit2(&d_stream, -MAX_WBITS) != Z_OK) {
414
0
          return NS_ERROR_FAILURE;
415
0
        }
416
0
417
0
        mStreamInitialized = true;
418
0
      }
419
0
420
0
      d_stream.next_in  = mInpBuffer;
421
0
      d_stream.avail_in = (uInt)streamLen;
422
0
423
0
      for (;;) {
424
0
        d_stream.next_out  = mOutBuffer;
425
0
        d_stream.avail_out = (uInt)mOutBufferLen;
426
0
427
0
        int code = inflate (&d_stream, Z_NO_FLUSH);
428
0
        unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out;
429
0
430
0
        if (code == Z_STREAM_END) {
431
0
          if (bytesWritten) {
432
0
            rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
433
0
            if (NS_FAILED (rv)) {
434
0
              return rv;
435
0
            }
436
0
          }
437
0
438
0
          inflateEnd(&d_stream);
439
0
          mStreamEnded = true;
440
0
          break;
441
0
        } else if (code == Z_OK) {
442
0
          if (bytesWritten) {
443
0
            rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
444
0
            if (NS_FAILED (rv)) {
445
0
              return rv;
446
0
            }
447
0
          }
448
0
        } else if (code == Z_BUF_ERROR) {
449
0
          if (bytesWritten) {
450
0
            rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
451
0
            if (NS_FAILED (rv)) {
452
0
              return rv;
453
0
            }
454
0
          }
455
0
          break;
456
0
        } else {
457
0
          return NS_ERROR_INVALID_CONTENT_ENCODING;
458
0
        }
459
0
      } /* for */
460
0
    } /* gzip */
461
0
    break;
462
0
463
0
  case HTTP_COMPRESS_BROTLI:
464
0
  {
465
0
    if (!mBrotli) {
466
0
      mBrotli = new BrotliWrapper();
467
0
    }
468
0
469
0
    mBrotli->mRequest = request;
470
0
    mBrotli->mContext = aContext;
471
0
    mBrotli->mSourceOffset = aSourceOffset;
472
0
473
0
    uint32_t countRead;
474
0
    rv = iStr->ReadSegments(BrotliHandler, this, streamLen, &countRead);
475
0
    if (NS_SUCCEEDED(rv)) {
476
0
      rv = mBrotli->mStatus;
477
0
    }
478
0
    if (NS_FAILED(rv)) {
479
0
      return rv;
480
0
    }
481
0
  }
482
0
    break;
483
0
484
0
  default:
485
0
    nsCOMPtr<nsIStreamListener> listener;
486
0
    {
487
0
      MutexAutoLock lock(mMutex);
488
0
      listener = mListener;
489
0
    }
490
0
    rv = listener->OnDataAvailable(request, aContext, iStr, aSourceOffset, aCount);
491
0
    if (NS_FAILED (rv)) {
492
0
      return rv;
493
0
    }
494
0
  } /* switch */
495
0
496
0
  return NS_OK;
497
0
} /* OnDataAvailable */
498
499
// XXX/ruslan: need to implement this too
500
501
NS_IMETHODIMP
502
nsHTTPCompressConv::Convert(nsIInputStream *aFromStream,
503
                            const char *aFromType,
504
                            const char *aToType,
505
                            nsISupports *aCtxt,
506
                            nsIInputStream **_retval)
507
0
{
508
0
  return NS_ERROR_NOT_IMPLEMENTED;
509
0
}
510
511
nsresult
512
nsHTTPCompressConv::do_OnDataAvailable(nsIRequest* request,
513
                                       nsISupports *context, uint64_t offset,
514
                                       const char *buffer, uint32_t count)
515
0
{
516
0
  if (!mStream) {
517
0
    mStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID);
518
0
    NS_ENSURE_STATE(mStream);
519
0
  }
520
0
521
0
  mStream->ShareData(buffer, count);
522
0
523
0
  nsCOMPtr<nsIStreamListener> listener;
524
0
  {
525
0
    MutexAutoLock lock(mMutex);
526
0
    listener = mListener;
527
0
  }
528
0
  nsresult rv = listener->OnDataAvailable(request, context, mStream,
529
0
                                          offset, count);
530
0
531
0
  // Make sure the stream no longer references |buffer| in case our listener
532
0
  // is crazy enough to try to read from |mStream| after ODA.
533
0
  mStream->ShareData("", 0);
534
0
  mDecodedDataLength += count;
535
0
536
0
  return rv;
537
0
}
538
539
#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
540
0
#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
541
0
#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
542
0
#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
543
0
#define COMMENT      0x10 /* bit 4 set: file comment present */
544
0
#define RESERVED     0xE0 /* bits 5..7: reserved */
545
546
static unsigned gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
547
548
uint32_t
549
nsHTTPCompressConv::check_header(nsIInputStream *iStr, uint32_t streamLen, nsresult *rs)
550
0
{
551
0
  enum  { GZIP_INIT = 0, GZIP_OS, GZIP_EXTRA0, GZIP_EXTRA1, GZIP_EXTRA2, GZIP_ORIG, GZIP_COMMENT, GZIP_CRC };
552
0
  char c;
553
0
554
0
  *rs = NS_OK;
555
0
556
0
  if (mCheckHeaderDone) {
557
0
    return streamLen;
558
0
  }
559
0
560
0
  while (streamLen) {
561
0
    switch (hMode) {
562
0
    case GZIP_INIT:
563
0
      uint32_t unused;
564
0
      iStr->Read(&c, 1, &unused);
565
0
      streamLen--;
566
0
567
0
      if (mSkipCount == 0 && ((unsigned)c & 0377) != gz_magic[0]) {
568
0
        *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
569
0
        return 0;
570
0
      }
571
0
572
0
      if (mSkipCount == 1 && ((unsigned)c & 0377) != gz_magic[1]) {
573
0
        *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
574
0
        return 0;
575
0
      }
576
0
577
0
      if (mSkipCount == 2 && ((unsigned)c & 0377) != Z_DEFLATED) {
578
0
        *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
579
0
        return 0;
580
0
      }
581
0
582
0
      mSkipCount++;
583
0
      if (mSkipCount == 4) {
584
0
        mFlags = (unsigned) c & 0377;
585
0
        if (mFlags & RESERVED) {
586
0
          *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
587
0
          return 0;
588
0
        }
589
0
        hMode = GZIP_OS;
590
0
        mSkipCount = 0;
591
0
      }
592
0
      break;
593
0
594
0
    case GZIP_OS:
595
0
      iStr->Read(&c, 1, &unused);
596
0
      streamLen--;
597
0
      mSkipCount++;
598
0
599
0
      if (mSkipCount == 6) {
600
0
        hMode = GZIP_EXTRA0;
601
0
      }
602
0
      break;
603
0
604
0
    case GZIP_EXTRA0:
605
0
      if (mFlags & EXTRA_FIELD) {
606
0
        iStr->Read(&c, 1, &unused);
607
0
        streamLen--;
608
0
        mLen = (uInt) c & 0377;
609
0
        hMode = GZIP_EXTRA1;
610
0
      } else {
611
0
        hMode = GZIP_ORIG;
612
0
      }
613
0
      break;
614
0
615
0
    case GZIP_EXTRA1:
616
0
      iStr->Read(&c, 1, &unused);
617
0
      streamLen--;
618
0
      mLen |= ((uInt) c & 0377) << 8;
619
0
      mSkipCount = 0;
620
0
      hMode = GZIP_EXTRA2;
621
0
      break;
622
0
623
0
    case GZIP_EXTRA2:
624
0
      if (mSkipCount == mLen) {
625
0
        hMode = GZIP_ORIG;
626
0
      } else {
627
0
        iStr->Read(&c, 1, &unused);
628
0
        streamLen--;
629
0
        mSkipCount++;
630
0
      }
631
0
      break;
632
0
633
0
    case GZIP_ORIG:
634
0
      if (mFlags & ORIG_NAME) {
635
0
        iStr->Read(&c, 1, &unused);
636
0
        streamLen--;
637
0
        if (c == 0)
638
0
          hMode = GZIP_COMMENT;
639
0
      } else {
640
0
        hMode = GZIP_COMMENT;
641
0
      }
642
0
      break;
643
0
644
0
    case GZIP_COMMENT:
645
0
      if (mFlags & COMMENT) {
646
0
        iStr->Read(&c, 1, &unused);
647
0
        streamLen--;
648
0
        if (c == 0) {
649
0
          hMode = GZIP_CRC;
650
0
          mSkipCount = 0;
651
0
        }
652
0
      } else {
653
0
        hMode = GZIP_CRC;
654
0
        mSkipCount = 0;
655
0
      }
656
0
      break;
657
0
658
0
    case GZIP_CRC:
659
0
      if (mFlags & HEAD_CRC) {
660
0
        iStr->Read(&c, 1, &unused);
661
0
        streamLen--;
662
0
        mSkipCount++;
663
0
        if (mSkipCount == 2) {
664
0
          mCheckHeaderDone = true;
665
0
          return streamLen;
666
0
        }
667
0
      } else {
668
0
        mCheckHeaderDone = true;
669
0
        return streamLen;
670
0
      }
671
0
      break;
672
0
    }
673
0
  }
674
0
  return streamLen;
675
0
}
676
677
NS_IMETHODIMP
678
nsHTTPCompressConv::CheckListenerChain()
679
0
{
680
0
  nsCOMPtr<nsIThreadRetargetableStreamListener> listener;
681
0
  {
682
0
    MutexAutoLock lock(mMutex);
683
0
    listener = do_QueryInterface(mListener);
684
0
  }
685
0
686
0
  if (!listener) {
687
0
    return NS_ERROR_NO_INTERFACE;
688
0
  }
689
0
690
0
  return listener->CheckListenerChain();
691
0
}
692
693
} // namespace net
694
} // namespace mozilla
695
696
nsresult
697
NS_NewHTTPCompressConv(mozilla::net::nsHTTPCompressConv **aHTTPCompressConv)
698
0
{
699
0
  MOZ_ASSERT(aHTTPCompressConv != nullptr, "null ptr");
700
0
  if (!aHTTPCompressConv) {
701
0
    return NS_ERROR_NULL_POINTER;
702
0
  }
703
0
704
0
  RefPtr<mozilla::net::nsHTTPCompressConv> outVal =
705
0
    new mozilla::net::nsHTTPCompressConv();
706
0
  if (!outVal) {
707
0
    return NS_ERROR_OUT_OF_MEMORY;
708
0
  }
709
0
  outVal.forget(aHTTPCompressConv);
710
0
  return NS_OK;
711
0
}