LCOV - code coverage report
Current view: top level - netwerk/streamconv/converters - nsHTTPCompressConv.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 351 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 17 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13