#include "squid.h"
-#include "Parsing.h"
-#include "TextException.h"
+#include "base/TextException.h"
#include "ChunkedCodingParser.h"
+#include "Debug.h"
#include "MemBuf.h"
+#include "Parsing.h"
-ChunkedCodingParser::Step ChunkedCodingParser::psChunkBeg = &ChunkedCodingParser::parseChunkBeg;
+ChunkedCodingParser::Step ChunkedCodingParser::psChunkSize = &ChunkedCodingParser::parseChunkSize;
+ChunkedCodingParser::Step ChunkedCodingParser::psUnusedChunkExtension = &ChunkedCodingParser::parseUnusedChunkExtension;
+ChunkedCodingParser::Step ChunkedCodingParser::psLastChunkExtension = &ChunkedCodingParser::parseLastChunkExtension;
ChunkedCodingParser::Step ChunkedCodingParser::psChunkBody = &ChunkedCodingParser::parseChunkBody;
ChunkedCodingParser::Step ChunkedCodingParser::psChunkEnd = &ChunkedCodingParser::parseChunkEnd;
ChunkedCodingParser::Step ChunkedCodingParser::psTrailer = &ChunkedCodingParser::parseTrailer;
void ChunkedCodingParser::reset()
{
- theStep = psChunkBeg;
+ theStep = psChunkSize;
theChunkSize = theLeftBodySize = 0;
doNeedMoreData = false;
theIn = theOut = NULL;
+ useOriginBody = -1;
+ inQuoted = inSlashed = false;
}
bool ChunkedCodingParser::parse(MemBuf *rawData, MemBuf *parsedContent)
return !needsMoreData() && !needsMoreSpace() && theStep != psMessageEnd;
}
-void ChunkedCodingParser::parseChunkBeg()
+void ChunkedCodingParser::parseChunkSize()
{
Must(theChunkSize <= 0); // Should(), really
- size_t crlfBeg = 0;
- size_t crlfEnd = 0;
-
- if (findCrlf(crlfBeg, crlfEnd)) {
- debugs(94,7, "found chunk-size end: " << crlfBeg << "-" << crlfEnd);
- int64_t size = -1;
- const char *p = 0;
-
- if (StringToInt64(theIn->content(), size, &p, 16)) {
- if (size < 0) {
- throw TexcHere("negative chunk size");
- return;
- }
+ const char *p = theIn->content();
+ while (p < theIn->space() && xisxdigit(*p)) ++p;
+ if (p >= theIn->space()) {
+ doNeedMoreData = true;
+ return;
+ }
- theIn->consume(crlfEnd);
- theChunkSize = theLeftBodySize = size;
- debugs(94,7, "found chunk: " << theChunkSize);
- theStep = theChunkSize == 0 ? psTrailer : psChunkBody;
- return;
+ int64_t size = -1;
+ if (StringToInt64(theIn->content(), size, &p, 16)) {
+ if (size < 0)
+ throw TexcHere("negative chunk size");
+
+ theChunkSize = theLeftBodySize = size;
+ debugs(94,7, "found chunk: " << theChunkSize);
+ // parse chunk extensions only in the last-chunk
+ if (theChunkSize)
+ theStep = psUnusedChunkExtension;
+ else {
+ theIn->consume(p - theIn->content());
+ theStep = psLastChunkExtension;
}
-
+ } else
throw TexcHere("corrupted chunk size");
- }
+}
- doNeedMoreData = true;
+void ChunkedCodingParser::parseUnusedChunkExtension()
+{
+ size_t crlfBeg = 0;
+ size_t crlfEnd = 0;
+ if (findCrlf(crlfBeg, crlfEnd, inQuoted, inSlashed)) {
+ inQuoted = inSlashed = false;
+ theIn->consume(crlfEnd);
+ theStep = theChunkSize ? psChunkBody : psTrailer;
+ } else {
+ theIn->consume(theIn->contentSize());
+ doNeedMoreData = true;
+ }
}
void ChunkedCodingParser::parseChunkBody()
if (findCrlf(crlfBeg, crlfEnd)) {
if (crlfBeg != 0) {
- throw TexcHere("found data bewteen chunk end and CRLF");
+ throw TexcHere("found data between chunk end and CRLF");
return;
}
theIn->consume(crlfEnd);
theChunkSize = 0; // done with the current chunk
- theStep = psChunkBeg;
+ theStep = psChunkSize;
return;
}
size_t crlfEnd = 0;
if (findCrlf(crlfBeg, crlfEnd)) {
- if (crlfBeg > 0)
- ; //theTrailer.append(theIn->content(), crlfEnd);
+#if TRAILERS_ARE_SUPPORTED
+ if (crlfBeg > 0)
+ theTrailer.append(theIn->content(), crlfEnd);
+#endif
theIn->consume(crlfEnd);
Must(false); // Should(), really
}
-// finds next CRLF
+/// Finds next CRLF. Does not store parsing state.
bool ChunkedCodingParser::findCrlf(size_t &crlfBeg, size_t &crlfEnd)
+{
+ bool quoted = false;
+ bool slashed = false;
+ return findCrlf(crlfBeg, crlfEnd, quoted, slashed);
+}
+
+/// Finds next CRLF. Parsing state stored in quoted and slashed
+/// parameters. Incremental: can resume when more data is available.
+bool ChunkedCodingParser::findCrlf(size_t &crlfBeg, size_t &crlfEnd, bool "ed, bool &slashed)
{
// XXX: This code was copied, with permission, from another software.
// There is a similar and probably better code inside httpHeaderParse
size_t size = theIn->contentSize();
ssize_t crOff = -1;
- bool quoted = false;
- bool slashed = false;
for (size_t i = 0; i < size; ++i) {
if (slashed) {
if (quoted) {
if (c == '\\')
slashed = true;
- else
- if (c == '"')
- quoted = false;
+ else if (c == '"')
+ quoted = false;
continue;
- } else
- if (c == '"') {
- quoted = true;
- crOff = -1;
- continue;
- }
+ } else if (c == '"') {
+ quoted = true;
+ crOff = -1;
+ continue;
+ }
if (crOff < 0) { // looking for the first CR or LF
return false;
}
+// chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+void ChunkedCodingParser::parseLastChunkExtension()
+{
+ size_t crlfBeg = 0;
+ size_t crlfEnd = 0;
+
+ if (!findCrlf(crlfBeg, crlfEnd)) {
+ doNeedMoreData = true;
+ return;
+ }
+
+ const char *const startExt = theIn->content();
+ const char *const endExt = theIn->content() + crlfBeg;
+
+ // chunk-extension starts at startExt and ends with LF at endEx
+ for (const char *p = startExt; p < endExt;) {
+
+ while (*p == ' ' || *p == '\t') ++p; // skip spaces before ';'
+
+ if (*p++ != ';') // each ext name=value pair is preceded with ';'
+ break;
+
+ while (*p == ' ' || *p == '\t') ++p; // skip spaces before name
+
+ if (p >= endExt)
+ break; // malformed extension: ';' without ext name=value pair
+
+ const int extSize = endExt - p;
+ // TODO: we need debugData() stream manipulator to dump data
+ debugs(94,7, "Found chunk extension; size=" << extSize);
+
+ // TODO: support implied *LWS around '='
+ if (extSize > 18 && strncmp(p, "use-original-body=", 18) == 0) {
+ (void)StringToInt64(p+18, useOriginBody, &p, 10);
+ debugs(94, 3, HERE << "use-original-body=" << useOriginBody);
+ break; // remove to support more than just use-original-body
+ } else {
+ debugs(94, 5, HERE << "skipping unknown chunk extension");
+ // TODO: support quoted-string chunk-ext-val
+ while (p < endExt && *p != ';') ++p; // skip until the next ';'
+ }
+ }
+
+ theIn->consume(crlfEnd);
+ theStep = theChunkSize ? psChunkBody : psTrailer;
+}