TeChunkedParser.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
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
9#include "squid.h"
10#include "base/TextException.h"
11#include "debug/Stream.h"
13#include "http/one/Tokenizer.h"
15#include "MemBuf.h"
16#include "parser/Tokenizer.h"
17#include "Parsing.h"
18#include "sbuf/Stream.h"
19#include "SquidConfig.h"
20
22 customExtensionValueParser(nullptr)
23{
24 // chunked encoding only exists in HTTP/1.1
26
27 clear();
28}
29
30void
32{
33 parsingStage_ = Http1::HTTP_PARSE_NONE;
34 buf_.clear();
35 theChunkSize = theLeftBodySize = 0;
36 theOut = nullptr;
37 // XXX: We do not reset customExtensionValueParser here. Based on the
38 // clear() API description, we must, but it makes little sense and could
39 // break method callers if they appear because some of them may forget to
40 // reset customExtensionValueParser. TODO: Remove Http1::Parser as our
41 // parent class and this unnecessary method with it.
42}
43
44bool
46{
47 buf_ = aBuf; // sync buffers first so calls to remaining() work properly if nothing done.
48
49 if (buf_.isEmpty()) // nothing to do (yet)
50 return false;
51
52 debugs(74, DBG_DATA, "Parse buf={length=" << aBuf.length() << ", data='" << aBuf << "'}");
53
54 Must(!buf_.isEmpty() && theOut);
55
56 if (parsingStage_ == Http1::HTTP_PARSE_NONE)
57 parsingStage_ = Http1::HTTP_PARSE_CHUNK_SZ;
58
59 Tokenizer tok(buf_);
60
61 // loop for as many chunks as we can
62 // use do-while instead of while so that we can incrementally
63 // restart in the middle of a chunk/frame
64 do {
65
66 if (parsingStage_ == Http1::HTTP_PARSE_CHUNK_EXT && !parseChunkMetadataSuffix(tok))
67 return false;
68
69 if (parsingStage_ == Http1::HTTP_PARSE_CHUNK && !parseChunkBody(tok))
70 return false;
71
72 if (parsingStage_ == Http1::HTTP_PARSE_MIME && !grabMimeBlock("Trailers", 64*1024 /* 64KB max */))
73 return false;
74
75 // loop for as many chunks as we can
76 } while (parsingStage_ == Http1::HTTP_PARSE_CHUNK_SZ && parseChunkSize(tok));
77
78 return !needsMoreData() && !needsMoreSpace();
79}
80
81bool
83{
84 assert(theOut);
85 return parsingStage_ == Http1::HTTP_PARSE_CHUNK && !theOut->hasPotentialSpace();
86}
87
89bool
91{
92 Must(theChunkSize <= 0); // Should(), really
93
94 int64_t size = -1;
95 if (tok.int64(size, 16, false) && !tok.atEnd()) {
96 if (size < 0)
97 throw TexcHere("negative chunk size");
98
99 theChunkSize = theLeftBodySize = size;
100 debugs(94,7, "found chunk: " << theChunkSize);
101 buf_ = tok.remaining(); // parse checkpoint
102 parsingStage_ = Http1::HTTP_PARSE_CHUNK_EXT;
103 return true;
104
105 } else if (tok.atEnd()) {
106 return false; // need more data
107 }
108
109 // else error
110 throw TexcHere("corrupted chunk size");
111 return false; // should not be reachable
112}
113
117bool
119{
120 // Code becomes much simpler when incremental parsing functions throw on
121 // bad or insufficient input, like in the code below. TODO: Expand up.
122 try {
123 parseChunkExtensions(tok); // a possibly empty chunk-ext list
124 skipLineTerminator(tok);
125 buf_ = tok.remaining();
126 parsingStage_ = theChunkSize ? Http1::HTTP_PARSE_CHUNK : Http1::HTTP_PARSE_MIME;
127 return true;
128 } catch (const InsufficientInput &) {
129 tok.reset(buf_); // backtrack to the last commit point
130 return false;
131 }
132 // other exceptions bubble up to kill message parsing
133}
134
137void
139{
140 do {
141 ParseBws(tok); // Bug 4492: IBM_HTTP_Server sends SP after chunk-size
142
143 if (!tok.skip(';'))
144 return; // reached the end of extensions (if any)
145
146 parseOneChunkExtension(tok);
147 buf_ = tok.remaining(); // got one extension
148 } while (true);
149}
150
151void
153{
154 const auto ignoredValue = tokenOrQuotedString(tok);
155 debugs(94, 5, extName << " with value " << ignoredValue);
156}
157
160void
162{
163 ParseBws(tok); // Bug 4492: ICAP servers send SP before chunk-ext-name
164
165 const auto extName = tok.prefix("chunk-ext-name", CharacterSet::TCHAR);
166
167 ParseBws(tok);
168
169 if (!tok.skip('='))
170 return; // parsed a valueless chunk-ext
171
172 ParseBws(tok);
173
174 // optimization: the only currently supported extension needs last-chunk
175 if (!theChunkSize && customExtensionValueParser)
176 customExtensionValueParser->parse(tok, extName);
177 else
179}
180
181bool
183{
184 if (theLeftBodySize > 0) {
185 buf_ = tok.remaining(); // sync buffers before buf_ use
186
187 // TODO fix type mismatches and casting for these
188 const size_t availSize = min(theLeftBodySize, (uint64_t)buf_.length());
189 const size_t safeSize = min(availSize, (size_t)theOut->potentialSpaceSize());
190
191 theOut->append(buf_.rawContent(), safeSize);
192 buf_.consume(safeSize);
193 theLeftBodySize -= safeSize;
194
195 tok.reset(buf_); // sync buffers after consume()
196 }
197
198 if (theLeftBodySize == 0)
199 return parseChunkEnd(tok);
200 else
201 Must(needsMoreData() || needsMoreSpace());
202
203 return true;
204}
205
206bool
208{
209 Must(theLeftBodySize == 0); // Should(), really
210
211 try {
212 skipLineTerminator(tok);
213 buf_ = tok.remaining(); // parse checkpoint
214 theChunkSize = 0; // done with the current chunk
215 parsingStage_ = Http1::HTTP_PARSE_CHUNK_SZ;
216 return true;
217 }
218 catch (const InsufficientInput &) {
219 return false;
220 }
221 // other exceptions bubble up to kill message parsing
222}
223
int size
Definition: ModDevPoll.cc:75
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:59
#define Must(condition)
Definition: TextException.h:71
#define assert(EX)
Definition: assert.h:19
static const CharacterSet TCHAR
Definition: CharacterSet.h:105
static void Ignore(Tokenizer &tok, const SBuf &extName)
extracts and ignores the value of a named extension
AnyP::ProtocolVersion msgProtocol_
what protocol label has been found in the first line (if any)
Definition: Parser.h:154
::Parser::Tokenizer Tokenizer
Definition: Parser.h:44
bool parseChunkBody(Tokenizer &tok)
void parseChunkExtensions(Tokenizer &)
bool parseChunkSize(Tokenizer &tok)
RFC 7230 section 4.1 chunk-size.
void parseOneChunkExtension(Tokenizer &)
bool parseChunkEnd(Tokenizer &tok)
bool parseChunkMetadataSuffix(Tokenizer &)
virtual bool parse(const SBuf &)
Definition: SBuf.h:94
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:415
A const & min(A const &lhs, A const &rhs)
#define DBG_DATA
Definition: Stream.h:43
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
void ParseBws(Parser::Tokenizer &)
Definition: Parser.cc:282
@ HTTP_PARSE_CHUNK_EXT
HTTP/1.1 chunked encoding chunk-ext.
Definition: Parser.h:26
@ HTTP_PARSE_CHUNK_SZ
HTTP/1.1 chunked encoding chunk-size.
Definition: Parser.h:25
@ HTTP_PARSE_MIME
HTTP/1 mime-header block.
Definition: Parser.h:28
@ HTTP_PARSE_NONE
initialized, but nothing usefully parsed yet
Definition: Parser.h:23
@ HTTP_PARSE_CHUNK
HTTP/1.1 chunked encoding chunk-data.
Definition: Parser.h:27
SBuf tokenOrQuotedString(Parser::Tokenizer &tok, const bool http1p0=false)
Definition: Tokenizer.cc:89
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
Definition: parse.c:160

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors