From: Amos Jeffries Date: Sun, 29 Mar 2015 14:11:36 +0000 (-0700) Subject: Inherit ChunkedCodingParser from Http1::Parser X-Git-Tag: merge-candidate-3-v1~86^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=350ec67a9773b0b55fe0ce8e53fdaa86b28ad069;p=thirdparty%2Fsquid.git Inherit ChunkedCodingParser from Http1::Parser * add temporary dummy parse(SBuf) method * re-write parse sequence using ParseState stages instead of Step method pointers * replace needsMoreData() with Http1::Parser method --- diff --git a/src/Makefile.am b/src/Makefile.am index a209ed4fd6..69b52450f6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1405,7 +1405,6 @@ tests_testCacheManager_SOURCES = \ carp.h \ tests/stub_carp.cc \ cbdata.cc \ - ChunkedCodingParser.cc \ client_db.h \ client_db.cc \ client_side.h \ @@ -1837,7 +1836,6 @@ tests_testEvent_SOURCES = \ carp.h \ tests/stub_carp.cc \ cbdata.cc \ - ChunkedCodingParser.cc \ client_db.h \ client_db.cc \ client_side.h \ @@ -2084,7 +2082,6 @@ tests_testEventLoop_SOURCES = \ carp.h \ tests/stub_carp.cc \ cbdata.cc \ - ChunkedCodingParser.cc \ client_db.h \ client_db.cc \ client_side.h \ @@ -2329,7 +2326,6 @@ tests_test_http_range_SOURCES = \ carp.h \ tests/stub_carp.cc \ cbdata.cc \ - ChunkedCodingParser.cc \ client_db.h \ client_db.cc \ client_side.h \ @@ -2636,7 +2632,6 @@ tests_testHttpRequest_SOURCES = \ carp.h \ tests/stub_carp.cc \ cbdata.cc \ - ChunkedCodingParser.cc \ client_db.h \ client_db.cc \ client_side.h \ @@ -3446,7 +3441,6 @@ tests_testURL_SOURCES = \ carp.h \ tests/stub_carp.cc \ cbdata.cc \ - ChunkedCodingParser.cc \ client_db.h \ client_db.cc \ client_side.h \ diff --git a/src/http/one/ChunkedCodingParser.cc b/src/http/one/ChunkedCodingParser.cc index aabbf28134..60c0d1c394 100644 --- a/src/http/one/ChunkedCodingParser.cc +++ b/src/http/one/ChunkedCodingParser.cc @@ -10,28 +10,23 @@ #include "base/TextException.h" #include "Debug.h" #include "http/one/ChunkedCodingParser.h" +#include "http/ProtocolVersion.h" #include "MemBuf.h" #include "Parsing.h" -Http::One::ChunkedCodingParser::Step Http::One::ChunkedCodingParser::psChunkSize = &Http::One::ChunkedCodingParser::parseChunkSize; -Http::One::ChunkedCodingParser::Step Http::One::ChunkedCodingParser::psUnusedChunkExtension = &Http::One::ChunkedCodingParser::parseUnusedChunkExtension; -Http::One::ChunkedCodingParser::Step Http::One::ChunkedCodingParser::psLastChunkExtension = &Http::One::ChunkedCodingParser::parseLastChunkExtension; -Http::One::ChunkedCodingParser::Step Http::One::ChunkedCodingParser::psChunkBody = &Http::One::ChunkedCodingParser::parseChunkBody; -Http::One::ChunkedCodingParser::Step Http::One::ChunkedCodingParser::psChunkEnd = &Http::One::ChunkedCodingParser::parseChunkEnd; -Http::One::ChunkedCodingParser::Step Http::One::ChunkedCodingParser::psTrailer = &Http::One::ChunkedCodingParser::parseTrailer; -Http::One::ChunkedCodingParser::Step Http::One::ChunkedCodingParser::psMessageEnd = &Http::One::ChunkedCodingParser::parseMessageEnd; - Http::One::ChunkedCodingParser::ChunkedCodingParser() { - reset(); + // chunked encoding only exists in HTTP/1.1 + Http1::Parser::msgProtocol_ = Http::ProtocolVersion(1,1); + + clear(); } void -Http::One::ChunkedCodingParser::reset() +Http::One::ChunkedCodingParser::clear() { - theStep = psChunkSize; + parsingStage_ = Http1::HTTP_PARSE_NONE; theChunkSize = theLeftBodySize = 0; - doNeedMoreData = false; theIn = theOut = NULL; useOriginBody = -1; inQuoted = inSlashed = false; @@ -44,37 +39,41 @@ Http::One::ChunkedCodingParser::parse(MemBuf *rawData, MemBuf *parsedContent) theIn = rawData; theOut = parsedContent; - // we must reset this all the time so that mayContinue() lets us - // output more content if we stopped due to needsMoreSpace() before - doNeedMoreData = !theIn->hasContent(); + if (parsingStage_ == Http1::HTTP_PARSE_NONE) + parsingStage_ = Http1::HTTP_PARSE_CHUNK_SZ; - while (mayContinue()) { - (this->*theStep)(); - } + // loop for as many chunks as we can + // use do-while instead of while so that we can incrementally + // restart in the middle of a chunk/frame + do { - return theStep == psMessageEnd; -} + if (parsingStage_ == Http1::HTTP_PARSE_CHUNK_EXT) { + if (theChunkSize) + parseUnusedChunkExtension(); + else + parseLastChunkExtension(); + } -bool -Http::One::ChunkedCodingParser::needsMoreData() const -{ - return doNeedMoreData; + if (parsingStage_ == Http1::HTTP_PARSE_CHUNK && !parseChunkBody()) + return false; + + if (parsingStage_ == Http1::HTTP_PARSE_MIME && !parseTrailer()) + return false; + + // loop for as many chunks as we can + } while (parsingStage_ == Http1::HTTP_PARSE_CHUNK_SZ && parseChunkSize()); + + return !needsMoreData() && !needsMoreSpace(); } bool Http::One::ChunkedCodingParser::needsMoreSpace() const { assert(theOut); - return theStep == psChunkBody && !theOut->hasPotentialSpace(); + return parsingStage_ == Http1::HTTP_PARSE_CHUNK && !theOut->hasPotentialSpace(); } bool -Http::One::ChunkedCodingParser::mayContinue() const -{ - return !needsMoreData() && !needsMoreSpace() && theStep != psMessageEnd; -} - -void Http::One::ChunkedCodingParser::parseChunkSize() { Must(theChunkSize <= 0); // Should(), really @@ -82,8 +81,7 @@ Http::One::ChunkedCodingParser::parseChunkSize() const char *p = theIn->content(); while (p < theIn->space() && xisxdigit(*p)) ++p; if (p >= theIn->space()) { - doNeedMoreData = true; - return; + return false; } int64_t size = -1; @@ -93,17 +91,17 @@ Http::One::ChunkedCodingParser::parseChunkSize() 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; - } + theIn->consume(p - theIn->content()); + parsingStage_ = Http1::HTTP_PARSE_CHUNK_EXT; + return true; } else throw TexcHere("corrupted chunk size"); + + return false; } +/// Skips a set of RFC 7230 section 4.1.1 chunk-ext +/// http://tools.ietf.org/html/rfc7230#section-4.1.1 void Http::One::ChunkedCodingParser::parseUnusedChunkExtension() { @@ -112,14 +110,14 @@ Http::One::ChunkedCodingParser::parseUnusedChunkExtension() if (findCrlf(crlfBeg, crlfEnd, inQuoted, inSlashed)) { inQuoted = inSlashed = false; theIn->consume(crlfEnd); - theStep = theChunkSize ? psChunkBody : psTrailer; + // non-0 chunk means data, 0-size means optional Trailer follows + parsingStage_ = theChunkSize ? Http1::HTTP_PARSE_CHUNK : Http1::HTTP_PARSE_MIME; } else { theIn->consume(theIn->contentSize()); - doNeedMoreData = true; } } -void +bool Http::One::ChunkedCodingParser::parseChunkBody() { Must(theLeftBodySize > 0); // Should, really @@ -127,20 +125,19 @@ Http::One::ChunkedCodingParser::parseChunkBody() const size_t availSize = min(theLeftBodySize, (uint64_t)theIn->contentSize()); const size_t safeSize = min(availSize, (size_t)theOut->potentialSpaceSize()); - doNeedMoreData = availSize < theLeftBodySize; - // and we may also need more space - theOut->append(theIn->content(), safeSize); theIn->consume(safeSize); theLeftBodySize -= safeSize; if (theLeftBodySize == 0) - theStep = psChunkEnd; + return parseChunkEnd(); else Must(needsMoreData() || needsMoreSpace()); + + return true; } -void +bool Http::One::ChunkedCodingParser::parseChunkEnd() { Must(theLeftBodySize == 0); // Should(), really @@ -151,28 +148,41 @@ Http::One::ChunkedCodingParser::parseChunkEnd() if (findCrlf(crlfBeg, crlfEnd)) { if (crlfBeg != 0) { throw TexcHere("found data between chunk end and CRLF"); - return; + return false; } theIn->consume(crlfEnd); theChunkSize = 0; // done with the current chunk - theStep = psChunkSize; - return; + parsingStage_ = Http1::HTTP_PARSE_CHUNK_SZ; + return true; } - doNeedMoreData = true; + return false; } -void +/** + * Parse Trailer RFC 7230 section 4.1.2 trailer-part + * http://tools.ietf.org/html/rfc7230#section-4.1.2 + * + * trailer-part = *( header-field CRLF ) + * + * Currently Trailer headers are ignored/skipped + */ +bool Http::One::ChunkedCodingParser::parseTrailer() { Must(theChunkSize == 0); // Should(), really - while (mayContinue()) - parseTrailerHeader(); + while (parsingStage_ == Http1::HTTP_PARSE_MIME) { + if (!parseTrailerHeader()) + return false; + } + + return true; } -void +/// skip one RFC 7230 header-field from theIn buffer +bool Http::One::ChunkedCodingParser::parseTrailerHeader() { size_t crlfBeg = 0; @@ -187,20 +197,14 @@ Http::One::ChunkedCodingParser::parseTrailerHeader() theIn->consume(crlfEnd); - if (crlfBeg == 0) - theStep = psMessageEnd; + if (crlfBeg == 0) { + parsingStage_ = Http1::HTTP_PARSE_DONE; + } - return; + return true; } - doNeedMoreData = true; -} - -void -Http::One::ChunkedCodingParser::parseMessageEnd() -{ - // termination step, should not be called - Must(false); // Should(), really + return false; } /// Finds next CRLF. Does not store parsing state. @@ -276,17 +280,24 @@ Http::One::ChunkedCodingParser::findCrlf(size_t &crlfBeg, size_t &crlfEnd, bool return false; } -// chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) +/** + * Parses a set of RFC 7230 section 4.1.1 chunk-ext + * http://tools.ietf.org/html/rfc7230#section-4.1.1 + * + * chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) + * chunk-ext-name = token + * chunk-ext-val = token / quoted-string + * + * ICAP 'use-original-body=N' extension is supported. + */ void Http::One::ChunkedCodingParser::parseLastChunkExtension() { size_t crlfBeg = 0; size_t crlfEnd = 0; - if (!findCrlf(crlfBeg, crlfEnd)) { - doNeedMoreData = true; + if (!findCrlf(crlfBeg, crlfEnd)) return; - } const char *const startExt = theIn->content(); const char *const endExt = theIn->content() + crlfBeg; @@ -321,6 +332,7 @@ Http::One::ChunkedCodingParser::parseLastChunkExtension() } theIn->consume(crlfEnd); - theStep = theChunkSize ? psChunkBody : psTrailer; + + parsingStage_ = theChunkSize ? Http1::HTTP_PARSE_CHUNK : Http1::HTTP_PARSE_MIME; } diff --git a/src/http/one/ChunkedCodingParser.h b/src/http/one/ChunkedCodingParser.h index f7e9753eed..820eac75ae 100644 --- a/src/http/one/ChunkedCodingParser.h +++ b/src/http/one/ChunkedCodingParser.h @@ -9,7 +9,7 @@ #ifndef SQUID_SRC_HTTP_ONE_CHUNKEDCODINGPARSER_H #define SQUID_SRC_HTTP_ONE_CHUNKEDCODINGPARSER_H -#include "http/one/forward.h" +#include "http/one/Parser.h" class MemBuf; @@ -28,13 +28,11 @@ namespace One * Ignores chunk extensions except for ICAP's ieof. * Has a trailer-handling placeholder. */ -class ChunkedCodingParser +class ChunkedCodingParser : public Http1::Parser { - public: ChunkedCodingParser(); - - void reset(); + virtual ~ChunkedCodingParser() {} /** \retval true complete success @@ -43,44 +41,32 @@ public: */ bool parse(MemBuf *rawData, MemBuf *parsedContent); - bool needsMoreData() const; bool needsMoreSpace() const; -private: - typedef void (Http1::ChunkedCodingParser::*Step)(); + /* Http1::Parser API */ + virtual void clear(); + virtual bool parse(const SBuf &) {return false;} // XXX implement + virtual size_type firstLineSize() const {return 0;} // has no meaning with multiple chunks private: - bool mayContinue() const; - - void parseChunkSize(); + bool parseChunkSize(); void parseUnusedChunkExtension(); void parseLastChunkExtension(); void parseChunkBeg(); - void parseChunkBody(); - void parseChunkEnd(); - void parseTrailer(); - void parseTrailerHeader(); - void parseMessageEnd(); + bool parseChunkBody(); + bool parseChunkEnd(); + bool parseTrailer(); + bool parseTrailerHeader(); bool findCrlf(size_t &crlfBeg, size_t &crlfEnd); bool findCrlf(size_t &crlfBeg, size_t &crlfEnd, bool "ed, bool &slashed); private: - static Step psChunkSize; - static Step psUnusedChunkExtension; - static Step psLastChunkExtension; - static Step psChunkBody; - static Step psChunkEnd; - static Step psTrailer; - static Step psMessageEnd; - MemBuf *theIn; MemBuf *theOut; - Step theStep; uint64_t theChunkSize; uint64_t theLeftBodySize; - bool doNeedMoreData; bool inQuoted; ///< stores parsing state for incremental findCrlf bool inSlashed; ///< stores parsing state for incremental findCrlf diff --git a/src/http/one/Parser.h b/src/http/one/Parser.h index 42ddb52201..05fbfb7c72 100644 --- a/src/http/one/Parser.h +++ b/src/http/one/Parser.h @@ -23,10 +23,13 @@ namespace One { // Parser states enum ParseState { - HTTP_PARSE_NONE, ///< initialized, but nothing usefully parsed yet - HTTP_PARSE_FIRST, ///< HTTP/1 message first-line - HTTP_PARSE_MIME, ///< HTTP/1 mime-header block - HTTP_PARSE_DONE ///< parsed a message header, or reached a terminal syntax error + HTTP_PARSE_NONE, ///< initialized, but nothing usefully parsed yet + HTTP_PARSE_FIRST, ///< HTTP/1 message first-line + HTTP_PARSE_CHUNK_SZ, ///< HTTP/1.1 chunked encoding chunk-size + HTTP_PARSE_CHUNK_EXT, ///< HTTP/1.1 chunked encoding chunk-ext + HTTP_PARSE_CHUNK, ///< HTTP/1.1 chunked encoding chunk-data + HTTP_PARSE_MIME, ///< HTTP/1 mime-header block + HTTP_PARSE_DONE ///< parsed a message header, or reached a terminal syntax error }; /** HTTP/1.x protocol parser