michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 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: // HttpLog.h should generally be included first michael@0: #include "HttpLog.h" michael@0: #include michael@0: #include "nsHttpChunkedDecoder.h" michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpChunkedDecoder michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: nsHttpChunkedDecoder::HandleChunkedContent(char *buf, michael@0: uint32_t count, michael@0: uint32_t *contentRead, michael@0: uint32_t *contentRemaining) michael@0: { michael@0: LOG(("nsHttpChunkedDecoder::HandleChunkedContent [count=%u]\n", count)); michael@0: michael@0: *contentRead = 0; michael@0: michael@0: // from RFC2617 section 3.6.1, the chunked transfer coding is defined as: michael@0: // michael@0: // Chunked-Body = *chunk michael@0: // last-chunk michael@0: // trailer michael@0: // CRLF michael@0: // chunk = chunk-size [ chunk-extension ] CRLF michael@0: // chunk-data CRLF michael@0: // chunk-size = 1*HEX michael@0: // last-chunk = 1*("0") [ chunk-extension ] CRLF michael@0: // michael@0: // chunk-extension = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) michael@0: // chunk-ext-name = token michael@0: // chunk-ext-val = token | quoted-string michael@0: // chunk-data = chunk-size(OCTET) michael@0: // trailer = *(entity-header CRLF) michael@0: // michael@0: // the chunk-size field is a string of hex digits indicating the size of the michael@0: // chunk. the chunked encoding is ended by any chunk whose size is zero, michael@0: // followed by the trailer, which is terminated by an empty line. michael@0: michael@0: while (count) { michael@0: if (mChunkRemaining) { michael@0: uint32_t amt = std::min(mChunkRemaining, count); michael@0: michael@0: count -= amt; michael@0: mChunkRemaining -= amt; michael@0: michael@0: *contentRead += amt; michael@0: buf += amt; michael@0: } michael@0: else if (mReachedEOF) michael@0: break; // done michael@0: else { michael@0: uint32_t bytesConsumed = 0; michael@0: michael@0: nsresult rv = ParseChunkRemaining(buf, count, &bytesConsumed); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: count -= bytesConsumed; michael@0: michael@0: if (count) { michael@0: // shift buf by bytesConsumed michael@0: memmove(buf, buf + bytesConsumed, count); michael@0: } michael@0: } michael@0: } michael@0: michael@0: *contentRemaining = count; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpChunkedDecoder michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: nsHttpChunkedDecoder::ParseChunkRemaining(char *buf, michael@0: uint32_t count, michael@0: uint32_t *bytesConsumed) michael@0: { michael@0: NS_PRECONDITION(mChunkRemaining == 0, "chunk remaining should be zero"); michael@0: NS_PRECONDITION(count, "unexpected"); michael@0: michael@0: *bytesConsumed = 0; michael@0: michael@0: char *p = static_cast(memchr(buf, '\n', count)); michael@0: if (p) { michael@0: *p = 0; michael@0: if ((p > buf) && (*(p-1) == '\r')) // eliminate a preceding CR michael@0: *(p-1) = 0; michael@0: *bytesConsumed = p - buf + 1; michael@0: michael@0: // make buf point to the full line buffer to parse michael@0: if (!mLineBuf.IsEmpty()) { michael@0: mLineBuf.Append(buf); michael@0: buf = (char *) mLineBuf.get(); michael@0: } michael@0: michael@0: if (mWaitEOF) { michael@0: if (*buf) { michael@0: LOG(("got trailer: %s\n", buf)); michael@0: // allocate a header array for the trailers on demand michael@0: if (!mTrailers) { michael@0: mTrailers = new nsHttpHeaderArray(); michael@0: } michael@0: mTrailers->ParseHeaderLine(buf); michael@0: } michael@0: else { michael@0: mWaitEOF = false; michael@0: mReachedEOF = true; michael@0: LOG(("reached end of chunked-body\n")); michael@0: } michael@0: } michael@0: else if (*buf) { michael@0: char *endptr; michael@0: unsigned long parsedval; // could be 64 bit, could be 32 michael@0: michael@0: // ignore any chunk-extensions michael@0: if ((p = PL_strchr(buf, ';')) != nullptr) michael@0: *p = 0; michael@0: michael@0: // mChunkRemaining is an uint32_t! michael@0: parsedval = strtoul(buf, &endptr, 16); michael@0: mChunkRemaining = (uint32_t) parsedval; michael@0: michael@0: if ((endptr == buf) || michael@0: ((errno == ERANGE) && (parsedval == ULONG_MAX)) || michael@0: (parsedval != mChunkRemaining) ) { michael@0: LOG(("failed parsing hex on string [%s]\n", buf)); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: // we've discovered the last chunk michael@0: if (mChunkRemaining == 0) michael@0: mWaitEOF = true; michael@0: } michael@0: michael@0: // ensure that the line buffer is clear michael@0: mLineBuf.Truncate(); michael@0: } michael@0: else { michael@0: // save the partial line; wait for more data michael@0: *bytesConsumed = count; michael@0: // ignore a trailing CR michael@0: if (buf[count-1] == '\r') michael@0: count--; michael@0: mLineBuf.Append(buf, count); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace mozilla::net michael@0: } // namespace mozilla