]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http/one/TeChunkedParser.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / http / one / TeChunkedParser.cc
CommitLineData
bbc27441 1/*
4ac4a490 2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
bbc27441
AJ
3 *
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.
7 */
8
582c2af2 9#include "squid.h"
3d93a84d 10#include "base/TextException.h"
602d9612 11#include "Debug.h"
db1720f8 12#include "http/one/TeChunkedParser.h"
cb4c8047 13#include "http/one/Tokenizer.h"
350ec67a 14#include "http/ProtocolVersion.h"
774c051c 15#include "MemBuf.h"
602d9612 16#include "Parsing.h"
9a4b5048 17#include "SquidConfig.h"
774c051c 18
db1720f8 19Http::One::TeChunkedParser::TeChunkedParser()
774c051c 20{
350ec67a
AJ
21 // chunked encoding only exists in HTTP/1.1
22 Http1::Parser::msgProtocol_ = Http::ProtocolVersion(1,1);
23
24 clear();
774c051c 25}
26
51c3e7f0 27void
db1720f8 28Http::One::TeChunkedParser::clear()
774c051c 29{
350ec67a 30 parsingStage_ = Http1::HTTP_PARSE_NONE;
be29ee33 31 buf_.clear();
774c051c 32 theChunkSize = theLeftBodySize = 0;
be29ee33 33 theOut = NULL;
83c51da9 34 useOriginBody = -1;
774c051c 35}
36
51c3e7f0 37bool
db1720f8 38Http::One::TeChunkedParser::parse(const SBuf &aBuf)
774c051c 39{
74faa994
AJ
40 buf_ = aBuf; // sync buffers first so calls to remaining() work properly if nothing done.
41
42 if (buf_.isEmpty()) // nothing to do (yet)
43 return false;
44
be29ee33 45 debugs(74, DBG_DATA, "Parse buf={length=" << aBuf.length() << ", data='" << aBuf << "'}");
74faa994 46
be29ee33 47 Must(!buf_.isEmpty() && theOut);
774c051c 48
350ec67a
AJ
49 if (parsingStage_ == Http1::HTTP_PARSE_NONE)
50 parsingStage_ = Http1::HTTP_PARSE_CHUNK_SZ;
774c051c 51
cb4c8047 52 Http1::Tokenizer tok(buf_);
be29ee33 53
350ec67a
AJ
54 // loop for as many chunks as we can
55 // use do-while instead of while so that we can incrementally
56 // restart in the middle of a chunk/frame
57 do {
774c051c 58
be29ee33
AJ
59 if (parsingStage_ == Http1::HTTP_PARSE_CHUNK_EXT && !parseChunkExtension(tok, theChunkSize))
60 return false;
774c051c 61
be29ee33 62 if (parsingStage_ == Http1::HTTP_PARSE_CHUNK && !parseChunkBody(tok))
350ec67a
AJ
63 return false;
64
be29ee33 65 if (parsingStage_ == Http1::HTTP_PARSE_MIME && !grabMimeBlock("Trailers", 64*1024 /* 64KB max */))
350ec67a
AJ
66 return false;
67
68 // loop for as many chunks as we can
be29ee33 69 } while (parsingStage_ == Http1::HTTP_PARSE_CHUNK_SZ && parseChunkSize(tok));
350ec67a
AJ
70
71 return !needsMoreData() && !needsMoreSpace();
774c051c 72}
73
51c3e7f0 74bool
db1720f8 75Http::One::TeChunkedParser::needsMoreSpace() const
774c051c 76{
77 assert(theOut);
350ec67a 78 return parsingStage_ == Http1::HTTP_PARSE_CHUNK && !theOut->hasPotentialSpace();
774c051c 79}
80
be29ee33 81/// RFC 7230 section 4.1 chunk-size
51c3e7f0 82bool
cb4c8047 83Http::One::TeChunkedParser::parseChunkSize(Http1::Tokenizer &tok)
774c051c 84{
85 Must(theChunkSize <= 0); // Should(), really
86
5c550f5f 87 int64_t size = -1;
be29ee33 88 if (tok.int64(size, 16, false) && !tok.atEnd()) {
5c550f5f
AR
89 if (size < 0)
90 throw TexcHere("negative chunk size");
91
92 theChunkSize = theLeftBodySize = size;
93 debugs(94,7, "found chunk: " << theChunkSize);
be29ee33 94 buf_ = tok.remaining(); // parse checkpoint
350ec67a
AJ
95 parsingStage_ = Http1::HTTP_PARSE_CHUNK_EXT;
96 return true;
350ec67a 97
be29ee33
AJ
98 } else if (tok.atEnd()) {
99 return false; // need more data
774c051c 100 }
101
be29ee33
AJ
102 // else error
103 throw TexcHere("corrupted chunk size");
104 return false; // should not be reachable
774c051c 105}
106
350ec67a 107/**
be29ee33
AJ
108 * Parses a set of RFC 7230 section 4.1.1 chunk-ext
109 * http://tools.ietf.org/html/rfc7230#section-4.1.1
350ec67a 110 *
be29ee33
AJ
111 * chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
112 * chunk-ext-name = token
113 * chunk-ext-val = token / quoted-string
350ec67a 114 *
be29ee33 115 * ICAP 'use-original-body=N' extension is supported.
350ec67a
AJ
116 */
117bool
cb4c8047 118Http::One::TeChunkedParser::parseChunkExtension(Http1::Tokenizer &tok, bool skipKnown)
774c051c 119{
aad871e3
DS
120 // Bug 4492: IBM_HTTP_Server sends SP padding
121 if (auto n = tok.skipAll(CharacterSet::SP)) {
122 debugs(94, 3, "skipping " << n << " spurious whitespace at start of chunk extension");
123 }
124
be29ee33 125 SBuf ext;
cb4c8047 126 SBuf value;
be29ee33 127 while (tok.skip(';') && tok.prefix(ext, CharacterSet::TCHAR)) {
350ec67a 128
be29ee33
AJ
129 // whole value part is optional. if no '=' expect next chunk-ext
130 if (tok.skip('=')) {
774c051c 131
be29ee33
AJ
132 if (!skipKnown) {
133 if (ext.cmp("use-original-body",17) == 0 && tok.int64(useOriginBody, 10)) {
134 debugs(94, 3, "Found chunk extension " << ext << "=" << useOriginBody);
135 buf_ = tok.remaining(); // parse checkpoint
136 continue;
137 }
138 }
774c051c 139
be29ee33 140 debugs(94, 5, "skipping unknown chunk extension " << ext);
774c051c 141
cb4c8047
AJ
142 // unknown might have a value token or quoted-string
143 if (tok.quotedStringOrToken(value) && !tok.atEnd()) {
be29ee33
AJ
144 buf_ = tok.remaining(); // parse checkpoint
145 continue;
146 }
774c051c 147
be29ee33
AJ
148 // otherwise need more data OR corrupt syntax
149 break;
350ec67a 150 }
774c051c 151
be29ee33
AJ
152 if (!tok.atEnd())
153 buf_ = tok.remaining(); // parse checkpoint (unless there might be more token name)
774c051c 154 }
155
be29ee33
AJ
156 if (skipLineTerminator(tok)) {
157 buf_ = tok.remaining(); // checkpoint
158 // non-0 chunk means data, 0-size means optional Trailer follows
159 parsingStage_ = theChunkSize ? Http1::HTTP_PARSE_CHUNK : Http1::HTTP_PARSE_MIME;
160 return true;
774c051c 161 }
162
163 return false;
164}
165
be29ee33 166bool
cb4c8047 167Http::One::TeChunkedParser::parseChunkBody(Http1::Tokenizer &tok)
83c51da9 168{
8103fa66
AR
169 if (theLeftBodySize > 0) {
170 buf_ = tok.remaining(); // sync buffers before buf_ use
5c550f5f 171
8103fa66
AR
172 // TODO fix type mismatches and casting for these
173 const size_t availSize = min(theLeftBodySize, (uint64_t)buf_.length());
174 const size_t safeSize = min(availSize, (size_t)theOut->potentialSpaceSize());
5c550f5f 175
8103fa66
AR
176 theOut->append(buf_.rawContent(), safeSize);
177 buf_.consume(safeSize);
178 theLeftBodySize -= safeSize;
5c550f5f 179
8103fa66
AR
180 tok.reset(buf_); // sync buffers after consume()
181 }
7ddcfbab 182
be29ee33
AJ
183 if (theLeftBodySize == 0)
184 return parseChunkEnd(tok);
185 else
186 Must(needsMoreData() || needsMoreSpace());
7ddcfbab 187
be29ee33
AJ
188 return true;
189}
7ddcfbab 190
be29ee33 191bool
cb4c8047 192Http::One::TeChunkedParser::parseChunkEnd(Http1::Tokenizer &tok)
be29ee33
AJ
193{
194 Must(theLeftBodySize == 0); // Should(), really
83c51da9 195
be29ee33
AJ
196 if (skipLineTerminator(tok)) {
197 buf_ = tok.remaining(); // parse checkpoint
198 theChunkSize = 0; // done with the current chunk
199 parsingStage_ = Http1::HTTP_PARSE_CHUNK_SZ;
200 return true;
83c51da9 201 }
5c550f5f 202
be29ee33 203 return false;
83c51da9 204}
f53969cc 205