michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: */ michael@0: michael@0: #include "StreamFunctions.h" michael@0: #include "nsDeflateConverter.h" michael@0: #include "nsIStringStream.h" michael@0: #include "nsIInputStreamPump.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsMemory.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "plstr.h" michael@0: michael@0: #define ZLIB_TYPE "deflate" michael@0: #define GZIP_TYPE "gzip" michael@0: #define X_GZIP_TYPE "x-gzip" michael@0: michael@0: /** michael@0: * nsDeflateConverter is a stream converter applies the deflate compression michael@0: * method to the data. michael@0: */ michael@0: NS_IMPL_ISUPPORTS(nsDeflateConverter, nsIStreamConverter, michael@0: nsIStreamListener, michael@0: nsIRequestObserver) michael@0: michael@0: nsresult nsDeflateConverter::Init() michael@0: { michael@0: int zerr; michael@0: michael@0: mOffset = 0; michael@0: michael@0: mZstream.zalloc = Z_NULL; michael@0: mZstream.zfree = Z_NULL; michael@0: mZstream.opaque = Z_NULL; michael@0: michael@0: int32_t window = MAX_WBITS; michael@0: switch (mWrapMode) { michael@0: case WRAP_NONE: michael@0: window = -window; michael@0: break; michael@0: case WRAP_GZIP: michael@0: window += 16; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: zerr = deflateInit2(&mZstream, mLevel, Z_DEFLATED, window, 8, michael@0: Z_DEFAULT_STRATEGY); michael@0: if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: mZstream.next_out = mWriteBuffer; michael@0: mZstream.avail_out = sizeof(mWriteBuffer); michael@0: michael@0: // mark the input buffer as empty. michael@0: mZstream.avail_in = 0; michael@0: mZstream.next_in = Z_NULL; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* nsIInputStream convert (in nsIInputStream aFromStream, in string aFromType michael@0: * in string aToType, in nsISupports aCtxt); */ michael@0: NS_IMETHODIMP nsDeflateConverter::Convert(nsIInputStream *aFromStream, michael@0: const char *aFromType, michael@0: const char *aToType, michael@0: nsISupports *aCtxt, michael@0: nsIInputStream **_retval) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: /* void asyncConvertData (in string aFromType, in string aToType, michael@0: * in nsIStreamListener aListener, michael@0: * in nsISupports aCtxt); */ michael@0: NS_IMETHODIMP nsDeflateConverter::AsyncConvertData(const char *aFromType, michael@0: const char *aToType, michael@0: nsIStreamListener *aListener, michael@0: nsISupports *aCtxt) michael@0: { michael@0: if (mListener) michael@0: return NS_ERROR_ALREADY_INITIALIZED; michael@0: michael@0: NS_ENSURE_ARG_POINTER(aListener); michael@0: michael@0: if (!PL_strncasecmp(aToType, ZLIB_TYPE, sizeof(ZLIB_TYPE)-1)) michael@0: mWrapMode = WRAP_ZLIB; michael@0: else if (!PL_strcasecmp(aToType, GZIP_TYPE) || michael@0: !PL_strcasecmp(aToType, X_GZIP_TYPE)) michael@0: mWrapMode = WRAP_GZIP; michael@0: else michael@0: mWrapMode = WRAP_NONE; michael@0: michael@0: nsresult rv = Init(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mListener = aListener; michael@0: mContext = aCtxt; michael@0: return rv; michael@0: } michael@0: michael@0: /* void onDataAvailable (in nsIRequest aRequest, in nsISupports aContext, michael@0: * in nsIInputStream aInputStream, michael@0: * in unsigned long long aOffset, michael@0: * in unsigned long aCount); */ michael@0: NS_IMETHODIMP nsDeflateConverter::OnDataAvailable(nsIRequest *aRequest, michael@0: nsISupports *aContext, michael@0: nsIInputStream *aInputStream, michael@0: uint64_t aOffset, michael@0: uint32_t aCount) michael@0: { michael@0: if (!mListener) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: nsAutoArrayPtr buffer(new char[aCount]); michael@0: NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: nsresult rv = ZW_ReadData(aInputStream, buffer.get(), aCount); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // make sure we aren't reading too much michael@0: mZstream.avail_in = aCount; michael@0: mZstream.next_in = (unsigned char*)buffer.get(); michael@0: michael@0: int zerr = Z_OK; michael@0: // deflate loop michael@0: while (mZstream.avail_in > 0 && zerr == Z_OK) { michael@0: zerr = deflate(&mZstream, Z_NO_FLUSH); michael@0: michael@0: while (mZstream.avail_out == 0) { michael@0: // buffer is full, push the data out to the listener michael@0: rv = PushAvailableData(aRequest, aContext); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: zerr = deflate(&mZstream, Z_NO_FLUSH); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void onStartRequest (in nsIRequest aRequest, in nsISupports aContext); */ michael@0: NS_IMETHODIMP nsDeflateConverter::OnStartRequest(nsIRequest *aRequest, michael@0: nsISupports *aContext) michael@0: { michael@0: if (!mListener) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: return mListener->OnStartRequest(aRequest, mContext); michael@0: } michael@0: michael@0: /* void onStopRequest (in nsIRequest aRequest, in nsISupports aContext, michael@0: * in nsresult aStatusCode); */ michael@0: NS_IMETHODIMP nsDeflateConverter::OnStopRequest(nsIRequest *aRequest, michael@0: nsISupports *aContext, michael@0: nsresult aStatusCode) michael@0: { michael@0: if (!mListener) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: nsresult rv; michael@0: michael@0: int zerr; michael@0: do { michael@0: zerr = deflate(&mZstream, Z_FINISH); michael@0: rv = PushAvailableData(aRequest, aContext); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } while (zerr == Z_OK); michael@0: michael@0: deflateEnd(&mZstream); michael@0: michael@0: return mListener->OnStopRequest(aRequest, mContext, aStatusCode); michael@0: } michael@0: michael@0: nsresult nsDeflateConverter::PushAvailableData(nsIRequest *aRequest, michael@0: nsISupports *aContext) michael@0: { michael@0: uint32_t bytesToWrite = sizeof(mWriteBuffer) - mZstream.avail_out; michael@0: // We don't need to do anything if there isn't any data michael@0: if (bytesToWrite == 0) michael@0: return NS_OK; michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr stream = michael@0: do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: stream->ShareData((char*)mWriteBuffer, bytesToWrite); michael@0: rv = mListener->OnDataAvailable(aRequest, mContext, stream, mOffset, michael@0: bytesToWrite); michael@0: michael@0: // now set the state for 'deflate' michael@0: mZstream.next_out = mWriteBuffer; michael@0: mZstream.avail_out = sizeof(mWriteBuffer); michael@0: michael@0: mOffset += bytesToWrite; michael@0: return rv; michael@0: }