From 7a4fa6a090dccd464b3cc0b360d2913e552a1668 Mon Sep 17 00:00:00 2001 From: Amos Jeffries Date: Thu, 15 May 2014 03:44:05 -0700 Subject: [PATCH] Fix outstanding build issues and parser audit results * Give SBuf I/O buffer directly to Http1::RequestParser * Redesign parser state engine to represent the current state being parsed instead of previous completed. This allows much more incremental resume of a parse and reliable consume() of the input buffer as sections complete instead of complex byte accounting outide the parser. * Maintain an internal counter of bytes parsed and consumed by the parser instead of a buffer offset. This allows much more reliable positioning of the state/section boundaries. * Remove erroneous fprintf debug left in previous commit. * Redesign HttpRequestMethod constructor to drop end parameter. * Redesign all parser unit tests. Marking RFC non-compliance for future fixing. --- src/HttpRequest.cc | 5 +- src/acl/MethodData.cc | 2 +- src/client_side.cc | 14 +- src/htcp.cc | 2 +- src/http/Http1Parser.cc | 99 +- src/http/Http1Parser.h | 42 +- src/http/RequestMethod.cc | 50 +- src/http/RequestMethod.h | 10 +- src/mgr/ActionParams.cc | 2 +- src/tests/testHttp1Parser.cc | 2043 +++++++++++++++------------- src/tests/testHttpRequestMethod.cc | 28 +- 11 files changed, 1225 insertions(+), 1072 deletions(-) diff --git a/src/HttpRequest.cc b/src/HttpRequest.cc index 5d187e9ea0..1997b25319 100644 --- a/src/HttpRequest.cc +++ b/src/HttpRequest.cc @@ -304,7 +304,7 @@ HttpRequest::sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, Http::Statu } /* See if the request buffer starts with a known HTTP request method. */ - if (HttpRequestMethod(buf->content(),NULL) == Http::METHOD_NONE) { + if (HttpRequestMethod(buf->content()) == Http::METHOD_NONE) { debugs(73, 3, "HttpRequest::sanityCheckStartLine: did not find HTTP request method"); *error = Http::scInvalidHeader; return false; @@ -317,7 +317,8 @@ bool HttpRequest::parseFirstLine(const char *start, const char *end) { const char *t = start + strcspn(start, w_space); - method = HttpRequestMethod(start, t); + SBuf m(start, start-t); + method = HttpRequestMethod(m); if (method == Http::METHOD_NONE) return false; diff --git a/src/acl/MethodData.cc b/src/acl/MethodData.cc index 495e89c822..f31ffb4113 100644 --- a/src/acl/MethodData.cc +++ b/src/acl/MethodData.cc @@ -91,7 +91,7 @@ ACLMethodData::parse() while ((t = strtokFile())) { if (strcmp(t, "PURGE") == 0) ++ThePurgeCount; // configuration code wants to know - CbDataList *q = new CbDataList (HttpRequestMethod(t, NULL)); + CbDataList *q = new CbDataList (HttpRequestMethod(t)); *(Tail) = q; Tail = &q->next; } diff --git a/src/client_side.cc b/src/client_side.cc index a4de94b376..5fc5d9c68b 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -2198,6 +2198,11 @@ parseHttpRequest(ConnStateData *csd, Http1::RequestParser &hp) { const bool parsedOk = hp.parse(); + if (hp.doneBytes()) { + // we are done with some of the buffer. update the ConnStateData copy now. + csd->in.buf = hp.buf; + } + if (!hp.isDone()) { debugs(33, 5, "Incomplete request, waiting for end of request line"); return NULL; @@ -2827,17 +2832,12 @@ ConnStateData::clientParseRequests() // a) dont have one already // b) have completed the previous request parsing already if (!parser_ || parser_->isDone()) - parser_ = new Http1::RequestParser(in.buf.c_str(), in.buf.length()); + parser_ = new Http1::RequestParser(in.buf); else // update the buffer space being parsed - parser_->bufsiz = in.buf.length(); + parser_->buf = in.buf; /* Process request */ ClientSocketContext *context = parseHttpRequest(this, *parser_); - if (parser_->doneBytes()) { - // we are done with some of the buffer. consume it now. - connNoteUseOfBuffer(this, parser_->doneBytes()); - parser_->noteBufferShift(parser_->doneBytes()); - } PROF_stop(parseHttpRequest); /* status -1 or 1 */ diff --git a/src/htcp.cc b/src/htcp.cc index 2e7778fd2b..350fe9b6c1 100644 --- a/src/htcp.cc +++ b/src/htcp.cc @@ -748,7 +748,7 @@ htcpUnpackSpecifier(char *buf, int sz) /* * Parse the request */ - method = HttpRequestMethod(s->method, NULL); + method = HttpRequestMethod(s->method); s->request = HttpRequest::CreateFromUrlAndMethod(s->uri, method == Http::METHOD_NONE ? HttpRequestMethod(Http::METHOD_GET) : method); diff --git a/src/http/Http1Parser.cc b/src/http/Http1Parser.cc index d89423775c..ad1523b027 100644 --- a/src/http/Http1Parser.cc +++ b/src/http/Http1Parser.cc @@ -11,8 +11,7 @@ Http::One::Parser::clear() { parsingStage_ = HTTP_PARSE_NONE; buf = NULL; - bufsiz = 0; - parseOffset_ = 0; + parsedCount_ = 0; msgProtocol_ = AnyP::ProtocolVersion(); mimeHeaderBlock_.clear(); } @@ -31,28 +30,13 @@ Http::One::RequestParser::clear() } void -Http::One::Parser::reset(const char *aBuf, int len) +Http::One::Parser::reset(const SBuf &aBuf) { clear(); // empty the state. parsingStage_ = HTTP_PARSE_NEW; - parseOffset_ = 0; + parsedCount_ = 0; buf = aBuf; - bufsiz = len; - debugs(74, DBG_DATA, "Parse " << Raw("buf", buf, bufsiz)); -} - -void -Http::One::RequestParser::noteBufferShift(int64_t n) -{ - // if parsing done, ignore buffer changes. - if (parsingStage_ == HTTP_PARSE_DONE) - return; - - // shift the parser resume point to match buffer content change - parseOffset_ -= n; - - // and remember where to stop before performing buffered data overreads - bufsiz -= n; + debugs(74, DBG_DATA, "Parse buf={length=" << aBuf.length() << ", data='" << aBuf << "'}"); } /** @@ -78,13 +62,16 @@ Http::One::RequestParser::skipGarbageLines() { #if WHEN_RFC_COMPLIANT // CRLF or bare-LF is what RFC 2616 tolerant parsers do ... if (Config.onoff.relaxed_header_parser) { - if (Config.onoff.relaxed_header_parser < 0 && (buf[parseOffset_] == '\r' || buf[parseOffset_] == '\n')) + if (Config.onoff.relaxed_header_parser < 0 && (buf[0] == '\r' || buf[0] == '\n')) debugs(74, DBG_IMPORTANT, "WARNING: Invalid HTTP Request: " << "CRLF bytes received ahead of request-line. " << "Ignored due to relaxed_header_parser."); // Be tolerant of prefix empty lines // ie any series of either \n or \r\n with no other characters and no repeated \r - for (; parseOffset_ < (size_t)bufsiz && (buf[parseOffset_] == '\n' || ((buf[parseOffset_] == '\r' && (buf[parseOffset_+1] == '\n')); ++parseOffset_); + while (!buf.isEmpty() && (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))) { + buf.consume(1); + ++parsedCount_; + } } #endif @@ -96,12 +83,15 @@ Http::One::RequestParser::skipGarbageLines() */ #if USE_HTTP_VIOLATIONS if (Config.onoff.relaxed_header_parser) { - if (Config.onoff.relaxed_header_parser < 0 && buf[parseOffset_] == ' ') + if (Config.onoff.relaxed_header_parser < 0 && buf[0] == ' ') debugs(74, DBG_IMPORTANT, "WARNING: Invalid HTTP Request: " << "Whitespace bytes received ahead of method. " << "Ignored due to relaxed_header_parser."); // Be tolerant of prefix spaces (other bytes are valid method values) - for (; parseOffset_ < (size_t)bufsiz && buf[parseOffset_] == ' '; ++parseOffset_); + while (!buf.isEmpty() && buf[0] == ' ') { + buf.consume(1); + ++parsedCount_; + } } #endif } @@ -128,14 +118,14 @@ Http::One::RequestParser::parseRequestFirstLine() int first_whitespace = -1, last_whitespace = -1; // track the first and last SP byte int line_end = -1; // tracks the last byte BEFORE terminal \r\n or \n sequence - debugs(74, 5, "parsing possible request: bufsiz=" << bufsiz << ", offset=" << parseOffset_); - debugs(74, DBG_DATA, Raw("(buf+offset)", buf+parseOffset_, bufsiz-parseOffset_)); + debugs(74, 5, "parsing possible request: buf.length=" << buf.length() << ", offset=" << parsedCount_); + debugs(74, DBG_DATA, buf); // Single-pass parse: (provided we have the whole line anyways) - req.start = parseOffset_; // avoid re-parsing any portion we managed to complete + req.start = 0; req.end = -1; - for (int i = 0; i < bufsiz; ++i) { + for (SBuf::size_type i = 0; i < buf.length(); ++i) { // track first and last whitespace (SP only) if (buf[i] == ' ') { last_whitespace = i; @@ -154,7 +144,7 @@ Http::One::RequestParser::parseRequestFirstLine() line_end = i - 1; break; } - if (i < bufsiz - 1 && buf[i] == '\r') { + if (i < buf.length() - 1 && buf[i] == '\r') { if (Config.onoff.relaxed_header_parser) { if (Config.onoff.relaxed_header_parser < 0 && buf[i + 1] == '\r') debugs(74, DBG_IMPORTANT, "WARNING: Invalid HTTP Request: " << @@ -164,7 +154,7 @@ Http::One::RequestParser::parseRequestFirstLine() // Be tolerant of invalid multiple \r prior to terminal \n if (buf[i + 1] == '\n' || buf[i + 1] == '\r') line_end = i - 1; - while (i < bufsiz - 1 && buf[i + 1] == '\r') + while (i < buf.length() - 1 && buf[i + 1] == '\r') ++i; if (buf[i + 1] == '\n') { @@ -188,7 +178,7 @@ Http::One::RequestParser::parseRequestFirstLine() if (req.end == -1) { // DoS protection against long first-line - if ( (size_t)bufsiz >= Config.maxRequestHeaderSize) { + if ((size_t)buf.length() >= Config.maxRequestHeaderSize) { debugs(33, 5, "Too large request-line"); // XXX: return URL-too-log status code if second_whitespace is not yet found. request_parse_status = Http::scHeaderTooLarge; @@ -234,7 +224,8 @@ Http::One::RequestParser::parseRequestFirstLine() } /* Set method_ */ - method_ = HttpRequestMethod(&buf[req.m_start], &buf[req.m_end]+1); + SBuf tmp = buf.substr(req.m_start, req.m_end - req.m_start + 1); + method_ = HttpRequestMethod(tmp); // First non-whitespace after first SP = beginning of URL+Version if (second_word > line_end || second_word < req.start) { @@ -248,6 +239,7 @@ Http::One::RequestParser::parseRequestFirstLine() if (last_whitespace < second_word && last_whitespace >= req.start) { 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 return 1; } else { @@ -260,7 +252,7 @@ Http::One::RequestParser::parseRequestFirstLine() request_parse_status = Http::scBadRequest; // missing URI return -1; } - uri_.assign(&buf[req.u_start], req.u_end - req.u_start + 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) { @@ -272,7 +264,7 @@ Http::One::RequestParser::parseRequestFirstLine() // We only accept HTTP protocol requests right now. // TODO: accept other protocols; RFC 2326 (RTSP protocol) etc - if ((req.v_end - req.v_start +1) < 5 || strncasecmp(&buf[req.v_start], "HTTP/", 5) != 0) { + if ((req.v_end - req.v_start +1) < 5 || buf.substr(req.v_start, 5).caseCmp(SBuf("HTTP/")) != 0) { #if USE_HTTP_VIOLATIONS // being lax; old parser accepted strange versions // there is a LOT of cases which are ambiguous, therefore we cannot use relaxed_header_parser here. @@ -347,18 +339,16 @@ Http::One::RequestParser::parseRequestFirstLine() request_parse_status = Http::scOkay; return 1; } -#include + bool Http::One::RequestParser::parse() { // stage 1: locate the request-line if (parsingStage_ == HTTP_PARSE_NEW) { -fprintf(stderr, "parse GARBAGE: '%s'\n", buf); skipGarbageLines(); -fprintf(stderr, "parse GBG A(%d) < B(%u)\n", bufsiz, parseOffset_); // if we hit something before EOS treat it as a message - if ((size_t)bufsiz > parseOffset_) + if (!buf.isEmpty()) parsingStage_ = HTTP_PARSE_FIRST; else return false; @@ -366,35 +356,32 @@ fprintf(stderr, "parse GBG A(%d) < B(%u)\n", bufsiz, parseOffset_); // stage 2: parse the request-line if (parsingStage_ == HTTP_PARSE_FIRST) { -fprintf(stderr, "parse FIRST: '%s'\n", buf); PROF_start(HttpParserParseReqLine); const int retcode = parseRequestFirstLine(); - debugs(74, 5, "request-line: retval " << retcode << ": from " << req.start << "->" << req.end << " " << Raw("line", &buf[req.start], req.end-req.start)); + debugs(74, 5, "request-line: retval " << retcode << ": from " << req.start << "->" << req.end << + " line={" << buf.length() << ", data='" << buf << "'}"); debugs(74, 5, "request-line: method " << req.m_start << "->" << req.m_end << " (" << method_ << ")"); debugs(74, 5, "request-line: url " << req.u_start << "->" << req.u_end << " (" << uri_ << ")"); debugs(74, 5, "request-line: proto " << req.v_start << "->" << req.v_end << " (" << msgProtocol_ << ")"); - debugs(74, 5, "Parser: parse-offset=" << parseOffset_); + debugs(74, 5, "Parser: bytes processed=" << parsedCount_); PROF_stop(HttpParserParseReqLine); // syntax errors already if (retcode < 0) { parsingStage_ = HTTP_PARSE_DONE; -fprintf(stderr, "parse FIRST DONE (error)\n"); return false; } // first-line (or a look-alike) found successfully. if (retcode > 0) { - parseOffset_ += firstLineSize(); // first line bytes including CRLF terminator are now done. + buf.consume(firstLineSize());// first line bytes including CRLF terminator are now done. + parsedCount_ += firstLineSize(); parsingStage_ = HTTP_PARSE_MIME; -fprintf(stderr, "parse FIRST (next: MIME)\n"); } -else fprintf(stderr, "parse FIRST: ret=%d\n",retcode); } // stage 3: locate the mime header block if (parsingStage_ == HTTP_PARSE_MIME) { -fprintf(stderr, "parse MIME: '%s'\n", buf); // 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. @@ -402,24 +389,22 @@ fprintf(stderr, "parse MIME: '%s'\n", buf); * (ie, none, so don't try parsing em) */ int64_t mimeHeaderBytes = 0; - if ((mimeHeaderBytes = headersEnd(buf+parseOffset_, bufsiz-parseOffset_)) == 0) { - if (bufsiz-parseOffset_ >= Config.maxRequestHeaderSize) { + 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::scHeaderTooLarge; parsingStage_ = HTTP_PARSE_DONE; -fprintf(stderr, "parse DONE: HTTP/1.x\n"); - } else { + } else debugs(33, 5, "Incomplete request, waiting for end of headers"); -fprintf(stderr, "parse MIME incomplete\n"); -} return false; + return false; } - mimeHeaderBlock_.assign(&buf[req.end+1], mimeHeaderBytes); - parseOffset_ += mimeHeaderBytes; // done with these bytes now. + mimeHeaderBlock_ = buf.substr(req.end+1, mimeHeaderBytes); + buf.consume(mimeHeaderBytes); // done with these bytes now. + parsedCount_ += mimeHeaderBytes; - } else { + } else debugs(33, 3, "Missing HTTP/1.x identifier"); -fprintf(stderr, "parse MIME: HTTP/0.9\n"); -} + // NP: we do not do any further stages here yet so go straight to DONE parsingStage_ = HTTP_PARSE_DONE; diff --git a/src/http/Http1Parser.h b/src/http/Http1Parser.h index 22140df02e..a863ad7588 100644 --- a/src/http/Http1Parser.h +++ b/src/http/Http1Parser.h @@ -13,11 +13,11 @@ namespace One { // Parser states enum ParseState { - HTTP_PARSE_NONE =0, ///< nothing. completely unset state. - HTTP_PARSE_NEW =1, ///< initialized, but nothing usefully parsed yet + HTTP_PARSE_NONE, ///< nothing. completely unset state. + HTTP_PARSE_NEW, ///< initialized, but nothing usefully parsed yet HTTP_PARSE_FIRST, ///< HTTP/1 message first line - HTTP_PARSE_MIME, ///< mime header block - HTTP_PARSE_DONE ///< completed with parsing a full request header + HTTP_PARSE_MIME, ///< HTTP/1 mime header block + HTTP_PARSE_DONE ///< parsed a message header, or reached a terminal syntax error }; /** HTTP protocol parser. @@ -38,21 +38,14 @@ public: * NOTE: This is *not* the buffer size, just the parse-able data length. * The parse routines may be called again later with more data. */ - Parser(const char *aBuf, int len) { reset(aBuf,len); } + Parser(const SBuf &aBuf) { reset(aBuf); } /// Set this parser back to a default state. /// Will DROP any reference to a buffer (does not free). virtual void clear(); /// Reset the parser for use on a new buffer. - void reset(const char *aBuf, int len); - - /** Adjust parser state to account for a buffer shift of n bytes. - * - * The leftmost n bytes bytes have been dropped and all other - * bytes shifted left n positions. - */ - virtual void noteBufferShift(const int64_t n) = 0; + void reset(const SBuf &aBuf); /** Whether the parser is already done processing the buffer. * Use to determine between incomplete data and errors results @@ -61,7 +54,7 @@ public: bool isDone() const {return parsingStage_==HTTP_PARSE_DONE;} /// number of bytes at the start of the buffer which are no longer needed - int64_t doneBytes() const {return (int64_t)parseOffset_;} + int64_t doneBytes() const {return (int64_t)parsedCount_;} /// size in bytes of the first line including CRLF terminator virtual int64_t firstLineSize() const = 0; @@ -92,20 +85,19 @@ public: char *getHeaderField(const char *name); public: - const char *buf; - int bufsiz; + SBuf buf; protected: /// what stage the parser is currently up to ParseState parsingStage_; - /// what protocol label has been found in the first line - AnyP::ProtocolVersion msgProtocol_; + /// total count of bytes parsed and consumed by the parser so far + size_t parsedCount_; - /// byte offset for non-parsed region of the buffer - size_t parseOffset_; + /// what protocol label has been found in the first line (if any) + AnyP::ProtocolVersion msgProtocol_; - /// buffer holding the mime headers + /// buffer holding the mime headers (if any) SBuf mimeHeaderBlock_; }; @@ -122,9 +114,8 @@ class RequestParser : public Http1::Parser public: /* Http::One::Parser API */ RequestParser() : Parser() {} - RequestParser(const char *aBuf, int len) : Parser(aBuf, len) {} + RequestParser(const SBuf &aBuf) : Parser(aBuf) {} virtual void clear(); - virtual void noteBufferShift(const int64_t n); virtual int64_t firstLineSize() const {return req.end - req.start + 1;} virtual bool parse(); @@ -134,8 +125,9 @@ 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 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; diff --git a/src/http/RequestMethod.cc b/src/http/RequestMethod.cc index f1f93b1b99..6eb1048aaa 100644 --- a/src/http/RequestMethod.cc +++ b/src/http/RequestMethod.cc @@ -16,22 +16,17 @@ operator++ (Http::MethodType &aMethod) } /** - * Construct a HttpRequestMethod from a NULL terminated string such as "GET" - * or from a range of chars, * such as "GET" from "GETFOOBARBAZ" - * (pass in pointer to G and pointer to F.) + * Construct a HttpRequestMethod from a C-string such as "GET" + * Assumes the string is either nul-terminated or contains whitespace + * + * \deprecated use SBuf constructor instead */ -HttpRequestMethod::HttpRequestMethod(char const *begin, char const *end) : theMethod(Http::METHOD_NONE) +HttpRequestMethod::HttpRequestMethod(char const *begin) : theMethod(Http::METHOD_NONE) { if (begin == NULL) return; - /* - * if e is NULL, b must be NULL terminated and we - * make e point to the first whitespace character - * after b. - */ - if (NULL == end) - end = begin + strcspn(begin, w_space); + char const *end = begin + strcspn(begin, w_space); if (end == begin) return; @@ -56,6 +51,39 @@ HttpRequestMethod::HttpRequestMethod(char const *begin, char const *end) : theMe theImage.assign(begin, end-begin); } +/** + * Construct a HttpRequestMethod from an SBuf string such as "GET" + * or from a range of chars such as "GET" from "GETFOOBARBAZ" + * + * Assumes the s parameter contains only the method string + */ +HttpRequestMethod::HttpRequestMethod(const SBuf &s) : theMethod(Http::METHOD_NONE) +{ + if (s.isEmpty()) + return; + + // XXX: still check for missing method name? + + // TODO: Optimize this linear search. + for (++theMethod; theMethod < Http::METHOD_ENUM_END; ++theMethod) { + // RFC 2616 section 5.1.1 - Method names are case-sensitive + // NP: this is not a HTTP_VIOLATIONS case since there is no MUST/SHOULD involved. + if (0 == image().caseCmp(s)) { + + // relaxed parser allows mixed-case and corrects them on output + if (Config.onoff.relaxed_header_parser) + return; + + if (0 == image().cmp(s)) + return; + } + } + + // if method not found and method string is not null then it is other method + theMethod = Http::METHOD_OTHER; + theImage = s; +} + const SBuf & HttpRequestMethod::image() const { diff --git a/src/http/RequestMethod.h b/src/http/RequestMethod.h index 14c075daeb..49bd8c3eac 100644 --- a/src/http/RequestMethod.h +++ b/src/http/RequestMethod.h @@ -20,14 +20,8 @@ class HttpRequestMethod : public RefCountable public: HttpRequestMethod() : theMethod(Http::METHOD_NONE), theImage() {} HttpRequestMethod(Http::MethodType const aMethod) : theMethod(aMethod), theImage() {} - - /** - \param begin string to convert to request method. - \param end end of the method string (relative to begin). Use NULL if this is unknown. - * - \note DO NOT give end a default (ie NULL). That will cause silent char* conversion clashes. - */ - HttpRequestMethod(char const * begin, char const * end); + explicit HttpRequestMethod(char const *); + explicit HttpRequestMethod(const SBuf &); HttpRequestMethod & operator = (const HttpRequestMethod& aMethod) { theMethod = aMethod.theMethod; diff --git a/src/mgr/ActionParams.cc b/src/mgr/ActionParams.cc index 2b7705e615..d1bd91fe21 100644 --- a/src/mgr/ActionParams.cc +++ b/src/mgr/ActionParams.cc @@ -18,7 +18,7 @@ Mgr::ActionParams::ActionParams(const Ipc::TypedMsgHdr &msg) String method; msg.getString(method); - httpMethod = HttpRequestMethod(method.termedBuf(), NULL); + httpMethod = HttpRequestMethod(method.termedBuf()); msg.getPod(httpFlags); msg.getString(httpOrigin); diff --git a/src/tests/testHttp1Parser.cc b/src/tests/testHttp1Parser.cc index fee350b5ad..92dd0dcd98 100644 --- a/src/tests/testHttp1Parser.cc +++ b/src/tests/testHttp1Parser.cc @@ -32,15 +32,60 @@ testHttp1Parser::globalSetup() Config.maxRequestHeaderSize = 1024; // XXX: unit test the RequestParser handling of this limit } +struct resultSet { + bool parsed; + bool done; + Http1::ParseState parserState; + Http::StatusCode status; + int msgStart; + int msgEnd; + SBuf::size_type suffixSz; + int methodStart; + int methodEnd; + HttpRequestMethod method; + int uriStart; + int uriEnd; + const char *uri; + int versionStart; + int versionEnd; + AnyP::ProtocolVersion version; +}; + +static void +testResults(int line, const SBuf &input, Http1::RequestParser &output, struct resultSet &expect) +{ +#if WHEN_TEST_DEBUG_IS_NEEDED + printf("TEST @%d, in=%u: " SQUIDSBUFPH "\n", line, input.length(), SQUIDSBUFPRINT(input)); +#endif + + CPPUNIT_ASSERT_EQUAL(expect.parsed, output.parse()); + CPPUNIT_ASSERT_EQUAL(expect.done, output.isDone()); + if (!output.isDone()) + CPPUNIT_ASSERT_EQUAL(expect.parserState, output.parsingStage_); + CPPUNIT_ASSERT_EQUAL(expect.status, output.request_parse_status); + CPPUNIT_ASSERT_EQUAL(expect.msgStart, output.req.start); + CPPUNIT_ASSERT_EQUAL(expect.msgEnd, output.req.end); + CPPUNIT_ASSERT_EQUAL(expect.suffixSz, output.buf.length()); + CPPUNIT_ASSERT_EQUAL(expect.methodStart, output.req.m_start); + CPPUNIT_ASSERT_EQUAL(expect.methodEnd, output.req.m_end); + CPPUNIT_ASSERT_EQUAL(expect.method, output.method_); + CPPUNIT_ASSERT_EQUAL(expect.uriStart, output.req.u_start); + CPPUNIT_ASSERT_EQUAL(expect.uriEnd, output.req.u_end); + if (expect.uri != NULL) + CPPUNIT_ASSERT_EQUAL(0, output.uri_.cmp(expect.uri)); + CPPUNIT_ASSERT_EQUAL(expect.versionStart, output.req.v_start); + CPPUNIT_ASSERT_EQUAL(expect.versionEnd, output.req.v_end); + CPPUNIT_ASSERT_EQUAL(expect.version, output.msgProtocol_); +} + void testHttp1Parser::testParseRequestLineProtocols() { // ensure MemPools etc exist globalSetup(); - MemBuf input; + SBuf input; Http1::RequestParser output; - input.init(); // TEST: Do we comply with RFC 1945 section 5.1 ? // TEST: Do we comply with RFC 2616 section 5.1 ? @@ -48,125 +93,132 @@ testHttp1Parser::testParseRequestLineProtocols() // RFC 1945 : HTTP/0.9 simple-request { input.append("GET /\r\n", 7); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(true, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0,memcmp("GET /\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start], (output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start], (output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = true, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // RFC 1945 : invalid HTTP/0.9 simple-request (only GET is valid) -#if 0 +#if WHEN_RFC_COMPLIANT { input.append("POST /\r\n", 7); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(true, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0,memcmp("POST /\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(3, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("POST", &output.buf[output.req.m_start], (output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_POST), output.method_); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start], (output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = true, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 3, + .method = HttpRequestMethod(Http::METHOD_POST), + .uriStart = 5, + .uriEnd = 5, + .uri = "/", + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } #endif - // RFC 1945 and 2616 : HTTP/1.0 request { input.append("GET / HTTP/1.0\r\n", 16); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.0\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.0", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 13, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // RFC 2616 : HTTP/1.1 request { input.append("GET / HTTP/1.1\r\n", 16); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 13, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // RFC 2616 : future version full-request { input.append("GET / HTTP/1.2\r\n", 16); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.2\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.2", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,2), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 13, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,2) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // RFC 2616 : future version full-request @@ -174,211 +226,212 @@ testHttp1Parser::testParseRequestLineProtocols() // IETF HTTPbis WG has made this two-digits format invalid. // it gets treated same as HTTP/0.9 for now input.append("GET / HTTP/10.12\r\n", 18); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/10.12\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(15, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/10.12", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,10,12), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scHttpVersionNotSupported, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 15, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,10,12) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } - // This stage of the parser does not yet accept non-HTTP protocol names. + // unknown non-HTTP protocol names { - // violations mode treats them as HTTP/0.9 requests! + // XXX: violations mode treats them as HTTP/0.9 requests! which is wrong. +#if !USE_HTTP_VIOLATIONS input.append("GET / FOO/1.0\n", 14); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); -#if USE_HTTP_VIOLATIONS - CPPUNIT_ASSERT_EQUAL(true, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(12, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/ FOO/1.0", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_); -#else - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scHttpVersionNotSupported, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 12, + .version = AnyP::ProtocolVersion() + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); #endif - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / FOO/1.0\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(12, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("FOO/1.0", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - input.reset(); } // no version { input.append("GET / HTTP/\n", 12); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(10, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scHttpVersionNotSupported, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 10, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // no major version { input.append("GET / HTTP/.1\n", 14); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(12, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scHttpVersionNotSupported, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 12, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // no version dot { input.append("GET / HTTP/11\n", 14); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/11\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(12, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/11", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scHttpVersionNotSupported, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 12, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // negative major version (bug 3062) { input.append("GET / HTTP/-999999.1\n", 21); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/-999999.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(19, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/-999999.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scHttpVersionNotSupported, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 19, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // no minor version { input.append("GET / HTTP/1.\n", 14); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(12, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scHttpVersionNotSupported, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 12, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // negative major version (bug 3062 corollary) { input.append("GET / HTTP/1.-999999\n", 21); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.-999999\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(19, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.-999999", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scHttpVersionNotSupported, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 19, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } } @@ -388,86 +441,86 @@ testHttp1Parser::testParseRequestLineStrange() // ensure MemPools etc exist globalSetup(); - MemBuf input; + SBuf input; Http1::RequestParser output; - input.init(); // space padded URL { input.append("GET / HTTP/1.1\r\n", 21); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(11, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(18, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 5, + .uriEnd = 5, + .uri = "/", + .versionStart = 11, + .versionEnd = 18, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // whitespace inside URI. (nasty but happens) + // XXX: depends on tolerant parser... { input.append("GET /fo o/ HTTP/1.1\n", 20); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0,memcmp("GET /fo o/ HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(9, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/fo o/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(11, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(18, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 9, + .uri = "/fo o/", + .versionStart = 11, + .versionEnd = 18, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // additional data in buffer { input.append("GET / HTTP/1.1\nboo!", 23); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-5, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); // strangeness generated by following RFC - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(10, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(17, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-5, + .suffixSz = 4, // strlen("boo!") + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 10, + .versionEnd = 17, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } } @@ -477,110 +530,110 @@ testHttp1Parser::testParseRequestLineTerminators() // ensure MemPools etc exist globalSetup(); - MemBuf input; + SBuf input; Http1::RequestParser output; - input.init(); // alternative EOL sequence: NL-only { input.append("GET / HTTP/1.1\n", 15); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 13, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // alternative EOL sequence: double-NL-only { input.append("GET / HTTP/1.1\n\n", 16); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(true, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-2, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = true, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-2, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 13, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } - // RELAXED alternative EOL sequence: multi-CR-NL + // alternative EOL sequence: multi-CR-NL { input.append("GET / HTTP/1.1\r\r\r\n", 18); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - Config.onoff.relaxed_header_parser = 1; // Being tolerant we can ignore and elide these apparently benign CR - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\r\r\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); - Config.onoff.relaxed_header_parser = 0; - } + Config.onoff.relaxed_header_parser = 1; + struct resultSet expectRelaxed = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 13, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expectRelaxed); - // STRICT alternative EOL sequence: multi-CR-NL - { - input.append("GET / HTTP/1.1\r\r\r\n", 18); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); // strict mode treats these as several bare-CR in the request line which is explicitly invalid. Config.onoff.relaxed_header_parser = 0; - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); + struct resultSet expectStrict = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = -1, + .suffixSz = input.length(), + .methodStart =-1, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; + output.reset(input); + testResults(__LINE__, input, output, expectStrict); + input.clear(); } // space padded version @@ -588,104 +641,27 @@ testHttp1Parser::testParseRequestLineTerminators() // RFC 1945 and 2616 specify version is followed by CRLF. No intermediary bytes. // NP: the terminal whitespace is a special case: invalid for even HTTP/0.9 with no version tag input.append("GET / HTTP/1.1 \n", 16); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1 \n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(13, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/ HTTP/1.1", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); - } - - // incomplete line at various positions - { - input.append("GET", 3); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); - - input.append("GET ", 4); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); - - input.append("GET / HT", 8); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); - - input.append("GET / HTTP/1.1", 14); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 13, + .uri = "/ HTTP/1.1", + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } } @@ -695,207 +671,264 @@ testHttp1Parser::testParseRequestLineMethods() // ensure MemPools etc exist globalSetup(); - MemBuf input; + SBuf input; Http1::RequestParser output; - input.init(); // RFC 2616 : . method { input.append(". / HTTP/1.1\n", 13); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp(". / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp(".", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(".", NULL), output.method_); - CPPUNIT_ASSERT_EQUAL(2, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(4, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(11, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 0, + .method = HttpRequestMethod("."), + .uriStart = 2, + .uriEnd = 2, + .uri = "/", + .versionStart = 4, + .versionEnd = 11, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // OPTIONS with * URL { input.append("OPTIONS * HTTP/1.1\n", 19); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("OPTIONS * HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(6, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("OPTIONS", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_OPTIONS), output.method_); - CPPUNIT_ASSERT_EQUAL(8, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(8, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("*", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(10, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(17, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 6, + .method = HttpRequestMethod(Http::METHOD_OPTIONS), + .uriStart = 8, + .uriEnd = 8, + .uri = "*", + .versionStart = 10, + .versionEnd = 17, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // unknown method { input.append("HELLOWORLD / HTTP/1.1\n", 22); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HELLOWORLD / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(9, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HELLOWORLD", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("HELLOWORLD",NULL), output.method_); - CPPUNIT_ASSERT_EQUAL(11, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(11, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(13, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(20, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 9, + .method = HttpRequestMethod("HELLOWORLD"), + .uriStart = 11, + .uriEnd = 11, + .uri = "/", + .versionStart = 13, + .versionEnd = 20, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // method-only { input.append("A\n", 2); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("A\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } - input.append("GET\n", 4); { - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); + input.append("GET\n", 4); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } - // RELAXED space padded method (in strict mode SP is reserved so invalid as a method byte) + // space padded method (in strict mode SP is reserved so invalid as a method byte) { input.append(" GET / HTTP/1.1\n", 16); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); + // RELAXED mode Squid custom tolerance ignores SP +#if USE_HTTP_VIOLATIONS Config.onoff.relaxed_header_parser = 1; - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(1, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(1, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(3, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(7, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(14, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); + struct resultSet expectRelaxed = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, // garbage collection consumes the SP + .msgEnd = (int)input.length()-2, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 13, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expectRelaxed); +#endif + + // STRICT mode obeys RFC syntax Config.onoff.relaxed_header_parser = 0; + struct resultSet expectStrict = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; + output.reset(input); + testResults(__LINE__, input, output, expectStrict); + input.clear(); } - // STRICT space padded method (in strict mode SP is reserved so invalid as a method byte) + // RFC 2616 defined tolerance: ignore empty line(s) prefix on messages +#if WHEN_RFC_COMPLIANT { - input.append(" GET / HTTP/1.1\n", 16); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - Config.onoff.relaxed_header_parser = 0; - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp(" GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_NONE,0,0), output.msgProtocol_); - input.reset(); + input.append("\r\n\r\n\nGET / HTTP/1.1\r\n", 21); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 5, + .msgEnd = (int)input.length()-1, + .suffixSz = 5, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 4, + .uri = "/", + .versionStart = 6, + .versionEnd = 13, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } +#endif // tab padded method (NP: tab is not SP so treated as any other binary) - // XXX: binary codes are non-compliant { input.append("\tGET / HTTP/1.1\n", 16); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("\tGET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(3, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("\tGET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(&output.buf[output.req.m_start],&output.buf[output.req.m_end+1]), output.method_); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(7, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(14, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); +#if WHEN_RFC_COMPLIANT + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = -1, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; +#else // XXX: currently broken + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, // garbage collection consumes the SP + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 3, + .method = HttpRequestMethod(SBuf("\tGET")), + .uriStart = 5, + .uriEnd = 5, + .uri = "/", + .versionStart = 7, + .versionEnd = 14, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; +#endif + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } } @@ -905,229 +938,307 @@ testHttp1Parser::testParseRequestLineInvalid() // ensure MemPools etc exist globalSetup(); - MemBuf input; + SBuf input; Http1::RequestParser output; - input.init(); // no method (but in a form which is ambiguous with HTTP/0.9 simple-request) { - // XXX: Bug: HTTP/0.9 requires method to be "GET" + // XXX: HTTP/0.9 requires method to be "GET" input.append("/ HTTP/1.0\n", 11); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(true, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/ HTTP/1.0\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("/",NULL), output.method_); - CPPUNIT_ASSERT_EQUAL(2, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(9, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.0", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = true, + .done = true, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 0, + .method = HttpRequestMethod("/"), + .uriStart = 2, + .uriEnd = 9, + .uri = "HTTP/1.0", + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } - // RELAXED no method (an invalid format) + // no method (an invalid format) { input.append(" / HTTP/1.0\n", 12); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - // BUG: When tolerantly ignoring SP prefix this case becomes ambiguous with HTTP/0.9 simple-request) - Config.onoff.relaxed_header_parser = 1; - CPPUNIT_ASSERT_EQUAL(true, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); -// CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_DONE, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(1, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/ HTTP/1.0\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(1, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("/",NULL), output.method_); - CPPUNIT_ASSERT_EQUAL(3, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(10, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.0", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_); - input.reset(); - Config.onoff.relaxed_header_parser = 0; - } - // STRICT no method (an invalid format) - { - input.append(" / HTTP/1.0\n", 12); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - // When tolerantly ignoring SP prefix this case becomes ambiguous with HTTP/0.9 simple-request) + // XXX: squid custom tolerance consumes initial SP. + Config.onoff.relaxed_header_parser = 1; + struct resultSet expectRelaxed = { + .parsed = true, + .done = true, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-2, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 0, + .method = HttpRequestMethod("/"), + .uriStart = 2, + .uriEnd = 9, + .uri = "HTTP/1.0", + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9) + }; + output.reset(input); + testResults(__LINE__, input, output, expectRelaxed); + + // STRICT detect as invalid Config.onoff.relaxed_header_parser = 0; - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp(" / HTTP/1.0\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_NONE,0,0), output.msgProtocol_); - input.reset(); +#if WHEN_RFC_COMPLIANT + // XXX: except Squid does not + struct resultSet expectStrict = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; +#else + struct resultSet expectStrict = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; +#endif + output.reset(input); + testResults(__LINE__, input, output, expectStrict); + input.clear(); } - // binary code in method (strange but ...) + // binary code in method (invalid) { input.append("GET\x0B / HTTP/1.1\n", 16); - //printf("TEST: %d-%d/%d '%.*s'\n", output.req.start, output.req.end, input.contentSize(), 16, input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\x0B / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(3, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\x0B", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); -// CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("GET\0x0B",NULL), output.method_); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(7, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(14, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); +#if WHEN_RFC_COMPLIANT + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = -1, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; +#else + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, // garbage collection consumes the SP + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 3, + .method = HttpRequestMethod(SBuf("GET\x0B")), + .uriStart = 5, + .uriEnd = 5, + .uri = "/", + .versionStart = 7, + .versionEnd = 14, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; +#endif + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // CR in method { // RFC 2616 sec 5.1 prohibits CR other than in terminator. input.append("GET\r / HTTP/1.1\r\n", 16); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = -1, // halt at the first \r + .suffixSz = input.length(), + .methodStart = -1, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // binary code NUL! in method (strange but ...) { input.append("GET\0 / HTTP/1.1\n", 16); - //printf("TEST: %d-%d/%d '%.*s'\n", output.req.start, output.req.end, input.contentSize(), 16, input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(false, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\0 / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(3, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\0", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); -// CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("GET\0",NULL), output.method_); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(7, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(14, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); - input.reset(); +#if WHEN_RFC_COMPLIANT + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = -1, // halt at the \0 + .suffixSz = input.length(), + .methodStart = -1, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; +#else + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_MIME, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 3, + .method = HttpRequestMethod(SBuf("GET\0",4)), + .uriStart = 5, + .uriEnd = 5, + .uri = "/", + .versionStart = 7, + .versionEnd = 14, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) + }; +#endif + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } - // no URL (grammer otherwise correct) + // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request) { input.append("GET HTTP/1.1\n", 14); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(true, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(12, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = true, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 5, + .uriEnd = 12, + .uri = "HTTP/1.1", + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request) { input.append("GET HTTP/1.1\n", 13); - //printf("TEST: '%s'\n",input.content()); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(true, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(11, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = true, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scOkay, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = 0, + .methodStart = 0, + .methodEnd = 2, + .method = HttpRequestMethod(Http::METHOD_GET), + .uriStart = 4, + .uriEnd = 11, + .uri = "HTTP/1.1", + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9) + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // binary line { input.append("\xB\xC\xE\xF\n", 5); - //printf("TEST: binary-line\n"); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("\xB\xC\xE\xF\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // mixed whitespace line @@ -1135,24 +1246,27 @@ testHttp1Parser::testParseRequestLineInvalid() // We accept non-space binary bytes for method so first \t shows up as that // but remaining space and tabs are skipped searching for URI-start input.append("\t \t \t\n", 6); - //printf("TEST: mixed whitespace\n"); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("\t \t \t\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(0, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(0, memcmp("\t", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(&output.buf[output.req.m_start],&output.buf[output.req.m_end+1]), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = (int)input.length()-1, + .suffixSz = input.length(), + .methodStart = 0, + .methodEnd = 0, + .method = HttpRequestMethod(SBuf("\t")), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } // mixed whitespace line with CR middle @@ -1160,22 +1274,27 @@ testHttp1Parser::testParseRequestLineInvalid() // CR aborts on sight, so even initial \t method is not marked as above // (not when parsing clean with whole line available anyway) input.append("\t \r \n", 6); - //printf("TEST: mixed whitespace with CR\n"); - output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(false, output.parse()); - CPPUNIT_ASSERT_EQUAL(true, output.isDone()); - CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); - CPPUNIT_ASSERT_EQUAL(0, output.req.start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); - CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); - CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); - input.reset(); + struct resultSet expect = { + .parsed = false, + .done = true, + .parserState = Http1::HTTP_PARSE_DONE, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = -1, // halt on the \r + .suffixSz = input.length(), + .methodStart = -1, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; + output.reset(input); + testResults(__LINE__, input, output, expect); + input.clear(); } } @@ -1186,60 +1305,94 @@ testHttp1Parser::testDripFeed() // extend the size of the buffer from 0 bytes to full request length // calling the parser repeatedly as visible data grows. - MemBuf mb; - mb.init(1024, 1024); - mb.append(" ", 12); - int garbageEnd = mb.contentSize(); - mb.append("GET http://example.com/ HTTP/1.1\r\n", 34); - int reqLineEnd = mb.contentSize(); - mb.append("Host: example.com\r\n\r\n", 21); - int mimeEnd = mb.contentSize(); - mb.append("...", 3); // trailer to catch mime EOS errors. + SBuf data; + data.append(" ", 12); + SBuf::size_type garbageEnd = data.length(); + data.append("GET http://example.com/ HTTP/1.1\r\n", 34); + SBuf::size_type reqLineEnd = data.length() - 1; + data.append("Host: example.com\r\n\r\n", 21); + SBuf::size_type mimeEnd = data.length() - 1; + data.append("...", 3); // trailer to catch mime EOS errors. - Http1::RequestParser hp(mb.content(), 0); + SBuf ioBuf; // begins empty + Http1::RequestParser hp(ioBuf); // only relaxed parser accepts the garbage whitespace Config.onoff.relaxed_header_parser = 1; - for (; hp.bufsiz <= mb.contentSize(); ++hp.bufsiz) { - bool parseResult = hp.parse(); - -#if WHEN_TEST_DEBUG_IS_NEEDED - printf("%d/%d :: %d, %d, %d '%c'\n", hp.bufsiz, mb.contentSize(), - garbageEnd, reqLineEnd, parseResult, - mb.content()[hp.bufsiz]); -#endif - - // before end of garbage found its a moving offset. - if (hp.bufsiz <= garbageEnd) { - CPPUNIT_ASSERT_EQUAL(hp.bufsiz, (int)hp.parseOffset_); - CPPUNIT_ASSERT_EQUAL(false, hp.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NEW, hp.parsingStage_); - continue; + // state of things we expect right now + struct resultSet expect = { + .parsed = false, + .done = false, + .parserState = Http1::HTTP_PARSE_NEW, + .status = Http::scBadRequest, + .msgStart = 0, + .msgEnd = -1, + .suffixSz = 0, + .methodStart = -1, + .methodEnd = -1, + .method = HttpRequestMethod(), + .uriStart = -1, + .uriEnd = -1, + .uri = NULL, + .versionStart = -1, + .versionEnd = -1, + .version = AnyP::ProtocolVersion() + }; + + Config.maxRequestHeaderSize = 1024; // large enough to hold the test data. + + for (SBuf::size_type pos = 0; pos <= data.length(); ++pos) { + + // simulate reading one more byte + ioBuf.append(data.substr(pos,1)); + + // sync the buffers like Squid does + hp.buf = ioBuf; + + // when the garbage is passed we expect to start seeing first-line bytes + if (pos == garbageEnd) { + expect.parserState = Http1::HTTP_PARSE_FIRST; + expect.msgStart = 0; } - // before request line found, parse announces incomplete - if (hp.bufsiz < reqLineEnd) { - CPPUNIT_ASSERT_EQUAL(garbageEnd, (int)hp.parseOffset_); - CPPUNIT_ASSERT_EQUAL(false, parseResult); - CPPUNIT_ASSERT_EQUAL(false, hp.isDone()); - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, hp.parsingStage_); - continue; + // all points after garbage start to see accumulated bytes looking for end of current section + if (pos >= garbageEnd) + expect.suffixSz = ioBuf.length(); + + // at end of request line expect to see method, URI, version details + // and switch to seeking Mime header section + if (pos == reqLineEnd) { + expect.parserState = Http1::HTTP_PARSE_MIME; + expect.suffixSz = 0; + expect.msgEnd = reqLineEnd-garbageEnd; + expect.status = Http::scOkay; + expect.methodStart = 0; + expect.methodEnd = 2; + expect.method = HttpRequestMethod(Http::METHOD_GET); + expect.uriStart = 4; + expect.uriEnd = 22; + expect.uri = "http://example.com/"; + expect.versionStart = 24; + expect.versionEnd = 31; + expect.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1); } - // before request headers entirely found, parse announces incomplete - if (hp.bufsiz < mimeEnd) { - CPPUNIT_ASSERT_EQUAL(reqLineEnd, (int)hp.parseOffset_); - CPPUNIT_ASSERT_EQUAL(false, parseResult); - CPPUNIT_ASSERT_EQUAL(false, hp.isDone()); - // TODO: add all the other usual tests for request-line details - CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, hp.parsingStage_); - continue; + // one mime header is done we are expectign a new request + // parse results say true and initial data is all gone from the buffer + if (pos == mimeEnd) { + expect.parsed = true; + expect.done = true; + expect.suffixSz = 0; } - // once request line is found (AND the following \n) current parser announces success - CPPUNIT_ASSERT_EQUAL(mimeEnd, (int)hp.parseOffset_); - CPPUNIT_ASSERT_EQUAL(true, parseResult); - CPPUNIT_ASSERT_EQUAL(true, hp.isDone()); + testResults(__LINE__, ioBuf, hp, expect); + + // sync the buffers like Squid does + ioBuf = hp.buf; + + // Squid stops using the parser once it reports done. + if (hp.isDone()) + break; } } diff --git a/src/tests/testHttpRequestMethod.cc b/src/tests/testHttpRequestMethod.cc index 10d42b3f8b..eb7c87549a 100644 --- a/src/tests/testHttpRequestMethod.cc +++ b/src/tests/testHttpRequestMethod.cc @@ -19,10 +19,10 @@ void testHttpRequestMethod::testConstructCharStart() { /* parse an empty string -> Http::METHOD_NONE */ - CPPUNIT_ASSERT(HttpRequestMethod(NULL,NULL) == Http::METHOD_NONE); + CPPUNIT_ASSERT(HttpRequestMethod(NULL) == Http::METHOD_NONE); /* parsing a literal should work */ - CPPUNIT_ASSERT(HttpRequestMethod("GET", NULL) == Http::METHOD_GET); - CPPUNIT_ASSERT(HttpRequestMethod("QWERTY", NULL) == Http::METHOD_OTHER); + CPPUNIT_ASSERT(HttpRequestMethod("GET") == Http::METHOD_GET); + CPPUNIT_ASSERT(HttpRequestMethod("QWERTY") == Http::METHOD_OTHER); } /* @@ -33,12 +33,12 @@ testHttpRequestMethod::testConstructCharStartEnd() { char const * buffer; /* parse an empty string -> Http::METHOD_NONE */ - CPPUNIT_ASSERT(HttpRequestMethod(NULL, NULL) == Http::METHOD_NONE); + CPPUNIT_ASSERT(HttpRequestMethod(NULL) == Http::METHOD_NONE); /* parsing a literal should work */ - CPPUNIT_ASSERT(HttpRequestMethod("GET", NULL) == Http::METHOD_GET); + CPPUNIT_ASSERT(HttpRequestMethod("GET") == Http::METHOD_GET); /* parsing with an explicit end should work */ buffer = "POSTPLUS"; - CPPUNIT_ASSERT(HttpRequestMethod(buffer, buffer + 4) == Http::METHOD_POST); + CPPUNIT_ASSERT(HttpRequestMethod(SBuf(buffer, 4)) == Http::METHOD_POST); } /* @@ -84,15 +84,15 @@ testHttpRequestMethod::testImage() { // relaxed RFC-compliance parse HTTP methods are upgraded to correct case Config.onoff.relaxed_header_parser = 1; - CPPUNIT_ASSERT_EQUAL(SBuf("POST"), HttpRequestMethod("POST",NULL).image()); - CPPUNIT_ASSERT_EQUAL(SBuf("POST"), HttpRequestMethod("pOsT",NULL).image()); - CPPUNIT_ASSERT_EQUAL(SBuf("POST"), HttpRequestMethod("post",NULL).image()); + CPPUNIT_ASSERT_EQUAL(SBuf("POST"), HttpRequestMethod("POST").image()); + CPPUNIT_ASSERT_EQUAL(SBuf("POST"), HttpRequestMethod("pOsT").image()); + CPPUNIT_ASSERT_EQUAL(SBuf("POST"), HttpRequestMethod("post").image()); // strict RFC-compliance parse HTTP methods are case sensitive Config.onoff.relaxed_header_parser = 0; - CPPUNIT_ASSERT_EQUAL(SBuf("POST"), HttpRequestMethod("POST",NULL).image()); - CPPUNIT_ASSERT_EQUAL(SBuf("pOsT"), HttpRequestMethod("pOsT",NULL).image()); - CPPUNIT_ASSERT_EQUAL(SBuf("post"), HttpRequestMethod("post",NULL).image()); + CPPUNIT_ASSERT_EQUAL(SBuf("POST"), HttpRequestMethod("POST").image()); + CPPUNIT_ASSERT_EQUAL(SBuf("pOsT"), HttpRequestMethod("pOsT").image()); + CPPUNIT_ASSERT_EQUAL(SBuf("post"), HttpRequestMethod("post").image()); } /* @@ -129,12 +129,12 @@ testHttpRequestMethod::testStream() // relaxed RFC-compliance parse HTTP methods are upgraded to correct case Config.onoff.relaxed_header_parser = 1; std::ostringstream buffer; - buffer << HttpRequestMethod("get", NULL); + buffer << HttpRequestMethod("get"); CPPUNIT_ASSERT_EQUAL(String("GET"), String(buffer.str().c_str())); // strict RFC-compliance parse HTTP methods are case sensitive Config.onoff.relaxed_header_parser = 0; std::ostringstream buffer2; - buffer2 << HttpRequestMethod("get", NULL); + buffer2 << HttpRequestMethod("get"); CPPUNIT_ASSERT_EQUAL(String("get"), String(buffer2.str().c_str())); } -- 2.47.2