TeChunkedParser.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2020 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.h"
13 #include "http/one/Tokenizer.h"
14 #include "http/ProtocolVersion.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 
30 void
32 {
33  parsingStage_ = Http1::HTTP_PARSE_NONE;
34  buf_.clear();
35  theChunkSize = theLeftBodySize = 0;
36  theOut = NULL;
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 
44 bool
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 
81 bool
83 {
84  assert(theOut);
85  return parsingStage_ == Http1::HTTP_PARSE_CHUNK && !theOut->hasPotentialSpace();
86 }
87 
89 bool
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 
117 bool
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 
137 void
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 
151 void
153 {
154  const auto ignoredValue = tokenOrQuotedString(tok);
155  debugs(94, 5, extName << " with value " << ignoredValue);
156 }
157 
160 void
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 
181 bool
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 
206 bool
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 
void parseOneChunkExtension(Tokenizer &)
@ HTTP_PARSE_MIME
HTTP/1 mime-header block.
Definition: Parser.h:28
Definition: SBuf.h:86
bool parseChunkMetadataSuffix(Tokenizer &)
@ HTTP_PARSE_CHUNK
HTTP/1.1 chunked encoding chunk-data.
Definition: Parser.h:27
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:55
void parseChunkExtensions(Tokenizer &)
int size
Definition: ModDevPoll.cc:77
#define NULL
Definition: types.h:166
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:128
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
@ HTTP_PARSE_NONE
initialized, but nothing usefully parsed yet
Definition: Parser.h:23
#define assert(EX)
Definition: assert.h:19
bool parseChunkEnd(Tokenizer &tok)
SBuf tokenOrQuotedString(Parser::Tokenizer &tok, const bool http1p0=false)
Definition: Tokenizer.cc:89
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:404
void ParseBws(Parser::Tokenizer &)
Definition: Parser.cc:282
virtual bool parse(const SBuf &)
bool parseChunkBody(Tokenizer &tok)
Definition: parse.c:160
::Parser::Tokenizer Tokenizer
Definition: Parser.h:44
@ HTTP_PARSE_CHUNK_SZ
HTTP/1.1 chunked encoding chunk-size.
Definition: Parser.h:25
AnyP::ProtocolVersion msgProtocol_
what protocol label has been found in the first line (if any)
Definition: Parser.h:154
@ HTTP_PARSE_CHUNK_EXT
HTTP/1.1 chunked encoding chunk-ext.
Definition: Parser.h:26
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:69
bool parseChunkSize(Tokenizer &tok)
RFC 7230 section 4.1 chunk-size.
const A & min(A const &lhs, A const &rhs)
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
#define DBG_DATA
Definition: Debug.h:48

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors