]> 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/*
ef57eb7b 2 * Copyright (C) 1996-2016 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);
9a4b5048
AJ
94
95#if USE_HTTP_VIOLATIONS
2c4e5226 96 // Bug 4492: IBM_HTTP_Server pads out to 4 bytes with SP characters
9a4b5048
AJ
97 if (Config.onoff.relaxed_header_parser && tok.skipAll(CharacterSet::SP)) {
98 debugs(94, violationLevel(), "WARNING: skipped invalid whitespace in chunk size");
99 }
100#endif
101
be29ee33 102 buf_ = tok.remaining(); // parse checkpoint
350ec67a
AJ
103 parsingStage_ = Http1::HTTP_PARSE_CHUNK_EXT;
104 return true;
350ec67a 105
be29ee33
AJ
106 } else if (tok.atEnd()) {
107 return false; // need more data
774c051c 108 }
109
be29ee33
AJ
110 // else error
111 throw TexcHere("corrupted chunk size");
112 return false; // should not be reachable
774c051c 113}
114
350ec67a 115/**
be29ee33
AJ
116 * Parses a set of RFC 7230 section 4.1.1 chunk-ext
117 * http://tools.ietf.org/html/rfc7230#section-4.1.1
350ec67a 118 *
be29ee33
AJ
119 * chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
120 * chunk-ext-name = token
121 * chunk-ext-val = token / quoted-string
350ec67a 122 *
be29ee33 123 * ICAP 'use-original-body=N' extension is supported.
350ec67a
AJ
124 */
125bool
cb4c8047 126Http::One::TeChunkedParser::parseChunkExtension(Http1::Tokenizer &tok, bool skipKnown)
774c051c 127{
be29ee33 128 SBuf ext;
cb4c8047 129 SBuf value;
be29ee33 130 while (tok.skip(';') && tok.prefix(ext, CharacterSet::TCHAR)) {
350ec67a 131
be29ee33
AJ
132 // whole value part is optional. if no '=' expect next chunk-ext
133 if (tok.skip('=')) {
774c051c 134
be29ee33
AJ
135 if (!skipKnown) {
136 if (ext.cmp("use-original-body",17) == 0 && tok.int64(useOriginBody, 10)) {
137 debugs(94, 3, "Found chunk extension " << ext << "=" << useOriginBody);
138 buf_ = tok.remaining(); // parse checkpoint
139 continue;
140 }
141 }
774c051c 142
be29ee33 143 debugs(94, 5, "skipping unknown chunk extension " << ext);
774c051c 144
cb4c8047
AJ
145 // unknown might have a value token or quoted-string
146 if (tok.quotedStringOrToken(value) && !tok.atEnd()) {
be29ee33
AJ
147 buf_ = tok.remaining(); // parse checkpoint
148 continue;
149 }
774c051c 150
be29ee33
AJ
151 // otherwise need more data OR corrupt syntax
152 break;
350ec67a 153 }
774c051c 154
be29ee33
AJ
155 if (!tok.atEnd())
156 buf_ = tok.remaining(); // parse checkpoint (unless there might be more token name)
774c051c 157 }
158
be29ee33
AJ
159 if (tok.atEnd())
160 return false;
774c051c 161
be29ee33
AJ
162 if (skipLineTerminator(tok)) {
163 buf_ = tok.remaining(); // checkpoint
164 // non-0 chunk means data, 0-size means optional Trailer follows
165 parsingStage_ = theChunkSize ? Http1::HTTP_PARSE_CHUNK : Http1::HTTP_PARSE_MIME;
166 return true;
774c051c 167 }
168
be29ee33 169 throw TexcHere("corrupted chunk extension value");
774c051c 170 return false;
171}
172
be29ee33 173bool
cb4c8047 174Http::One::TeChunkedParser::parseChunkBody(Http1::Tokenizer &tok)
83c51da9 175{
8103fa66
AR
176 if (theLeftBodySize > 0) {
177 buf_ = tok.remaining(); // sync buffers before buf_ use
5c550f5f 178
8103fa66
AR
179 // TODO fix type mismatches and casting for these
180 const size_t availSize = min(theLeftBodySize, (uint64_t)buf_.length());
181 const size_t safeSize = min(availSize, (size_t)theOut->potentialSpaceSize());
5c550f5f 182
8103fa66
AR
183 theOut->append(buf_.rawContent(), safeSize);
184 buf_.consume(safeSize);
185 theLeftBodySize -= safeSize;
5c550f5f 186
8103fa66
AR
187 tok.reset(buf_); // sync buffers after consume()
188 }
7ddcfbab 189
be29ee33
AJ
190 if (theLeftBodySize == 0)
191 return parseChunkEnd(tok);
192 else
193 Must(needsMoreData() || needsMoreSpace());
7ddcfbab 194
be29ee33
AJ
195 return true;
196}
7ddcfbab 197
be29ee33 198bool
cb4c8047 199Http::One::TeChunkedParser::parseChunkEnd(Http1::Tokenizer &tok)
be29ee33
AJ
200{
201 Must(theLeftBodySize == 0); // Should(), really
83c51da9 202
be29ee33
AJ
203 if (skipLineTerminator(tok)) {
204 buf_ = tok.remaining(); // parse checkpoint
205 theChunkSize = 0; // done with the current chunk
206 parsingStage_ = Http1::HTTP_PARSE_CHUNK_SZ;
207 return true;
83c51da9 208
be29ee33
AJ
209 } else if (!tok.atEnd()) {
210 throw TexcHere("found data between chunk end and CRLF");
83c51da9 211 }
5c550f5f 212
be29ee33 213 return false;
83c51da9 214}
f53969cc 215