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