diff options
-rw-r--r-- | netwerk/streamconv/converters/nsHTTPCompressConv.cpp | 735 | ||||
-rw-r--r-- | netwerk/streamconv/converters/nsHTTPCompressConv.h | 136 |
2 files changed, 466 insertions, 405 deletions
diff --git a/netwerk/streamconv/converters/nsHTTPCompressConv.cpp b/netwerk/streamconv/converters/nsHTTPCompressConv.cpp index 37b4b28b0..c35b0dcac 100644 --- a/netwerk/streamconv/converters/nsHTTPCompressConv.cpp +++ b/netwerk/streamconv/converters/nsHTTPCompressConv.cpp @@ -20,7 +20,7 @@ // brotli headers #include "state.h" -#include "decode.h" +#include "brotli/decode.h" namespace mozilla { namespace net { @@ -33,7 +33,8 @@ NS_IMPL_ISUPPORTS(nsHTTPCompressConv, nsIStreamConverter, nsIStreamListener, nsIRequestObserver, - nsICompressConvStats) + nsICompressConvStats, + nsIThreadRetargetableStreamListener) // nsFTPDirListingConv methods nsHTTPCompressConv::nsHTTPCompressConv() @@ -45,11 +46,14 @@ nsHTTPCompressConv::nsHTTPCompressConv() , mCheckHeaderDone(false) , mStreamEnded(false) , mStreamInitialized(false) + , mDummyStreamInitialised(false) + , d_stream{} , mLen(0) , hMode(0) , mSkipCount(0) , mFlags(0) , mDecodedDataLength(0) + , mMutex("nsHTTPCompressConv") { LOG(("nsHttpCompresssConv %p ctor\n", this)); if (NS_IsMainThread()) { @@ -60,8 +64,7 @@ nsHTTPCompressConv::nsHTTPCompressConv() } } -nsHTTPCompressConv::~nsHTTPCompressConv() -{ +nsHTTPCompressConv::~nsHTTPCompressConv() { LOG(("nsHttpCompresssConv %p dtor\n", this)); if (mInpBuffer) { free(mInpBuffer); @@ -74,63 +77,64 @@ nsHTTPCompressConv::~nsHTTPCompressConv() // For some reason we are not getting Z_STREAM_END. But this was also seen // for mozilla bug 198133. Need to handle this case. if (mStreamInitialized && !mStreamEnded) { - inflateEnd (&d_stream); + inflateEnd(&d_stream); } } NS_IMETHODIMP -nsHTTPCompressConv::GetDecodedDataLength(uint64_t *aDecodedDataLength) -{ - *aDecodedDataLength = mDecodedDataLength; - return NS_OK; +nsHTTPCompressConv::GetDecodedDataLength(uint64_t* aDecodedDataLength) { + *aDecodedDataLength = mDecodedDataLength; + return NS_OK; } NS_IMETHODIMP -nsHTTPCompressConv::AsyncConvertData(const char *aFromType, - const char *aToType, - nsIStreamListener *aListener, - nsISupports *aCtxt) -{ - if (!PL_strncasecmp(aFromType, HTTP_COMPRESS_TYPE, sizeof(HTTP_COMPRESS_TYPE)-1) || - !PL_strncasecmp(aFromType, HTTP_X_COMPRESS_TYPE, sizeof(HTTP_X_COMPRESS_TYPE)-1)) { +nsHTTPCompressConv::AsyncConvertData(const char* aFromType, const char* aToType, + nsIStreamListener* aListener, + nsISupports* aCtxt) { + if (!PL_strncasecmp(aFromType, HTTP_COMPRESS_TYPE, sizeof(HTTP_COMPRESS_TYPE) - 1) || + !PL_strncasecmp(aFromType, HTTP_X_COMPRESS_TYPE, sizeof(HTTP_X_COMPRESS_TYPE) - 1)) { mMode = HTTP_COMPRESS_COMPRESS; - } else if (!PL_strncasecmp(aFromType, HTTP_GZIP_TYPE, sizeof(HTTP_GZIP_TYPE)-1) || - !PL_strncasecmp(aFromType, HTTP_X_GZIP_TYPE, sizeof(HTTP_X_GZIP_TYPE)-1)) { + } else if (!PL_strncasecmp(aFromType, HTTP_GZIP_TYPE, sizeof(HTTP_GZIP_TYPE) - 1) || + !PL_strncasecmp(aFromType, HTTP_X_GZIP_TYPE, sizeof(HTTP_X_GZIP_TYPE) - 1)) { mMode = HTTP_COMPRESS_GZIP; - } else if (!PL_strncasecmp(aFromType, HTTP_DEFLATE_TYPE, sizeof(HTTP_DEFLATE_TYPE)-1)) { + } else if (!PL_strncasecmp(aFromType, HTTP_DEFLATE_TYPE, sizeof(HTTP_DEFLATE_TYPE) - 1)) { mMode = HTTP_COMPRESS_DEFLATE; - } else if (!PL_strncasecmp(aFromType, HTTP_BROTLI_TYPE, sizeof(HTTP_BROTLI_TYPE)-1)) { + } else if (!PL_strncasecmp(aFromType, HTTP_BROTLI_TYPE, sizeof(HTTP_BROTLI_TYPE) - 1)) { mMode = HTTP_COMPRESS_BROTLI; } - LOG(("nsHttpCompresssConv %p AsyncConvertData %s %s mode %d\n", - this, aFromType, aToType, mMode)); + LOG(("nsHttpCompresssConv %p AsyncConvertData %s %s mode %d\n", this, + aFromType, aToType, (CompressMode)mMode)); + MutexAutoLock lock(mMutex); // hook ourself up with the receiving listener. mListener = aListener; - mAsyncConvContext = aCtxt; return NS_OK; } NS_IMETHODIMP -nsHTTPCompressConv::OnStartRequest(nsIRequest* request, nsISupports *aContext) -{ +nsHTTPCompressConv::OnStartRequest(nsIRequest* request, nsISupports* aContext) { LOG(("nsHttpCompresssConv %p onstart\n", this)); - return mListener->OnStartRequest(request, aContext); + nsCOMPtr<nsIStreamListener> listener; + { + MutexAutoLock lock(mMutex); + listener = mListener; + } + return listener->OnStartRequest(request, aContext); } NS_IMETHODIMP -nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext, - nsresult aStatus) -{ +nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports* aContext, + nsresult aStatus) { nsresult status = aStatus; - LOG(("nsHttpCompresssConv %p onstop %x\n", this, aStatus)); - + LOG(("nsHttpCompresssConv %p onstop %" PRIx32 "\n", this, + static_cast<uint32_t>(aStatus))); + // Framing integrity is enforced for content-encoding: gzip, but not for // content-encoding: deflate. Note that gzip vs deflate is NOT determined // by content sniffing but only via header. if (!mStreamEnded && NS_SUCCEEDED(status) && - (mFailUncleanStops && (mMode == HTTP_COMPRESS_GZIP)) ) { + (mFailUncleanStops && (mMode == HTTP_COMPRESS_GZIP))) { // This is not a clean end of gzip stream: the transfer is incomplete. status = NS_ERROR_NET_PARTIAL_TRANSFER; LOG(("nsHttpCompresssConv %p onstop partial gzip\n", this)); @@ -144,31 +148,40 @@ nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext, if (fpChannel && !isPending) { fpChannel->ForcePending(true); } - if (mBrotli && (mBrotli->mTotalOut == 0) && !BrotliStateIsStreamEnd(&mBrotli->mState)) { + if (mBrotli && (mBrotli->mTotalOut == 0) && !mBrotli->mBrotliStateIsStreamEnd) { status = NS_ERROR_INVALID_CONTENT_ENCODING; } - LOG(("nsHttpCompresssConv %p onstop brotlihandler rv %x\n", this, status)); + LOG(("nsHttpCompresssConv %p onstop brotlihandler rv %" PRIx32 "\n", this, + static_cast<uint32_t>(status))); if (fpChannel && !isPending) { fpChannel->ForcePending(false); } } - return mListener->OnStopRequest(request, aContext, status); -} + nsCOMPtr<nsIStreamListener> listener; + { + MutexAutoLock lock(mMutex); + listener = mListener; + } + return listener->OnStopRequest(request, aContext, status); +} -/* static */ nsresult -nsHTTPCompressConv::BrotliHandler(nsIInputStream *stream, void *closure, const char *dataIn, - uint32_t, uint32_t aAvail, uint32_t *countRead) -{ +/* static */ +nsresult nsHTTPCompressConv::BrotliHandler(nsIInputStream* stream, + void* closure, + const char* dataIn, + uint32_t, + uint32_t aAvail, + uint32_t* countRead) { MOZ_ASSERT(stream); - nsHTTPCompressConv *self = static_cast<nsHTTPCompressConv *>(closure); + nsHTTPCompressConv* self = static_cast<nsHTTPCompressConv*>(closure); *countRead = 0; - const size_t kOutSize = 128 * 1024; // just a chunk size, we call in a loop - uint8_t *outPtr; + const size_t kOutSize = 128 * 1024; // just a chunk size, we call in a loop + uint8_t* outPtr; size_t outSize; size_t avail = aAvail; - BrotliResult res; + BrotliDecoderResult res; if (!self->mBrotli) { *countRead = aAvail; @@ -186,15 +199,21 @@ nsHTTPCompressConv::BrotliHandler(nsIInputStream *stream, void *closure, const c outPtr = outBuffer.get(); // brotli api is documented in brotli/dec/decode.h and brotli/dec/decode.c - LOG(("nsHttpCompresssConv %p brotlihandler decompress %d\n", self, avail)); - res = ::BrotliDecompressStream( - &avail, reinterpret_cast<const unsigned char **>(&dataIn), - &outSize, &outPtr, &self->mBrotli->mTotalOut, &self->mBrotli->mState); + LOG(("nsHttpCompresssConv %p brotlihandler decompress %zu\n", self, avail)); + size_t totalOut = self->mBrotli->mTotalOut; + res = ::BrotliDecoderDecompressStream( + &self->mBrotli->mState, &avail, + reinterpret_cast<const unsigned char**>(&dataIn), &outSize, &outPtr, + &totalOut); outSize = kOutSize - outSize; - LOG(("nsHttpCompresssConv %p brotlihandler decompress rv=%x out=%d\n", - self, res, outSize)); - - if (res == BROTLI_RESULT_ERROR) { + self->mBrotli->mTotalOut = totalOut; + self->mBrotli->mBrotliStateIsStreamEnd = + BrotliDecoderIsFinished(&self->mBrotli->mState); + LOG(("nsHttpCompresssConv %p brotlihandler decompress rv=%" PRIx32 + " out=%zu\n", + self, static_cast<uint32_t>(res), outSize)); + + if (res == BROTLI_DECODER_RESULT_ERROR) { LOG(("nsHttpCompressConv %p marking invalid encoding", self)); self->mBrotli->mStatus = NS_ERROR_INVALID_CONTENT_ENCODING; return self->mBrotli->mStatus; @@ -202,7 +221,7 @@ nsHTTPCompressConv::BrotliHandler(nsIInputStream *stream, void *closure, const c // in 'the current implementation' brotli must consume everything before // asking for more input - if (res == BROTLI_RESULT_NEEDS_MORE_INPUT) { + if (res == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { MOZ_ASSERT(!avail); if (avail) { LOG(("nsHttpCompressConv %p did not consume all input", self)); @@ -214,22 +233,23 @@ nsHTTPCompressConv::BrotliHandler(nsIInputStream *stream, void *closure, const c nsresult rv = self->do_OnDataAvailable(self->mBrotli->mRequest, self->mBrotli->mContext, self->mBrotli->mSourceOffset, - reinterpret_cast<const char *>(outBuffer.get()), + reinterpret_cast<const char*>(outBuffer.get()), outSize); - LOG(("nsHttpCompressConv %p BrotliHandler ODA rv=%x", self, rv)); + LOG(("nsHttpCompressConv %p BrotliHandler ODA rv=%" PRIx32, self, + static_cast<uint32_t>(rv))); if (NS_FAILED(rv)) { self->mBrotli->mStatus = rv; return self->mBrotli->mStatus; } } - if (res == BROTLI_RESULT_SUCCESS || - res == BROTLI_RESULT_NEEDS_MORE_INPUT) { + if (res == BROTLI_DECODER_RESULT_SUCCESS || + res == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { *countRead = aAvail; return NS_OK; } - MOZ_ASSERT (res == BROTLI_RESULT_NEEDS_MORE_OUTPUT); - } while (res == BROTLI_RESULT_NEEDS_MORE_OUTPUT); + MOZ_ASSERT(res == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT); + } while (res == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT); self->mBrotli->mStatus = NS_ERROR_UNEXPECTED; return self->mBrotli->mStatus; @@ -237,11 +257,10 @@ nsHTTPCompressConv::BrotliHandler(nsIInputStream *stream, void *closure, const c NS_IMETHODIMP nsHTTPCompressConv::OnDataAvailable(nsIRequest* request, - nsISupports *aContext, - nsIInputStream *iStr, + nsISupports* aContext, + nsIInputStream* iStr, uint64_t aSourceOffset, - uint32_t aCount) -{ + uint32_t aCount) { nsresult rv = NS_ERROR_INVALID_CONTENT_ENCODING; uint32_t streamLen = aCount; LOG(("nsHttpCompressConv %p OnDataAvailable %d", this, aCount)); @@ -260,206 +279,216 @@ nsHTTPCompressConv::OnDataAvailable(nsIRequest* request, } switch (mMode) { - case HTTP_COMPRESS_GZIP: - streamLen = check_header(iStr, streamLen, &rv); + case HTTP_COMPRESS_GZIP: + streamLen = check_header(iStr, streamLen, &rv); - if (rv != NS_OK) { - return rv; - } + if (rv != NS_OK) { + return rv; + } - if (streamLen == 0) { - return NS_OK; - } + if (streamLen == 0) { + return NS_OK; + } - MOZ_FALLTHROUGH; + MOZ_FALLTHROUGH; - case HTTP_COMPRESS_DEFLATE: + case HTTP_COMPRESS_DEFLATE: - if (mInpBuffer != nullptr && streamLen > mInpBufferLen) { - mInpBuffer = (unsigned char *) realloc(mInpBuffer, mInpBufferLen = streamLen); + if (mInpBuffer != nullptr && streamLen > mInpBufferLen) { + unsigned char* originalInpBuffer = mInpBuffer; + if (!(mInpBuffer = (unsigned char*)realloc(originalInpBuffer, mInpBufferLen = streamLen))) { + free(originalInpBuffer); + } + + if (mOutBufferLen < streamLen * 2) { + unsigned char* originalOutBuffer = mOutBuffer; + if (!(mOutBuffer = (unsigned char*)realloc(mOutBuffer, mOutBufferLen = streamLen * 3))) { + free(originalOutBuffer); + } + } - if (mOutBufferLen < streamLen * 2) { - mOutBuffer = (unsigned char *) realloc(mOutBuffer, mOutBufferLen = streamLen * 3); + if (mInpBuffer == nullptr || mOutBuffer == nullptr) { + return NS_ERROR_OUT_OF_MEMORY; + } } - if (mInpBuffer == nullptr || mOutBuffer == nullptr) { - return NS_ERROR_OUT_OF_MEMORY; + if (mInpBuffer == nullptr) { + mInpBuffer = (unsigned char*)malloc(mInpBufferLen = streamLen); } - } - if (mInpBuffer == nullptr) { - mInpBuffer = (unsigned char *) malloc(mInpBufferLen = streamLen); - } + if (mOutBuffer == nullptr) { + mOutBuffer = (unsigned char*)malloc(mOutBufferLen = streamLen * 3); + } - if (mOutBuffer == nullptr) { - mOutBuffer = (unsigned char *) malloc(mOutBufferLen = streamLen * 3); - } + if (mInpBuffer == nullptr || mOutBuffer == nullptr) { + return NS_ERROR_OUT_OF_MEMORY; + } - if (mInpBuffer == nullptr || mOutBuffer == nullptr) { - return NS_ERROR_OUT_OF_MEMORY; - } + uint32_t unused; + iStr->Read((char*)mInpBuffer, streamLen, &unused); - uint32_t unused; - iStr->Read((char *)mInpBuffer, streamLen, &unused); + if (mMode == HTTP_COMPRESS_DEFLATE) { + if (!mStreamInitialized) { + memset(&d_stream, 0, sizeof(d_stream)); - if (mMode == HTTP_COMPRESS_DEFLATE) { - if (!mStreamInitialized) { - memset(&d_stream, 0, sizeof (d_stream)); + if (inflateInit(&d_stream) != Z_OK) { + return NS_ERROR_FAILURE; + } - if (inflateInit(&d_stream) != Z_OK) { - return NS_ERROR_FAILURE; + mStreamInitialized = true; } - - mStreamInitialized = true; - } - d_stream.next_in = mInpBuffer; - d_stream.avail_in = (uInt)streamLen; - - mDummyStreamInitialised = false; - for (;;) { - d_stream.next_out = mOutBuffer; - d_stream.avail_out = (uInt)mOutBufferLen; - - int code = inflate(&d_stream, Z_NO_FLUSH); - unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out; - - if (code == Z_STREAM_END) { - if (bytesWritten) { - rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); - if (NS_FAILED (rv)) { - return rv; + d_stream.next_in = mInpBuffer; + d_stream.avail_in = (uInt)streamLen; + + mDummyStreamInitialised = false; + for (;;) { + d_stream.next_out = mOutBuffer; + d_stream.avail_out = (uInt)mOutBufferLen; + + int code = inflate(&d_stream, Z_NO_FLUSH); + unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out; + + if (code == Z_STREAM_END) { + if (bytesWritten) { + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char*)mOutBuffer, bytesWritten); + if (NS_FAILED(rv)) { + return rv; + } } - } - inflateEnd(&d_stream); - mStreamEnded = true; - break; - } else if (code == Z_OK) { - if (bytesWritten) { - rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); - if (NS_FAILED (rv)) { - return rv; + inflateEnd(&d_stream); + mStreamEnded = true; + break; + } else if (code == Z_OK) { + if (bytesWritten) { + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char*)mOutBuffer, bytesWritten); + if (NS_FAILED(rv)) { + return rv; + } } - } - } else if (code == Z_BUF_ERROR) { - if (bytesWritten) { - rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); - if (NS_FAILED (rv)) { - return rv; + } else if (code == Z_BUF_ERROR) { + if (bytesWritten) { + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char*)mOutBuffer, bytesWritten); + if (NS_FAILED(rv)) { + return rv; + } } - } - break; - } else if (code == Z_DATA_ERROR) { - // some servers (notably Apache with mod_deflate) don't generate zlib headers - // insert a dummy header and try again - static char dummy_head[2] = - { - 0x8 + 0x7 * 0x10, - (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, + break; + } else if (code == Z_DATA_ERROR) { + // some servers (notably Apache with mod_deflate) don't generate + // zlib headers insert a dummy header and try again + static char dummy_head[2] = { + 0x8 + 0x7 * 0x10, + (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, }; - inflateReset(&d_stream); - d_stream.next_in = (Bytef*) dummy_head; - d_stream.avail_in = sizeof(dummy_head); + inflateReset(&d_stream); + d_stream.next_in = (Bytef*)dummy_head; + d_stream.avail_in = sizeof(dummy_head); - code = inflate(&d_stream, Z_NO_FLUSH); - if (code != Z_OK) { - return NS_ERROR_FAILURE; - } + code = inflate(&d_stream, Z_NO_FLUSH); + if (code != Z_OK) { + return NS_ERROR_FAILURE; + } - // stop an endless loop caused by non-deflate data being labelled as deflate - if (mDummyStreamInitialised) { - NS_WARNING("endless loop detected" - " - invalid deflate"); + // stop an endless loop caused by non-deflate data being labelled as + // deflate + if (mDummyStreamInitialised) { + NS_WARNING( + "endless loop detected" + " - invalid deflate"); + return NS_ERROR_INVALID_CONTENT_ENCODING; + } + mDummyStreamInitialised = true; + // reset stream pointers to our original data + d_stream.next_in = mInpBuffer; + d_stream.avail_in = (uInt)streamLen; + } else { return NS_ERROR_INVALID_CONTENT_ENCODING; } - mDummyStreamInitialised = true; - // reset stream pointers to our original data - d_stream.next_in = mInpBuffer; - d_stream.avail_in = (uInt)streamLen; - } else { - return NS_ERROR_INVALID_CONTENT_ENCODING; - } - } /* for */ - } else { - if (!mStreamInitialized) { - memset(&d_stream, 0, sizeof (d_stream)); + } /* for */ + } else { + if (!mStreamInitialized) { + memset(&d_stream, 0, sizeof(d_stream)); - if (inflateInit2(&d_stream, -MAX_WBITS) != Z_OK) { - return NS_ERROR_FAILURE; - } + if (inflateInit2(&d_stream, -MAX_WBITS) != Z_OK) { + return NS_ERROR_FAILURE; + } - mStreamInitialized = true; - } + mStreamInitialized = true; + } - d_stream.next_in = mInpBuffer; - d_stream.avail_in = (uInt)streamLen; + d_stream.next_in = mInpBuffer; + d_stream.avail_in = (uInt)streamLen; - for (;;) { - d_stream.next_out = mOutBuffer; - d_stream.avail_out = (uInt)mOutBufferLen; + for (;;) { + d_stream.next_out = mOutBuffer; + d_stream.avail_out = (uInt)mOutBufferLen; - int code = inflate (&d_stream, Z_NO_FLUSH); - unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out; + int code = inflate(&d_stream, Z_NO_FLUSH); + unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out; - if (code == Z_STREAM_END) { - if (bytesWritten) { - rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); - if (NS_FAILED (rv)) { - return rv; + if (code == Z_STREAM_END) { + if (bytesWritten) { + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char*)mOutBuffer, bytesWritten); + if (NS_FAILED(rv)) { + return rv; + } } - } - inflateEnd(&d_stream); - mStreamEnded = true; - break; - } else if (code == Z_OK) { - if (bytesWritten) { - rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); - if (NS_FAILED (rv)) { - return rv; + inflateEnd(&d_stream); + mStreamEnded = true; + break; + } else if (code == Z_OK) { + if (bytesWritten) { + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char*)mOutBuffer, bytesWritten); + if (NS_FAILED(rv)) { + return rv; + } } - } - } else if (code == Z_BUF_ERROR) { - if (bytesWritten) { - rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); - if (NS_FAILED (rv)) { - return rv; + } else if (code == Z_BUF_ERROR) { + if (bytesWritten) { + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char*)mOutBuffer, bytesWritten); + if (NS_FAILED(rv)) { + return rv; + } } + break; + } else { + return NS_ERROR_INVALID_CONTENT_ENCODING; } - break; - } else { - return NS_ERROR_INVALID_CONTENT_ENCODING; - } - } /* for */ - } /* gzip */ - break; + } /* for */ + } /* gzip */ + break; - case HTTP_COMPRESS_BROTLI: - { - if (!mBrotli) { - mBrotli = new BrotliWrapper(); - } + case HTTP_COMPRESS_BROTLI: { + if (!mBrotli) { + mBrotli = new BrotliWrapper(); + } - mBrotli->mRequest = request; - mBrotli->mContext = aContext; - mBrotli->mSourceOffset = aSourceOffset; + mBrotli->mRequest = request; + mBrotli->mContext = nullptr; + mBrotli->mSourceOffset = aSourceOffset; - uint32_t countRead; - rv = iStr->ReadSegments(BrotliHandler, this, streamLen, &countRead); - if (NS_SUCCEEDED(rv)) { - rv = mBrotli->mStatus; - } - if (NS_FAILED(rv)) { - return rv; - } - } - break; + uint32_t countRead; + rv = iStr->ReadSegments(BrotliHandler, this, streamLen, &countRead); + if (NS_SUCCEEDED(rv)) { + rv = mBrotli->mStatus; + } + if (NS_FAILED(rv)) { + return rv; + } + } break; - default: - rv = mListener->OnDataAvailable(request, aContext, iStr, aSourceOffset, aCount); - if (NS_FAILED (rv)) { - return rv; - } + default: + nsCOMPtr<nsIStreamListener> listener; + { + MutexAutoLock lock(mMutex); + listener = mListener; + } + rv = listener->OnDataAvailable(request, aContext, iStr, aSourceOffset, aCount); + if (NS_FAILED(rv)) { + return rv; + } } /* switch */ return NS_OK; @@ -468,20 +497,19 @@ nsHTTPCompressConv::OnDataAvailable(nsIRequest* request, // XXX/ruslan: need to implement this too NS_IMETHODIMP -nsHTTPCompressConv::Convert(nsIInputStream *aFromStream, - const char *aFromType, - const char *aToType, - nsISupports *aCtxt, - nsIInputStream **_retval) -{ +nsHTTPCompressConv::Convert(nsIInputStream* aFromStream, + const char* aFromType, + const char* aToType, + nsISupports* aCtxt, + nsIInputStream** _retval) { return NS_ERROR_NOT_IMPLEMENTED; } -nsresult -nsHTTPCompressConv::do_OnDataAvailable(nsIRequest* request, - nsISupports *context, uint64_t offset, - const char *buffer, uint32_t count) -{ +nsresult nsHTTPCompressConv::do_OnDataAvailable(nsIRequest* request, + nsISupports* context, + uint64_t offset, + const char* buffer, + uint32_t count) { if (!mStream) { mStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID); NS_ENSURE_STATE(mStream); @@ -489,8 +517,12 @@ nsHTTPCompressConv::do_OnDataAvailable(nsIRequest* request, mStream->ShareData(buffer, count); - nsresult rv = mListener->OnDataAvailable(request, context, mStream, - offset, count); + nsCOMPtr<nsIStreamListener> listener; + { + MutexAutoLock lock(mMutex); + listener = mListener; + } + nsresult rv = listener->OnDataAvailable(request, context, mStream, offset, count); // Make sure the stream no longer references |buffer| in case our listener // is crazy enough to try to read from |mStream| after ODA. @@ -500,19 +532,27 @@ nsHTTPCompressConv::do_OnDataAvailable(nsIRequest* request, return rv; } -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ -#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ -#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ -#define COMMENT 0x10 /* bit 4 set: file comment present */ -#define RESERVED 0xE0 /* bits 5..7: reserved */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ static unsigned gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ -uint32_t -nsHTTPCompressConv::check_header(nsIInputStream *iStr, uint32_t streamLen, nsresult *rs) -{ - enum { GZIP_INIT = 0, GZIP_OS, GZIP_EXTRA0, GZIP_EXTRA1, GZIP_EXTRA2, GZIP_ORIG, GZIP_COMMENT, GZIP_CRC }; +uint32_t nsHTTPCompressConv::check_header(nsIInputStream* iStr, + uint32_t streamLen, nsresult* rs) { + enum { + GZIP_INIT = 0, + GZIP_OS, + GZIP_EXTRA0, + GZIP_EXTRA1, + GZIP_EXTRA2, + GZIP_ORIG, + GZIP_COMMENT, + GZIP_CRC + }; char c; *rs = NS_OK; @@ -523,134 +563,147 @@ nsHTTPCompressConv::check_header(nsIInputStream *iStr, uint32_t streamLen, nsres while (streamLen) { switch (hMode) { - case GZIP_INIT: - uint32_t unused; - iStr->Read(&c, 1, &unused); - streamLen--; - - if (mSkipCount == 0 && ((unsigned)c & 0377) != gz_magic[0]) { - *rs = NS_ERROR_INVALID_CONTENT_ENCODING; - return 0; - } - - if (mSkipCount == 1 && ((unsigned)c & 0377) != gz_magic[1]) { - *rs = NS_ERROR_INVALID_CONTENT_ENCODING; - return 0; - } + case GZIP_INIT: + uint32_t unused; + iStr->Read(&c, 1, &unused); + streamLen--; - if (mSkipCount == 2 && ((unsigned)c & 0377) != Z_DEFLATED) { - *rs = NS_ERROR_INVALID_CONTENT_ENCODING; - return 0; - } + if (mSkipCount == 0 && ((unsigned)c & 0377) != gz_magic[0]) { + *rs = NS_ERROR_INVALID_CONTENT_ENCODING; + return 0; + } - mSkipCount++; - if (mSkipCount == 4) { - mFlags = (unsigned) c & 0377; - if (mFlags & RESERVED) { + if (mSkipCount == 1 && ((unsigned)c & 0377) != gz_magic[1]) { *rs = NS_ERROR_INVALID_CONTENT_ENCODING; return 0; } - hMode = GZIP_OS; - mSkipCount = 0; - } - break; - case GZIP_OS: - iStr->Read(&c, 1, &unused); - streamLen--; - mSkipCount++; + if (mSkipCount == 2 && ((unsigned)c & 0377) != Z_DEFLATED) { + *rs = NS_ERROR_INVALID_CONTENT_ENCODING; + return 0; + } - if (mSkipCount == 6) { - hMode = GZIP_EXTRA0; - } - break; + mSkipCount++; + if (mSkipCount == 4) { + mFlags = (unsigned)c & 0377; + if (mFlags & RESERVED) { + *rs = NS_ERROR_INVALID_CONTENT_ENCODING; + return 0; + } + hMode = GZIP_OS; + mSkipCount = 0; + } + break; - case GZIP_EXTRA0: - if (mFlags & EXTRA_FIELD) { + case GZIP_OS: iStr->Read(&c, 1, &unused); streamLen--; - mLen = (uInt) c & 0377; - hMode = GZIP_EXTRA1; - } else { - hMode = GZIP_ORIG; - } - break; + mSkipCount++; - case GZIP_EXTRA1: - iStr->Read(&c, 1, &unused); - streamLen--; - mLen |= ((uInt) c & 0377) << 8; - mSkipCount = 0; - hMode = GZIP_EXTRA2; - break; + if (mSkipCount == 6) { + hMode = GZIP_EXTRA0; + } + break; + + case GZIP_EXTRA0: + if (mFlags & EXTRA_FIELD) { + iStr->Read(&c, 1, &unused); + streamLen--; + mLen = (uInt)c & 0377; + hMode = GZIP_EXTRA1; + } else { + hMode = GZIP_ORIG; + } + break; - case GZIP_EXTRA2: - if (mSkipCount == mLen) { - hMode = GZIP_ORIG; - } else { + case GZIP_EXTRA1: iStr->Read(&c, 1, &unused); streamLen--; - mSkipCount++; - } - break; + mLen |= ((uInt)c & 0377) << 8; + mSkipCount = 0; + hMode = GZIP_EXTRA2; + break; - case GZIP_ORIG: - if (mFlags & ORIG_NAME) { - iStr->Read(&c, 1, &unused); - streamLen--; - if (c == 0) - hMode = GZIP_COMMENT; - } else { - hMode = GZIP_COMMENT; - } - break; + case GZIP_EXTRA2: + if (mSkipCount == mLen) { + hMode = GZIP_ORIG; + } else { + iStr->Read(&c, 1, &unused); + streamLen--; + mSkipCount++; + } + break; - case GZIP_COMMENT: - if (mFlags & COMMENT) { - iStr->Read(&c, 1, &unused); - streamLen--; - if (c == 0) { + case GZIP_ORIG: + if (mFlags & ORIG_NAME) { + iStr->Read(&c, 1, &unused); + streamLen--; + if (c == 0) hMode = GZIP_COMMENT; + } else { + hMode = GZIP_COMMENT; + } + break; + + case GZIP_COMMENT: + if (mFlags & COMMENT) { + iStr->Read(&c, 1, &unused); + streamLen--; + if (c == 0) { + hMode = GZIP_CRC; + mSkipCount = 0; + } + } else { hMode = GZIP_CRC; mSkipCount = 0; } - } else { - hMode = GZIP_CRC; - mSkipCount = 0; - } - break; - - case GZIP_CRC: - if (mFlags & HEAD_CRC) { - iStr->Read(&c, 1, &unused); - streamLen--; - mSkipCount++; - if (mSkipCount == 2) { + break; + + case GZIP_CRC: + if (mFlags & HEAD_CRC) { + iStr->Read(&c, 1, &unused); + streamLen--; + mSkipCount++; + if (mSkipCount == 2) { + mCheckHeaderDone = true; + return streamLen; + } + } else { mCheckHeaderDone = true; return streamLen; } - } else { - mCheckHeaderDone = true; - return streamLen; - } - break; + break; } } return streamLen; } -} // namespace net -} // namespace mozilla +NS_IMETHODIMP +nsHTTPCompressConv::CheckListenerChain() { + nsCOMPtr<nsIThreadRetargetableStreamListener> listener; + { + MutexAutoLock lock(mMutex); + listener = do_QueryInterface(mListener); + } -nsresult -NS_NewHTTPCompressConv(mozilla::net::nsHTTPCompressConv **aHTTPCompressConv) -{ - NS_PRECONDITION(aHTTPCompressConv != nullptr, "null ptr"); + if (!listener) { + return NS_ERROR_NO_INTERFACE; + } + + return listener->CheckListenerChain(); +} + +} // namespace net +} // namespace mozilla + +nsresult NS_NewHTTPCompressConv( + mozilla::net::nsHTTPCompressConv** aHTTPCompressConv) { + MOZ_ASSERT(aHTTPCompressConv != nullptr, "null ptr"); if (!aHTTPCompressConv) { return NS_ERROR_NULL_POINTER; } RefPtr<mozilla::net::nsHTTPCompressConv> outVal = - new mozilla::net::nsHTTPCompressConv(); + new mozilla::net::nsHTTPCompressConv(); if (!outVal) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/netwerk/streamconv/converters/nsHTTPCompressConv.h b/netwerk/streamconv/converters/nsHTTPCompressConv.h index 38889a8f7..6721a1e34 100644 --- a/netwerk/streamconv/converters/nsHTTPCompressConv.h +++ b/netwerk/streamconv/converters/nsHTTPCompressConv.h @@ -4,13 +4,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#if !defined (__nsHTTPCompressConv__h__) -#define __nsHTTPCompressConv__h__ 1 +#if !defined(__nsHTTPCompressConv__h__) +#define __nsHTTPCompressConv__h__ 1 #include "nsIStreamConverter.h" #include "nsICompressConvStats.h" +#include "nsIThreadRetargetableStreamListener.h" #include "nsCOMPtr.h" #include "nsAutoPtr.h" +#include "mozilla/Atomics.h" +#include "mozilla/Mutex.h" #include "zlib.h" @@ -30,20 +33,19 @@ class nsIStringInputStream; {0xab, 0xf4, 0x07, 0x98, 0x61, 0x51, 0x02, 0x2d} \ } - -#define HTTP_DEFLATE_TYPE "deflate" -#define HTTP_GZIP_TYPE "gzip" -#define HTTP_X_GZIP_TYPE "x-gzip" -#define HTTP_COMPRESS_TYPE "compress" -#define HTTP_X_COMPRESS_TYPE "x-compress" -#define HTTP_BROTLI_TYPE "br" -#define HTTP_IDENTITY_TYPE "identity" -#define HTTP_UNCOMPRESSED_TYPE "uncompressed" +#define HTTP_DEFLATE_TYPE "deflate" +#define HTTP_GZIP_TYPE "gzip" +#define HTTP_X_GZIP_TYPE "x-gzip" +#define HTTP_COMPRESS_TYPE "compress" +#define HTTP_X_COMPRESS_TYPE "x-compress" +#define HTTP_BROTLI_TYPE "br" +#define HTTP_IDENTITY_TYPE "identity" +#define HTTP_UNCOMPRESSED_TYPE "uncompressed" namespace mozilla { namespace net { -typedef enum { +typedef enum { HTTP_COMPRESS_GZIP, HTTP_COMPRESS_DEFLATE, HTTP_COMPRESS_COMPRESS, @@ -51,82 +53,88 @@ typedef enum { HTTP_COMPRESS_IDENTITY } CompressMode; -class BrotliWrapper -{ +class BrotliWrapper { public: BrotliWrapper() - : mTotalOut(0) - , mStatus(NS_OK) - { - BrotliStateInit(&mState); - } - ~BrotliWrapper() - { - BrotliStateCleanup(&mState); + : mTotalOut(0), + mStatus(NS_OK), + mBrotliStateIsStreamEnd(false), + mRequest(nullptr), + mContext(nullptr), + mSourceOffset(0) { + BrotliDecoderStateInit(&mState, 0, 0, 0); } + ~BrotliWrapper() { BrotliDecoderStateCleanup(&mState); } - BrotliState mState; - size_t mTotalOut; - nsresult mStatus; + BrotliDecoderState mState; + Atomic<size_t, Relaxed> mTotalOut; + nsresult mStatus; + Atomic<bool, Relaxed> mBrotliStateIsStreamEnd; - nsIRequest *mRequest; - nsISupports *mContext; - uint64_t mSourceOffset; + nsIRequest* mRequest; + nsISupports* mContext; + uint64_t mSourceOffset; }; -class nsHTTPCompressConv - : public nsIStreamConverter - , public nsICompressConvStats -{ - public: +class nsHTTPCompressConv : public nsIStreamConverter, + public nsICompressConvStats, + public nsIThreadRetargetableStreamListener { +public: // nsISupports methods - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIREQUESTOBSERVER - NS_DECL_NSISTREAMLISTENER - NS_DECL_NSICOMPRESSCONVSTATS + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSICOMPRESSCONVSTATS + NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER // nsIStreamConverter methods - NS_DECL_NSISTREAMCONVERTER + NS_DECL_NSISTREAMCONVERTER - nsHTTPCompressConv (); + nsHTTPCompressConv(); private: - virtual ~nsHTTPCompressConv (); + virtual ~nsHTTPCompressConv(); + + nsCOMPtr<nsIStreamListener> mListener; // this guy gets the converted data via his OnDataAvailable () + Atomic<CompressMode, Relaxed> mMode; - nsCOMPtr<nsIStreamListener> mListener; // this guy gets the converted data via his OnDataAvailable () - CompressMode mMode; + unsigned char* mOutBuffer; + unsigned char* mInpBuffer; - unsigned char *mOutBuffer; - unsigned char *mInpBuffer; + uint32_t mOutBufferLen; + uint32_t mInpBufferLen; - uint32_t mOutBufferLen; - uint32_t mInpBufferLen; + nsAutoPtr<BrotliWrapper> mBrotli; - nsAutoPtr<BrotliWrapper> mBrotli; + nsCOMPtr<nsIStringInputStream> mStream; - nsCOMPtr<nsISupports> mAsyncConvContext; - nsCOMPtr<nsIStringInputStream> mStream; + static nsresult BrotliHandler(nsIInputStream* stream, + void* closure, + const char* dataIn, + uint32_t, + uint32_t avail, + uint32_t* countRead); - static nsresult - BrotliHandler(nsIInputStream *stream, void *closure, const char *dataIn, - uint32_t, uint32_t avail, uint32_t *countRead); + nsresult do_OnDataAvailable(nsIRequest* request, + nsISupports* aContext, + uint64_t aSourceOffset, + const char* buffer, + uint32_t aCount); - nsresult do_OnDataAvailable (nsIRequest *request, nsISupports *aContext, - uint64_t aSourceOffset, const char *buffer, - uint32_t aCount); + bool mCheckHeaderDone; + Atomic<bool> mStreamEnded; + bool mStreamInitialized; + bool mDummyStreamInitialised; + bool mFailUncleanStops; - bool mCheckHeaderDone; - bool mStreamEnded; - bool mStreamInitialized; - bool mDummyStreamInitialised; - bool mFailUncleanStops; + z_stream d_stream; + unsigned mLen, hMode, mSkipCount, mFlags; - z_stream d_stream; - unsigned mLen, hMode, mSkipCount, mFlags; + uint32_t check_header(nsIInputStream* iStr, uint32_t streamLen, nsresult* rv); - uint32_t check_header (nsIInputStream *iStr, uint32_t streamLen, nsresult *rv); + Atomic<uint32_t, Relaxed> mDecodedDataLength; - uint32_t mDecodedDataLength; + mutable mozilla::Mutex mMutex; }; } // namespace net |