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"
24 #include "nsIChannel.h"
30 #include "brotli/decode.h"
32 #include "zstd/zstd.h"
37 extern LazyLogModule gHttpLog
;
39 MOZ_LOG(mozilla::net::gHttpLog, mozilla::LogLevel::Debug, args)
44 BrotliDecoderStateInit(&mState
, nullptr, nullptr, nullptr);
46 ~BrotliWrapper() { BrotliDecoderStateCleanup(&mState
); }
48 BrotliDecoderState mState
{};
49 Atomic
<size_t, Relaxed
> mTotalOut
{0};
50 nsresult mStatus
= NS_OK
;
51 Atomic
<bool, Relaxed
> mBrotliStateIsStreamEnd
{false};
53 nsIRequest
* mRequest
{nullptr};
54 nsISupports
* mContext
{nullptr};
55 uint64_t mSourceOffset
{0};
61 mDStream
= ZSTD_createDStream();
62 MOZ_RELEASE_ASSERT(mDStream
); // we'll crash anyways if it fails
63 ZSTD_DCtx_setParameter(mDStream
, ZSTD_d_windowLogMax
, 23 /*8*1024*1024*/);
67 ZSTD_freeDStream(mDStream
);
71 UniquePtr
<uint8_t[]> mOutBuffer
;
72 nsresult mStatus
= NS_OK
;
73 nsIRequest
* mRequest
{nullptr};
74 nsISupports
* mContext
{nullptr};
75 uint64_t mSourceOffset
{0};
76 ZSTD_DStream
* mDStream
{nullptr};
79 // nsISupports implementation
80 NS_IMPL_ISUPPORTS(nsHTTPCompressConv
, nsIStreamConverter
, nsIStreamListener
,
81 nsIRequestObserver
, nsICompressConvStats
,
82 nsIThreadRetargetableStreamListener
)
84 // nsFTPDirListingConv methods
85 nsHTTPCompressConv::nsHTTPCompressConv() {
86 LOG(("nsHttpCompresssConv %p ctor\n", this));
87 if (NS_IsMainThread()) {
89 Preferences::GetBool("network.http.enforce-framing.http", false);
91 mFailUncleanStops
= false;
95 nsHTTPCompressConv::~nsHTTPCompressConv() {
96 LOG(("nsHttpCompresssConv %p dtor\n", this));
105 // For some reason we are not getting Z_STREAM_END. But this was also seen
106 // for mozilla bug 198133. Need to handle this case.
107 if (mStreamInitialized
&& !mStreamEnded
) {
108 inflateEnd(&d_stream
);
113 nsHTTPCompressConv::GetDecodedDataLength(uint64_t* aDecodedDataLength
) {
114 *aDecodedDataLength
= mDecodedDataLength
;
119 nsHTTPCompressConv::AsyncConvertData(const char* aFromType
, const char* aToType
,
120 nsIStreamListener
* aListener
,
121 nsISupports
* aCtxt
) {
122 if (!nsCRT::strncasecmp(aFromType
, HTTP_COMPRESS_TYPE
,
123 sizeof(HTTP_COMPRESS_TYPE
) - 1) ||
124 !nsCRT::strncasecmp(aFromType
, HTTP_X_COMPRESS_TYPE
,
125 sizeof(HTTP_X_COMPRESS_TYPE
) - 1)) {
126 mMode
= HTTP_COMPRESS_COMPRESS
;
127 } else if (!nsCRT::strncasecmp(aFromType
, HTTP_GZIP_TYPE
,
128 sizeof(HTTP_GZIP_TYPE
) - 1) ||
129 !nsCRT::strncasecmp(aFromType
, HTTP_X_GZIP_TYPE
,
130 sizeof(HTTP_X_GZIP_TYPE
) - 1)) {
131 mMode
= HTTP_COMPRESS_GZIP
;
132 } else if (!nsCRT::strncasecmp(aFromType
, HTTP_DEFLATE_TYPE
,
133 sizeof(HTTP_DEFLATE_TYPE
) - 1)) {
134 mMode
= HTTP_COMPRESS_DEFLATE
;
135 } else if (!nsCRT::strncasecmp(aFromType
, HTTP_BROTLI_TYPE
,
136 sizeof(HTTP_BROTLI_TYPE
) - 1)) {
137 mMode
= HTTP_COMPRESS_BROTLI
;
138 } else if (!nsCRT::strncasecmp(aFromType
, HTTP_ZSTD_TYPE
,
139 sizeof(HTTP_ZSTD_TYPE
) - 1)) {
140 mMode
= HTTP_COMPRESS_ZSTD
;
141 } else if (!nsCRT::strncasecmp(aFromType
, HTTP_ZST_TYPE
,
142 sizeof(HTTP_ZST_TYPE
) - 1)) {
143 mMode
= HTTP_COMPRESS_ZSTD
;
145 LOG(("nsHttpCompresssConv %p AsyncConvertData %s %s mode %d\n", this,
146 aFromType
, aToType
, (CompressMode
)mMode
));
148 MutexAutoLock
lock(mMutex
);
149 // hook ourself up with the receiving listener.
150 mListener
= aListener
;
156 nsHTTPCompressConv::GetConvertedType(const nsACString
& aFromType
,
157 nsIChannel
* aChannel
,
158 nsACString
& aToType
) {
159 return NS_ERROR_NOT_IMPLEMENTED
;
163 nsHTTPCompressConv::MaybeRetarget(nsIRequest
* request
) {
164 MOZ_ASSERT(NS_IsMainThread());
166 nsCOMPtr
<nsIThreadRetargetableRequest
> req
= do_QueryInterface(request
);
168 return NS_ERROR_NO_INTERFACE
;
170 if (!StaticPrefs::network_decompression_off_mainthread2()) {
173 nsCOMPtr
<nsISerialEventTarget
> target
;
174 rv
= req
->GetDeliveryTarget(getter_AddRefs(target
));
175 if (NS_FAILED(rv
) || !target
|| target
->IsOnCurrentThread()) {
176 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(request
));
179 channel
->GetContentLength(&length
);
180 // If this fails we'll retarget
184 StaticPrefs::network_decompression_off_mainthread_min_size()) {
185 LOG(("MaybeRetarget: Retargeting to background thread: Length %" PRId64
,
187 // No retargetting was performed. Decompress off MainThread,
188 // and dispatch results back to MainThread.
189 // Don't do this if the input is small, if we know the length.
190 // If the length is 0 (unknown), always use OMT.
191 nsCOMPtr
<nsISerialEventTarget
> backgroundThread
;
192 rv
= NS_CreateBackgroundTaskQueue("nsHTTPCompressConv",
193 getter_AddRefs(backgroundThread
));
194 NS_ENSURE_SUCCESS(rv
, rv
);
195 rv
= req
->RetargetDeliveryTo(backgroundThread
);
196 NS_ENSURE_SUCCESS(rv
, rv
);
197 if (NS_SUCCEEDED(rv
)) {
198 mDispatchToMainThread
= true;
201 LOG(("MaybeRetarget: Not retargeting: Length %" PRId64
, length
));
204 LOG(("MaybeRetarget: Don't need to retarget"));
211 nsHTTPCompressConv::OnStartRequest(nsIRequest
* request
) {
212 LOG(("nsHttpCompresssConv %p onstart\n", this));
213 nsCOMPtr
<nsIStreamListener
> listener
;
215 MutexAutoLock
lock(mMutex
);
216 listener
= mListener
;
218 nsresult rv
= listener
->OnStartRequest(request
);
219 if (NS_SUCCEEDED(rv
)) {
220 if (XRE_IsContentProcess()) {
221 nsCOMPtr
<nsIThreadRetargetableStreamListener
> retargetlistener
=
222 do_QueryInterface(listener
);
223 // |nsHTTPCompressConv| should *always* be dispatched off of the main
224 // thread from a content process, even if its listeners don't support it.
226 // If its listener chain does not support being retargeted off of the
227 // main thread, it will be dispatched back to the main thread in
228 // |do_OnDataAvailable| and |OnStopRequest|.
229 if (!retargetlistener
||
230 NS_FAILED(retargetlistener
->CheckListenerChain())) {
231 mDispatchToMainThread
= true;
239 nsHTTPCompressConv::OnStopRequest(nsIRequest
* request
, nsresult aStatus
) {
240 nsresult status
= aStatus
;
241 // Bug 1886237 : TRRServiceChannel calls OnStopRequest OMT
242 // MOZ_ASSERT(NS_IsMainThread());
243 LOG(("nsHttpCompresssConv %p onstop %" PRIx32
" mDispatchToMainThread %d\n",
244 this, static_cast<uint32_t>(aStatus
), mDispatchToMainThread
));
246 // Framing integrity is enforced for content-encoding: gzip, but not for
247 // content-encoding: deflate. Note that gzip vs deflate is NOT determined
248 // by content sniffing but only via header.
249 if (!mStreamEnded
&& NS_SUCCEEDED(status
) &&
250 (mFailUncleanStops
&& (mMode
== HTTP_COMPRESS_GZIP
))) {
251 // This is not a clean end of gzip stream: the transfer is incomplete.
252 status
= NS_ERROR_NET_PARTIAL_TRANSFER
;
253 LOG(("nsHttpCompresssConv %p onstop partial gzip\n", this));
255 if (NS_SUCCEEDED(status
) && mMode
== HTTP_COMPRESS_BROTLI
) {
256 nsCOMPtr
<nsIForcePendingChannel
> fpChannel
= do_QueryInterface(request
);
257 bool isPending
= false;
259 request
->IsPending(&isPending
);
261 if (fpChannel
&& !isPending
) {
262 fpChannel
->ForcePending(true);
264 if (mBrotli
&& NS_FAILED(mBrotli
->mStatus
)) {
265 status
= NS_ERROR_INVALID_CONTENT_ENCODING
;
267 LOG(("nsHttpCompresssConv %p onstop brotlihandler rv %" PRIx32
"\n", this,
268 static_cast<uint32_t>(status
)));
269 if (fpChannel
&& !isPending
) {
270 fpChannel
->ForcePending(false);
274 nsCOMPtr
<nsIStreamListener
> listener
;
276 MutexAutoLock
lock(mMutex
);
277 listener
= mListener
;
280 return listener
->OnStopRequest(request
, status
);
284 nsresult
nsHTTPCompressConv::BrotliHandler(nsIInputStream
* stream
,
285 void* closure
, const char* dataIn
,
286 uint32_t, uint32_t aAvail
,
287 uint32_t* countRead
) {
289 nsHTTPCompressConv
* self
= static_cast<nsHTTPCompressConv
*>(closure
);
292 const size_t kOutSize
= 128 * 1024; // just a chunk size, we call in a loop
295 size_t avail
= aAvail
;
296 BrotliDecoderResult res
;
298 if (!self
->mBrotli
) {
303 auto outBuffer
= MakeUniqueFallible
<uint8_t[]>(kOutSize
);
304 if (outBuffer
== nullptr) {
305 self
->mBrotli
->mStatus
= NS_ERROR_OUT_OF_MEMORY
;
306 return self
->mBrotli
->mStatus
;
310 outPtr
= outBuffer
.get();
312 // brotli api is documented in brotli/dec/decode.h and brotli/dec/decode.c
313 LOG(("nsHttpCompresssConv %p brotlihandler decompress %zu\n", self
, avail
));
314 size_t totalOut
= self
->mBrotli
->mTotalOut
;
315 res
= ::BrotliDecoderDecompressStream(
316 &self
->mBrotli
->mState
, &avail
,
317 reinterpret_cast<const unsigned char**>(&dataIn
), &outSize
, &outPtr
,
319 outSize
= kOutSize
- outSize
;
320 self
->mBrotli
->mTotalOut
= totalOut
;
321 self
->mBrotli
->mBrotliStateIsStreamEnd
=
322 BrotliDecoderIsFinished(&self
->mBrotli
->mState
);
323 LOG(("nsHttpCompresssConv %p brotlihandler decompress rv=%" PRIx32
325 self
, static_cast<uint32_t>(res
), outSize
));
327 if (res
== BROTLI_DECODER_RESULT_ERROR
) {
328 LOG(("nsHttpCompressConv %p marking invalid encoding", self
));
329 self
->mBrotli
->mStatus
= NS_ERROR_INVALID_CONTENT_ENCODING
;
330 return self
->mBrotli
->mStatus
;
333 // in 'the current implementation' brotli must consume everything before
334 // asking for more input
335 if (res
== BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
) {
338 LOG(("nsHttpCompressConv %p did not consume all input", self
));
339 self
->mBrotli
->mStatus
= NS_ERROR_UNEXPECTED
;
340 return self
->mBrotli
->mStatus
;
344 auto callOnDataAvailable
= [&](uint64_t aSourceOffset
, const char* aBuffer
,
346 nsresult rv
= self
->do_OnDataAvailable(self
->mBrotli
->mRequest
,
347 aSourceOffset
, aBuffer
, aCount
);
348 LOG(("nsHttpCompressConv %p BrotliHandler ODA rv=%" PRIx32
, self
,
349 static_cast<uint32_t>(rv
)));
351 self
->mBrotli
->mStatus
= rv
;
358 if (NS_FAILED(callOnDataAvailable(
359 self
->mBrotli
->mSourceOffset
,
360 reinterpret_cast<const char*>(outBuffer
.get()), outSize
))) {
361 return self
->mBrotli
->mStatus
;
363 self
->mBrotli
->mSourceOffset
+= outSize
;
366 // See bug 1759745. If the decoder has more output data, take it.
367 while (::BrotliDecoderHasMoreOutput(&self
->mBrotli
->mState
)) {
369 const uint8_t* buffer
=
370 ::BrotliDecoderTakeOutput(&self
->mBrotli
->mState
, &outSize
);
371 if (NS_FAILED(callOnDataAvailable(self
->mBrotli
->mSourceOffset
,
372 reinterpret_cast<const char*>(buffer
),
374 return self
->mBrotli
->mStatus
;
376 self
->mBrotli
->mSourceOffset
+= outSize
;
379 if (res
== BROTLI_DECODER_RESULT_SUCCESS
||
380 res
== BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
) {
384 MOZ_ASSERT(res
== BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT
);
385 } while (res
== BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT
);
387 self
->mBrotli
->mStatus
= NS_ERROR_UNEXPECTED
;
388 return self
->mBrotli
->mStatus
;
392 nsresult
nsHTTPCompressConv::ZstdHandler(nsIInputStream
* stream
, void* closure
,
393 const char* dataIn
, uint32_t,
394 uint32_t aAvail
, uint32_t* countRead
) {
396 nsHTTPCompressConv
* self
= static_cast<nsHTTPCompressConv
*>(closure
);
399 const size_t kOutSize
= ZSTD_DStreamOutSize(); // normally 128K
401 size_t avail
= aAvail
;
403 // Stop decompressing after an error
404 if (self
->mZstd
->mStatus
!= NS_OK
) {
409 if (!self
->mZstd
->mOutBuffer
) {
410 self
->mZstd
->mOutBuffer
= MakeUniqueFallible
<uint8_t[]>(kOutSize
);
411 if (!self
->mZstd
->mOutBuffer
) {
412 self
->mZstd
->mStatus
= NS_ERROR_OUT_OF_MEMORY
;
413 return self
->mZstd
->mStatus
;
416 ZSTD_inBuffer inBuffer
= {.src
= dataIn
, .size
= aAvail
, .pos
= 0};
417 uint32_t last_pos
= 0;
418 while (inBuffer
.pos
< inBuffer
.size
) {
419 outPtr
= self
->mZstd
->mOutBuffer
.get();
421 LOG(("nsHttpCompresssConv %p zstdhandler decompress %zu\n", self
, avail
));
422 // Use ZSTD_(de)compressStream to (de)compress the input buffer into the
423 // output buffer, and fill aReadCount with the number of bytes consumed.
424 ZSTD_outBuffer outBuffer
{.dst
= outPtr
, .size
= kOutSize
};
430 ZSTD_decompressStream(self
->mZstd
->mDStream
, &outBuffer
, &inBuffer
);
432 // If we errored when writing, flag this and abort writing.
433 if (ZSTD_isError(result
)) {
434 self
->mZstd
->mStatus
= NS_ERROR_INVALID_CONTENT_ENCODING
;
435 return self
->mZstd
->mStatus
;
438 nsresult rv
= self
->do_OnDataAvailable(
439 self
->mZstd
->mRequest
, self
->mZstd
->mSourceOffset
,
440 reinterpret_cast<const char*>(outPtr
), outBuffer
.pos
);
442 self
->mZstd
->mStatus
= rv
;
445 self
->mZstd
->mSourceOffset
+= inBuffer
.pos
- last_pos
;
446 last_pos
= inBuffer
.pos
;
447 output_full
= outBuffer
.pos
== outBuffer
.size
;
448 // in the unlikely case that the output buffer was full, loop to
449 // drain it before processing more input
450 } while (output_full
);
452 *countRead
= inBuffer
.pos
;
457 nsHTTPCompressConv::OnDataAvailable(nsIRequest
* request
, nsIInputStream
* iStr
,
458 uint64_t aSourceOffset
, uint32_t aCount
) {
459 nsresult rv
= NS_ERROR_INVALID_CONTENT_ENCODING
;
460 uint32_t streamLen
= aCount
;
461 LOG(("nsHttpCompressConv %p OnDataAvailable aSourceOffset:%" PRIu64
463 this, aSourceOffset
, aCount
));
465 if (streamLen
== 0) {
466 NS_ERROR("count of zero passed to OnDataAvailable");
467 return NS_ERROR_UNEXPECTED
;
471 // Hmm... this may just indicate that the data stream is done and that
472 // what's left is either metadata or padding of some sort.... throwing
473 // it out is probably the safe thing to do.
475 return iStr
->ReadSegments(NS_DiscardSegment
, nullptr, streamLen
, &n
);
479 case HTTP_COMPRESS_GZIP
:
480 streamLen
= check_header(iStr
, streamLen
, &rv
);
486 if (streamLen
== 0) {
492 case HTTP_COMPRESS_DEFLATE
:
494 if (mInpBuffer
!= nullptr && streamLen
> mInpBufferLen
) {
495 unsigned char* originalInpBuffer
= mInpBuffer
;
496 if (!(mInpBuffer
= (unsigned char*)realloc(
497 originalInpBuffer
, mInpBufferLen
= streamLen
))) {
498 free(originalInpBuffer
);
501 if (mOutBufferLen
< streamLen
* 2) {
502 unsigned char* originalOutBuffer
= mOutBuffer
;
503 if (!(mOutBuffer
= (unsigned char*)realloc(
504 mOutBuffer
, mOutBufferLen
= streamLen
* 3))) {
505 free(originalOutBuffer
);
509 if (mInpBuffer
== nullptr || mOutBuffer
== nullptr) {
510 return NS_ERROR_OUT_OF_MEMORY
;
514 if (mInpBuffer
== nullptr) {
515 mInpBuffer
= (unsigned char*)malloc(mInpBufferLen
= streamLen
);
518 if (mOutBuffer
== nullptr) {
519 mOutBuffer
= (unsigned char*)malloc(mOutBufferLen
= streamLen
* 3);
522 if (mInpBuffer
== nullptr || mOutBuffer
== nullptr) {
523 return NS_ERROR_OUT_OF_MEMORY
;
527 iStr
->Read((char*)mInpBuffer
, streamLen
, &unused
);
529 if (mMode
== HTTP_COMPRESS_DEFLATE
) {
530 if (!mStreamInitialized
) {
531 memset(&d_stream
, 0, sizeof(d_stream
));
533 if (inflateInit(&d_stream
) != Z_OK
) {
534 return NS_ERROR_FAILURE
;
537 mStreamInitialized
= true;
539 d_stream
.next_in
= mInpBuffer
;
540 d_stream
.avail_in
= (uInt
)streamLen
;
542 mDummyStreamInitialised
= false;
544 d_stream
.next_out
= mOutBuffer
;
545 d_stream
.avail_out
= (uInt
)mOutBufferLen
;
547 int code
= inflate(&d_stream
, Z_NO_FLUSH
);
548 unsigned bytesWritten
= (uInt
)mOutBufferLen
- d_stream
.avail_out
;
550 if (code
== Z_STREAM_END
) {
552 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
559 inflateEnd(&d_stream
);
565 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
571 } else if (code
== Z_BUF_ERROR
) {
573 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
580 } else if (code
== Z_DATA_ERROR
) {
581 // some servers (notably Apache with mod_deflate) don't generate
582 // zlib headers insert a dummy header and try again
583 static char dummy_head
[2] = {
585 (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
587 inflateReset(&d_stream
);
588 d_stream
.next_in
= (Bytef
*)dummy_head
;
589 d_stream
.avail_in
= sizeof(dummy_head
);
591 code
= inflate(&d_stream
, Z_NO_FLUSH
);
593 return NS_ERROR_FAILURE
;
596 // stop an endless loop caused by non-deflate data being labelled as
598 if (mDummyStreamInitialised
) {
600 "endless loop detected"
601 " - invalid deflate");
602 return NS_ERROR_INVALID_CONTENT_ENCODING
;
604 mDummyStreamInitialised
= true;
605 // reset stream pointers to our original data
606 d_stream
.next_in
= mInpBuffer
;
607 d_stream
.avail_in
= (uInt
)streamLen
;
609 return NS_ERROR_INVALID_CONTENT_ENCODING
;
613 if (!mStreamInitialized
) {
614 memset(&d_stream
, 0, sizeof(d_stream
));
616 if (inflateInit2(&d_stream
, -MAX_WBITS
) != Z_OK
) {
617 return NS_ERROR_FAILURE
;
620 mStreamInitialized
= true;
623 d_stream
.next_in
= mInpBuffer
;
624 d_stream
.avail_in
= (uInt
)streamLen
;
627 d_stream
.next_out
= mOutBuffer
;
628 d_stream
.avail_out
= (uInt
)mOutBufferLen
;
630 int code
= inflate(&d_stream
, Z_NO_FLUSH
);
631 unsigned bytesWritten
= (uInt
)mOutBufferLen
- d_stream
.avail_out
;
633 if (code
== Z_STREAM_END
) {
635 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
642 inflateEnd(&d_stream
);
648 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
654 } else if (code
== Z_BUF_ERROR
) {
656 rv
= do_OnDataAvailable(request
, aSourceOffset
, (char*)mOutBuffer
,
664 return NS_ERROR_INVALID_CONTENT_ENCODING
;
670 case HTTP_COMPRESS_BROTLI
: {
672 mBrotli
= MakeUnique
<BrotliWrapper
>();
675 mBrotli
->mRequest
= request
;
676 mBrotli
->mContext
= nullptr;
677 mBrotli
->mSourceOffset
= aSourceOffset
;
680 rv
= iStr
->ReadSegments(BrotliHandler
, this, streamLen
, &countRead
);
681 if (NS_SUCCEEDED(rv
)) {
682 rv
= mBrotli
->mStatus
;
689 case HTTP_COMPRESS_ZSTD
: {
691 mZstd
= MakeUnique
<ZstdWrapper
>();
694 mZstd
->mRequest
= request
;
695 mZstd
->mContext
= nullptr;
696 mZstd
->mSourceOffset
= aSourceOffset
;
699 rv
= iStr
->ReadSegments(ZstdHandler
, this, streamLen
, &countRead
);
700 if (NS_SUCCEEDED(rv
)) {
709 nsCOMPtr
<nsIStreamListener
> listener
;
711 MutexAutoLock
lock(mMutex
);
712 listener
= mListener
;
714 rv
= listener
->OnDataAvailable(request
, iStr
, aSourceOffset
, aCount
);
721 } /* OnDataAvailable */
723 // XXX/ruslan: need to implement this too
726 nsHTTPCompressConv::Convert(nsIInputStream
* aFromStream
, const char* aFromType
,
727 const char* aToType
, nsISupports
* aCtxt
,
728 nsIInputStream
** _retval
) {
729 return NS_ERROR_NOT_IMPLEMENTED
;
732 nsresult
nsHTTPCompressConv::do_OnDataAvailable(nsIRequest
* request
,
737 ("nsHttpCompressConv %p do_OnDataAvailable mDispatchToMainThread %d "
739 this, mDispatchToMainThread
, count
));
741 // Never send 0-byte OnDataAvailables; imglib at least barfs on them and
742 // they're not useful
745 if (mDispatchToMainThread
&& !NS_IsMainThread()) {
746 nsCOMPtr
<nsIInputStream
> stream
;
747 MOZ_TRY(NS_NewByteInputStream(getter_AddRefs(stream
), Span(buffer
, count
),
748 nsAssignmentType::NS_ASSIGNMENT_COPY
));
750 nsCOMPtr
<nsIStreamListener
> listener
;
752 MutexAutoLock
lock(mMutex
);
753 listener
= mListener
;
756 // This is safe and will always run before OnStopRequest, because
757 // ChanneleventQueue means that we can't enqueue OnStopRequest until after
758 // the OMT OnDataAvailable call has completed. So Dispatching here will
759 // ensure it's in the MainThread event queue before OnStopRequest
760 nsCOMPtr
<nsIRunnable
> handler
= NS_NewRunnableFunction(
761 "nsHTTPCompressConv::do_OnDataAvailable",
762 [request
{RefPtr
<nsIRequest
>(request
)}, stream
{std::move(stream
)},
763 listener
{std::move(listener
)}, offset
, count
]() {
764 LOG(("nsHttpCompressConv Calling OnDataAvailable on Mainthread"));
765 Unused
<< listener
->OnDataAvailable(request
, stream
, offset
, count
);
768 mDecodedDataLength
+= count
;
769 return NS_DispatchToMainThread(handler
);
773 mStream
= do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID
);
774 NS_ENSURE_STATE(mStream
);
777 mStream
->ShareData(buffer
, count
);
779 nsCOMPtr
<nsIStreamListener
> listener
;
781 MutexAutoLock
lock(mMutex
);
782 listener
= mListener
;
784 LOG(("nsHTTPCompressConv::do_OnDataAvailable req:%p offset: offset:%" PRIu64
786 request
, offset
, count
));
787 nsresult rv
= listener
->OnDataAvailable(request
, mStream
, offset
, count
);
789 // Make sure the stream no longer references |buffer| in case our listener
790 // is crazy enough to try to read from |mStream| after ODA.
791 mStream
->ShareData("", 0);
792 mDecodedDataLength
+= count
;
797 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
798 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
799 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
800 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
801 #define COMMENT 0x10 /* bit 4 set: file comment present */
802 #define RESERVED 0xE0 /* bits 5..7: reserved */
804 static unsigned gz_magic
[2] = {0x1f, 0x8b}; /* gzip magic header */
806 uint32_t nsHTTPCompressConv::check_header(nsIInputStream
* iStr
,
807 uint32_t streamLen
, nsresult
* rs
) {
822 if (mCheckHeaderDone
) {
830 iStr
->Read(&c
, 1, &unused
);
833 if (mSkipCount
== 0 && ((unsigned)c
& 0377) != gz_magic
[0]) {
834 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
838 if (mSkipCount
== 1 && ((unsigned)c
& 0377) != gz_magic
[1]) {
839 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
843 if (mSkipCount
== 2 && ((unsigned)c
& 0377) != Z_DEFLATED
) {
844 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
849 if (mSkipCount
== 4) {
850 mFlags
= (unsigned)c
& 0377;
851 if (mFlags
& RESERVED
) {
852 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
861 iStr
->Read(&c
, 1, &unused
);
865 if (mSkipCount
== 6) {
871 if (mFlags
& EXTRA_FIELD
) {
872 iStr
->Read(&c
, 1, &unused
);
874 mLen
= (uInt
)c
& 0377;
882 iStr
->Read(&c
, 1, &unused
);
884 mLen
|= ((uInt
)c
& 0377) << 8;
890 if (mSkipCount
== mLen
) {
893 iStr
->Read(&c
, 1, &unused
);
900 if (mFlags
& ORIG_NAME
) {
901 iStr
->Read(&c
, 1, &unused
);
903 if (c
== 0) hMode
= GZIP_COMMENT
;
905 hMode
= GZIP_COMMENT
;
910 if (mFlags
& COMMENT
) {
911 iStr
->Read(&c
, 1, &unused
);
924 if (mFlags
& HEAD_CRC
) {
925 iStr
->Read(&c
, 1, &unused
);
928 if (mSkipCount
== 2) {
929 mCheckHeaderDone
= true;
933 mCheckHeaderDone
= true;
943 nsHTTPCompressConv::CheckListenerChain() {
944 if (XRE_IsContentProcess() &&
945 StaticPrefs::network_decompression_off_mainthread2()) {
946 // handle decompression OMT always. If the chain needs to be MT,
947 // we'll determine that in OnStartRequest and dispatch to MT
950 nsCOMPtr
<nsIThreadRetargetableStreamListener
> listener
;
952 MutexAutoLock
lock(mMutex
);
953 listener
= do_QueryInterface(mListener
);
956 return NS_ERROR_NO_INTERFACE
;
959 return listener
->CheckListenerChain();
963 nsHTTPCompressConv::OnDataFinished(nsresult aStatus
) {
964 nsCOMPtr
<nsIThreadRetargetableStreamListener
> listener
;
967 MutexAutoLock
lock(mMutex
);
968 listener
= do_QueryInterface(mListener
);
972 if (mDispatchToMainThread
&& !NS_IsMainThread()) {
973 nsCOMPtr
<nsIRunnable
> handler
= NS_NewRunnableFunction(
974 "dispatch", [listener
{std::move(listener
)}, aStatus
]() {
975 Unused
<< listener
->OnDataFinished(aStatus
);
978 return NS_DispatchToMainThread(handler
);
981 return listener
->OnDataFinished(aStatus
);
988 } // namespace mozilla
990 nsresult
NS_NewHTTPCompressConv(
991 mozilla::net::nsHTTPCompressConv
** aHTTPCompressConv
) {
992 MOZ_ASSERT(aHTTPCompressConv
!= nullptr, "null ptr");
993 if (!aHTTPCompressConv
) {
994 return NS_ERROR_NULL_POINTER
;
997 RefPtr
<mozilla::net::nsHTTPCompressConv
> outVal
=
998 new mozilla::net::nsHTTPCompressConv();
1000 return NS_ERROR_OUT_OF_MEMORY
;
1002 outVal
.forget(aHTTPCompressConv
);