1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/nsHttpChunkedDecoder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,163 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// HttpLog.h should generally be included first 1.10 +#include "HttpLog.h" 1.11 +#include <errno.h> 1.12 +#include "nsHttpChunkedDecoder.h" 1.13 +#include <algorithm> 1.14 + 1.15 +namespace mozilla { 1.16 +namespace net { 1.17 + 1.18 +//----------------------------------------------------------------------------- 1.19 +// nsHttpChunkedDecoder <public> 1.20 +//----------------------------------------------------------------------------- 1.21 + 1.22 +nsresult 1.23 +nsHttpChunkedDecoder::HandleChunkedContent(char *buf, 1.24 + uint32_t count, 1.25 + uint32_t *contentRead, 1.26 + uint32_t *contentRemaining) 1.27 +{ 1.28 + LOG(("nsHttpChunkedDecoder::HandleChunkedContent [count=%u]\n", count)); 1.29 + 1.30 + *contentRead = 0; 1.31 + 1.32 + // from RFC2617 section 3.6.1, the chunked transfer coding is defined as: 1.33 + // 1.34 + // Chunked-Body = *chunk 1.35 + // last-chunk 1.36 + // trailer 1.37 + // CRLF 1.38 + // chunk = chunk-size [ chunk-extension ] CRLF 1.39 + // chunk-data CRLF 1.40 + // chunk-size = 1*HEX 1.41 + // last-chunk = 1*("0") [ chunk-extension ] CRLF 1.42 + // 1.43 + // chunk-extension = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) 1.44 + // chunk-ext-name = token 1.45 + // chunk-ext-val = token | quoted-string 1.46 + // chunk-data = chunk-size(OCTET) 1.47 + // trailer = *(entity-header CRLF) 1.48 + // 1.49 + // the chunk-size field is a string of hex digits indicating the size of the 1.50 + // chunk. the chunked encoding is ended by any chunk whose size is zero, 1.51 + // followed by the trailer, which is terminated by an empty line. 1.52 + 1.53 + while (count) { 1.54 + if (mChunkRemaining) { 1.55 + uint32_t amt = std::min(mChunkRemaining, count); 1.56 + 1.57 + count -= amt; 1.58 + mChunkRemaining -= amt; 1.59 + 1.60 + *contentRead += amt; 1.61 + buf += amt; 1.62 + } 1.63 + else if (mReachedEOF) 1.64 + break; // done 1.65 + else { 1.66 + uint32_t bytesConsumed = 0; 1.67 + 1.68 + nsresult rv = ParseChunkRemaining(buf, count, &bytesConsumed); 1.69 + if (NS_FAILED(rv)) return rv; 1.70 + 1.71 + count -= bytesConsumed; 1.72 + 1.73 + if (count) { 1.74 + // shift buf by bytesConsumed 1.75 + memmove(buf, buf + bytesConsumed, count); 1.76 + } 1.77 + } 1.78 + } 1.79 + 1.80 + *contentRemaining = count; 1.81 + return NS_OK; 1.82 +} 1.83 + 1.84 +//----------------------------------------------------------------------------- 1.85 +// nsHttpChunkedDecoder <private> 1.86 +//----------------------------------------------------------------------------- 1.87 + 1.88 +nsresult 1.89 +nsHttpChunkedDecoder::ParseChunkRemaining(char *buf, 1.90 + uint32_t count, 1.91 + uint32_t *bytesConsumed) 1.92 +{ 1.93 + NS_PRECONDITION(mChunkRemaining == 0, "chunk remaining should be zero"); 1.94 + NS_PRECONDITION(count, "unexpected"); 1.95 + 1.96 + *bytesConsumed = 0; 1.97 + 1.98 + char *p = static_cast<char *>(memchr(buf, '\n', count)); 1.99 + if (p) { 1.100 + *p = 0; 1.101 + if ((p > buf) && (*(p-1) == '\r')) // eliminate a preceding CR 1.102 + *(p-1) = 0; 1.103 + *bytesConsumed = p - buf + 1; 1.104 + 1.105 + // make buf point to the full line buffer to parse 1.106 + if (!mLineBuf.IsEmpty()) { 1.107 + mLineBuf.Append(buf); 1.108 + buf = (char *) mLineBuf.get(); 1.109 + } 1.110 + 1.111 + if (mWaitEOF) { 1.112 + if (*buf) { 1.113 + LOG(("got trailer: %s\n", buf)); 1.114 + // allocate a header array for the trailers on demand 1.115 + if (!mTrailers) { 1.116 + mTrailers = new nsHttpHeaderArray(); 1.117 + } 1.118 + mTrailers->ParseHeaderLine(buf); 1.119 + } 1.120 + else { 1.121 + mWaitEOF = false; 1.122 + mReachedEOF = true; 1.123 + LOG(("reached end of chunked-body\n")); 1.124 + } 1.125 + } 1.126 + else if (*buf) { 1.127 + char *endptr; 1.128 + unsigned long parsedval; // could be 64 bit, could be 32 1.129 + 1.130 + // ignore any chunk-extensions 1.131 + if ((p = PL_strchr(buf, ';')) != nullptr) 1.132 + *p = 0; 1.133 + 1.134 + // mChunkRemaining is an uint32_t! 1.135 + parsedval = strtoul(buf, &endptr, 16); 1.136 + mChunkRemaining = (uint32_t) parsedval; 1.137 + 1.138 + if ((endptr == buf) || 1.139 + ((errno == ERANGE) && (parsedval == ULONG_MAX)) || 1.140 + (parsedval != mChunkRemaining) ) { 1.141 + LOG(("failed parsing hex on string [%s]\n", buf)); 1.142 + return NS_ERROR_UNEXPECTED; 1.143 + } 1.144 + 1.145 + // we've discovered the last chunk 1.146 + if (mChunkRemaining == 0) 1.147 + mWaitEOF = true; 1.148 + } 1.149 + 1.150 + // ensure that the line buffer is clear 1.151 + mLineBuf.Truncate(); 1.152 + } 1.153 + else { 1.154 + // save the partial line; wait for more data 1.155 + *bytesConsumed = count; 1.156 + // ignore a trailing CR 1.157 + if (buf[count-1] == '\r') 1.158 + count--; 1.159 + mLineBuf.Append(buf, count); 1.160 + } 1.161 + 1.162 + return NS_OK; 1.163 +} 1.164 + 1.165 +} // namespace mozilla::net 1.166 +} // namespace mozilla