From: Amos Jeffries Date: Sun, 9 Nov 2014 15:02:19 +0000 (-0800) Subject: Add HTTP Response parser X-Git-Tag: merge-candidate-3-v1~240^2~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f1d5359e87a7fb5f7356865c851f09fc4d8b23ce;p=thirdparty%2Fsquid.git Add HTTP Response parser --- diff --git a/src/client_side.cc b/src/client_side.cc index 34a77c01ec..58a89895fb 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -2049,7 +2049,7 @@ prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, const Http1 #if SHOULD_REJECT_UNKNOWN_URLS // reject URI which are not well-formed even after the processing above if (url.isEmpty() || url[0] != '/') { - hp->request_parse_status = Http::scBadRequest; + hp->parseStatusCode = Http::scBadRequest; return conn->abortRequestParsing("error:invalid-request"); } #endif @@ -2163,7 +2163,7 @@ parseHttpRequest(ConnStateData *csd, const Http1::RequestParserPointer &hp) } if (!parsedOk) { - if (hp->request_parse_status == Http::scRequestHeaderFieldsTooLarge || hp->request_parse_status == Http::scUriTooLong) + if (hp->parseStatusCode == Http::scRequestHeaderFieldsTooLarge || hp->parseStatusCode == Http::scUriTooLong) return csd->abortRequestParsing("error:request-too-large"); return csd->abortRequestParsing("error:invalid-request"); @@ -2181,13 +2181,13 @@ parseHttpRequest(ConnStateData *csd, const Http1::RequestParserPointer &hp) if (hp->method() == Http::METHOD_CONNECT && csd->port != NULL && csd->port->flags.accelSurrogate) { debugs(33, DBG_IMPORTANT, "WARNING: CONNECT method received on " << csd->transferProtocol << " Accelerator port " << csd->port->s.port()); debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol()); - hp->request_parse_status = Http::scMethodNotAllowed; + hp->parseStatusCode = Http::scMethodNotAllowed; return csd->abortRequestParsing("error:method-not-allowed"); } if (hp->method() == Http::METHOD_NONE) { debugs(33, DBG_IMPORTANT, "WARNING: Unsupported method: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol()); - hp->request_parse_status = Http::scMethodNotAllowed; + hp->parseStatusCode = Http::scMethodNotAllowed; return csd->abortRequestParsing("error:unsupported-request-method"); } @@ -2494,7 +2494,7 @@ clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, // determine which error page templates to use for specific parsing errors err_type errPage = ERR_INVALID_REQ; - switch (hp->request_parse_status) { + switch (hp->parseStatusCode) { case Http::scRequestHeaderFieldsTooLarge: // fall through to next case case Http::scUriTooLong: @@ -2510,7 +2510,7 @@ clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, // use default ERR_INVALID_REQ set above. break; } - repContext->setReplyToError(errPage, hp->request_parse_status, hp->method(), http->uri, + repContext->setReplyToError(errPage, hp->parseStatusCode, hp->method(), http->uri, conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL); assert(context->http->out.offset == 0); context->pullData(); diff --git a/src/http/one/Makefile.am b/src/http/one/Makefile.am index 17ac59068f..7c11f5dea2 100644 --- a/src/http/one/Makefile.am +++ b/src/http/one/Makefile.am @@ -8,4 +8,6 @@ libhttp1_la_SOURCES = \ Parser.cc \ Parser.h \ RequestParser.cc \ - RequestParser.h + RequestParser.h \ + ResponseParser.cc \ + ResponseParser.h diff --git a/src/http/one/Parser.cc b/src/http/one/Parser.cc index 48585d4388..313cc6fd1c 100644 --- a/src/http/one/Parser.cc +++ b/src/http/one/Parser.cc @@ -9,6 +9,7 @@ #include "squid.h" #include "Debug.h" #include "http/one/Parser.h" +#include "mime_header.h" #include "parser/Tokenizer.h" /// RFC 7230 section 2.6 - 7 magic octets @@ -23,6 +24,44 @@ Http::One::Parser::clear() mimeHeaderBlock_.clear(); } +bool +Http::One::Parser::findMimeBlock(const char *which, size_t limit) +{ + if (msgProtocol_.major == 1) { + /* NOTE: HTTP/0.9 messages do not have a mime header block. + * So the rest of the code will need to deal with '0'-byte headers + * (ie, none, so don't try parsing em) + */ + int64_t mimeHeaderBytes = 0; + // XXX: c_str() reallocates. performance regression. + if ((mimeHeaderBytes = headersEnd(buf_.c_str(), buf_.length())) == 0) { + if (buf_.length()+firstLineSize() >= limit) { + debugs(33, 5, "Too large " << which); + parseStatusCode = Http::scHeaderTooLarge; + parsingStage_ = HTTP_PARSE_DONE; + } else + debugs(33, 5, "Incomplete " << which << ", waiting for end of headers"); + return false; + } + mimeHeaderBlock_ = buf_.consume(mimeHeaderBytes); + debugs(74, 5, "mime header (0-" << mimeHeaderBytes << ") {" << mimeHeaderBlock_ << "}"); + + } else + debugs(33, 3, "Missing HTTP/1.x identifier"); + + // NP: we do not do any further stages here yet so go straight to DONE + parsingStage_ = HTTP_PARSE_DONE; + + // Squid could handle these headers, but admin does not want to + if (messageHeaderSize() >= limit) { + debugs(33, 5, "Too large " << which); + parseStatusCode = Http::scHeaderTooLarge; + return false; + } + + return true; +} + // arbitrary maximum-length for headers which can be found by Http1Parser::getHeaderField() #define GET_HDR_SZ 1024 diff --git a/src/http/one/Parser.h b/src/http/one/Parser.h index 2c9c584b4e..1259792047 100644 --- a/src/http/one/Parser.h +++ b/src/http/one/Parser.h @@ -11,6 +11,7 @@ #include "anyp/ProtocolVersion.h" #include "http/one/forward.h" +#include "http/StatusCode.h" #include "SBuf.h" namespace Http { @@ -37,7 +38,7 @@ class Parser : public RefCountable public: typedef SBuf::size_type size_type; - Parser() : parsingStage_(HTTP_PARSE_NONE) {} + Parser() : parseStatusCode(Http::scNone), parsingStage_(HTTP_PARSE_NONE) {} virtual ~Parser() {} /// Set this parser back to a default state. @@ -74,7 +75,7 @@ public: const AnyP::ProtocolVersion & messageProtocol() const {return msgProtocol_;} /** - * Scan the mime header block (badly) for a header with teh given name. + * Scan the mime header block (badly) for a header with the given name. * * BUG: omits lines when searching for headers with obs-fold or multiple entries. * @@ -87,7 +88,20 @@ public: /// the remaining unprocessed section of buffer const SBuf &remaining() const {return buf_;} + /** + * HTTP status code resulting from the parse process. + * to be used on the invalid message handling. + * + * Http::scNone indicates incomplete parse, + * Http::scOkay indicates no error, + * other codes represent a parse error. + */ + Http::StatusCode parseStatusCode; + protected: + /// parse scan to find the mime headers block for current message + bool findMimeBlock(const char *which, size_t limit); + /// RFC 7230 section 2.6 - 7 magic octets static const SBuf Http1magic; diff --git a/src/http/one/RequestParser.cc b/src/http/one/RequestParser.cc index 64cd6616c5..b5ff770746 100644 --- a/src/http/one/RequestParser.cc +++ b/src/http/one/RequestParser.cc @@ -2,13 +2,11 @@ #include "Debug.h" #include "http/one/RequestParser.h" #include "http/ProtocolVersion.h" -#include "mime_header.h" #include "profiler/Profiler.h" #include "SquidConfig.h" Http::One::RequestParser::RequestParser() : - Parser(), - request_parse_status(Http::scNone) + Parser() { req.start = req.end = -1; req.m_start = req.m_end = -1; @@ -75,7 +73,7 @@ Http::One::RequestParser::skipGarbageLines() * begins parsing from scratch on every call. * The return value tells you whether the parsing state fields are valid or not. * - * \retval -1 an error occurred. request_parse_status indicates HTTP status result. + * \retval -1 an error occurred. parseStatusCode indicates HTTP status result. * \retval 1 successful parse. member fields contain the request-line items * \retval 0 more data is needed to complete the parse */ @@ -141,7 +139,7 @@ Http::One::RequestParser::parseRequestFirstLine() // However it does explicitly state an exact syntax which omits un-encoded CR // and defines 400 (Bad Request) as the required action when // handed an invalid request-line. - request_parse_status = Http::scBadRequest; + parseStatusCode = Http::scBadRequest; return -1; } } @@ -151,7 +149,7 @@ Http::One::RequestParser::parseRequestFirstLine() if ((size_t)buf_.length() >= Config.maxRequestHeaderSize) { debugs(33, 5, "Too large request-line"); // RFC 7230 section 3.1.1 mandatory 414 response if URL longer than acceptible. - request_parse_status = Http::scUriTooLong; + parseStatusCode = Http::scUriTooLong; return -1; } @@ -168,7 +166,7 @@ Http::One::RequestParser::parseRequestFirstLine() // DoS protection against long first-line if ((size_t)(req.end-req.start) >= Config.maxRequestHeaderSize) { debugs(33, 5, "Too large request-line"); - request_parse_status = Http::scUriTooLong; + parseStatusCode = Http::scUriTooLong; return -1; } @@ -177,19 +175,19 @@ Http::One::RequestParser::parseRequestFirstLine() // First non-whitespace = beginning of method if (req.start > line_end) { - request_parse_status = Http::scBadRequest; + parseStatusCode = Http::scBadRequest; return -1; } req.m_start = req.start; // First whitespace = end of method if (first_whitespace > line_end || first_whitespace < req.start) { - request_parse_status = Http::scBadRequest; // no method + parseStatusCode = Http::scBadRequest; // no method return -1; } req.m_end = first_whitespace - 1; if (req.m_end < req.m_start) { - request_parse_status = Http::scBadRequest; // missing URI? + parseStatusCode = Http::scBadRequest; // missing URI? return -1; } @@ -199,7 +197,7 @@ Http::One::RequestParser::parseRequestFirstLine() // First non-whitespace after first SP = beginning of URL+Version if (second_word > line_end || second_word < req.start) { - request_parse_status = Http::scBadRequest; // missing URI + parseStatusCode = Http::scBadRequest; // missing URI return -1; } req.u_start = second_word; @@ -210,7 +208,7 @@ Http::One::RequestParser::parseRequestFirstLine() msgProtocol_ = Http::ProtocolVersion(0,9); req.u_end = line_end; uri_ = buf_.substr(req.u_start, req.u_end - req.u_start + 1); - request_parse_status = Http::scOkay; // HTTP/0.9 + parseStatusCode = Http::scOkay; // HTTP/0.9 return 1; } else { // otherwise last whitespace is somewhere after end of URI. @@ -219,14 +217,14 @@ Http::One::RequestParser::parseRequestFirstLine() for (; req.u_end >= req.u_start && xisspace(buf_[req.u_end]); --req.u_end); } if (req.u_end < req.u_start) { - request_parse_status = Http::scBadRequest; // missing URI + parseStatusCode = Http::scBadRequest; // missing URI return -1; } uri_ = buf_.substr(req.u_start, req.u_end - req.u_start + 1); // Last whitespace SP = before start of protocol/version if (last_whitespace >= line_end) { - request_parse_status = Http::scBadRequest; // missing version + parseStatusCode = Http::scBadRequest; // missing version return -1; } req.v_start = last_whitespace + 1; @@ -235,7 +233,7 @@ Http::One::RequestParser::parseRequestFirstLine() /* RFC 7230 section 2.6 : handle unsupported HTTP major versions cleanly. */ if ((req.v_end - req.v_start +1) < (int)Http1magic.length() || !buf_.substr(req.v_start, SBuf::npos).startsWith(Http1magic)) { // non-HTTP/1 protocols not supported / implemented. - request_parse_status = Http::scHttpVersionNotSupported; + parseStatusCode = Http::scHttpVersionNotSupported; return -1; } // NP: magic octets include the protocol name and major version DIGIT. @@ -246,12 +244,12 @@ Http::One::RequestParser::parseRequestFirstLine() // catch missing minor part if (++i > line_end) { - request_parse_status = Http::scHttpVersionNotSupported; + parseStatusCode = Http::scHttpVersionNotSupported; return -1; } /* next should be one or more digits */ if (!isdigit(buf_[i])) { - request_parse_status = Http::scHttpVersionNotSupported; + parseStatusCode = Http::scHttpVersionNotSupported; return -1; } int min = 0; @@ -261,7 +259,7 @@ Http::One::RequestParser::parseRequestFirstLine() } // catch too-big values or trailing garbage if (min >= 65536 || i < line_end) { - request_parse_status = Http::scHttpVersionNotSupported; + parseStatusCode = Http::scHttpVersionNotSupported; return -1; } msgProtocol_.minor = min; @@ -269,7 +267,7 @@ Http::One::RequestParser::parseRequestFirstLine() /* * Rightio - we have all the schtuff. Return true; we've got enough. */ - request_parse_status = Http::scOkay; + parseStatusCode = Http::scOkay; return 1; } @@ -319,35 +317,9 @@ Http::One::RequestParser::parse(const SBuf &aBuf) // stage 3: locate the mime header block if (parsingStage_ == HTTP_PARSE_MIME) { // HTTP/1.x request-line is valid and parsing completed. - if (msgProtocol_.major == 1) { - /* NOTE: HTTP/0.9 requests do not have a mime header block. - * So the rest of the code will need to deal with '0'-byte headers - * (ie, none, so don't try parsing em) - */ - int64_t mimeHeaderBytes = 0; - // XXX: c_str() reallocates. performance regression. - if ((mimeHeaderBytes = headersEnd(buf_.c_str(), buf_.length())) == 0) { - if (buf_.length()+firstLineSize() >= Config.maxRequestHeaderSize) { - debugs(33, 5, "Too large request"); - request_parse_status = Http::scRequestHeaderFieldsTooLarge; - parsingStage_ = HTTP_PARSE_DONE; - } else - debugs(33, 5, "Incomplete request, waiting for end of headers"); - return false; - } - mimeHeaderBlock_ = buf_.consume(mimeHeaderBytes); - debugs(74, 5, "mime header (0-" << mimeHeaderBytes << ") {" << mimeHeaderBlock_ << "}"); - - } else - debugs(33, 3, "Missing HTTP/1.x identifier"); - - // NP: we do not do any further stages here yet so go straight to DONE - parsingStage_ = HTTP_PARSE_DONE; - - // Squid could handle these headers, but admin does not want to - if (messageHeaderSize() >= Config.maxRequestHeaderSize) { - debugs(33, 5, "Too large request"); - request_parse_status = Http::scRequestHeaderFieldsTooLarge; + if (!findMimeBlock("Request", Config.maxRequestHeaderSize)) { + if (parseStatusCode == Http::scHeaderTooLarge) + parseStatusCode = Http::scRequestHeaderFieldsTooLarge; return false; } } diff --git a/src/http/one/RequestParser.h b/src/http/one/RequestParser.h index c72b54dc89..9cad922fdf 100644 --- a/src/http/one/RequestParser.h +++ b/src/http/one/RequestParser.h @@ -3,7 +3,6 @@ #include "http/one/Parser.h" #include "http/RequestMethod.h" -#include "http/StatusCode.h" namespace Http { namespace One { @@ -33,12 +32,6 @@ public: /// the request-line URI if this is a request message, or an empty string. const SBuf &requestUri() const {return uri_;} - /** HTTP status code to be used on the invalid-request error page. - * Http::scNone indicates incomplete parse, - * Http::scOkay indicates no error. - */ - Http::StatusCode request_parse_status; - private: void skipGarbageLines(); int parseRequestFirstLine(); diff --git a/src/http/one/ResponseParser.cc b/src/http/one/ResponseParser.cc new file mode 100644 index 0000000000..f1138c379d --- /dev/null +++ b/src/http/one/ResponseParser.cc @@ -0,0 +1,211 @@ +#include "squid.h" +#include "Debug.h" +#include "http/one/ResponseParser.h" +#include "http/ProtocolVersion.h" +#include "parser/Tokenizer.h" +#include "profiler/Profiler.h" +#include "SquidConfig.h" + +const SBuf Http::One::ResponseParser::IcyMagic("ICY "); + +Http1::Parser::size_type +Http::One::ResponseParser::firstLineSize() const +{ + Http1::Parser::size_type result = 0; + + switch (msgProtocol_.protocol) + { + case AnyP::PROTO_HTTP: + result += Http1magic.length(); + break; + case AnyP::PROTO_ICY: + result += IcyMagic.length(); + break; + default: // no other protocols supported + return result; + } + // NP: the parser does not accept >2 DIGIT for version numbers + if (msgProtocol_.minor >10) + result += 2; + else + result += 1; + + result += 5; /* 5 octets in: SP status SP */ + result += reasonPhrase_.length(); + return result; +} + +// NP: we found the protocol version and consumed it already. +// just need the status code and reason phrase +const int +Http::One::ResponseParser::parseResponseStatusAndReason() +{ + if (buf_.isEmpty()) + return 0; + + ::Parser::Tokenizer tok(buf_); + + if (!completedStatus_) { + SBuf status; + // status code is 3 DIGIT octets + if(!tok.prefix(status, CharacterSet::DIGIT, 3)) + return -1; // invalid status + // NOTE: multiple SP or non-SP bytes between version and status code are invalid. + if (tok.atEnd()) + return 0; // need more to be sure we have it all + if(!tok.skip(' ')) + return -1; // invalid status, a single SP terminator required + // NOTE: any whitespace after the single SP is part of the reason phrase. + + // get the actual numeric value of the 0-3 digits we found + ::Parser::Tokenizer t2(status); + int64_t statusValue; + if (!t2.int64(statusValue)) + return -1; // ouch. digits not forming a valid number? + if (statusValue < 0 || statusValue > 999) + return -1; // ouch. digits not within valid status code range. + + statusCode_ = static_cast(statusValue); + + buf_ = tok.remaining(); // resume checkpoint + completedStatus_ = true; + } + + if (tok.atEnd()) + return 0; // need more to be sure we have it all + + /* RFC 7230 says we SHOULD ignore the reason phrase content + * but it has a definite valid vs invalid character set. + * We interpret the SHOULD as ignoring absence and syntax, but + * producing an error if it contains an invalid octet. + */ + + // if we got here we are still looking for reason-phrase bytes + static const CharacterSet phraseChars = CharacterSet::WSP + CharacterSet::VCHAR + CharacterSet::OBSTEXT; + tok.prefix(reasonPhrase_, phraseChars); // optional, no error if missing + tok.skip('\r'); // optional trailing CR + + if (tok.atEnd()) + return 0; // need more to be sure we have it all + + // LF existence matters + if (!tok.skip('\n')) { + reasonPhrase_.clear(); + return -1; // found invalid characters in the phrase + } + + buf_ = tok.remaining(); // resume checkpoint + return 1; +} + +const int +Http::One::ResponseParser::parseResponseFirstLine() +{ + ::Parser::Tokenizer tok(buf_); + + if (msgProtocol_.protocol != AnyP::PROTO_NONE) { + // we already found the magic, but not the full line. keep going. + return parseResponseStatusAndReason(); + + } else if (tok.skip(Http1magic)) { + // HTTP Response status-line parse + + // magic contains major version, still need to find minor + SBuf verMinor; + // NP: we limit to 2-digits for speed, there really is no limit + // XXX: the protocols we accept dont have valid versions > 10 anyway + if (!tok.prefix(verMinor, CharacterSet::DIGIT, 2)) + return -1; // invalid version minor code + if (tok.atEnd()) + return 0; // need more to be sure we have it all + if(!tok.skip(' ')) + return -1; // invalid version, a single SP terminator required + + // get the actual numeric value of the 0-3 digits we found + ::Parser::Tokenizer t2(verMinor); + int64_t tvm = 0; + if (!t2.int64(tvm)) + return -1; // ouch. digits not forming a valid number? + msgProtocol_.minor = static_cast(tvm); + + msgProtocol_.protocol = AnyP::PROTO_HTTP; + msgProtocol_.major = 1; + buf_ = tok.remaining(); // resume checkpoint + return parseResponseStatusAndReason(); + + } else if (tok.skip(IcyMagic)) { + // ICY Response status-line parse (same as HTTP/1 after the magic version) + msgProtocol_.protocol = AnyP::PROTO_ICY; + // NP: ICY has no /major.minor details + buf_ = tok.remaining(); // resume checkpoint + return parseResponseStatusAndReason(); + + } else if (buf_.length() > Http1magic.length() && buf_.length() > IcyMagic.length()) { + // found something that looks like an HTTP/0.9 response + msgProtocol_ = Http::ProtocolVersion(1,1); + // XXX: probably should use version 0.9 here and upgrade on output, + // but the old code did 1.1 transformation now. + statusCode_ = Http::scOkay; + static const SBuf gatewayPhrase("Gatewaying"); + reasonPhrase_ = gatewayPhrase; + static const SBuf fakeHttpMimeBlock("X-Transformed-From: HTTP/0.9\r\n" + /* Server: visible_appname_string */ + "Mime-Version: 1.0\r\n" + /* Date: squid_curtime */ + "Expires: -1\r\n\r\n"); + return 1; // no more parsing + } + + return 0; // need more to parse anything. +} + +bool +Http::One::ResponseParser::parse(const SBuf &aBuf) +{ + buf_ = aBuf; + debugs(74, DBG_DATA, "Parse buf={length=" << aBuf.length() << ", data='" << aBuf << "'}"); + + // stage 1: locate the status-line + if (parsingStage_ == HTTP_PARSE_NONE) { + // RFC 7230 explicitly states whether garbage whitespace is to be handled + // at each point of the message framing boundaries. + // It omits mentioning garbage prior to HTTP Responses. + // Therefore, if we receive anything at all treat it as Response message. + if (!buf_.isEmpty()) + parsingStage_ = HTTP_PARSE_FIRST; + else + return false; + } + + // stage 2: parse the status-line + if (parsingStage_ == HTTP_PARSE_FIRST) { + PROF_start(HttpParserParseReplyLine); + + int retcode = parseResponseFirstLine(); + + // first-line (or a look-alike) found successfully. + if (retcode > 0) + parsingStage_ = HTTP_PARSE_MIME; + debugs(74, 5, "status-line: retval " << retcode); + debugs(74, 5, "status-line: proto " << msgProtocol_); + debugs(74, 5, "status-line: status-code " << statusCode_); + debugs(74, 5, "status-line: reason-phrase " << reasonPhrase_); + debugs(74, 5, "Parser: bytes processed=" << (aBuf.length()-buf_.length())); + PROF_stop(HttpParserParseReplyLine); + + // syntax errors already + if (retcode < 0) { + parsingStage_ = HTTP_PARSE_DONE; + statusCode_ = scInvalidHeader; + return false; + } + } + + // stage 3: locate the mime header block + if (parsingStage_ == HTTP_PARSE_MIME) { + if (!findMimeBlock("Response", Config.maxReplyHeaderSize)) + return false; + } + + return !needsMoreData(); +} diff --git a/src/http/one/ResponseParser.h b/src/http/one/ResponseParser.h new file mode 100644 index 0000000000..d81f1d4189 --- /dev/null +++ b/src/http/one/ResponseParser.h @@ -0,0 +1,57 @@ +#ifndef _SQUID_SRC_HTTP_ONE_RESPONSEPARSER_H +#define _SQUID_SRC_HTTP_ONE_RESPONSEPARSER_H + +#include "http/one/Parser.h" +#include "http/StatusCode.h" + +namespace Http { +namespace One { + +/** HTTP/1.x protocol response parser + * + * Also capable of parsing unexpected ICY responses and + * upgrading HTTP/0.9 syntax responses to HTTP/1.1 + * + * Works on a raw character I/O buffer and tokenizes the content into + * the major CRLF delimited segments of an HTTP/1 respone message: + * + * \item status-line (version SP status SP reash-phrase) + * \item mime-header (set of RFC2616 syntax header fields) + */ +class ResponseParser : public Http1::Parser +{ +public: + ResponseParser() : Parser(), completedStatus_(false) {} + virtual ~ResponseParser() {} + + /* Http::One::Parser API */ + virtual void clear() {*this=ResponseParser();} + virtual Http1::Parser::size_type firstLineSize() const; + virtual bool parse(const SBuf &aBuf); + + /* respone specific fields, read-only */ + Http::StatusCode messageStatus() const { return statusCode_;} + SBuf reasonPhrase() const { return reasonPhrase_;} + +private: + const int parseResponseFirstLine(); + const int parseResponseStatusAndReason(); + + /// magic prefix for identifying ICY response messages + static const SBuf IcyMagic; + + /// Whether we found the status code yet. + /// We cannot rely on status value because server may send "000". + bool completedStatus_; + + /// HTTP/1 status-line status code + Http::StatusCode statusCode_; + + /// HTTP/1 status-line reason phrase + SBuf reasonPhrase_; +}; + +} // namespace One +} // namespace Http + +#endif /* _SQUID_SRC_HTTP_ONE_RESPONSEPARSER_H */ diff --git a/src/tests/testHttp1Parser.cc b/src/tests/testHttp1Parser.cc index 7bfff5ec03..1bf0096556 100644 --- a/src/tests/testHttp1Parser.cc +++ b/src/tests/testHttp1Parser.cc @@ -69,7 +69,7 @@ testResults(int line, const SBuf &input, Http1::RequestParser &output, struct re CPPUNIT_ASSERT_EQUAL(expect.needsMore, output.needsMoreData()); if (output.needsMoreData()) CPPUNIT_ASSERT_EQUAL(expect.parserState, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(expect.status, output.request_parse_status); + CPPUNIT_ASSERT_EQUAL(expect.status, output.parseStatusCode); CPPUNIT_ASSERT_EQUAL(expect.msgStart, output.req.start); CPPUNIT_ASSERT_EQUAL(expect.msgEnd, output.req.end); CPPUNIT_ASSERT_EQUAL(expect.suffixSz, output.buf_.length()); @@ -93,7 +93,7 @@ testHttp1Parser::testParserConstruct() Http1::RequestParser output; CPPUNIT_ASSERT_EQUAL(true, output.needsMoreData()); CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status); // XXX: clear() not being called. + CPPUNIT_ASSERT_EQUAL(Http::scNone, output.parseStatusCode); // XXX: clear() not being called. CPPUNIT_ASSERT_EQUAL(-1, output.req.start); CPPUNIT_ASSERT_EQUAL(-1, output.req.end); CPPUNIT_ASSERT(output.buf_.isEmpty()); @@ -113,7 +113,7 @@ testHttp1Parser::testParserConstruct() Http1::RequestParser *output = new Http1::RequestParser; CPPUNIT_ASSERT_EQUAL(true, output->needsMoreData()); CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output->parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scNone, output->request_parse_status); + CPPUNIT_ASSERT_EQUAL(Http::scNone, output->parseStatusCode); CPPUNIT_ASSERT_EQUAL(-1, output->req.start); CPPUNIT_ASSERT_EQUAL(-1, output->req.end); CPPUNIT_ASSERT(output->buf_.isEmpty());