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/. */
7 #include "nsHTTPCompressConv.h"
12 #include "nsIThreadRetargetableStreamListener.h"
13 #include "nsStreamUtils.h"
14 #include "nsStringStream.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsThreadUtils.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/StaticPrefs_network.h"
19 #include "mozilla/Logging.h"
20 #include "nsIForcePendingChannel.h"
21 #include "nsIRequest.h"
22 #include "mozilla/UniquePtrExtensions.h"
23 #include "nsIThreadRetargetableRequest.h"
29 #include "brotli/decode.h"
34 extern LazyLogModule gHttpLog
;
36 MOZ_LOG(mozilla::net::gHttpLog, mozilla::LogLevel::Debug, args)
41 BrotliDecoderStateInit(&mState
, nullptr, nullptr, nullptr);
43 ~BrotliWrapper() { BrotliDecoderStateCleanup(&mState
); }
45 BrotliDecoderState mState
{};
46 Atomic
<size_t, Relaxed
> mTotalOut
{0};
47 nsresult mStatus
= NS_OK
;
48 Atomic
<bool, Relaxed
> mBrotliStateIsStreamEnd
{false};
50 nsIRequest
* mRequest
{nullptr};
51 nsISupports
* mContext
{nullptr};
52 uint64_t mSourceOffset
{0};
55 // nsISupports implementation
56 NS_IMPL_ISUPPORTS(nsHTTPCompressConv
, nsIStreamConverter
, nsIStreamListener
,
57 nsIRequestObserver
, nsICompressConvStats
,
58 nsIThreadRetargetableStreamListener
)
60 // nsFTPDirListingConv methods
61 nsHTTPCompressConv::nsHTTPCompressConv() {
62 LOG(("nsHttpCompresssConv %p ctor\n", this));
63 if (NS_IsMainThread()) {
65 Preferences::GetBool("network.http.enforce-framing.http", false);
67 mFailUncleanStops
= false;
71 nsHTTPCompressConv::~nsHTTPCompressConv() {
72 LOG(("nsHttpCompresssConv %p dtor\n", this));
81 // For some reason we are not getting Z_STREAM_END. But this was also seen
82 // for mozilla bug 198133. Need to handle this case.
83 if (mStreamInitialized
&& !mStreamEnded
) {
84 inflateEnd(&d_stream
);
89 nsHTTPCompressConv::GetDecodedDataLength(uint64_t* aDecodedDataLength
) {
90 *aDecodedDataLength
= mDecodedDataLength
;
95 nsHTTPCompressConv::AsyncConvertData(const char* aFromType
, const char* aToType
,
96 nsIStreamListener
* aListener
,
98 if (!nsCRT::strncasecmp(aFromType
, HTTP_COMPRESS_TYPE
,
99 sizeof(HTTP_COMPRESS_TYPE
) - 1) ||
100 !nsCRT::strncasecmp(aFromType
, HTTP_X_COMPRESS_TYPE
,
101 sizeof(HTTP_X_COMPRESS_TYPE
) - 1)) {
102 mMode
= HTTP_COMPRESS_COMPRESS
;
103 } else if (!nsCRT::strncasecmp(aFromType
, HTTP_GZIP_TYPE
,
104 sizeof(HTTP_GZIP_TYPE
) - 1) ||
105 !nsCRT::strncasecmp(aFromType
, HTTP_X_GZIP_TYPE
,
106 sizeof(HTTP_X_GZIP_TYPE
) - 1)) {
107 mMode
= HTTP_COMPRESS_GZIP
;
108 } else if (!nsCRT::strncasecmp(aFromType
, HTTP_DEFLATE_TYPE
,
109 sizeof(HTTP_DEFLATE_TYPE
) - 1)) {
110 mMode
= HTTP_COMPRESS_DEFLATE
;
111 } else if (!nsCRT::strncasecmp(aFromType
, HTTP_BROTLI_TYPE
,
112 sizeof(HTTP_BROTLI_TYPE
) - 1)) {
113 mMode
= HTTP_COMPRESS_BROTLI
;
115 LOG(("nsHttpCompresssConv %p AsyncConvertData %s %s mode %d\n", this,
116 aFromType
, aToType
, (CompressMode
)mMode
));
118 MutexAutoLock
lock(mMutex
);
119 // hook ourself up with the receiving listener.
120 mListener
= aListener
;
126 nsHTTPCompressConv::GetConvertedType(const nsACString
& aFromType
,
127 nsIChannel
* aChannel
,
128 nsACString
& aToType
) {
129 return NS_ERROR_NOT_IMPLEMENTED
;
133 nsHTTPCompressConv::MaybeRetarget(nsIRequest
* request
) {
134 MOZ_ASSERT(NS_IsMainThread());
136 nsCOMPtr
<nsIThreadRetargetableRequest
> req
= do_QueryInterface(request
);
138 return NS_ERROR_NO_INTERFACE
;
140 if (!StaticPrefs::network_decompression_off_mainthread()) {
143 nsCOMPtr
<nsISerialEventTarget
> target
;
144 rv
= req
->GetDeliveryTarget(getter_AddRefs(target
));
145 if (NS_FAILED(rv
) || !target
|| target
->IsOnCurrentThread()) {
146 // No retargetting was performed. Decompress off MainThread,
147 // and dispatch results back to MainThread
148 nsCOMPtr
<nsISerialEventTarget
> backgroundThread
;
149 rv
= NS_CreateBackgroundTaskQueue("nsHTTPCompressConv",
150 getter_AddRefs(backgroundThread
));
151 NS_ENSURE_SUCCESS(rv
, rv
);
152 rv
= req
->RetargetDeliveryTo(backgroundThread
);
153 NS_ENSURE_SUCCESS(rv
, rv
);
154 if (NS_SUCCEEDED(rv
)) {
155 mDispatchToMainThread
= true;
163 nsHTTPCompressConv::OnStartRequest(nsIRequest
* request
) {
164 LOG(("nsHttpCompresssConv %p onstart\n", this));
165 nsCOMPtr
<nsIStreamListener
> listener
;
167 MutexAutoLock
lock(mMutex
);
168 listener
= mListener
;
170 nsresult rv
= listener
->OnStartRequest(request
);
171 if (NS_SUCCEEDED(rv
)) {
172 if (XRE_IsContentProcess()) {
173 nsCOMPtr
<nsIThreadRetargetableStreamListener
> retargetlistener
=
174 do_QueryInterface(listener
);
175 // |nsHTTPCompressConv| should *always* be dispatched off of the main
176 // thread from a content process, even if its listeners don't support it.
178 // If its listener chain does not support being retargeted off of the
179 // main thread, it will be dispatched back to the main thread in
180 // |do_OnDataAvailable| and |OnStopRequest|.
181 if (!retargetlistener
||
182 NS_FAILED(retargetlistener
->CheckListenerChain())) {
183 mDispatchToMainThread
= true;
191 nsHTTPCompressConv::OnStopRequest(nsIRequest
* request
, nsresult aStatus
) {
192 nsresult status
= aStatus
;
193 // Bug 1886237 : TRRServiceChannel calls OnStopRequest OMT
194 // MOZ_ASSERT(NS_IsMainThread());
195 LOG(("nsHttpCompresssConv %p onstop %" PRIx32
" mDispatchToMainThread %d\n",
196 this, static_cast<uint32_t>(aStatus
), mDispatchToMainThread
));
198 // Framing integrity is enforced for content-encoding: gzip, but not for
199 // content-encoding: deflate. Note that gzip vs deflate is NOT determined
200 // by content sniffing but only via header.
201 if (!mStreamEnded
&& NS_SUCCEEDED(status
) &&
202 (mFailUncleanStops
&& (mMode
== HTTP_COMPRESS_GZIP
))) {
203 // This is not a clean end of gzip stream: the transfer is incomplete.
204 status
= NS_ERROR_NET_PARTIAL_TRANSFER
;
205 LOG(("nsHttpCompresssConv %p onstop partial gzip\n", this));
207 if (NS_SUCCEEDED(status
) && mMode
== HTTP_COMPRESS_BROTLI
) {
208 nsCOMPtr
<nsIForcePendingChannel
> fpChannel
= do_QueryInterface(request
);
209 bool isPending
= false;
211 request
->IsPending(&isPending
);
213 if (fpChannel
&& !isPending
) {
214 fpChannel
->ForcePending(true);
216 bool allowTruncatedEmpty
=
217 StaticPrefs::network_compress_allow_truncated_empty_brotli();
218 if (mBrotli
&& ((allowTruncatedEmpty
&& NS_FAILED(mBrotli
->mStatus
)) ||
219 (!allowTruncatedEmpty
&& mBrotli
->mTotalOut
== 0 &&
220 !mBrotli
->mBrotliStateIsStreamEnd
))) {
221 status
= NS_ERROR_INVALID_CONTENT_ENCODING
;
223 LOG(("nsHttpCompresssConv %p onstop brotlihandler rv %" PRIx32
"\n", this,
224 static_cast<uint32_t>(status
)));
225 if (fpChannel
&& !isPending
) {
226 fpChannel
->ForcePending(false);
230 nsCOMPtr
<nsIStreamListener
> listener
;
232 MutexAutoLock
lock(mMutex
);
233 listener
= mListener
;
236 return listener
->OnStopRequest(request
, status
);
240 nsresult
nsHTTPCompressConv::BrotliHandler(nsIInputStream
* stream
,
241 void* closure
, const char* dataIn
,
242 uint32_t, uint32_t aAvail
,
243 uint32_t* countRead
) {
245 nsHTTPCompressConv
* self
= static_cast<nsHTTPCompressConv
*>(closure
);
248 const size_t kOutSize
= 128 * 1024; // just a chunk size, we call in a loop
251 size_t avail
= aAvail
;
252 BrotliDecoderResult res
;
254 if (!self
->mBrotli
) {
259 auto outBuffer
= MakeUniqueFallible
<uint8_t[]>(kOutSize
);
260 if (outBuffer
== nullptr) {
261 self
->mBrotli
->mStatus
= NS_ERROR_OUT_OF_MEMORY
;
262 return self
->mBrotli
->mStatus
;
266 outPtr
= outBuffer
.get();
268 // brotli api is documented in brotli/dec/decode.h and brotli/dec/decode.c
269 LOG(("nsHttpCompresssConv %p brotlihandler decompress %zu\n", self
, avail
));
270 size_t totalOut
= self
->mBrotli
->mTotalOut
;
271 res
= ::BrotliDecoderDecompressStream(
272 &self
->mBrotli
->mState
, &avail
,
273 reinterpret_cast<const unsigned char**>(&dataIn
), &outSize
, &outPtr
,
275 outSize
= kOutSize
- outSize
;
276 self
->mBrotli
->mTotalOut
= totalOut
;
277 self
->mBrotli
->mBrotliStateIsStreamEnd
=
278 BrotliDecoderIsFinished(&self
->mBrotli
->mState
);
279 LOG(("nsHttpCompresssConv %p brotlihandler decompress rv=%" PRIx32
281 self
, static_cast<uint32_t>(res
), outSize
));
283 if (res
== BROTLI_DECODER_RESULT_ERROR
) {
284 LOG(("nsHttpCompressConv %p marking invalid encoding", self
));
285 self
->mBrotli
->mStatus
= NS_ERROR_INVALID_CONTENT_ENCODING
;
286 return self
->mBrotli
->mStatus
;
289 // in 'the current implementation' brotli must consume everything before
290 // asking for more input
291 if (res
== BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
) {
294 LOG(("nsHttpCompressConv %p did not consume all input", self
));
295 self
->mBrotli
->mStatus
= NS_ERROR_UNEXPECTED
;
296 return self
->mBrotli
->mStatus
;
300 auto callOnDataAvailable
= [&](uint64_t aSourceOffset
, const char* aBuffer
,
302 nsresult rv
= self
->do_OnDataAvailable(self
->mBrotli
->mRequest
,
303 aSourceOffset
, aBuffer
, aCount
);
304 LOG(("nsHttpCompressConv %p BrotliHandler ODA rv=%" PRIx32
, self
,
305 static_cast<uint32_t>(rv
)));
307 self
->mBrotli
->mStatus
= rv
;
314 if (NS_FAILED(callOnDataAvailable(
315 self
->mBrotli
->mSourceOffset
,
316 reinterpret_cast<const char*>(outBuffer
.get()), outSize
))) {
317 return self
->mBrotli
->mStatus
;
319 self
->mBrotli
->mSourceOffset
+= outSize
;
322 // See bug 1759745. If the decoder has more output data, take it.
323 while (::BrotliDecoderHasMoreOutput(&self
->mBrotli
->mState
)) {
325 const uint8_t* buffer
=
326 ::BrotliDecoderTakeOutput(&self
->mBrotli
->mState
, &outSize
);
327 if (NS_FAILED(callOnDataAvailable(self
->mBrotli
->mSourceOffset
,
328 reinterpret_cast<const char*>(buffer
),
330 return self
->mBrotli
->mStatus
;
332 self
->mBrotli
->mSourceOffset
+= outSize
;
335 if (res
== BROTLI_DECODER_RESULT_SUCCESS
||
336 res
== BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
) {
340 MOZ_ASSERT(res
== BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT
);
341 } while (res
== BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT
);
343 self
->mBrotli
->mStatus
= NS_ERROR_UNEXPECTED
;
344 return self
->mBrotli
->mStatus
;
348 nsHTTPCompressConv::OnDataAvailable(nsIRequest
* request
, nsIInputStream
* iStr
,
349 uint64_t aSourceOffset
, uint32_t aCount
) {
350 nsresult rv
= NS_ERROR_INVALID_CONTENT_ENCODING
;
351 uint32_t streamLen
= aCount
;
352 LOG(("nsHttpCompressConv %p OnDataAvailable aSourceOffset:%" PRIu64
354 this, aSourceOffset
, aCount
));
356 if (streamLen
== 0) {
357 NS_ERROR("count of zero passed to OnDataAvailable");
358 return NS_ERROR_UNEXPECTED
;
362 // Hmm... this may just indicate that the data stream is done and that
363 // what's left is either metadata or padding of some sort.... throwing
364 // it out is probably the safe thing to do.
366 return iStr
->ReadSegments(NS_DiscardSegment
, nullptr, streamLen
, &n
);
370 case HTTP_COMPRESS_GZIP
:
371 streamLen
= check_header(iStr
, streamLen
, &rv
);
377 if (streamLen
== 0) {
383 case HTTP_COMPRESS_DEFLATE
:
385 if (mInpBuffer
!= nullptr && streamLen
> mInpBufferLen
) {
386 unsigned char* originalInpBuffer
= mInpBuffer
;
387 if (!(mInpBuffer
= (unsigned char*)realloc(
388 originalInpBuffer
, mInpBufferLen
= streamLen
))) {
389 free(originalInpBuffer
);
392 if (mOutBufferLen
< streamLen
* 2) {
393 unsigned char* originalOutBuffer
= mOutBuffer
;
394 if (!(mOutBuffer
= (unsigned char*)realloc(
395 mOutBuffer
, mOutBufferLen
= streamLen
* 3))) {
396 free(originalOutBuffer
);
400 if (mInpBuffer
== nullptr || mOutBuffer
== nullptr) {
401 return NS_ERROR_OUT_OF_MEMORY
;
405 if (mInpBuffer
== nullptr) {
406 mInpBuffer
= (unsigned char*)malloc(mInpBufferLen
= streamLen
);
409 if (mOutBuffer
== nullptr) {
410 mOutBuffer
= (unsigned char*)malloc(mOutBufferLen
= streamLen
* 3);
413 if (mInpBuffer
== nullptr || mOutBuffer
== nullptr) {
414 return NS_ERROR_OUT_OF_MEMORY
;
418 iStr
->Read((char*)mInpBuffer
, streamLen
, &unused
);
420 if (mMode
== HTTP_COMPRESS_DEFLATE
) {
421 if (!mStreamInitialized
) {
422 memset(&d_stream
, 0, sizeof(d_stream
));
424 if (inflateInit(&d_stream
) != Z_OK
) {
425 return NS_ERROR_FAILURE
;
428 mStreamInitialized
= true;
430 d_stream
.next_in
= mInpBuffer
;
431 d_stream
.avail_in
= (uInt
)streamLen
;
433 mDummyStreamInitialised
= false;
435 d_stream
.next_out
= mOutBuffer
;
436 d_stream
.avail_out
= (uInt
)mOutBufferLen
;
438 int code
= inflate(&d_stream
, Z_NO_FLUSH
);
439 unsigned bytesWritten
= (uInt
)mOutBufferLen
- d_stream
.avail_out
;
441 if (code
== Z_STREAM_END
) {
443 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
450 inflateEnd(&d_stream
);
456 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
462 } else if (code
== Z_BUF_ERROR
) {
464 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
471 } else if (code
== Z_DATA_ERROR
) {
472 // some servers (notably Apache with mod_deflate) don't generate
473 // zlib headers insert a dummy header and try again
474 static char dummy_head
[2] = {
476 (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
478 inflateReset(&d_stream
);
479 d_stream
.next_in
= (Bytef
*)dummy_head
;
480 d_stream
.avail_in
= sizeof(dummy_head
);
482 code
= inflate(&d_stream
, Z_NO_FLUSH
);
484 return NS_ERROR_FAILURE
;
487 // stop an endless loop caused by non-deflate data being labelled as
489 if (mDummyStreamInitialised
) {
491 "endless loop detected"
492 " - invalid deflate");
493 return NS_ERROR_INVALID_CONTENT_ENCODING
;
495 mDummyStreamInitialised
= true;
496 // reset stream pointers to our original data
497 d_stream
.next_in
= mInpBuffer
;
498 d_stream
.avail_in
= (uInt
)streamLen
;
500 return NS_ERROR_INVALID_CONTENT_ENCODING
;
504 if (!mStreamInitialized
) {
505 memset(&d_stream
, 0, sizeof(d_stream
));
507 if (inflateInit2(&d_stream
, -MAX_WBITS
) != Z_OK
) {
508 return NS_ERROR_FAILURE
;
511 mStreamInitialized
= true;
514 d_stream
.next_in
= mInpBuffer
;
515 d_stream
.avail_in
= (uInt
)streamLen
;
518 d_stream
.next_out
= mOutBuffer
;
519 d_stream
.avail_out
= (uInt
)mOutBufferLen
;
521 int code
= inflate(&d_stream
, Z_NO_FLUSH
);
522 unsigned bytesWritten
= (uInt
)mOutBufferLen
- d_stream
.avail_out
;
524 if (code
== Z_STREAM_END
) {
526 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
533 inflateEnd(&d_stream
);
539 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
545 } else if (code
== Z_BUF_ERROR
) {
547 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
555 return NS_ERROR_INVALID_CONTENT_ENCODING
;
561 case HTTP_COMPRESS_BROTLI
: {
563 mBrotli
= MakeUnique
<BrotliWrapper
>();
566 mBrotli
->mRequest
= request
;
567 mBrotli
->mContext
= nullptr;
568 mBrotli
->mSourceOffset
= aSourceOffset
;
571 rv
= iStr
->ReadSegments(BrotliHandler
, this, streamLen
, &countRead
);
572 if (NS_SUCCEEDED(rv
)) {
573 rv
= mBrotli
->mStatus
;
581 nsCOMPtr
<nsIStreamListener
> listener
;
583 MutexAutoLock
lock(mMutex
);
584 listener
= mListener
;
586 rv
= listener
->OnDataAvailable(request
, iStr
, aSourceOffset
, aCount
);
593 } /* OnDataAvailable */
595 // XXX/ruslan: need to implement this too
598 nsHTTPCompressConv::Convert(nsIInputStream
* aFromStream
, const char* aFromType
,
599 const char* aToType
, nsISupports
* aCtxt
,
600 nsIInputStream
** _retval
) {
601 return NS_ERROR_NOT_IMPLEMENTED
;
604 nsresult
nsHTTPCompressConv::do_OnDataAvailable(nsIRequest
* request
,
608 LOG(("nsHttpCompressConv %p do_OnDataAvailable mDispatchToMainThread %d",
609 this, mDispatchToMainThread
));
610 if (mDispatchToMainThread
&& !NS_IsMainThread()) {
611 nsCOMPtr
<nsIInputStream
> stream
;
612 MOZ_TRY(NS_NewByteInputStream(getter_AddRefs(stream
), Span(buffer
, count
),
613 nsAssignmentType::NS_ASSIGNMENT_COPY
));
615 nsCOMPtr
<nsIStreamListener
> listener
;
617 MutexAutoLock
lock(mMutex
);
618 listener
= mListener
;
621 // This is safe and will always run before OnStopRequest, because
622 // ChanneleventQueue means that we can't enqueue OnStopRequest until after
623 // the OMT OnDataAvailable call has completed. So Dispatching here will
624 // ensure it's in the MainThread event queue before OnStopRequest
625 nsCOMPtr
<nsIRunnable
> handler
= NS_NewRunnableFunction(
626 "nsHTTPCompressConv::do_OnDataAvailable",
627 [request
{RefPtr
<nsIRequest
>(request
)}, stream
{std::move(stream
)},
628 listener
{std::move(listener
)}, offset
, count
]() {
629 LOG(("nsHttpCompressConv Calling OnDataAvailable on Mainthread"));
630 Unused
<< listener
->OnDataAvailable(request
, stream
, offset
, count
);
633 mDecodedDataLength
+= count
;
634 return NS_DispatchToMainThread(handler
);
638 mStream
= do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID
);
639 NS_ENSURE_STATE(mStream
);
642 mStream
->ShareData(buffer
, count
);
644 nsCOMPtr
<nsIStreamListener
> listener
;
646 MutexAutoLock
lock(mMutex
);
647 listener
= mListener
;
649 LOG(("nsHTTPCompressConv::do_OnDataAvailable req:%p offset: offset:%" PRIu64
651 request
, offset
, count
));
652 nsresult rv
= listener
->OnDataAvailable(request
, mStream
, offset
, count
);
654 // Make sure the stream no longer references |buffer| in case our listener
655 // is crazy enough to try to read from |mStream| after ODA.
656 mStream
->ShareData("", 0);
657 mDecodedDataLength
+= count
;
662 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
663 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
664 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
665 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
666 #define COMMENT 0x10 /* bit 4 set: file comment present */
667 #define RESERVED 0xE0 /* bits 5..7: reserved */
669 static unsigned gz_magic
[2] = {0x1f, 0x8b}; /* gzip magic header */
671 uint32_t nsHTTPCompressConv::check_header(nsIInputStream
* iStr
,
672 uint32_t streamLen
, nsresult
* rs
) {
687 if (mCheckHeaderDone
) {
695 iStr
->Read(&c
, 1, &unused
);
698 if (mSkipCount
== 0 && ((unsigned)c
& 0377) != gz_magic
[0]) {
699 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
703 if (mSkipCount
== 1 && ((unsigned)c
& 0377) != gz_magic
[1]) {
704 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
708 if (mSkipCount
== 2 && ((unsigned)c
& 0377) != Z_DEFLATED
) {
709 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
714 if (mSkipCount
== 4) {
715 mFlags
= (unsigned)c
& 0377;
716 if (mFlags
& RESERVED
) {
717 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
726 iStr
->Read(&c
, 1, &unused
);
730 if (mSkipCount
== 6) {
736 if (mFlags
& EXTRA_FIELD
) {
737 iStr
->Read(&c
, 1, &unused
);
739 mLen
= (uInt
)c
& 0377;
747 iStr
->Read(&c
, 1, &unused
);
749 mLen
|= ((uInt
)c
& 0377) << 8;
755 if (mSkipCount
== mLen
) {
758 iStr
->Read(&c
, 1, &unused
);
765 if (mFlags
& ORIG_NAME
) {
766 iStr
->Read(&c
, 1, &unused
);
768 if (c
== 0) hMode
= GZIP_COMMENT
;
770 hMode
= GZIP_COMMENT
;
775 if (mFlags
& COMMENT
) {
776 iStr
->Read(&c
, 1, &unused
);
789 if (mFlags
& HEAD_CRC
) {
790 iStr
->Read(&c
, 1, &unused
);
793 if (mSkipCount
== 2) {
794 mCheckHeaderDone
= true;
798 mCheckHeaderDone
= true;
808 nsHTTPCompressConv::CheckListenerChain() {
809 if (XRE_IsContentProcess()) {
810 // handle decompression OMT always. If the chain needs to be MT,
811 // we'll determine that in OnStartRequest and dispatch to MT
814 nsCOMPtr
<nsIThreadRetargetableStreamListener
> listener
;
816 MutexAutoLock
lock(mMutex
);
817 listener
= do_QueryInterface(mListener
);
820 return NS_ERROR_NO_INTERFACE
;
823 return listener
->CheckListenerChain();
827 nsHTTPCompressConv::OnDataFinished(nsresult aStatus
) {
828 nsCOMPtr
<nsIThreadRetargetableStreamListener
> listener
;
831 MutexAutoLock
lock(mMutex
);
832 listener
= do_QueryInterface(mListener
);
836 if (mDispatchToMainThread
&& !NS_IsMainThread()) {
837 nsCOMPtr
<nsIRunnable
> handler
= NS_NewRunnableFunction(
838 "dispatch", [listener
{std::move(listener
)}, aStatus
]() {
839 Unused
<< listener
->OnDataFinished(aStatus
);
842 return NS_DispatchToMainThread(handler
);
845 return listener
->OnDataFinished(aStatus
);
852 } // namespace mozilla
854 nsresult
NS_NewHTTPCompressConv(
855 mozilla::net::nsHTTPCompressConv
** aHTTPCompressConv
) {
856 MOZ_ASSERT(aHTTPCompressConv
!= nullptr, "null ptr");
857 if (!aHTTPCompressConv
) {
858 return NS_ERROR_NULL_POINTER
;
861 RefPtr
<mozilla::net::nsHTTPCompressConv
> outVal
=
862 new mozilla::net::nsHTTPCompressConv();
864 return NS_ERROR_OUT_OF_MEMORY
;
866 outVal
.forget(aHTTPCompressConv
);