netwerk/streamconv/converters/nsHTTPCompressConv.cpp

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim:set ts=4 sw=4 sts=4 cindent et: */
     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"
     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"
    16 // nsISupports implementation
    17 NS_IMPL_ISUPPORTS(nsHTTPCompressConv,
    18                   nsIStreamConverter,
    19                   nsIStreamListener,
    20                   nsIRequestObserver)
    22 // nsFTPDirListingConv methods
    23 nsHTTPCompressConv::nsHTTPCompressConv()
    24     : mListener(nullptr)
    25     , mMode(HTTP_COMPRESS_IDENTITY)
    26     , mOutBuffer(nullptr)
    27     , mInpBuffer(nullptr)
    28     , mOutBufferLen(0)
    29     , mInpBufferLen(0)
    30     , mCheckHeaderDone(false)
    31     , mStreamEnded(false)
    32     , mStreamInitialized(false)
    33     , mLen(0)
    34     , hMode(0)
    35     , mSkipCount(0)
    36     , mFlags(0)
    37 {
    38 }
    40 nsHTTPCompressConv::~nsHTTPCompressConv()
    41 {
    42     NS_IF_RELEASE(mListener);
    44     if (mInpBuffer)
    45         nsMemory::Free(mInpBuffer);
    47     if (mOutBuffer)
    48         nsMemory::Free(mOutBuffer);
    50     // For some reason we are not getting Z_STREAM_END.  But this was also seen
    51     //    for mozilla bug 198133.  Need to handle this case.
    52     if (mStreamInitialized && !mStreamEnded)
    53         inflateEnd (&d_stream);
    54 }
    56 NS_IMETHODIMP
    57 nsHTTPCompressConv::AsyncConvertData(const char *aFromType, 
    58                                      const char *aToType, 
    59                                      nsIStreamListener *aListener, 
    60                                      nsISupports *aCtxt)
    61 {
    62     if (!PL_strncasecmp(aFromType, HTTP_COMPRESS_TYPE, sizeof(HTTP_COMPRESS_TYPE)-1) ||
    63         !PL_strncasecmp(aFromType, HTTP_X_COMPRESS_TYPE, sizeof(HTTP_X_COMPRESS_TYPE)-1))
    64         mMode = HTTP_COMPRESS_COMPRESS;
    66     else if (!PL_strncasecmp(aFromType, HTTP_GZIP_TYPE, sizeof(HTTP_GZIP_TYPE)-1) ||
    67              !PL_strncasecmp(aFromType, HTTP_X_GZIP_TYPE, sizeof(HTTP_X_GZIP_TYPE)-1))
    68         mMode = HTTP_COMPRESS_GZIP;
    70     else if (!PL_strncasecmp(aFromType, HTTP_DEFLATE_TYPE, sizeof(HTTP_DEFLATE_TYPE)-1))
    71         mMode = HTTP_COMPRESS_DEFLATE;
    73     // hook ourself up with the receiving listener. 
    74     mListener = aListener;
    75     NS_ADDREF(mListener);
    77     mAsyncConvContext = aCtxt;
    78     return NS_OK; 
    79 } 
    81 NS_IMETHODIMP
    82 nsHTTPCompressConv::OnStartRequest(nsIRequest* request, nsISupports *aContext)
    83 {
    84     return mListener->OnStartRequest(request, aContext);
    85 } 
    87 NS_IMETHODIMP
    88 nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext, 
    89                                   nsresult aStatus)
    90 {
    91     return mListener->OnStopRequest(request, aContext, aStatus);
    92 } 
    94 NS_IMETHODIMP
    95 nsHTTPCompressConv::OnDataAvailable(nsIRequest* request, 
    96                                     nsISupports *aContext, 
    97                                     nsIInputStream *iStr, 
    98                                     uint64_t aSourceOffset, 
    99                                     uint32_t aCount)
   100 {
   101     nsresult rv = NS_ERROR_INVALID_CONTENT_ENCODING;
   102     uint32_t streamLen = aCount;
   104     if (streamLen == 0)
   105     {
   106         NS_ERROR("count of zero passed to OnDataAvailable");
   107         return NS_ERROR_UNEXPECTED;
   108     }
   110     if (mStreamEnded)
   111     {
   112         // Hmm... this may just indicate that the data stream is done and that
   113         // what's left is either metadata or padding of some sort.... throwing
   114         // it out is probably the safe thing to do.
   115         uint32_t n;
   116         return iStr->ReadSegments(NS_DiscardSegment, nullptr, streamLen, &n);
   117     }
   119     switch (mMode)
   120     {
   121         case HTTP_COMPRESS_GZIP:
   122             streamLen = check_header(iStr, streamLen, &rv);
   124             if (rv != NS_OK)
   125                 return rv;
   127             if (streamLen == 0)
   128                 return NS_OK;
   130             // FALLTHROUGH
   132         case HTTP_COMPRESS_DEFLATE:
   134             if (mInpBuffer != nullptr && streamLen > mInpBufferLen)
   135             {
   136                 mInpBuffer = (unsigned char *) moz_realloc(mInpBuffer, mInpBufferLen = streamLen);
   138                 if (mOutBufferLen < streamLen * 2)
   139                     mOutBuffer = (unsigned char *) moz_realloc(mOutBuffer, mOutBufferLen = streamLen * 3);
   141                 if (mInpBuffer == nullptr || mOutBuffer == nullptr)
   142                     return NS_ERROR_OUT_OF_MEMORY;
   143             }
   145             if (mInpBuffer == nullptr)
   146                 mInpBuffer = (unsigned char *) moz_malloc(mInpBufferLen = streamLen);
   148             if (mOutBuffer == nullptr)
   149                 mOutBuffer = (unsigned char *) moz_malloc(mOutBufferLen = streamLen * 3);
   151             if (mInpBuffer == nullptr || mOutBuffer == nullptr)
   152                 return NS_ERROR_OUT_OF_MEMORY;
   154             uint32_t unused;
   155             iStr->Read((char *)mInpBuffer, streamLen, &unused);
   157             if (mMode == HTTP_COMPRESS_DEFLATE)
   158             {
   159                 if (!mStreamInitialized)
   160                 {
   161                     memset(&d_stream, 0, sizeof (d_stream));
   163                     if (inflateInit(&d_stream) != Z_OK)
   164                         return NS_ERROR_FAILURE;
   166                     mStreamInitialized = true;
   167                 }
   168                 d_stream.next_in = mInpBuffer;
   169                 d_stream.avail_in = (uInt)streamLen;
   171                 mDummyStreamInitialised = false;
   172                 for (;;)
   173                 {
   174                     d_stream.next_out = mOutBuffer;
   175                     d_stream.avail_out = (uInt)mOutBufferLen;
   177                     int code = inflate(&d_stream, Z_NO_FLUSH);
   178                     unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out;
   180                     if (code == Z_STREAM_END)
   181                     {
   182                         if (bytesWritten)
   183                         {
   184                             rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
   185                             if (NS_FAILED (rv))
   186                                 return rv;
   187                         }
   189                         inflateEnd(&d_stream);
   190                         mStreamEnded = true;
   191                         break;
   192                     }
   193                     else if (code == Z_OK)
   194                     {
   195                         if (bytesWritten)
   196                         {
   197                             rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
   198                             if (NS_FAILED (rv))
   199                                 return rv;
   200                         }
   201                     }
   202                     else if (code == Z_BUF_ERROR)
   203                     {
   204                         if (bytesWritten)
   205                         {
   206                             rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
   207                             if (NS_FAILED (rv))
   208                                 return rv;
   209                         }
   210                         break;
   211                     }
   212                     else if (code == Z_DATA_ERROR)
   213                     {
   214                         // some servers (notably Apache with mod_deflate) don't generate zlib headers
   215                         // insert a dummy header and try again
   216                         static char dummy_head[2] =
   217                         {
   218                             0x8 + 0x7 * 0x10,
   219                             (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
   220                         };
   221                         inflateReset(&d_stream);
   222                         d_stream.next_in = (Bytef*) dummy_head;
   223                         d_stream.avail_in = sizeof(dummy_head);
   225                         code = inflate(&d_stream, Z_NO_FLUSH);
   226                         if (code != Z_OK)
   227                             return NS_ERROR_FAILURE;
   229                         // stop an endless loop caused by non-deflate data being labelled as deflate
   230                         if (mDummyStreamInitialised) {
   231                             NS_WARNING("endless loop detected"
   232                                        " - invalid deflate");
   233                             return NS_ERROR_INVALID_CONTENT_ENCODING;
   234                         }
   235                         mDummyStreamInitialised = true;
   236                         // reset stream pointers to our original data
   237                         d_stream.next_in = mInpBuffer;
   238                         d_stream.avail_in = (uInt)streamLen;
   239                     }    
   240                     else
   241                         return NS_ERROR_INVALID_CONTENT_ENCODING;
   242                 } /* for */
   243             }
   244             else
   245             {
   246                 if (!mStreamInitialized)
   247                 {
   248                     memset(&d_stream, 0, sizeof (d_stream));
   250                     if (inflateInit2(&d_stream, -MAX_WBITS) != Z_OK)
   251                         return NS_ERROR_FAILURE;
   253                     mStreamInitialized = true;
   254                 }
   256                 d_stream.next_in  = mInpBuffer;
   257                 d_stream.avail_in = (uInt)streamLen;
   259                 for (;;)
   260                 {
   261                     d_stream.next_out  = mOutBuffer;
   262                     d_stream.avail_out = (uInt)mOutBufferLen;
   264                     int code = inflate (&d_stream, Z_NO_FLUSH);
   265                     unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out;
   267                     if (code == Z_STREAM_END)
   268                     {
   269                         if (bytesWritten)
   270                         {
   271                             rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
   272                             if (NS_FAILED (rv))
   273                                 return rv;
   274                         }
   276                         inflateEnd(&d_stream);
   277                         mStreamEnded = true;
   278                         break;
   279                     }
   280                     else if (code == Z_OK)
   281                     {
   282                         if (bytesWritten)
   283                         {
   284                             rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
   285                             if (NS_FAILED (rv))
   286                                 return rv;
   287                         }
   288                     }
   289                     else if (code == Z_BUF_ERROR)
   290                     {
   291                         if (bytesWritten)
   292                         {
   293                             rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
   294                             if (NS_FAILED (rv))
   295                                 return rv;
   296                         }
   297                         break;
   298                     }
   299                     else
   300                         return NS_ERROR_INVALID_CONTENT_ENCODING;
   301                 } /* for */
   302             } /* gzip */
   303             break;
   305         default: 
   306             rv = mListener->OnDataAvailable(request, aContext, iStr, aSourceOffset, aCount);
   307             if (NS_FAILED (rv))
   308                 return rv;
   309     } /* switch */
   311 	return NS_OK;
   312 } /* OnDataAvailable */
   315 // XXX/ruslan: need to implement this too
   317 NS_IMETHODIMP
   318 nsHTTPCompressConv::Convert(nsIInputStream *aFromStream, 
   319                             const char *aFromType, 
   320                             const char *aToType, 
   321                             nsISupports *aCtxt, 
   322                             nsIInputStream **_retval)
   323 { 
   324     return NS_ERROR_NOT_IMPLEMENTED;
   325 } 
   327 nsresult
   328 nsHTTPCompressConv::do_OnDataAvailable(nsIRequest* request,
   329                                        nsISupports *context, uint64_t offset,
   330                                        const char *buffer, uint32_t count)
   331 {
   332     if (!mStream) {
   333         mStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID);
   334         NS_ENSURE_STATE(mStream);
   335     }
   337     mStream->ShareData(buffer, count);
   339     nsresult rv = mListener->OnDataAvailable(request, context, mStream,
   340                                              offset, count);
   342     // Make sure the stream no longer references |buffer| in case our listener
   343     // is crazy enough to try to read from |mStream| after ODA.
   344     mStream->ShareData("", 0);
   346     return rv;
   347 }
   349 #define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
   350 #define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
   351 #define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
   352 #define ORIG_NAME    0x08 /* bit 3 set: original file name present */
   353 #define COMMENT      0x10 /* bit 4 set: file comment present */
   354 #define RESERVED     0xE0 /* bits 5..7: reserved */
   356 static unsigned gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
   358 uint32_t
   359 nsHTTPCompressConv::check_header(nsIInputStream *iStr, uint32_t streamLen, nsresult *rs)
   360 {
   361     enum  { GZIP_INIT = 0, GZIP_OS, GZIP_EXTRA0, GZIP_EXTRA1, GZIP_EXTRA2, GZIP_ORIG, GZIP_COMMENT, GZIP_CRC };
   362     char c;
   364     *rs = NS_OK;
   366     if (mCheckHeaderDone)
   367         return streamLen;
   369     while (streamLen)
   370     {
   371         switch (hMode)
   372         {
   373             case GZIP_INIT:
   374                 uint32_t unused;
   375                 iStr->Read(&c, 1, &unused);
   376                 streamLen--;
   378                 if (mSkipCount == 0 && ((unsigned)c & 0377) != gz_magic[0])
   379                 {
   380                     *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
   381                     return 0;
   382                 }
   384                 if (mSkipCount == 1 && ((unsigned)c & 0377) != gz_magic[1])
   385                 {
   386                     *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
   387                     return 0;
   388                 }
   390                 if (mSkipCount == 2 && ((unsigned)c & 0377) != Z_DEFLATED)
   391                 {
   392                     *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
   393                     return 0;
   394                 }
   396                 mSkipCount++;
   397                 if (mSkipCount == 4)
   398                 {
   399                     mFlags = (unsigned) c & 0377;
   400                     if (mFlags & RESERVED)
   401                     {
   402                         *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
   403                         return 0;
   404                     }
   405                     hMode = GZIP_OS;
   406                     mSkipCount = 0;
   407                 }
   408                 break;
   410             case GZIP_OS:
   411                 iStr->Read(&c, 1, &unused);
   412                 streamLen--;
   413                 mSkipCount++;
   415                 if (mSkipCount == 6)
   416                     hMode = GZIP_EXTRA0;
   417                 break;
   419             case GZIP_EXTRA0:
   420                 if (mFlags & EXTRA_FIELD)
   421                 {
   422                     iStr->Read(&c, 1, &unused);
   423                     streamLen--;
   424                     mLen = (uInt) c & 0377;
   425                     hMode = GZIP_EXTRA1;
   426                 }
   427                 else
   428                     hMode = GZIP_ORIG;
   429                 break;
   431             case GZIP_EXTRA1:
   432                 iStr->Read(&c, 1, &unused);
   433                 streamLen--;
   434                 mLen = ((uInt) c & 0377) << 8;
   435                 mSkipCount = 0;
   436                 hMode = GZIP_EXTRA2;
   437                 break;
   439             case GZIP_EXTRA2:
   440                 if (mSkipCount == mLen)
   441                     hMode = GZIP_ORIG;
   442                 else
   443                 {
   444                     iStr->Read(&c, 1, &unused);
   445                     streamLen--;
   446                     mSkipCount++;
   447                 }
   448                 break;
   450             case GZIP_ORIG:
   451                 if (mFlags & ORIG_NAME)
   452                 {
   453                     iStr->Read(&c, 1, &unused);
   454                     streamLen--;
   455                     if (c == 0)
   456                         hMode = GZIP_COMMENT;
   457                 }
   458                 else
   459                     hMode = GZIP_COMMENT;
   460                 break;
   462             case GZIP_COMMENT:
   463                 if (mFlags & COMMENT)
   464                 {
   465                     iStr->Read(&c, 1, &unused);
   466                     streamLen--;
   467                     if (c == 0)
   468                     {
   469                         hMode = GZIP_CRC;
   470                         mSkipCount = 0;
   471                     }
   472                 }
   473                 else
   474                 {
   475                     hMode = GZIP_CRC;
   476                     mSkipCount = 0;
   477                 }
   478                 break;
   480             case GZIP_CRC:
   481                 if (mFlags & HEAD_CRC)
   482                 {
   483                     iStr->Read(&c, 1, &unused);
   484                     streamLen--;
   485                     mSkipCount++;
   486                     if (mSkipCount == 2)
   487                     {
   488                         mCheckHeaderDone = true;
   489                         return streamLen;
   490                     }
   491                 }
   492                 else
   493                 {
   494                     mCheckHeaderDone = true;
   495                     return streamLen;
   496                 }
   497             break;
   498         }
   499     }
   500     return streamLen;
   501 }
   503 nsresult
   504 NS_NewHTTPCompressConv(nsHTTPCompressConv **aHTTPCompressConv)
   505 {
   506     NS_PRECONDITION(aHTTPCompressConv != nullptr, "null ptr");
   508     if (!aHTTPCompressConv)
   509         return NS_ERROR_NULL_POINTER;
   511     *aHTTPCompressConv = new nsHTTPCompressConv();
   513     if (!*aHTTPCompressConv)
   514         return NS_ERROR_OUT_OF_MEMORY;
   516     NS_ADDREF(*aHTTPCompressConv);
   517     return NS_OK;
   518 }

mercurial