tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsHttpChunkedDecoder.cpp (5230B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 // HttpLog.h should generally be included first
      7 #include "HttpLog.h"
      8 
      9 #include <errno.h>
     10 #include "nsHttpChunkedDecoder.h"
     11 #include <algorithm>
     12 #include <string.h>
     13 
     14 namespace mozilla {
     15 namespace net {
     16 
     17 //-----------------------------------------------------------------------------
     18 // nsHttpChunkedDecoder <public>
     19 //-----------------------------------------------------------------------------
     20 
     21 nsresult nsHttpChunkedDecoder::HandleChunkedContent(
     22    char* buf, uint32_t count, uint32_t* contentRead,
     23    uint32_t* contentRemaining) {
     24  LOG(("nsHttpChunkedDecoder::HandleChunkedContent [count=%u]\n", count));
     25 
     26  *contentRead = 0;
     27 
     28  // from RFC2616 section 3.6.1, the chunked transfer coding is defined as:
     29  //
     30  //   Chunked-Body    = *chunk
     31  //                     last-chunk
     32  //                     trailer
     33  //                     CRLF
     34  //   chunk           = chunk-size [ chunk-extension ] CRLF
     35  //                     chunk-data CRLF
     36  //   chunk-size      = 1*HEX
     37  //   last-chunk      = 1*("0") [ chunk-extension ] CRLF
     38  //
     39  //   chunk-extension = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
     40  //   chunk-ext-name  = token
     41  //   chunk-ext-val   = token | quoted-string
     42  //   chunk-data      = chunk-size(OCTET)
     43  //   trailer         = *(entity-header CRLF)
     44  //
     45  // the chunk-size field is a string of hex digits indicating the size of the
     46  // chunk.  the chunked encoding is ended by any chunk whose size is zero,
     47  // followed by the trailer, which is terminated by an empty line.
     48 
     49  while (count) {
     50    if (mChunkRemaining) {
     51      uint32_t amt = std::min(mChunkRemaining, count);
     52 
     53      count -= amt;
     54      mChunkRemaining -= amt;
     55 
     56      *contentRead += amt;
     57      buf += amt;
     58    } else if (mReachedEOF) {
     59      break;  // done
     60    } else {
     61      uint32_t bytesConsumed = 0;
     62 
     63      nsresult rv = ParseChunkRemaining(buf, count, &bytesConsumed);
     64      if (NS_FAILED(rv)) return rv;
     65 
     66      count -= bytesConsumed;
     67 
     68      if (count) {
     69        // shift buf by bytesConsumed
     70        memmove(buf, buf + bytesConsumed, count);
     71      }
     72    }
     73  }
     74 
     75  *contentRemaining = count;
     76  return NS_OK;
     77 }
     78 
     79 //-----------------------------------------------------------------------------
     80 // nsHttpChunkedDecoder <private>
     81 //-----------------------------------------------------------------------------
     82 
     83 nsresult nsHttpChunkedDecoder::ParseChunkRemaining(char* buf, uint32_t count,
     84                                                   uint32_t* bytesConsumed) {
     85  MOZ_ASSERT(mChunkRemaining == 0, "chunk remaining should be zero");
     86  MOZ_ASSERT(count, "unexpected");
     87 
     88  *bytesConsumed = 0;
     89 
     90  char* p = static_cast<char*>(memchr(buf, '\n', count));
     91  if (p) {
     92    *p = 0;
     93    count = p - buf;                        // new length
     94    *bytesConsumed = count + 1;             // length + newline
     95    if ((p > buf) && (*(p - 1) == '\r')) {  // eliminate a preceding CR
     96      *(p - 1) = 0;
     97      count--;
     98    }
     99 
    100    // make buf point to the full line buffer to parse
    101    if (!mLineBuf.IsEmpty()) {
    102      mLineBuf.Append(buf, count);
    103      buf = (char*)mLineBuf.get();
    104      count = mLineBuf.Length();
    105    }
    106 
    107    if (mWaitEOF) {
    108      if (*buf) {
    109        LOG(("got trailer: %s\n", buf));
    110        // allocate a header array for the trailers on demand
    111        if (!mTrailers) {
    112          mTrailers = MakeUnique<nsHttpHeaderArray>();
    113        }
    114 
    115        nsHttpAtom hdr;
    116        nsAutoCString headerNameOriginal;
    117        nsAutoCString val;
    118        if (NS_SUCCEEDED(
    119                mTrailers->ParseHeaderLine(nsDependentCSubstring(buf, count),
    120                                           &hdr, &headerNameOriginal, &val))) {
    121          if (hdr == nsHttp::Server_Timing) {
    122            (void)mTrailers->SetHeaderFromNet(hdr, headerNameOriginal, val,
    123                                              true);
    124          }
    125        }
    126      } else {
    127        mWaitEOF = false;
    128        mReachedEOF = true;
    129        LOG(("reached end of chunked-body\n"));
    130      }
    131    } else if (*buf) {
    132      char* endptr;
    133      unsigned long parsedval;  // could be 64 bit, could be 32
    134 
    135      // ignore any chunk-extensions
    136      if ((p = strchr(buf, ';')) != nullptr) {
    137        *p = 0;
    138      }
    139 
    140      // mChunkRemaining is an uint32_t!
    141      parsedval = strtoul(buf, &endptr, 16);
    142      mChunkRemaining = (uint32_t)parsedval;
    143 
    144      if ((endptr == buf) || ((errno == ERANGE) && (parsedval == ULONG_MAX)) ||
    145          (parsedval != mChunkRemaining)) {
    146        LOG(("failed parsing hex on string [%s]\n", buf));
    147        return NS_ERROR_UNEXPECTED;
    148      }
    149 
    150      // we've discovered the last chunk
    151      if (mChunkRemaining == 0) mWaitEOF = true;
    152    }
    153 
    154    // ensure that the line buffer is clear
    155    mLineBuf.Truncate();
    156  } else {
    157    // save the partial line; wait for more data
    158    *bytesConsumed = count;
    159    // ignore a trailing CR
    160    if (buf[count - 1] == '\r') count--;
    161    mLineBuf.Append(buf, count);
    162  }
    163 
    164  return NS_OK;
    165 }
    166 
    167 }  // namespace net
    168 }  // namespace mozilla