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