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