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