]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/http/one/TeChunkedParser.cc
2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
10 #include "base/TextException.h"
12 #include "http/one/TeChunkedParser.h"
13 #include "http/one/Tokenizer.h"
14 #include "http/ProtocolVersion.h"
18 Http::One::TeChunkedParser::TeChunkedParser()
20 // chunked encoding only exists in HTTP/1.1
21 Http1::Parser::msgProtocol_
= Http::ProtocolVersion(1,1);
27 Http::One::TeChunkedParser::clear()
29 parsingStage_
= Http1::HTTP_PARSE_NONE
;
31 theChunkSize
= theLeftBodySize
= 0;
37 Http::One::TeChunkedParser::parse(const SBuf
&aBuf
)
39 buf_
= aBuf
; // sync buffers first so calls to remaining() work properly if nothing done.
41 if (buf_
.isEmpty()) // nothing to do (yet)
44 debugs(74, DBG_DATA
, "Parse buf={length=" << aBuf
.length() << ", data='" << aBuf
<< "'}");
46 Must(!buf_
.isEmpty() && theOut
);
48 if (parsingStage_
== Http1::HTTP_PARSE_NONE
)
49 parsingStage_
= Http1::HTTP_PARSE_CHUNK_SZ
;
51 Http1::Tokenizer
tok(buf_
);
53 // loop for as many chunks as we can
54 // use do-while instead of while so that we can incrementally
55 // restart in the middle of a chunk/frame
58 if (parsingStage_
== Http1::HTTP_PARSE_CHUNK_EXT
&& !parseChunkExtension(tok
, theChunkSize
))
61 if (parsingStage_
== Http1::HTTP_PARSE_CHUNK
&& !parseChunkBody(tok
))
64 if (parsingStage_
== Http1::HTTP_PARSE_MIME
&& !grabMimeBlock("Trailers", 64*1024 /* 64KB max */))
67 // loop for as many chunks as we can
68 } while (parsingStage_
== Http1::HTTP_PARSE_CHUNK_SZ
&& parseChunkSize(tok
));
70 return !needsMoreData() && !needsMoreSpace();
74 Http::One::TeChunkedParser::needsMoreSpace() const
77 return parsingStage_
== Http1::HTTP_PARSE_CHUNK
&& !theOut
->hasPotentialSpace();
80 /// RFC 7230 section 4.1 chunk-size
82 Http::One::TeChunkedParser::parseChunkSize(Http1::Tokenizer
&tok
)
84 Must(theChunkSize
<= 0); // Should(), really
87 if (tok
.int64(size
, 16, false) && !tok
.atEnd()) {
89 throw TexcHere("negative chunk size");
91 theChunkSize
= theLeftBodySize
= size
;
92 debugs(94,7, "found chunk: " << theChunkSize
);
93 buf_
= tok
.remaining(); // parse checkpoint
94 parsingStage_
= Http1::HTTP_PARSE_CHUNK_EXT
;
97 } else if (tok
.atEnd()) {
98 return false; // need more data
102 throw TexcHere("corrupted chunk size");
103 return false; // should not be reachable
107 * Parses a set of RFC 7230 section 4.1.1 chunk-ext
108 * http://tools.ietf.org/html/rfc7230#section-4.1.1
110 * chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
111 * chunk-ext-name = token
112 * chunk-ext-val = token / quoted-string
114 * ICAP 'use-original-body=N' extension is supported.
117 Http::One::TeChunkedParser::parseChunkExtension(Http1::Tokenizer
&tok
, bool skipKnown
)
121 while (tok
.skip(';') && tok
.prefix(ext
, CharacterSet::TCHAR
)) {
123 // whole value part is optional. if no '=' expect next chunk-ext
127 if (ext
.cmp("use-original-body",17) == 0 && tok
.int64(useOriginBody
, 10)) {
128 debugs(94, 3, "Found chunk extension " << ext
<< "=" << useOriginBody
);
129 buf_
= tok
.remaining(); // parse checkpoint
134 debugs(94, 5, "skipping unknown chunk extension " << ext
);
136 // unknown might have a value token or quoted-string
137 if (tok
.quotedStringOrToken(value
) && !tok
.atEnd()) {
138 buf_
= tok
.remaining(); // parse checkpoint
142 // otherwise need more data OR corrupt syntax
147 buf_
= tok
.remaining(); // parse checkpoint (unless there might be more token name)
153 if (skipLineTerminator(tok
)) {
154 buf_
= tok
.remaining(); // checkpoint
155 // non-0 chunk means data, 0-size means optional Trailer follows
156 parsingStage_
= theChunkSize
? Http1::HTTP_PARSE_CHUNK
: Http1::HTTP_PARSE_MIME
;
160 throw TexcHere("corrupted chunk extension value");
165 Http::One::TeChunkedParser::parseChunkBody(Http1::Tokenizer
&tok
)
167 if (theLeftBodySize
> 0) {
168 buf_
= tok
.remaining(); // sync buffers before buf_ use
170 // TODO fix type mismatches and casting for these
171 const size_t availSize
= min(theLeftBodySize
, (uint64_t)buf_
.length());
172 const size_t safeSize
= min(availSize
, (size_t)theOut
->potentialSpaceSize());
174 theOut
->append(buf_
.rawContent(), safeSize
);
175 buf_
.consume(safeSize
);
176 theLeftBodySize
-= safeSize
;
178 tok
.reset(buf_
); // sync buffers after consume()
181 if (theLeftBodySize
== 0)
182 return parseChunkEnd(tok
);
184 Must(needsMoreData() || needsMoreSpace());
190 Http::One::TeChunkedParser::parseChunkEnd(Http1::Tokenizer
&tok
)
192 Must(theLeftBodySize
== 0); // Should(), really
194 if (skipLineTerminator(tok
)) {
195 buf_
= tok
.remaining(); // parse checkpoint
196 theChunkSize
= 0; // done with the current chunk
197 parsingStage_
= Http1::HTTP_PARSE_CHUNK_SZ
;
200 } else if (!tok
.atEnd()) {
201 throw TexcHere("found data between chunk end and CRLF");