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