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