=== modified file 'configure.ac' --- configure.ac 2013-12-12 09:41:39 +0000 +++ configure.ac 2013-12-18 06:15:47 +0000 @@ -3429,6 +3429,7 @@ src/ipc/Makefile src/ssl/Makefile src/mgr/Makefile + src/parser/Makefile src/snmp/Makefile contrib/Makefile icons/Makefile === modified file 'src/AccessLogEntry.h' --- src/AccessLogEntry.h 2013-12-06 14:59:47 +0000 +++ src/AccessLogEntry.h 2013-12-23 16:20:05 +0000 @@ -36,7 +36,7 @@ #include "HierarchyLogEntry.h" #include "http/ProtocolVersion.h" #include "HttpHeader.h" -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" #include "icp_opcode.h" #include "ip/Address.h" #include "LogTags.h" === modified file 'src/HttpMsg.h' --- src/HttpMsg.h 2013-10-25 00:13:46 +0000 +++ src/HttpMsg.h 2013-12-23 16:27:13 +0000 @@ -33,10 +33,10 @@ #include "base/Lock.h" #include "BodyPipe.h" +#include "http/forward.h" #include "http/ProtocolVersion.h" #include "http/StatusCode.h" #include "HttpHeader.h" -#include "HttpRequestMethod.h" /// common parts of HttpRequest and HttpReply class HttpMsg : public RefCountable @@ -67,6 +67,7 @@ bool persistent() const; public: + /// transport protocol version for this message Http::ProtocolVersion http_ver; HttpHeader header; @@ -80,6 +81,7 @@ int64_t content_length; + /// URL scheme protocol (if relevant) AnyP::ProtocolType protocol; HttpMsgParseState pstate; /* the current parsing state */ === modified file 'src/HttpRequest.h' --- src/HttpRequest.h 2013-07-15 07:49:43 +0000 +++ src/HttpRequest.h 2013-12-23 16:26:48 +0000 @@ -36,7 +36,7 @@ #include "err_type.h" #include "HierarchyLogEntry.h" #include "HttpMsg.h" -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" #include "Notes.h" #include "RequestFlags.h" === modified file 'src/Makefile.am' --- src/Makefile.am 2014-01-03 10:32:53 +0000 +++ src/Makefile.am 2014-01-04 23:15:54 +0000 @@ -46,8 +46,8 @@ LoadableModules.h \ LoadableModules.cc -SUBDIRS = base anyp comm eui acl format fs repl -DIST_SUBDIRS = base anyp comm eui acl format fs repl +SUBDIRS = base anyp parser comm eui acl format fs repl +DIST_SUBDIRS = base anyp parser comm eui acl format fs repl if ENABLE_AUTH SUBDIRS += auth @@ -408,16 +408,12 @@ HttpControlMsg.h \ HttpMsg.cc \ HttpMsg.h \ - HttpParser.cc \ - HttpParser.h \ HttpReply.cc \ HttpReply.h \ RequestFlags.h \ RequestFlags.cc \ HttpRequest.cc \ HttpRequest.h \ - HttpRequestMethod.cc \ - HttpRequestMethod.h \ ICP.h \ icp_opcode.h \ icp_v2.cc \ @@ -646,6 +642,7 @@ $(ESI_LIBS) \ $(SSL_LIBS) \ $(SNMP_LIBS) \ + parser/libsquid-parser.la \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ @@ -743,7 +740,7 @@ dlink.cc \ HelperChildConfig.h \ tests/stub_HelperChildConfig.cc \ - HttpRequestMethod.cc \ + http/RequestMethod.cc \ RemovalPolicy.cc \ $(WIN32_SOURCE) \ fd.h \ @@ -1070,7 +1067,7 @@ tests/testEvent \ tests/testEventLoop \ tests/test_http_range \ - tests/testHttpParser \ + tests/testHttp1Parser \ tests/testHttpReply \ tests/testHttpRequest \ tests/testStore \ @@ -1249,7 +1246,6 @@ HttpHdrSc.cc \ HttpHdrScTarget.cc \ HttpMsg.cc \ - HttpRequestMethod.cc \ int.h \ int.cc \ MasterXaction.cc \ @@ -1385,12 +1381,9 @@ tests_testCacheManager_SOURCES = \ AccessLogEntry.cc \ debug.cc \ - HttpParser.cc \ - HttpParser.h \ RequestFlags.h \ RequestFlags.cc \ HttpRequest.cc \ - HttpRequestMethod.cc \ Mem.h \ tests/stub_mem.cc \ String.cc \ @@ -1670,7 +1663,6 @@ HttpHeader.cc \ HttpMsg.cc \ HttpReply.cc \ - HttpRequestMethod.cc \ int.h \ int.cc \ SquidList.h \ @@ -1898,13 +1890,10 @@ HttpHdrSc.cc \ HttpHdrScTarget.cc \ HttpMsg.cc \ - HttpParser.cc \ - HttpParser.h \ HttpReply.cc \ RequestFlags.h \ RequestFlags.cc \ HttpRequest.cc \ - HttpRequestMethod.cc \ icp_v2.cc \ icp_v3.cc \ $(IPC_SOURCE) \ @@ -2146,13 +2135,10 @@ HttpHdrSc.cc \ HttpHdrScTarget.cc \ HttpMsg.cc \ - HttpParser.cc \ - HttpParser.h \ HttpReply.cc \ RequestFlags.h \ RequestFlags.cc \ HttpRequest.cc \ - HttpRequestMethod.cc \ icp_v2.cc \ icp_v3.cc \ $(IPC_SOURCE) \ @@ -2390,13 +2376,10 @@ HttpHeaderTools.h \ HttpHeaderTools.cc \ HttpMsg.cc \ - HttpParser.cc \ - HttpParser.h \ HttpReply.cc \ RequestFlags.h \ RequestFlags.cc \ HttpRequest.cc \ - HttpRequestMethod.cc \ icp_v2.cc \ icp_v3.cc \ int.h \ @@ -2547,15 +2530,15 @@ tests_test_http_range_DEPENDENCIES = \ $(SQUID_CPPUNIT_LA) -tests_testHttpParser_SOURCES = \ +tests_testHttp1Parser_SOURCES = \ Debug.h \ - HttpParser.cc \ - HttpParser.h \ MemBuf.cc \ MemBuf.h \ tests/stub_MemObject.cc \ Mem.h \ tests/stub_mem.cc \ + mime_header.cc \ + mime_header.h \ String.cc \ cache_cf.h \ YesNoNone.h \ @@ -2573,16 +2556,17 @@ tests/stub_store_stats.cc \ tools.h \ tests/stub_tools.cc \ - tests/testHttpParser.cc \ - tests/testHttpParser.h \ + tests/testHttp1Parser.cc \ + tests/testHttp1Parser.h \ tests/testMain.cc \ tests/stub_time.cc \ wordlist.h \ wordlist.cc -nodist_tests_testHttpParser_SOURCES = \ +nodist_tests_testHttp1Parser_SOURCES = \ $(TESTSOURCES) -tests_testHttpParser_LDADD= \ +tests_testHttp1Parser_LDADD= \ http/libsquid-http.la \ + anyp/libanyp.la \ SquidConfig.o \ base/libbase.la \ ip/libip.la \ @@ -2590,19 +2574,16 @@ $(SQUID_CPPUNIT_LIBS) \ $(COMPAT_LIB) \ $(XTRA_LIBS) -tests_testHttpParser_LDFLAGS = $(LIBADD_DL) -tests_testHttpParser_DEPENDENCIES = \ +tests_testHttp1Parser_LDFLAGS = $(LIBADD_DL) +tests_testHttp1Parser_DEPENDENCIES = \ $(SQUID_CPPUNIT_LA) ## Tests of the HttpRequest module. tests_testHttpRequest_SOURCES = \ AccessLogEntry.cc \ - HttpParser.cc \ - HttpParser.h \ RequestFlags.h \ RequestFlags.cc \ HttpRequest.cc \ - HttpRequestMethod.cc \ Mem.h \ tests/stub_mem.cc \ String.cc \ @@ -2873,7 +2854,6 @@ HttpHeader.h \ HttpHeader.cc \ HttpMsg.cc \ - HttpRequestMethod.cc \ RequestFlags.cc \ RequestFlags.h \ int.h \ @@ -3140,7 +3120,6 @@ store_dir.cc \ repl_modules.h \ store.cc \ - HttpRequestMethod.cc \ store_key_md5.h \ store_key_md5.cc \ Parsing.cc \ @@ -3298,7 +3277,6 @@ HttpHeaderTools.cc \ HttpMsg.cc \ HttpReply.cc \ - HttpRequestMethod.cc \ int.h \ int.cc \ SquidList.h \ @@ -3511,13 +3489,10 @@ HttpHeaderTools.h \ HttpHeaderTools.cc \ HttpMsg.cc \ - HttpParser.cc \ - HttpParser.h \ HttpReply.cc \ RequestFlags.h \ RequestFlags.cc \ HttpRequest.cc \ - HttpRequestMethod.cc \ icp_v2.cc \ icp_v3.cc \ $(IPC_SOURCE) \ === modified file 'src/MemObject.h' --- src/MemObject.h 2013-12-31 18:49:41 +0000 +++ src/MemObject.h 2014-01-04 23:15:54 +0000 @@ -33,7 +33,7 @@ #include "CommRead.h" #include "dlink.h" -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" #include "RemovalPolicy.h" #include "stmem.h" #include "StoreIOBuffer.h" === modified file 'src/Store.h' --- src/Store.h 2014-01-03 10:32:53 +0000 +++ src/Store.h 2014-01-04 23:16:39 +0000 @@ -39,8 +39,9 @@ #include "comm/forward.h" #include "CommRead.h" #include "hash.h" +#include "http/forward.h" +#include "http/RequestMethod.h" #include "HttpReply.h" -#include "HttpRequestMethod.h" #include "MemObject.h" #include "Range.h" #include "RemovalPolicy.h" === modified file 'src/acl/Method.h' --- src/acl/Method.h 2013-10-25 00:13:46 +0000 +++ src/acl/Method.h 2013-12-23 16:20:15 +0000 @@ -35,7 +35,7 @@ #include "acl/Strategised.h" #include "acl/Strategy.h" -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" /// \ingroup ACLAPI class ACLMethodStrategy : public ACLStrategy === modified file 'src/acl/MethodData.cc' --- src/acl/MethodData.cc 2013-10-25 00:13:46 +0000 +++ src/acl/MethodData.cc 2013-12-23 16:21:24 +0000 @@ -36,7 +36,6 @@ #include "acl/Checklist.h" #include "acl/MethodData.h" #include "cache_cf.h" -#include "HttpRequestMethod.h" #include "wordlist.h" int ACLMethodData::ThePurgeCount = 0; === modified file 'src/acl/MethodData.h' --- src/acl/MethodData.h 2012-09-01 14:38:36 +0000 +++ src/acl/MethodData.h 2013-12-23 16:20:33 +0000 @@ -36,7 +36,7 @@ #include "acl/Acl.h" #include "acl/Data.h" #include "CbDataList.h" -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" /// \ingroup ACLAPI class ACLMethodData : public ACLData === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2013-10-25 00:13:46 +0000 +++ src/cache_cf.cc 2013-12-23 16:28:10 +0000 @@ -53,7 +53,6 @@ #include "format/Format.h" #include "globals.h" #include "HttpHeaderTools.h" -#include "HttpRequestMethod.h" #include "ident/Config.h" #include "ip/Intercept.h" #include "ip/QosConfig.h" === modified file 'src/client_side.cc' --- src/client_side.cc 2013-12-06 23:52:26 +0000 +++ src/client_side.cc 2014-01-04 23:15:54 +0000 @@ -104,6 +104,7 @@ #include "FwdState.h" #include "globals.h" #include "http.h" +#include "http/Http1Parser.h" #include "HttpHdrContRange.h" #include "HttpHeaderTools.h" #include "HttpReply.h" @@ -205,7 +206,7 @@ #endif static CTCB clientLifetimeTimeout; static ClientSocketContext *parseHttpRequestAbort(ConnStateData * conn, const char *uri); -static ClientSocketContext *parseHttpRequest(ConnStateData *, HttpParser *, HttpRequestMethod *, Http::ProtocolVersion *); +static ClientSocketContext *parseHttpRequest(ConnStateData *, Http1::RequestParser &); #if USE_IDENT static IDCB clientIdentDone; #endif @@ -2079,20 +2080,21 @@ } static void -prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr) +prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, Http1::RequestParser &hp) { int vhost = conn->port->vhost; int vport = conn->port->vport; - char *host; - char ipbuf[MAX_IPSTRLEN]; + static char ipbuf[MAX_IPSTRLEN]; http->flags.accel = true; /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */ - if (strncasecmp(url, "cache_object://", 15) == 0) + static const SBuf cache_object("cache_object://"); + if (hp.requestUri().startsWith(cache_object)) return; /* already in good shape */ + const char *url = SBuf(hp.requestUri()).c_str(); // XXX: performance regression. convert to SBuf parse if (*url != '/') { if (conn->port->vhost) return; /* already in good shape */ @@ -2103,7 +2105,7 @@ #if SHOULD_REJECT_UNKNOWN_URLS if (!url) { - hp->request_parse_status = Http::scBadRequest; + hp.request_parse_status = Http::scBadRequest; return parseHttpRequestAbort(conn, "error:invalid-request"); } #endif @@ -2120,7 +2122,8 @@ const bool switchedToHttps = conn->switchedToHttps(); const bool tryHostHeader = vhost || switchedToHttps; - if (tryHostHeader && (host = mime_get_header(req_hdr, "Host")) != NULL) { + char *host = NULL; + if (tryHostHeader && (host = hp.getHeaderField("Host"))) { debugs(33, 5, "ACCEL VHOST REWRITE: vhost=" << host << " + vport=" << vport); char thost[256]; if (vport > 0) { @@ -2135,7 +2138,7 @@ host = thost; } } // else nothing to alter port-wise. - int url_sz = strlen(url) + 32 + Config.appendDomainLen + + const int url_sz = hp.requestUri().length() + 32 + Config.appendDomainLen + strlen(host); http->uri = (char *)xcalloc(url_sz, 1); const char *protocol = switchedToHttps ? @@ -2144,7 +2147,7 @@ debugs(33, 5, "ACCEL VHOST REWRITE: '" << http->uri << "'"); } else if (conn->port->defaultsite /* && !vhost */) { debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << conn->port->defaultsite << " + vport=" << vport); - int url_sz = strlen(url) + 32 + Config.appendDomainLen + + const int url_sz = hp.requestUri().length() + 32 + Config.appendDomainLen + strlen(conn->port->defaultsite); http->uri = (char *)xcalloc(url_sz, 1); char vportStr[32]; @@ -2158,7 +2161,7 @@ } else if (vport > 0 /* && (!vhost || no Host:) */) { debugs(33, 5, "ACCEL VPORT REWRITE: http_port IP + vport=" << vport); /* Put the local socket IP address as the hostname, with whatever vport we found */ - int url_sz = strlen(url) + 32 + Config.appendDomainLen; + const int url_sz = hp.requestUri().length() + 32 + Config.appendDomainLen; http->uri = (char *)xcalloc(url_sz, 1); http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN); snprintf(http->uri, url_sz, "%s://%s:%d%s", @@ -2169,30 +2172,30 @@ } static void -prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr) +prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, Http1::RequestParser &hp) { - char *host; - char ipbuf[MAX_IPSTRLEN]; + static char ipbuf[MAX_IPSTRLEN]; - if (*url != '/') + // TODO Must() on URI length>0 when the parser supports throw. For now avoid assert(). + if (!hp.requestUri().length() && hp.requestUri()[0] != '/') return; /* already in good shape */ /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */ - if ((host = mime_get_header(req_hdr, "Host")) != NULL) { - int url_sz = strlen(url) + 32 + Config.appendDomainLen + + if (const char *host = hp.getHeaderField("Host")) { + const int url_sz = hp.requestUri().length() + 32 + Config.appendDomainLen + strlen(host); http->uri = (char *)xcalloc(url_sz, 1); - snprintf(http->uri, url_sz, "%s://%s%s", URLScheme(conn->port->transport.protocol).const_str(), host, url); + snprintf(http->uri, url_sz, "%s://%s%s", URLScheme(conn->port->transport.protocol).const_str(), host, SBuf(hp.requestUri()).c_str()); debugs(33, 5, "TRANSPARENT HOST REWRITE: '" << http->uri <<"'"); } else { /* Put the local socket IP address as the hostname. */ - int url_sz = strlen(url) + 32 + Config.appendDomainLen; + const int url_sz = hp.requestUri().length() + 32 + Config.appendDomainLen; http->uri = (char *)xcalloc(url_sz, 1); http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN); snprintf(http->uri, url_sz, "%s://%s:%d%s", URLScheme(http->getConn()->port->transport.protocol).const_str(), - ipbuf, http->getConn()->clientConnection->local.port(), url); + ipbuf, http->getConn()->clientConnection->local.port(), SBuf(hp.requestUri()).c_str()); debugs(33, 5, "TRANSPARENT REWRITE: '" << http->uri << "'"); } } @@ -2202,7 +2205,7 @@ * \note Sets result->flags.parsed_ok to 0 if failed to parse the request, * to 1 if the request was correctly parsed. * \param[in] csd a ConnStateData. The caller must make sure it is not null - * \param[in] hp an HttpParser + * \param[in] hp an Http1::RequestParser * \param[out] mehtod_p will be set as a side-effect of the parsing. * Pointed-to value will be set to Http::METHOD_NONE in case of * parsing failure @@ -2211,91 +2214,40 @@ * a ClientSocketContext structure on success or failure. */ static ClientSocketContext * -parseHttpRequest(ConnStateData *csd, HttpParser *hp, HttpRequestMethod * method_p, Http::ProtocolVersion *http_ver) +parseHttpRequest(ConnStateData *csd, Http1::RequestParser &hp) { - char *req_hdr = NULL; - char *end; - size_t req_sz; - ClientHttpRequest *http; - ClientSocketContext *result; - StoreIOBuffer tempBuffer; - int r; - - /* pre-set these values to make aborting simpler */ - *method_p = Http::METHOD_NONE; - - /* NP: don't be tempted to move this down or remove again. - * It's the only DDoS protection old-String has against long URL */ - if ( hp->bufsiz <= 0) { - debugs(33, 5, "Incomplete request, waiting for end of request line"); - return NULL; - } else if ( (size_t)hp->bufsiz >= Config.maxRequestHeaderSize && headersEnd(hp->buf, Config.maxRequestHeaderSize) == 0) { - debugs(33, 5, "parseHttpRequest: Too large request"); - hp->request_parse_status = Http::scHeaderTooLarge; - return parseHttpRequestAbort(csd, "error:request-too-large"); - } - - /* Attempt to parse the first line; this'll define the method, url, version and header begin */ - r = HttpParserParseReqLine(hp); - - if (r == 0) { - debugs(33, 5, "Incomplete request, waiting for end of request line"); - return NULL; - } - - if (r == -1) { - return parseHttpRequestAbort(csd, "error:invalid-request"); - } - - /* Request line is valid here .. */ - *http_ver = Http::ProtocolVersion(hp->req.v_maj, hp->req.v_min); - - /* This call scans the entire request, not just the headers */ - if (hp->req.v_maj > 0) { - if ((req_sz = headersEnd(hp->buf, hp->bufsiz)) == 0) { - debugs(33, 5, "Incomplete request, waiting for end of headers"); + /* Attempt to parse the first line; this will define where the method, url, version and header begin */ + { + const bool parsedOk = hp.parse(); + + if (!hp.isDone()) { + debugs(33, 5, "Incomplete request, waiting for end of request line"); return NULL; } - } else { - debugs(33, 3, "parseHttpRequest: Missing HTTP identifier"); - req_sz = HttpParserReqSz(hp); - } - - /* We know the whole request is in hp->buf now */ - - assert(req_sz <= (size_t) hp->bufsiz); - - /* Will the following be true with HTTP/0.9 requests? probably not .. */ - /* So the rest of the code will need to deal with '0'-byte headers (ie, none, so don't try parsing em) */ - assert(req_sz > 0); - - hp->hdr_end = req_sz - 1; - - hp->hdr_start = hp->req.end + 1; - - /* Enforce max_request_size */ - if (req_sz >= Config.maxRequestHeaderSize) { - debugs(33, 5, "parseHttpRequest: Too large request"); - hp->request_parse_status = Http::scHeaderTooLarge; - return parseHttpRequestAbort(csd, "error:request-too-large"); - } - - /* Set method_p */ - *method_p = HttpRequestMethod(&hp->buf[hp->req.m_start], &hp->buf[hp->req.m_end]+1); + + if (!parsedOk) { + if (hp.request_parse_status == Http::scHeaderTooLarge) + return parseHttpRequestAbort(csd, "error:request-too-large"); + + return parseHttpRequestAbort(csd, "error:invalid-request"); + } + } + + /* We know the whole request is in hp.buf now */ /* deny CONNECT via accelerated ports */ - if (*method_p == Http::METHOD_CONNECT && csd->port && csd->port->flags.accelSurrogate) { + if (hp.method() == Http::METHOD_CONNECT && csd->port && csd->port->flags.accelSurrogate) { debugs(33, DBG_IMPORTANT, "WARNING: CONNECT method received on " << csd->port->transport.protocol << " Accelerator port " << csd->port->s.port()); /* XXX need a way to say "this many character length string" */ - debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->buf); - hp->request_parse_status = Http::scMethodNotAllowed; + debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp.buf); + hp.request_parse_status = Http::scMethodNotAllowed; return parseHttpRequestAbort(csd, "error:method-not-allowed"); } - if (*method_p == Http::METHOD_NONE) { + if (hp.method() == Http::METHOD_NONE) { /* XXX need a way to say "this many character length string" */ - debugs(33, DBG_IMPORTANT, "clientParseRequestMethod: Unsupported method in request '" << hp->buf << "'"); - hp->request_parse_status = Http::scMethodNotAllowed; + debugs(33, DBG_IMPORTANT, "clientParseRequestMethod: Unsupported method in request '" << hp.buf << "'"); + hp.request_parse_status = Http::scMethodNotAllowed; return parseHttpRequestAbort(csd, "error:unsupported-request-method"); } @@ -2303,24 +2255,18 @@ * Process headers after request line * TODO: Use httpRequestParse here. */ - /* XXX this code should be modified to take a const char * later! */ - req_hdr = (char *) hp->buf + hp->req.end + 1; - - debugs(33, 3, "parseHttpRequest: req_hdr = {" << req_hdr << "}"); - - end = (char *) hp->buf + hp->hdr_end; - - debugs(33, 3, "parseHttpRequest: end = {" << end << "}"); - - debugs(33, 3, "parseHttpRequest: prefix_sz = " << - (int) HttpParserRequestLen(hp) << ", req_line_sz = " << - HttpParserReqSz(hp)); + debugs(33, 3, Raw("req_hdr", hp.rawHeaderBuf(), hp.headerBlockSize())); + debugs(33, 3, "prefix_sz = " << hp.messageHeaderSize() << + ", request-line-size=" << hp.firstLineSize() << + ", mime-header-size=" << hp.headerBlockSize()); /* Ok, all headers are received */ - http = new ClientHttpRequest(csd); - - http->req_sz = HttpParserRequestLen(hp); - result = ClientSocketContextNew(csd->clientConnection, http); + ClientHttpRequest *http = new ClientHttpRequest(csd); + + http->req_sz = hp.messageHeaderSize(); + ClientSocketContext *result = ClientSocketContextNew(csd->clientConnection, http); + + StoreIOBuffer tempBuffer; tempBuffer.data = result->reqbuf; tempBuffer.length = HTTP_REQBUF_SZ; @@ -2330,25 +2276,10 @@ clientReplyStatus, newServer, clientSocketRecipient, clientSocketDetach, newClient, tempBuffer); - debugs(33, 5, "parseHttpRequest: Request Header is\n" <<(hp->buf) + hp->hdr_start); + debugs(33, 5, "parseHttpRequest: Request Header is\n" << hp.rawHeaderBuf()); /* set url */ - /* - * XXX this should eventually not use a malloc'ed buffer; the transformation code - * below needs to be modified to not expect a mutable nul-terminated string. - */ - char *url = (char *)xmalloc(hp->req.u_end - hp->req.u_start + 16); - - memcpy(url, hp->buf + hp->req.u_start, hp->req.u_end - hp->req.u_start + 1); - - url[hp->req.u_end - hp->req.u_start + 1] = '\0'; - -#if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION - - if ((t = strchr(url, '#'))) /* remove HTML anchors */ - *t = '\0'; - -#endif + const char *url = SBuf(hp.requestUri()).c_str(); debugs(33,5, HERE << "repare absolute URL from " << (csd->transparent()?"intercept":(csd->port->flags.accelSurrogate ? "accel":""))); @@ -2365,7 +2296,7 @@ */ if (csd->transparent()) { /* intercept or transparent mode, properly working with no failures */ - prepareTransparentURL(csd, http, url, req_hdr); + prepareTransparentURL(csd, http, hp); } else if (internalCheck(url)) { /* internal URL mode */ @@ -2377,13 +2308,13 @@ } else if (csd->port->flags.accelSurrogate || csd->switchedToHttps()) { /* accelerator mode */ - prepareAcceleratedURL(csd, http, url, req_hdr); + prepareAcceleratedURL(csd, http, hp); } if (!http->uri) { /* No special rewrites have been applied above, use the * requested url. may be rewritten later, so make extra room */ - int url_sz = strlen(url) + Config.appendDomainLen + 5; + int url_sz = hp.requestUri().length() + Config.appendDomainLen + 5; http->uri = (char *)xcalloc(url_sz, 1); strcpy(http->uri, url); } @@ -2392,10 +2323,12 @@ // XXX: crop this dump at the end of headers. No need for extras debugs(11, 2, "HTTP Client " << csd->clientConnection); - debugs(11, 2, "HTTP Client REQUEST:\n---------\n" << (hp->buf) + hp->req.m_start << "\n----------"); + debugs(11, 2, "HTTP Client REQUEST:\n---------\n" << + hp.method() << " " << hp.requestUri() << " " << hp.messageProtocol() << "\n" << + hp.rawHeaderBuf() << + "\n----------"); result->flags.parsed_ok = 1; - xfree(url); return result; } @@ -2504,27 +2437,6 @@ memmove(conn->in.buf, conn->in.buf + byteCount, conn->in.notYetUsed); } -/// respond with ERR_TOO_BIG if request header exceeds request_header_max_size -void -ConnStateData::checkHeaderLimits() -{ - if (in.notYetUsed < Config.maxRequestHeaderSize) - return; // can accumulte more header data - - debugs(33, 3, "Request header is too large (" << in.notYetUsed << " > " << - Config.maxRequestHeaderSize << " bytes)"); - - ClientSocketContext *context = parseHttpRequestAbort(this, "error:request-too-large"); - clientStreamNode *node = context->getClientReplyContext(); - clientReplyContext *repContext = dynamic_cast(node->data.getRaw()); - assert (repContext); - repContext->setReplyToError(ERR_TOO_BIG, - Http::scBadRequest, Http::METHOD_NONE, NULL, - clientConnection->remote, NULL, NULL, NULL); - context->registerWithConn(); - context->pullData(); -} - void ConnStateData::clientAfterReadingRequests() { @@ -2638,15 +2550,16 @@ #endif // USE_SSL static void -clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, Http::ProtocolVersion http_ver) +clientProcessRequest(ConnStateData *conn, Http1::RequestParser &hp, ClientSocketContext *context) { ClientHttpRequest *http = context->http; HttpRequest::Pointer request; - bool notedUseOfBuffer = false; bool chunked = false; bool mustReplyToOptions = false; bool unsupportedTe = false; bool expectBody = false; + const AnyP::ProtocolVersion &http_ver = hp.messageProtocol(); + const HttpRequestMethod &method = hp.method(); /* We have an initial client stream in place should it be needed */ /* setup our private context */ @@ -2654,13 +2567,13 @@ if (context->flags.parsed_ok == 0) { clientStreamNode *node = context->getClientReplyContext(); - debugs(33, 2, "clientProcessRequest: Invalid Request"); + debugs(33, 2, "Invalid Request"); conn->quitAfterError(NULL); // setLogUri should called before repContext->setReplyToError setLogUri(http, http->uri, true); clientReplyContext *repContext = dynamic_cast(node->data.getRaw()); assert (repContext); - switch (hp->request_parse_status) { + switch (hp.request_parse_status) { case Http::scHeaderTooLarge: repContext->setReplyToError(ERR_TOO_BIG, Http::scBadRequest, method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf, NULL); break; @@ -2669,7 +2582,7 @@ conn->clientConnection->remote, NULL, conn->in.buf, NULL); break; default: - repContext->setReplyToError(ERR_INVALID_REQ, hp->request_parse_status, method, http->uri, + repContext->setReplyToError(ERR_INVALID_REQ, hp.request_parse_status, method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf, NULL); } assert(context->http->out.offset == 0); @@ -2697,25 +2610,23 @@ (http_ver.major > 1) ) { clientStreamNode *node = context->getClientReplyContext(); - debugs(33, 5, "Unsupported HTTP version discovered. :\n" << HttpParserHdrBuf(hp)); + debugs(33, 5, "Unsupported HTTP version discovered. :\n" << hp.messageProtocol()); conn->quitAfterError(request.getRaw()); // setLogUri should called before repContext->setReplyToError setLogUri(http, http->uri, true); clientReplyContext *repContext = dynamic_cast(node->data.getRaw()); assert (repContext); repContext->setReplyToError(ERR_UNSUP_HTTPVERSION, Http::scHttpVersionNotSupported, method, http->uri, - conn->clientConnection->remote, NULL, HttpParserHdrBuf(hp), NULL); + conn->clientConnection->remote, NULL, hp.rawHeaderBuf(), NULL); assert(context->http->out.offset == 0); context->pullData(); goto finish; } /* compile headers */ - /* we should skip request line! */ - /* XXX should actually know the damned buffer size here */ - if (http_ver.major >= 1 && !request->parseHeader(HttpParserHdrBuf(hp), HttpParserHdrSz(hp))) { + if (http_ver.major >= 1 && !request->parseHeader(hp.rawHeaderBuf(), hp.headerBlockSize())) { clientStreamNode *node = context->getClientReplyContext(); - debugs(33, 5, "Failed to parse request headers:\n" << HttpParserHdrBuf(hp)); + debugs(33, 5, "Failed to parse request headers:\n" << hp.rawHeaderBuf()); conn->quitAfterError(request.getRaw()); // setLogUri should called before repContext->setReplyToError setLogUri(http, http->uri, true); @@ -2786,7 +2697,11 @@ #endif /* FOLLOW_X_FORWARDED_FOR */ request->my_addr = conn->clientConnection->local; request->myportname = conn->port->name; - request->http_ver = http_ver; + // XXX: for non-HTTP messages instantiate a different HttpMsg child type + // for now Squid only supports HTTP requests + assert(request->http_ver.protocol == http_ver.protocol); + request->http_ver.major = http_ver.major; + request->http_ver.minor = http_ver.minor; // Link this HttpRequest to ConnStateData relatively early so the following complex handling can use it // TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later. @@ -2864,10 +2779,6 @@ request->body_pipe = conn->expectRequestBody( chunked ? -1 : request->content_length); - // consume header early so that body pipe gets just the body - connNoteUseOfBuffer(conn, http->req_sz); - notedUseOfBuffer = true; - /* Is it too large? */ if (!chunked && // if chunked, we will check as we accumulate clientIsRequestBodyTooLargeForPolicy(request->content_length)) { @@ -2900,9 +2811,6 @@ http->doCallouts(); finish: - if (!notedUseOfBuffer) - connNoteUseOfBuffer(conn, http->req_sz); - /* * DPW 2007-05-18 * Moved the TCP_RESET feature from clientReplyContext::sendMoreData @@ -2958,7 +2866,6 @@ bool ConnStateData::clientParseRequests() { - HttpRequestMethod method; bool parsed_req = false; debugs(33, 5, HERE << clientConnection << ": attempting to parse"); @@ -2966,7 +2873,7 @@ // Loop while we have read bytes that are not needed for producing the body // On errors, bodyPipe may become nil, but readMore will be cleared while (in.notYetUsed > 0 && !bodyPipe && flags.readMore) { - connStripBufferWhitespace(this); + connStripBufferWhitespace(this); // XXX: should not be needed anymore. /* Don't try to parse if the buffer is empty */ if (in.notYetUsed == 0) @@ -2982,21 +2889,24 @@ /* Begin the parsing */ PROF_start(parseHttpRequest); - HttpParserInit(&parser_, in.buf, in.notYetUsed); + + // parser is incremental. Generate new parser state if we, + // a) dont have one already + // b) have completed the previous request parsing already + if (!parser_ || parser_->isDone()) + parser_ = new Http1::RequestParser(in.buf, in.notYetUsed); + else // update the buffer space being parsed + parser_->bufsiz = in.notYetUsed; /* Process request */ - Http::ProtocolVersion http_ver; - ClientSocketContext *context = parseHttpRequest(this, &parser_, &method, &http_ver); + ClientSocketContext *context = parseHttpRequest(this, *parser_); + if (parser_->messageOffset()) { + // nothing but prefix garbage in the buffer. consume it. + connNoteUseOfBuffer(this, parser_->messageOffset()); + parser_->noteBufferShift(parser_->messageOffset()); + } PROF_stop(parseHttpRequest); - /* partial or incomplete request */ - if (!context) { - // TODO: why parseHttpRequest can just return parseHttpRequestAbort - // (which becomes context) but checkHeaderLimits cannot? - checkHeaderLimits(); - break; - } - /* status -1 or 1 */ if (context) { debugs(33, 5, HERE << clientConnection << ": parsed a request"); @@ -3004,7 +2914,10 @@ CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http)); commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall); - clientProcessRequest(this, &parser_, context, method, http_ver); + // Request has now been shifted out of the buffer. + // Consume header early so that next action starts with just the next bytes. + connNoteUseOfBuffer(this, parser_->messageHeaderSize() + parser_->messageOffset()); + clientProcessRequest(this, *parser_, context); parsed_req = true; // XXX: do we really need to parse everything right NOW ? === modified file 'src/client_side.h' --- src/client_side.h 2013-08-22 18:39:41 +0000 +++ src/client_side.h 2014-01-04 15:11:57 +0000 @@ -35,7 +35,7 @@ #include "comm.h" #include "HttpControlMsg.h" -#include "HttpParser.h" +#include "http/forward.h" #if USE_AUTH #include "auth/UserRequest.h" #endif @@ -201,7 +201,6 @@ void addContextToQueue(ClientSocketContext * context); int getConcurrentRequestCount() const; bool isOpen() const; - void checkHeaderLimits(); // HttpControlMsgSink API virtual void sendControlMsg(HttpControlMsg msg); @@ -398,9 +397,8 @@ Auth::UserRequest::Pointer auth_; #endif - HttpParser parser_; - - // XXX: CBDATA plays with public/private and leaves the following 'private' fields all public... :( + /// the parser state for current HTTP/1.x input buffer processing + Http1::RequestParserPointer parser_; #if USE_SSL bool switchedToHttps_; === modified file 'src/htcp.h' --- src/htcp.h 2012-12-30 12:53:01 +0000 +++ src/htcp.h 2013-12-23 16:27:51 +0000 @@ -33,11 +33,9 @@ #if USE_HTCP #include "HttpHeader.h" -#include "HttpRequestMethod.h" +#include "http/forward.h" #include "ip/forward.h" -class HttpRequest; - /// \ingroup ServerProtocolHTCP class HtcpReplyData { === renamed file 'src/HttpParser.cc' => 'src/http/Http1Parser.cc' --- src/HttpParser.cc 2013-03-16 04:57:43 +0000 +++ src/http/Http1Parser.cc 2014-01-04 18:26:27 +0000 @@ -1,54 +1,167 @@ #include "squid.h" #include "Debug.h" -#include "HttpParser.h" +#include "http/Http1Parser.h" +#include "http/RequestMethod.h" +#include "mime_header.h" #include "profiler/Profiler.h" #include "SquidConfig.h" void -HttpParser::clear() +Http1::Parser::clear() { - state = HTTP_PARSE_NONE; - request_parse_status = Http::scNone; + completedState_ = HTTP_PARSE_NONE; buf = NULL; bufsiz = 0; + parseOffset_ = 0; + msgProtocol_ = AnyP::ProtocolVersion(); + mimeHeaderBlock_.clear(); +} + +void +Http1::RequestParser::clear() +{ + Http1::Parser::clear(); + + request_parse_status = Http::scNone; req.start = req.end = -1; - hdr_start = hdr_end = -1; req.m_start = req.m_end = -1; req.u_start = req.u_end = -1; req.v_start = req.v_end = -1; - req.v_maj = req.v_min = 0; + method_ = HttpRequestMethod(); } void -HttpParser::reset(const char *aBuf, int len) +Http1::Parser::reset(const char *aBuf, int len) { clear(); // empty the state. - state = HTTP_PARSE_NEW; + completedState_ = HTTP_PARSE_NEW; buf = aBuf; bufsiz = len; - debugs(74, 5, HERE << "Request buffer is " << buf); -} - + debugs(74, DBG_DATA, "Parse " << Raw("buf", buf, bufsiz)); +} + +void +Http1::RequestParser::noteBufferShift(int64_t n) +{ + bufsiz -= n; + + // if parsing done, ignore buffer changes. + if (completedState_ == HTTP_PARSE_DONE) + return; + + // shift the parser resume point to match buffer content + parseOffset_ -= n; + +#if WHEN_INCREMENTAL_PARSING + + // if have not yet finished request-line + if (completedState_ == HTTP_PARSE_NEW) { + // check for and adjust the known request-line offsets. + + /* TODO: when the first-line is parsed incrementally we + * will need to recalculate the offsets for req.* + * For now, they are all re-calculated based on parserOffset_ + * with each parse attempt. + */ + } + + // if finished request-line but not mime header + // adjust the mime header states + if (completedState_ == HTTP_PARSE_FIRST) { + /* TODO: when the mime-header is parsed incrementally we + * will need to store the initial offset of mime-header block + * instead of locatign it from req.end or parseOffset_. + * Since req.end may no longer be valid, and parseOffset_ may + * have moved into the mime-block interior. + */ + } +#endif +} + +/** + * Attempt to parse the first line of a new request message. + * + * Governed by RFC 2616 section 4.1 + * " + * In the interest of robustness, servers SHOULD ignore any empty + * line(s) received where a Request-Line is expected. In other words, if + * the server is reading the protocol stream at the beginning of a + * message and receives a CRLF first, it should ignore the CRLF. + * + * ... To restate what is explicitly forbidden by the + * BNF, an HTTP/1.1 client MUST NOT preface or follow a request with an + * extra CRLF. + * " + * + * Parsing state is stored between calls to avoid repeating buffer scans. + * \return true if garbage whitespace was found + */ +bool +Http1::RequestParser::skipGarbageLines() +{ + req.start = parseOffset_; // avoid re-parsing any portion we managed to complete + +#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[req.start] == '\r' || buf[req.start] == '\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 + for (; req.start < bufsiz && (buf[req.start] == '\n' || ((buf[req.start] == '\r' && (buf[req.start+1] == '\n')); ++req.start); + parseOffset_ = req.start; + } +#endif + + /* XXX: this is a Squid-specific tolerance + * it appears never to have been relevant outside out unit-tests + * because the ConnStateData parser loop starts with consumeWhitespace() + * which absorbs any SP HTAB VTAB CR LF characters. + * But unit-tests called the HttpParser method directly without that pruning. + */ +#if USE_HTTP_VIOLATIONS + if (Config.onoff.relaxed_header_parser) { + if (Config.onoff.relaxed_header_parser < 0 && buf[req.start] == ' ') + 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 (; req.start < bufsiz && buf[req.start] == ' '; ++req.start); + parseOffset_ = req.start; + } +#endif + + return (parseOffset_ > 0); +} + +/** + * Attempt to parse the first line of a new request message. + * + * Governed by: + * RFC 1945 section 5.1 + * RFC 2616 section 5.1 + * + * Parsing state is stored between calls. However the current implementation + * begins parsing from scratch on every call. + * The return value tells you whether the parsing state fields are valid or not. + * + * \retval -1 an error occurred. request_parse_status indicates HTTP status result. + * \retval 1 successful parse. member fields contain the request-line items + * \retval 0 more data is needed to complete the parse + */ int -HttpParser::parseRequestFirstLine() +Http1::RequestParser::parseRequestFirstLine() { int second_word = -1; // track the suspected URI start 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, HERE << "parsing possible request: " << buf); + debugs(74, 5, "parsing possible request: bufsiz=" << bufsiz << ", offset=" << parseOffset_); + debugs(74, DBG_DATA, Raw("(buf+offset)", buf+parseOffset_, bufsiz-parseOffset_)); // Single-pass parse: (provided we have the whole line anyways) - req.start = 0; - if (Config.onoff.relaxed_header_parser) { - if (Config.onoff.relaxed_header_parser < 0 && buf[req.start] == ' ') - 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 (; req.start < bufsiz && buf[req.start] == ' '; ++req.start); - } + req.start = parseOffset_; // avoid re-parsing any portion we managed to complete req.end = -1; for (int i = 0; i < bufsiz; ++i) { // track first and last whitespace (SP only) @@ -100,7 +213,16 @@ return -1; } } + if (req.end == -1) { + // DoS protection against long first-line + if ( (size_t)bufsiz >= 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; + return -1; + } + debugs(74, 5, "Parser: retval 0: from " << req.start << "->" << req.end << ": needs more data to complete first line."); return 0; @@ -111,6 +233,13 @@ // Input Validation: + // DoS protection against long first-line + if ((size_t)(req.end-req.start) >= Config.maxRequestHeaderSize) { + debugs(33, 5, "Too large request-line"); + request_parse_status = Http::scHeaderTooLarge; + return -1; + } + // Process what we now know about the line structure into field offsets // generating HTTP status for any aborts as we go. @@ -132,6 +261,9 @@ return -1; } + /* Set method_ */ + method_ = HttpRequestMethod(&buf[req.m_start], &buf[req.m_end]+1); + // First non-whitespace after first SP = beginning of URL+Version if (second_word > line_end || second_word < req.start) { request_parse_status = Http::scBadRequest; // missing URI @@ -142,10 +274,11 @@ // RFC 1945: SP and version following URI are optional, marking version 0.9 // we identify this by the last whitespace being earlier than URI start if (last_whitespace < second_word && last_whitespace >= req.start) { - req.v_maj = 0; - req.v_min = 9; + msgProtocol_ = Http::ProtocolVersion(0,9); req.u_end = line_end; request_parse_status = Http::scOkay; // HTTP/0.9 + completedState_ = HTTP_PARSE_FIRST; + parseOffset_ = line_end; return 1; } else { // otherwise last whitespace is somewhere after end of URI. @@ -157,6 +290,7 @@ request_parse_status = Http::scBadRequest; // missing URI return -1; } + uri_.assign(&buf[req.u_start], req.u_end - req.u_start + 1); // Last whitespace SP = before start of protocol/version if (last_whitespace >= line_end) { @@ -172,10 +306,11 @@ #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. - req.v_maj = 0; - req.v_min = 9; + msgProtocol_ = Http::ProtocolVersion(0,9); req.u_end = line_end; request_parse_status = Http::scOkay; // treat as HTTP/0.9 + completedState_ = HTTP_PARSE_FIRST; + parseOffset_ = req.end; return 1; #else // protocol not supported / implemented. @@ -183,6 +318,7 @@ return -1; #endif } + msgProtocol_.protocol = AnyP::PROTO_HTTP; int i = req.v_start + sizeof("HTTP/") -1; @@ -201,7 +337,7 @@ request_parse_status = Http::scHttpVersionNotSupported; return -1; } - req.v_maj = maj; + msgProtocol_.major = maj; /* next should be .; we -have- to have this as we have a whole line.. */ if (buf[i] != '.') { @@ -228,67 +364,138 @@ request_parse_status = Http::scHttpVersionNotSupported; return -1; } - req.v_min = min; + msgProtocol_.minor = min; /* * Rightio - we have all the schtuff. Return true; we've got enough. */ request_parse_status = Http::scOkay; + parseOffset_ = req.end+1; // req.end is the \n byte. Next parse step needs to start *after* that byte. + completedState_ = HTTP_PARSE_FIRST; return 1; } -int -HttpParserParseReqLine(HttpParser *hmsg) -{ - PROF_start(HttpParserParseReqLine); - int retcode = hmsg->parseRequestFirstLine(); - debugs(74, 5, "Parser: retval " << retcode << ": from " << hmsg->req.start << - "->" << hmsg->req.end << ": method " << hmsg->req.m_start << "->" << - hmsg->req.m_end << "; url " << hmsg->req.u_start << "->" << hmsg->req.u_end << - "; version " << hmsg->req.v_start << "->" << hmsg->req.v_end << " (" << hmsg->req.v_maj << - "/" << hmsg->req.v_min << ")"); - PROF_stop(HttpParserParseReqLine); - return retcode; -} - -#if MSGDODEBUG -/* XXX This should eventually turn into something inlined or #define'd */ -int -HttpParserReqSz(HttpParser *hp) -{ - assert(hp->state == HTTP_PARSE_NEW); - assert(hp->req.start != -1); - assert(hp->req.end != -1); - return hp->req.end - hp->req.start + 1; -} - -/* - * This +1 makes it 'right' but won't make any sense if - * there's a 0 byte header? This won't happen normally - a valid header - * is at -least- a blank line (\n, or \r\n.) - */ -int -HttpParserHdrSz(HttpParser *hp) -{ - assert(hp->state == HTTP_PARSE_NEW); - assert(hp->hdr_start != -1); - assert(hp->hdr_end != -1); - return hp->hdr_end - hp->hdr_start + 1; -} - -const char * -HttpParserHdrBuf(HttpParser *hp) -{ - assert(hp->state == HTTP_PARSE_NEW); - assert(hp->hdr_start != -1); - assert(hp->hdr_end != -1); - return hp->buf + hp->hdr_start; -} - -int -HttpParserRequestLen(HttpParser *hp) -{ - return hp->hdr_end - hp->req.start + 1; -} -#endif - +bool +Http1::RequestParser::parse() +{ + // stage 1: locate the request-line + if (completedState_ == HTTP_PARSE_NEW) { + if (skipGarbageLines() && (size_t)bufsiz < parseOffset_) + return false; + } + + // stage 2: parse the request-line + if (completedState_ == HTTP_PARSE_NEW) { + PROF_start(HttpParserParseReqLine); + 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: 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_); + PROF_stop(HttpParserParseReqLine); + if (retcode < 0) { + completedState_ = HTTP_PARSE_DONE; + return false; + } + } + + // stage 3: locate the mime header block + if (completedState_ == HTTP_PARSE_FIRST) { + // HTTP/1.x request-line is valid and parsing completed. + if (msgProtocol_.major == 1) { + /* NOTE: HTTP/0.9 requests do not have a mime header block. + * So the rest of the code will need to deal with '0'-byte headers + * (ie, none, so don't try parsing em) + */ + int64_t mimeHeaderBytes = 0; + if ((mimeHeaderBytes = headersEnd(buf+parseOffset_, bufsiz-parseOffset_)) == 0) { + if (bufsiz-parseOffset_ >= Config.maxRequestHeaderSize) { + debugs(33, 5, "Too large request"); + request_parse_status = Http::scHeaderTooLarge; + completedState_ = HTTP_PARSE_DONE; + } else + debugs(33, 5, "Incomplete request, waiting for end of headers"); + return false; + } + mimeHeaderBlock_.assign(&buf[req.end+1], mimeHeaderBytes); + + } else + debugs(33, 3, "Missing HTTP/1.x identifier"); + + // NP: planned name for this stage is HTTP_PARSE_MIME + // but we do not do any further stages here yet so go straight to DONE + completedState_ = HTTP_PARSE_DONE; + + // Squid could handle these headers, but admin does not want to + if (messageHeaderSize() >= Config.maxRequestHeaderSize) { + debugs(33, 5, "Too large request"); + request_parse_status = Http::scHeaderTooLarge; + return false; + } + } + + return isDone(); +} + +// arbitrary maximum-length for headers which can be found by Http1Parser::getHeaderField() +#define GET_HDR_SZ 1024 + +char * +Http1::Parser::getHeaderField(const char *name) +{ + LOCAL_ARRAY(char, header, GET_HDR_SZ); + const char *p = NULL; + char *q = NULL; + char got = 0; + const int namelen = name ? strlen(name) : 0; + + if (!headerBlockSize() || !name) + return NULL; + + debugs(25, 5, "looking for '" << name << "'"); + + for (p = rawHeaderBuf(); *p; p += strcspn(p, "\n\r")) { + if (strcmp(p, "\r\n\r\n") == 0 || strcmp(p, "\n\n") == 0) + return NULL; + + while (xisspace(*p)) + ++p; + + if (strncasecmp(p, name, namelen)) + continue; + + if (!xisspace(p[namelen]) && p[namelen] != ':') + continue; + + int l = strcspn(p, "\n\r") + 1; + + if (l > GET_HDR_SZ) + l = GET_HDR_SZ; + + xstrncpy(header, p, l); + + debugs(25, 5, "checking '" << header << "'"); + + q = header; + + q += namelen; + + if (*q == ':') { + ++q; + got = 1; + } + + while (xisspace(*q)) { + ++q; + got = 1; + } + + if (got) { + debugs(25, 5, "returning '" << q << "'"); + return q; + } + } + + return NULL; +} === renamed file 'src/HttpParser.h' => 'src/http/Http1Parser.h' --- src/HttpParser.h 2013-03-16 04:57:43 +0000 +++ src/http/Http1Parser.h 2014-01-04 23:14:55 +0000 @@ -1,97 +1,161 @@ -#ifndef _SQUID_SRC_HTTPPARSER_H -#define _SQUID_SRC_HTTPPARSER_H +#ifndef _SQUID_SRC_HTTP_ONEREQUESTPARSER_H +#define _SQUID_SRC_HTTP_ONEREQUESTPARSER_H +#include "base/RefCount.h" +#include "http/forward.h" +#include "http/ProtocolVersion.h" +#include "http/RequestMethod.h" #include "http/StatusCode.h" +#include "SBuf.h" + +namespace Http { +namespace One { // Parser states #define HTTP_PARSE_NONE 0 // nothing. completely unset state. #define HTTP_PARSE_NEW 1 // initialized, but nothing usefully parsed yet. +#define HTTP_PARSE_FIRST 2 // have parsed request first line +#define HTTP_PARSE_MIME 3 // have located end of mime header block +#define HTTP_PARSE_DONE 99 // have done with parsing so far /** HTTP protocol parser. * - * Works on a raw character I/O buffer and tokenizes the content into - * either an error state or, an HTTP procotol request major segments: + * Works on a raw character I/O buffer and separates the content into + * either an error state or HTTP procotol major sections: * - * \item Request Line (method, URL, protocol, version) - * \item Mime header block + * \item first-line (request-line / simple-request / status-line) + * \item mime-header block */ -class HttpParser +class Parser : public RefCountable { public: - HttpParser() { clear(); } + Parser() { clear(); } /** Initialize a new parser. - * Presenting it a buffer to work on and the current length of available - * data. + * Presenting it a buffer to work on and the current length of available data. * NOTE: This is *not* the buffer size, just the parse-able data length. * The parse routines may be called again later with more data. */ - HttpParser(const char *aBuf, int len) { reset(aBuf,len); }; + Parser(const char *aBuf, int len) { reset(aBuf,len); } /// Set this parser back to a default state. /// Will DROP any reference to a buffer (does not free). - void clear(); + 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(int64_t n) = 0; + + /** Whether the parser is already done processing the buffer. + * Use to determine between incomplete data and errors results + * from the parse. + */ + bool isDone() const {return completedState_==HTTP_PARSE_DONE;} + + /// number of bytes in buffer before the message + virtual int64_t messageOffset() const = 0; + + /// size in bytes of the first line including CRLF terminator + virtual int64_t firstLineSize() const = 0; + + /// size in bytes of the message headers including CRLF terminator(s) + /// but excluding first-line bytes + int64_t headerBlockSize() const {return mimeHeaderBlock_.length();} + + /// size in bytes of HTTP message block, includes first-line and mime headers + /// excludes any body/entity/payload bytes + /// excludes any garbage prefix before the first-line + int64_t messageHeaderSize() const {return firstLineSize() + headerBlockSize();} + + /// buffer containing HTTP mime headers, excluding message first-line. + const char *rawHeaderBuf() {return mimeHeaderBlock_.c_str();} + + /// attempt to parse a message from the buffer + virtual bool parse() = 0; + + /// the protocol label for this message + const AnyP::ProtocolVersion & messageProtocol() const {return msgProtocol_;} + /** - * Attempt to parse the first line of a new request message. - * - * Governed by: - * RFC 1945 section 5.1 - * RFC 2616 section 5.1 - * - * Parsing state is stored between calls. However the current implementation - * begins parsing from scratch on every call. - * The return value tells you whether the parsing state fields are valid or not. - * - * \retval -1 an error occurred. request_parse_status indicates HTTP status result. - * \retval 1 successful parse. member fields contain the request-line items - * \retval 0 more data is needed to complete the parse + * \return A pointer to a field-value of the first matching field-name, or NULL. */ - int parseRequestFirstLine(); + char *getHeaderField(const char *name); public: - uint8_t state; const char *buf; int bufsiz; +protected: + /// what stage the parser is currently up to + uint8_t completedState_; + + /// what protocol label has been found in the first line + AnyP::ProtocolVersion msgProtocol_; + + /// byte offset for non-parsed region of the buffer + size_t parseOffset_; + + /// buffer holding the mime headers + SBuf mimeHeaderBlock_; +}; + +/** HTTP protocol request parser. + * + * Works on a raw character I/O buffer and tokenizes the content into + * either an error state or, an HTTP procotol request major segments: + * + * \item Request Line (method, URL, protocol, version) + * \item Mime header block + */ +class RequestParser : public Http1::Parser +{ +public: + /* Http::One::Parser API */ + RequestParser() : Parser() {} + RequestParser(const char *aBuf, int len) : Parser(aBuf, len) {} + virtual void clear(); + virtual void noteBufferShift(int64_t n); + virtual int64_t messageOffset() const {return req.start;} + virtual int64_t firstLineSize() const {return req.end - req.start + 1;} + virtual bool parse(); + + /// the HTTP method if this is a request message + const HttpRequestMethod & method() const {return method_;} + + /// the request-line URI if this is a request message, or an empty string. + const SBuf &requestUri() const {return uri_;} + + /** HTTP status code to be used on the invalid-request error page + * Http::scNone indicates incomplete parse, Http::scOkay indicates no error. + */ + Http::StatusCode request_parse_status; + +private: + bool skipGarbageLines(); + int parseRequestFirstLine(); + /// Offsets for pieces of the (HTTP request) Request-Line as per RFC 2616 struct request_offsets { int start, end; int m_start, m_end; // method int u_start, u_end; // url int v_start, v_end; // version (full text) - int v_maj, v_min; // version numerics } req; - // Offsets for pieces of the MiME Header segment - int hdr_start, hdr_end; - - // TODO: Offsets for pieces of the (HTTP reply) Status-Line as per RFC 2616 - - /** 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; + /// what request method has been found on the first line + HttpRequestMethod method_; + + /// raw copy of the origina client reqeust-line URI field + SBuf uri_; }; -// Legacy functions -#define HttpParserInit(h,b,l) (h)->reset((b),(l)) -int HttpParserParseReqLine(HttpParser *hp); - -#define MSGDODEBUG 0 -#if MSGDODEBUG -int HttpParserReqSz(HttpParser *); -int HttpParserHdrSz(HttpParser *); -const char * HttpParserHdrBuf(HttpParser *); -int HttpParserRequestLen(HttpParser *hp); -#else -#define HttpParserReqSz(hp) ( (hp)->req.end - (hp)->req.start + 1 ) -#define HttpParserHdrSz(hp) ( (hp)->hdr_end - (hp)->hdr_start + 1 ) -#define HttpParserHdrBuf(hp) ( (hp)->buf + (hp)->hdr_start ) -#define HttpParserRequestLen(hp) ( (hp)->hdr_end - (hp)->req.start + 1 ) -#endif - -#endif /* _SQUID_SRC_HTTPPARSER_H */ +} // namespace One +} // namespace Http + +#endif /* _SQUID_SRC_HTTP_HTTP1PARSER_H */ === modified file 'src/http/Makefile.am' --- src/http/Makefile.am 2013-03-18 04:55:51 +0000 +++ src/http/Makefile.am 2014-01-02 23:24:27 +0000 @@ -4,9 +4,14 @@ noinst_LTLIBRARIES = libsquid-http.la libsquid_http_la_SOURCES = \ + forward.h \ + Http1Parser.cc \ + Http1Parser.h \ MethodType.cc \ MethodType.h \ ProtocolVersion.h \ + RequestMethod.cc \ + RequestMethod.h \ StatusCode.cc \ StatusCode.h \ StatusLine.cc \ === renamed file 'src/HttpRequestMethod.cc' => 'src/http/RequestMethod.cc' --- src/HttpRequestMethod.cc 2013-12-18 00:48:33 +0000 +++ src/http/RequestMethod.cc 2013-12-23 16:27:27 +0000 @@ -3,7 +3,7 @@ */ #include "squid.h" -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" #include "SquidConfig.h" #include "wordlist.h" === renamed file 'src/HttpRequestMethod.h' => 'src/http/RequestMethod.h' --- src/HttpRequestMethod.h 2012-10-26 11:36:45 +0000 +++ src/http/RequestMethod.h 2013-12-23 18:02:41 +0000 @@ -1,11 +1,9 @@ #ifndef SQUID_HTTPREQUESTMETHOD_H #define SQUID_HTTPREQUESTMETHOD_H +#include "http/forward.h" #include "http/MethodType.h" #include "SquidString.h" -#include "SquidString.h" - -class SquidConfig; #include @@ -15,14 +13,10 @@ * It has a runtime extension facility to allow it to * efficiently support new methods */ -class HttpRequestMethod +class HttpRequestMethod : public RefCountable { - public: -// static void Configure(SquidConfig &Config); - HttpRequestMethod() : theMethod(Http::METHOD_NONE), theImage() {} - HttpRequestMethod(Http::MethodType const aMethod) : theMethod(aMethod), theImage() {} /** === added file 'src/http/forward.h' --- src/http/forward.h 1970-01-01 00:00:00 +0000 +++ src/http/forward.h 2014-01-04 11:39:16 +0000 @@ -0,0 +1,27 @@ +#ifndef SQUID_SRC_HTTP_FORWARD_H +#define SQUID_SRC_HTTP_FORWARD_H + +#include "base/RefCount.h" + +// TODO move these classes into Http namespace +class HttpRequestMethod; +typedef RefCount HttpRequestMethodPointer; + +class HttpRequest; +typedef RefCount HttpRequestPointer; + +class HttpReply; +typedef RefCount HttpReplyPointer; + +namespace Http { + +namespace One { +class RequestParser; +typedef RefCount RequestParserPointer; +} // namespace One + +} // namespace Http + +namespace Http1 = Http::One; + +#endif /* SQUID_SRC_HTTP_FORWARD_H */ === modified file 'src/mgr/ActionParams.h' --- src/mgr/ActionParams.h 2012-10-26 11:36:45 +0000 +++ src/mgr/ActionParams.h 2013-12-23 16:25:42 +0000 @@ -6,7 +6,7 @@ #ifndef SQUID_MGR_ACTION_PARAMS_H #define SQUID_MGR_ACTION_PARAMS_H -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" #include "ipc/forward.h" #include "mgr/QueryParams.h" #include "RequestFlags.h" === modified file 'src/mgr/ActionWriter.h' --- src/mgr/ActionWriter.h 2012-09-01 14:38:36 +0000 +++ src/mgr/ActionWriter.h 2013-12-23 16:26:01 +0000 @@ -7,7 +7,6 @@ #define SQUID_MGR_ACTION_WRITER_H #include "comm/forward.h" -#include "HttpRequestMethod.h" #include "mgr/StoreToCommWriter.h" namespace Mgr === modified file 'src/mgr/Filler.h' --- src/mgr/Filler.h 2012-09-01 14:38:36 +0000 +++ src/mgr/Filler.h 2013-12-23 16:26:11 +0000 @@ -7,7 +7,6 @@ #define SQUID_MGR_FILLER_H #include "comm/forward.h" -#include "HttpRequestMethod.h" #include "mgr/Action.h" #include "mgr/StoreToCommWriter.h" === modified file 'src/mime.cc' --- src/mime.cc 2013-12-19 04:53:35 +0000 +++ src/mime.cc 2014-01-04 23:15:54 +0000 @@ -51,8 +51,6 @@ #include #endif -#define GET_HDR_SZ 1024 - /* forward declarations */ static void mimeFreeMemory(void); static char const *mimeGetIcon(const char *fn); === modified file 'src/mime_header.cc' --- src/mime_header.cc 2012-08-29 12:36:10 +0000 +++ src/mime_header.cc 2013-12-31 03:43:08 +0000 @@ -31,91 +31,9 @@ */ #include "squid.h" - -#define GET_HDR_SZ 1024 #include "Debug.h" #include "profiler/Profiler.h" -/* - * returns a pointer to a field-value of the first matching field-name where - * field-value matches prefix if any - */ -char * -mime_get_header_field(const char *mime, const char *name, const char *prefix) -{ - LOCAL_ARRAY(char, header, GET_HDR_SZ); - const char *p = NULL; - char *q = NULL; - char got = 0; - const int namelen = name ? strlen(name) : 0; - const int preflen = prefix ? strlen(prefix) : 0; - int l; - - if (NULL == mime) - return NULL; - - assert(NULL != name); - - debugs(25, 5, "mime_get_header: looking for '" << name << "'"); - - for (p = mime; *p; p += strcspn(p, "\n\r")) { - if (strcmp(p, "\r\n\r\n") == 0 || strcmp(p, "\n\n") == 0) - return NULL; - - while (xisspace(*p)) - ++p; - - if (strncasecmp(p, name, namelen)) - continue; - - if (!xisspace(p[namelen]) && p[namelen] != ':') - continue; - - l = strcspn(p, "\n\r") + 1; - - if (l > GET_HDR_SZ) - l = GET_HDR_SZ; - - xstrncpy(header, p, l); - - debugs(25, 5, "mime_get_header: checking '" << header << "'"); - - q = header; - - q += namelen; - - if (*q == ':') { - ++q; - got = 1; - } - - while (xisspace(*q)) { - ++q; - got = 1; - } - - if (got && prefix) { - /* we could process list entries here if we had strcasestr(). */ - /* make sure we did not match a part of another field-value */ - got = !strncasecmp(q, prefix, preflen) && !xisalpha(q[preflen]); - } - - if (got) { - debugs(25, 5, "mime_get_header: returning '" << q << "'"); - return q; - } - } - - return NULL; -} - -/* returns a pointer to a field-value of the first matching field-name */ -char * -mime_get_header(const char *mime, const char *name) -{ - return mime_get_header_field(mime, name, NULL); -} - size_t headersEnd(const char *mime, size_t l) { === modified file 'src/mime_header.h' --- src/mime_header.h 2012-09-21 14:57:30 +0000 +++ src/mime_header.h 2013-12-31 03:43:08 +0000 @@ -33,8 +33,6 @@ #ifndef SQUID_MIME_HEADER_H_ #define SQUID_MIME_HEADER_H_ -char *mime_get_header(const char *mime, const char *header); -char *mime_get_header_field(const char *mime, const char *name, const char *prefix); size_t headersEnd(const char *, size_t); #endif /* SQUID_MIME_HEADER_H_ */ === modified file 'src/tests/stub_client_side.cc' --- src/tests/stub_client_side.cc 2013-06-27 15:58:46 +0000 +++ src/tests/stub_client_side.cc 2014-01-04 15:12:25 +0000 @@ -39,7 +39,6 @@ void ConnStateData::addContextToQueue(ClientSocketContext * context) STUB int ConnStateData::getConcurrentRequestCount() const STUB_RETVAL(0) bool ConnStateData::isOpen() const STUB_RETVAL(false) -void ConnStateData::checkHeaderLimits() STUB void ConnStateData::sendControlMsg(HttpControlMsg msg) STUB char *ConnStateData::In::addressToReadInto() const STUB_RETVAL(NULL) int64_t ConnStateData::mayNeedToReadMoreBody() const STUB_RETVAL(0) === renamed file 'src/tests/testHttpParser.cc' => 'src/tests/testHttp1Parser.cc' --- src/tests/testHttpParser.cc 2013-11-18 17:03:55 +0000 +++ src/tests/testHttp1Parser.cc 2014-01-04 14:51:10 +0000 @@ -3,16 +3,21 @@ #include -#include "HttpParser.h" +#define private public +#define protected public + +#include "testHttp1Parser.h" +#include "http/Http1Parser.h" +#include "http/RequestMethod.h" #include "Mem.h" #include "MemBuf.h" #include "SquidConfig.h" -#include "testHttpParser.h" +#include "testHttp1Parser.h" -CPPUNIT_TEST_SUITE_REGISTRATION( testHttpParser ); +CPPUNIT_TEST_SUITE_REGISTRATION( testHttp1Parser ); void -testHttpParser::globalSetup() +testHttp1Parser::globalSetup() { static bool setup_done = false; if (setup_done) @@ -20,16 +25,21 @@ Mem::Init(); setup_done = true; + + // default to strict parser. set for loose parsing specifically where behaviour differs. + Config.onoff.relaxed_header_parser = 0; + + Config.maxRequestHeaderSize = 1024; // XXX: unit test the RequestParser handling of this limit } void -testHttpParser::testParseRequestLineProtocols() +testHttp1Parser::testParseRequestLineProtocols() { // ensure MemPools etc exist globalSetup(); MemBuf input; - HttpParser output; + Http1::RequestParser output; input.init(); // TEST: Do we comply with RFC 1945 section 5.1 ? @@ -39,7 +49,8 @@ { input.append("GET /\r\n", 7); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + 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); @@ -47,13 +58,13 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(9, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_); input.reset(); } @@ -62,21 +73,22 @@ { input.append("POST /\r\n", 7); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + 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,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(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(4, output.req.u_start); - CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); + 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(9, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } #endif @@ -85,7 +97,9 @@ { input.append("GET / HTTP/1.0\r\n", 16); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -93,14 +107,14 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0), output.msgProtocol_); input.reset(); } @@ -108,7 +122,9 @@ { input.append("GET / HTTP/1.1\r\n", 16); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -116,22 +132,25 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } // RFC 2616 : future version full-request - { input.append("GET / HTTP/1.2\r\n", 16); + { + input.append("GET / HTTP/1.2\r\n", 16); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -139,24 +158,26 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(2, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,2), output.msgProtocol_); input.reset(); } // RFC 2616 : future version full-request { - // XXX: IETF HTTPbis WG has made this two-digits format invalid. + // 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(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(true, output.parse()); // BUG: declares true + 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); @@ -164,14 +185,14 @@ 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(10, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(12, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,10,12), output.msgProtocol_); input.reset(); } @@ -182,19 +203,19 @@ //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); #if USE_HTTP_VIOLATIONS - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(9, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_); #else - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); #endif CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); @@ -202,6 +223,7 @@ 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); @@ -214,7 +236,8 @@ input.append("GET / HTTP/\n", 12); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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); @@ -222,14 +245,14 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_); input.reset(); } @@ -238,7 +261,8 @@ input.append("GET / HTTP/.1\n", 14); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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); @@ -246,14 +270,14 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_); input.reset(); } @@ -262,7 +286,8 @@ input.append("GET / HTTP/11\n", 14); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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); @@ -270,14 +295,14 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_); input.reset(); } @@ -286,7 +311,8 @@ input.append("GET / HTTP/-999999.1\n", 21); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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); @@ -294,14 +320,14 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_); input.reset(); } @@ -310,7 +336,8 @@ input.append("GET / HTTP/1.\n", 14); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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); @@ -318,14 +345,14 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0), output.msgProtocol_); input.reset(); } @@ -334,7 +361,8 @@ input.append("GET / HTTP/1.-999999\n", 21); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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); @@ -342,26 +370,26 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0), output.msgProtocol_); input.reset(); } } void -testHttpParser::testParseRequestLineStrange() +testHttp1Parser::testParseRequestLineStrange() { // ensure MemPools etc exist globalSetup(); MemBuf input; - HttpParser output; + Http1::RequestParser output; input.init(); // space padded URL @@ -369,7 +397,9 @@ input.append("GET / HTTP/1.1\r\n", 21); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -377,14 +407,14 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } @@ -393,7 +423,9 @@ 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(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -401,14 +433,14 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } @@ -417,7 +449,9 @@ input.append("GET / HTTP/1.1\nboo!", 23); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -425,26 +459,26 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } } void -testHttpParser::testParseRequestLineTerminators() +testHttp1Parser::testParseRequestLineTerminators() { // ensure MemPools etc exist globalSetup(); MemBuf input; - HttpParser output; + Http1::RequestParser output; input.init(); // alternative EOL sequence: NL-only @@ -452,7 +486,9 @@ input.append("GET / HTTP/1.1\n", 15); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -460,14 +496,14 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } @@ -476,7 +512,8 @@ input.append("GET / HTTP/1.1\n\n", 16); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + 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); @@ -484,14 +521,14 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } @@ -502,7 +539,9 @@ 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(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -510,15 +549,16 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); + Config.onoff.relaxed_header_parser = 0; } // STRICT alternative EOL sequence: multi-CR-NL @@ -528,7 +568,8 @@ 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(-1, HttpParserParseReqLine(&output)); + 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); @@ -538,8 +579,7 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } @@ -550,7 +590,8 @@ input.append("GET / HTTP/1.1 \n", 16); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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); @@ -558,13 +599,13 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } @@ -573,81 +614,85 @@ input.append("GET", 3); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(0, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + 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(0, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + 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(0, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + 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(0, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } } void -testHttpParser::testParseRequestLineMethods() +testHttp1Parser::testParseRequestLineMethods() { // ensure MemPools etc exist globalSetup(); MemBuf input; - HttpParser output; + Http1::RequestParser output; input.init(); // RFC 2616 : . method @@ -655,7 +700,9 @@ input.append(". / HTTP/1.1\n", 13); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -663,14 +710,14 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } @@ -679,7 +726,9 @@ input.append("OPTIONS * HTTP/1.1\n", 19); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -687,14 +736,14 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } @@ -703,7 +752,9 @@ input.append("HELLOWORLD / HTTP/1.1\n", 22); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -711,14 +762,14 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } @@ -727,19 +778,20 @@ input.append("A\n", 2); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } @@ -747,19 +799,20 @@ { //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } @@ -769,7 +822,9 @@ //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); Config.onoff.relaxed_header_parser = 1; - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -777,15 +832,16 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); + Config.onoff.relaxed_header_parser = 0; } // STRICT space padded method (in strict mode SP is reserved so invalid as a method byte) @@ -794,19 +850,20 @@ //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); Config.onoff.relaxed_header_parser = 0; - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_NONE,0,0), output.msgProtocol_); input.reset(); } @@ -815,7 +872,9 @@ input.append("\tGET / HTTP/1.1\n", 16); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -823,26 +882,26 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } } void -testHttpParser::testParseRequestLineInvalid() +testHttp1Parser::testParseRequestLineInvalid() { // ensure MemPools etc exist globalSetup(); MemBuf input; - HttpParser output; + Http1::RequestParser output; input.init(); // no method (but in a form which is ambiguous with HTTP/0.9 simple-request) @@ -851,7 +910,8 @@ input.append("/ HTTP/1.0\n", 11); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + 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); @@ -859,13 +919,13 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(9, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_); input.reset(); } @@ -874,9 +934,11 @@ 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) + // 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(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(true, output.parse()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); +// CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_NEW, output.completedState_); 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); @@ -884,14 +946,15 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(9, output.req.v_min); + 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) @@ -901,19 +964,20 @@ output.reset(input.content(), input.contentSize()); // When tolerantly ignoring SP prefix this case becomes ambiguous with HTTP/0.9 simple-request) Config.onoff.relaxed_header_parser = 0; - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_NONE,0,0), output.msgProtocol_); input.reset(); } @@ -922,7 +986,9 @@ 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(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -930,14 +996,14 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } @@ -947,18 +1013,19 @@ 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(-1, HttpParserParseReqLine(&output)); + 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } @@ -967,7 +1034,9 @@ 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(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parse()); + CPPUNIT_ASSERT_EQUAL(false, output.isDone()); + CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_); 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); @@ -975,14 +1044,14 @@ 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(1, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(1, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_); input.reset(); } @@ -991,7 +1060,8 @@ input.append("GET HTTP/1.1\n", 14); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + 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); @@ -999,13 +1069,13 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(9, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_); input.reset(); } @@ -1014,7 +1084,8 @@ input.append("GET HTTP/1.1\n", 13); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + 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); @@ -1022,13 +1093,13 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(9, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_); input.reset(); } @@ -1037,19 +1108,20 @@ input.append("\xB\xC\xE\xF\n", 5); //printf("TEST: binary-line\n"); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } @@ -1060,7 +1132,8 @@ input.append("\t \t \t\n", 6); //printf("TEST: mixed whitespace\n"); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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); @@ -1068,12 +1141,12 @@ 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } @@ -1084,18 +1157,81 @@ input.append("\t \r \n", 6); //printf("TEST: mixed whitespace with CR\n"); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + 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(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } } + +void +testHttp1Parser::testDripFeed() +{ + // Simulate a client drip-feeding Squid a few bytes at a time. + // 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.", 22); + + Http1::RequestParser hp(mb.content(), 0); + + // 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()); + continue; + } + + // 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((uint8_t)HTTP_PARSE_NEW, hp.completedState_); + continue; + } + + // before request headers entirely found, parse announces incomplete + if (hp.bufsiz < mb.contentSize()-1) { + 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((uint8_t)HTTP_PARSE_FIRST, hp.completedState_); + continue; + } + + // once request line is found (AND the following \n) current parser announces success + CPPUNIT_ASSERT_EQUAL(reqLineEnd, (int)hp.parseOffset_); + CPPUNIT_ASSERT_EQUAL(true, parseResult); + CPPUNIT_ASSERT_EQUAL(true, hp.isDone()); + } +} === renamed file 'src/tests/testHttpParser.h' => 'src/tests/testHttp1Parser.h' --- src/tests/testHttpParser.h 2012-06-13 03:17:53 +0000 +++ src/tests/testHttp1Parser.h 2013-12-23 11:36:29 +0000 @@ -1,16 +1,17 @@ -#ifndef SQUID_SRC_TESTS_TESTHTTPPARSER_H -#define SQUID_SRC_TESTS_TESTHTTPPARSER_H +#ifndef SQUID_SRC_TESTS_TESTHTTP1PARSER_H +#define SQUID_SRC_TESTS_TESTHTTP1PARSER_H #include -class testHttpParser : public CPPUNIT_NS::TestFixture +class testHttp1Parser : public CPPUNIT_NS::TestFixture { - CPPUNIT_TEST_SUITE( testHttpParser ); + CPPUNIT_TEST_SUITE( testHttp1Parser ); CPPUNIT_TEST( testParseRequestLineTerminators ); CPPUNIT_TEST( testParseRequestLineMethods ); CPPUNIT_TEST( testParseRequestLineProtocols ); CPPUNIT_TEST( testParseRequestLineStrange ); CPPUNIT_TEST( testParseRequestLineInvalid ); + CPPUNIT_TEST( testDripFeed ); CPPUNIT_TEST_SUITE_END(); protected: @@ -22,6 +23,8 @@ void testParseRequestLineProtocols(); // protocol tokens handled correctly void testParseRequestLineStrange(); // strange but valid lines accepted void testParseRequestLineInvalid(); // rejection of invalid lines happens + + void testDripFeed(); // test incremental parse works }; #endif === modified file 'src/tests/testHttpRequestMethod.cc' --- src/tests/testHttpRequestMethod.cc 2013-12-18 03:00:01 +0000 +++ src/tests/testHttpRequestMethod.cc 2013-12-23 16:24:57 +0000 @@ -3,7 +3,7 @@ #include "squid.h" #include -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" #include "Mem.h" #include "SquidConfig.h" #include "testHttpRequestMethod.h"