=== 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 @@ -19,41 +19,41 @@ * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * Copyright (c) 2003, Robert Collins */ #ifndef SQUID_HTTPACCESSLOGENTRY_H #define SQUID_HTTPACCESSLOGENTRY_H #include "anyp/PortCfg.h" #include "base/RefCount.h" #include "comm/Connection.h" #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" #include "MessageSizes.h" #include "Notes.h" #if ICAP_CLIENT #include "adaptation/icap/Elements.h" #endif #if USE_SSL #include "ssl/gadgets.h" #endif /* forward decls */ class HttpReply; class HttpRequest; class CustomLog; class AccessLogEntry: public RefCountable { === 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 @@ -16,87 +16,89 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #ifndef SQUID_HTTPMSG_H #define SQUID_HTTPMSG_H #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 { public: typedef RefCount Pointer; HttpMsg(http_hdr_owner_type owner); virtual ~HttpMsg(); virtual void reset() = 0; // will have body when http*Clean()s are gone void packInto(Packer * p, bool full_uri) const; ///< produce a message copy, except for a few connection-specific settings virtual HttpMsg *clone() const = 0; ///< \todo rename: not a true copy? /// [re]sets Content-Length header and cached value void setContentLength(int64_t clen); /** * \retval true the message sender asks to keep the connection open. * \retval false the message sender will close the connection. * * Factors other than the headers may result in connection closure. */ bool persistent() const; public: + /// transport protocol version for this message Http::ProtocolVersion http_ver; HttpHeader header; HttpHdrCc *cache_control; /* Unsupported, writable, may disappear/change in the future * For replies, sums _stored_ status-line, headers, and . * Also used to report parsed header size if parse() is successful */ int hdr_sz; int64_t content_length; + /// URL scheme protocol (if relevant) AnyP::ProtocolType protocol; HttpMsgParseState pstate; /* the current parsing state */ BodyPipe::Pointer body_pipe; // optional pipeline to receive message body // returns true and sets hdr_sz on success // returns false and sets *error to zero when needs more data // returns false and sets *error to a positive Http::StatusCode on error bool parse(MemBuf *buf, bool eol, Http::StatusCode *error); bool parseCharBuf(const char *buf, ssize_t end); int httpMsgParseStep(const char *buf, int len, int atEnd); virtual int httpMsgParseError(); virtual bool expectingBody(const HttpRequestMethod&, int64_t&) const = 0; void firstLineBuf(MemBuf&); === 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 @@ -19,41 +19,41 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #ifndef SQUID_HTTPREQUEST_H #define SQUID_HTTPREQUEST_H #include "base/CbcPointer.h" #include "Debug.h" #include "err_type.h" #include "HierarchyLogEntry.h" #include "HttpMsg.h" -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" #include "Notes.h" #include "RequestFlags.h" #if USE_AUTH #include "auth/UserRequest.h" #endif #if USE_ADAPTATION #include "adaptation/History.h" #endif #if ICAP_CLIENT #include "adaptation/icap/History.h" #endif #if USE_SQUID_EUI #include "eui/Eui48.h" #include "eui/Eui64.h" #endif class ConnStateData; /* Http Request */ === modified file 'src/Makefile.am' --- src/Makefile.am 2013-12-17 17:05:17 +0000 +++ src/Makefile.am 2013-12-30 22:04:52 +0000 @@ -389,50 +389,46 @@ HttpHdrRange.cc \ HttpHdrSc.cc \ HttpHdrSc.h \ HttpHdrScTarget.cc \ HttpHdrScTarget.h \ HttpHdrContRange.cc \ HttpHdrContRange.h \ HttpHeaderStat.h \ HttpHeader.h \ HttpHeader.cc \ HttpHeaderMask.h \ HttpHeaderRange.h \ HttpHeaderFieldInfo.h \ HttpHeaderTools.h \ HttpHeaderTools.cc \ HttpBody.h \ HttpBody.cc \ 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 \ icp_v3.cc \ int.h \ int.cc \ internal.h \ internal.cc \ $(IPC_SOURCE) \ ipcache.cc \ ipcache.h \ $(LEAKFINDERSOURCE) \ SquidList.h \ SquidList.cc \ LogTags.h \ lookup_t.h \ main.cc \ MasterXaction.cc \ MasterXaction.h \ Mem.h \ @@ -723,41 +720,41 @@ int.h \ int.cc \ Mem.h \ mem.cc \ MemBuf.cc \ MemBuf.cci \ MemBuf.h \ Parsing.h \ store_key_md5.h \ store_key_md5.cc \ tests/stub_StoreMeta.cc \ StoreMetaUnpacker.cc \ String.cc \ SquidNew.cc \ tests/stub_time.cc \ ufsdump.cc \ dlink.h \ dlink.cc \ HelperChildConfig.h \ tests/stub_HelperChildConfig.cc \ - HttpRequestMethod.cc \ + http/RequestMethod.cc \ RemovalPolicy.cc \ $(WIN32_SOURCE) \ fd.h \ tests/stub_fd.cc ufsdump_LDADD = \ ident/libident.la \ acl/libacls.la \ eui/libeui.la \ acl/libstate.la \ acl/libapi.la \ base/libbase.la \ libsquid.la \ ip/libip.la \ fs/libfs.la \ ipc/libipc.la \ mgr/libmgr.la \ $(XTRA_OBJS) \ $(REPL_OBJS) \ $(CRYPTLIB) \ $(REGEXLIB) \ @@ -1050,41 +1047,41 @@ test_tools.cc: $(top_srcdir)/test-suite/test_tools.cc cp $(top_srcdir)/test-suite/test_tools.cc . # stock tools for unit tests - library independent versions of dlink_list # etc. # globals.cc is needed by test_tools.cc. # Neither of these should be disted from here. TESTSOURCES= \ tests/STUB.h \ test_tools.cc \ globals.cc check_PROGRAMS+=\ tests/testBoilerplate \ tests/testCacheManager \ tests/testDiskIO \ tests/testEvent \ tests/testEventLoop \ tests/test_http_range \ - tests/testHttpParser \ + tests/testHttp1Parser \ tests/testHttpReply \ tests/testHttpRequest \ tests/testStore \ tests/testString \ tests/testURL \ tests/testSBuf \ tests/testSBufList \ tests/testConfigParser \ tests/testStatHist \ tests/testVector if HAVE_FS_ROCK check_PROGRAMS += tests/testRock endif if HAVE_FS_UFS check_PROGRAMS += tests/testUfs endif ## NP: required to run the above list. check_PROGRAMS only builds the binaries... TESTS += $(check_PROGRAMS) @@ -1228,41 +1225,40 @@ tests/stub_fatal.cc \ FileMap.h \ filemap.cc \ HelperChildConfig.h \ HelperChildConfig.cc \ HttpBody.cc \ HttpHeader.h \ HttpHeader.cc \ HttpHeaderFieldInfo.h \ HttpHeaderTools.h \ HttpHeaderTools.cc \ HttpHdrContRange.cc \ HttpHdrRange.cc \ HttpHeaderFieldStat.h \ HttpHdrCc.h \ HttpHdrCc.cc \ HttpHdrCc.cci \ HttpHdrSc.cc \ HttpHdrScTarget.cc \ HttpMsg.cc \ - HttpRequestMethod.cc \ int.h \ int.cc \ MasterXaction.cc \ MasterXaction.h \ Notes.cc \ Notes.h \ SquidList.h \ SquidList.cc \ mem_node.cc \ Packer.cc \ Parsing.cc \ SquidMath.cc \ StatCounters.cc \ StatCounters.h \ StatHist.h \ StrList.h \ StrList.cc \ tests/stub_StatHist.cc \ stmem.cc \ $(SBUF_SOURCE) \ @@ -1362,46 +1358,43 @@ tests_testBoilerplate_SOURCES = \ tests/testBoilerplate.cc \ tests/testMain.cc \ tests/testBoilerplate.h \ tests/stub_time.cc nodist_tests_testBoilerplate_SOURCES = \ $(TESTSOURCES) tests_testBoilerplate_LDADD= \ $(SQUID_CPPUNIT_LIBS) \ $(SSLLIB) \ $(COMPAT_LIB) \ $(XTRA_LIBS) tests_testBoilerplate_LDFLAGS = $(LIBADD_DL) tests_testBoilerplate_DEPENDENCIES = \ $(SQUID_CPPUNIT_LA) ## Tests of the CacheManager module. 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 \ tests/testCacheManager.cc \ tests/testCacheManager.h \ tests/testMain.cc \ tests/stub_main_cc.cc \ tests/stub_ipc_Forwarder.cc \ tests/stub_store_stats.cc \ tests/stub_EventLoop.cc \ time.cc \ BodyPipe.cc \ cache_manager.cc \ cache_cf.h \ AuthReg.h \ YesNoNone.h \ YesNoNone.cc \ RefreshPattern.h \ cache_cf.cc \ CacheDigest.h \ @@ -1644,41 +1637,40 @@ fde.cc \ FileMap.h \ filemap.cc \ HttpBody.h \ HttpBody.cc \ HttpHeaderFieldStat.h \ HttpHdrCc.h \ HttpHdrCc.cc \ HttpHdrCc.cci \ HttpHdrContRange.cc \ HttpHdrSc.cc \ HttpHdrScTarget.cc \ HttpHdrRange.cc \ HttpHeaderFieldInfo.h \ HttpHeaderTools.h \ HttpHeaderTools.cc \ HttpHeader.h \ HttpHeader.cc \ HttpMsg.cc \ HttpReply.cc \ - HttpRequestMethod.cc \ int.h \ int.cc \ SquidList.h \ SquidList.cc \ MasterXaction.cc \ MasterXaction.h \ MemBuf.cc \ MemObject.cc \ mem_node.cc \ Mem.h \ tests/stub_mem.cc \ Notes.h \ Notes.cc \ Packer.cc \ Parsing.cc \ refresh.h \ refresh.cc \ RemovalPolicy.cc \ RequestFlags.h \ RequestFlags.cc \ @@ -1869,47 +1861,44 @@ HelperReply.h \ hier_code.h \ $(HTCPSOURCE) \ http.cc \ HttpBody.h \ HttpBody.cc \ HttpHeader.h \ HttpHeader.cc \ HttpHeaderFieldInfo.h \ HttpHeaderTools.h \ HttpHeaderTools.cc \ HttpHeaderFieldStat.h \ HttpHdrCc.h \ HttpHdrCc.cc \ HttpHdrCc.cci \ HttpHdrContRange.cc \ HttpHdrRange.cc \ 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) \ ipcache.cc \ int.h \ int.cc \ internal.h \ internal.cc \ SquidList.h \ SquidList.cc \ MasterXaction.cc \ MasterXaction.h \ Mem.h \ tests/stub_mem.cc \ mem_node.cc \ MemBuf.cc \ MemObject.cc \ mime.h \ mime.cc \ mime_header.h \ @@ -2115,47 +2104,44 @@ HelperReply.h \ hier_code.h \ $(HTCPSOURCE) \ http.cc \ HttpBody.h \ HttpBody.cc \ HttpHeader.h \ HttpHeader.cc \ HttpHeaderFieldInfo.h \ HttpHeaderTools.h \ HttpHeaderTools.cc \ HttpHeaderFieldStat.h \ HttpHdrCc.h \ HttpHdrCc.cc \ HttpHdrCc.cci \ HttpHdrContRange.cc \ HttpHdrRange.cc \ 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) \ ipcache.cc \ int.h \ int.cc \ internal.h \ internal.cc \ SquidList.h \ SquidList.cc \ MasterXaction.cc \ MasterXaction.h \ MemBuf.cc \ MemObject.cc \ Mem.h \ tests/stub_mem.cc \ mem_node.cc \ mime.h \ mime.cc \ mime_header.h \ @@ -2357,47 +2343,44 @@ HelperReply.h \ hier_code.h \ $(HTCPSOURCE) \ http.cc \ HttpBody.h \ HttpBody.cc \ HttpHeaderFieldStat.h \ HttpHdrCc.h \ HttpHdrCc.cc \ HttpHdrCc.cci \ HttpHdrContRange.cc \ HttpHdrRange.cc \ HttpHdrSc.cc \ HttpHdrScTarget.cc \ HttpHeader.h \ HttpHeader.cc \ HttpHeaderFieldInfo.h \ 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 \ int.cc \ internal.h \ internal.cc \ $(IPC_SOURCE) \ ipcache.cc \ SquidList.h \ SquidList.cc \ MasterXaction.cc \ MasterXaction.h \ MemBuf.cc \ MemObject.cc \ Mem.h \ tests/stub_mem.cc \ mem_node.cc \ mime.h \ mime.cc \ mime_header.h \ @@ -2513,90 +2496,88 @@ $(ESI_LIBS) \ $(SSL_LIBS) \ ipc/libipc.la \ base/libbase.la \ mgr/libmgr.la \ $(SNMP_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(REGEXLIB) \ $(SQUID_CPPUNIT_LIBS) \ $(SQUID_CPPUNIT_LA) \ $(SSLLIB) \ $(KRB5LIBS) \ $(COMPAT_LIB) \ $(XTRA_LIBS) tests_test_http_range_LDFLAGS = $(LIBADD_DL) tests_test_http_range_DEPENDENCIES = \ $(SQUID_CPPUNIT_LA) -tests_testHttpParser_SOURCES = \ +tests_testHttp1Parser_SOURCES = \ Debug.h \ - HttpParser.cc \ - HttpParser.h \ MemBuf.cc \ MemBuf.h \ Mem.h \ tests/stub_mem.cc \ + mime_header.cc \ + mime_header.h \ String.cc \ cache_cf.h \ YesNoNone.h \ $(SBUF_SOURCE) \ tests/stub_SBufDetailedStats.cc \ tests/stub_cache_cf.cc \ tests/stub_cache_manager.cc \ tests/stub_debug.cc \ tests/stub_event.cc \ tests/stub_HelperChildConfig.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 \ $(top_builddir)/lib/libmiscutil.la \ $(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 \ tests/testHttpRequest.h \ tests/testHttpRequest.cc \ tests/testHttpRequestMethod.h \ tests/testHttpRequestMethod.cc \ tests/testMain.cc \ tests/stub_DiskIOModule.cc \ tests/stub_libauth.cc \ tests/stub_main_cc.cc \ tests/stub_ipc_Forwarder.cc \ tests/stub_libeui.cc \ tests/stub_store_stats.cc \ tests/stub_EventLoop.cc \ time.cc \ BodyPipe.cc \ cache_manager.cc \ cache_cf.h \ AuthReg.h \ @@ -2830,41 +2811,40 @@ event.cc \ EventLoop.cc \ fatal.h \ tests/stub_fatal.cc \ FileMap.h \ filemap.cc \ HttpHeaderFieldStat.h \ HttpHdrCc.h \ HttpHdrCc.cc \ HttpHdrCc.cci \ HttpHdrContRange.cc \ HttpHdrRange.cc \ HttpHdrSc.cc \ HttpHdrScTarget.cc \ HttpHeaderFieldInfo.h \ HttpHeaderTools.h \ HttpHeaderTools.cc \ HttpHeader.h \ HttpHeader.cc \ HttpMsg.cc \ - HttpRequestMethod.cc \ RequestFlags.cc \ RequestFlags.h \ int.h \ int.cc \ SquidList.h \ SquidList.cc \ MasterXaction.cc \ MasterXaction.h \ Mem.h \ tests/stub_mem.cc \ mem_node.cc \ MemBuf.cc \ MemObject.cc \ Notes.h \ Notes.cc \ Packer.cc \ Parsing.cc \ RemovalPolicy.cc \ refresh.h \ refresh.cc \ @@ -3092,41 +3072,40 @@ StoreIOState.cc \ StoreMetaUnpacker.cc \ $(STOREMETA_SOURCE) \ StoreFileSystem.cc \ store_io.cc \ store_swapout.cc \ store_swapmeta.cc \ $(UNLINKDSOURCE) \ $(WIN32_SOURCE) \ event.cc \ $(DELAY_POOL_SOURCE) \ CacheDigest.h \ tests/stub_CacheDigest.cc \ ConfigParser.cc \ EventLoop.cc \ HttpMsg.cc \ RemovalPolicy.cc \ store_dir.cc \ repl_modules.h \ store.cc \ - HttpRequestMethod.cc \ store_key_md5.h \ store_key_md5.cc \ Parsing.cc \ ConfigOption.cc \ SwapDir.cc \ tests/stub_acl.cc \ cache_cf.h \ YesNoNone.h \ tests/stub_cache_cf.cc \ tests/stub_helper.cc \ cbdata.cc \ $(SBUF_SOURCE) \ SBufDetailedStats.h \ tests/stub_SBufDetailedStats.cc \ String.cc \ tests/stub_debug.cc \ tests/stub_client_side_request.cc \ tests/stub_http.cc \ tests/stub_libauth.cc \ mem_node.cc \ @@ -3248,41 +3227,40 @@ fd.cc \ fde.h \ fde.cc \ FileMap.h \ filemap.cc \ HttpHeaderFieldStat.h \ HttpBody.h \ HttpBody.cc \ HttpHdrCc.cc \ HttpHdrContRange.cc \ HttpHdrRange.cc \ HttpHdrSc.cc \ HttpHdrScTarget.cc \ HttpHeader.h \ HttpHeader.cc \ HttpHeaderFieldInfo.h \ HttpHeaderTools.h \ HttpHeaderTools.cc \ HttpMsg.cc \ HttpReply.cc \ - HttpRequestMethod.cc \ int.h \ int.cc \ SquidList.h \ SquidList.cc \ MasterXaction.cc \ MasterXaction.h \ Mem.h \ mem.cc \ MemBuf.cc \ MemObject.cc \ mem_node.cc \ Notes.h \ Notes.cc \ Packer.cc \ Parsing.cc \ RemovalPolicy.cc \ RequestFlags.cc \ RequestFlags.h \ StatCounters.h \ StatCounters.cc \ @@ -3457,47 +3435,44 @@ HelperReply.h \ hier_code.h \ $(HTCPSOURCE) \ http.cc \ HttpBody.h \ HttpBody.cc \ HttpHeaderFieldStat.h \ HttpHdrCc.h \ HttpHdrCc.cc \ HttpHdrCc.cci \ HttpHdrContRange.cc \ HttpHdrRange.cc \ HttpHdrSc.cc \ HttpHdrScTarget.cc \ HttpHeader.h \ HttpHeader.cc \ HttpHeaderFieldInfo.h \ 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) \ ipcache.cc \ int.h \ int.cc \ internal.h \ internal.cc \ SquidList.h \ SquidList.cc \ MasterXaction.cc \ MasterXaction.h \ multicast.h \ multicast.cc \ Mem.h \ tests/stub_mem.cc \ mem_node.cc \ MemBuf.cc \ MemObject.cc \ mime.h \ === modified file 'src/MemObject.h' --- src/MemObject.h 2012-09-22 20:07:31 +0000 +++ src/MemObject.h 2013-12-23 16:26:23 +0000 @@ -16,41 +16,41 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #ifndef SQUID_MEMOBJECT_H #define SQUID_MEMOBJECT_H #include "CommRead.h" #include "dlink.h" -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" #include "RemovalPolicy.h" #include "stmem.h" #include "StoreIOBuffer.h" #include "StoreIOState.h" #if USE_DELAY_POOLS #include "DelayId.h" #endif typedef void STMCB (void *data, StoreIOBuffer wroteBuffer); class store_client; class HttpRequest; class HttpReply; class MemObject { public: static size_t inUseCount(); === modified file 'src/Store.h' --- src/Store.h 2013-07-15 07:49:43 +0000 +++ src/Store.h 2013-12-23 16:25:31 +0000 @@ -22,42 +22,42 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #ifndef SQUID_STORE_H #define SQUID_STORE_H /** \defgroup StoreAPI Store API \ingroup FileSystems */ #include "base/RefCount.h" #include "comm/forward.h" #include "CommRead.h" #include "hash.h" +#include "http/forward.h" #include "HttpReply.h" -#include "HttpRequestMethod.h" #include "Range.h" #include "RemovalPolicy.h" #include "StoreIOBuffer.h" #include "StoreStats.h" #if USE_SQUID_ESI #include "esi/Element.h" #endif #if HAVE_OSTREAM #include #endif class AsyncCall; class HttpRequest; class MemObject; class Packer; class RequestFlags; class StoreClient; class StoreSearch; === 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 @@ -18,41 +18,41 @@ * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * * Copyright (c) 2003, Robert Collins */ #ifndef SQUID_ACLMETHOD_H #define SQUID_ACLMETHOD_H #include "acl/Strategised.h" #include "acl/Strategy.h" -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" /// \ingroup ACLAPI class ACLMethodStrategy : public ACLStrategy { public: virtual int match (ACLData * &, ACLFilledChecklist *, ACLFlags &); virtual bool requiresRequest() const {return true;} static ACLMethodStrategy *Instance(); /** * Not implemented to prevent copies of the instance. \par * Not private to prevent brain dead g+++ warnings about * private constructors with no friends */ ACLMethodStrategy(ACLMethodStrategy const &); private: === 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 @@ -19,41 +19,40 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * * Copyright (c) 2003, Robert Collins */ #include "squid.h" #include "acl/Checklist.h" #include "acl/MethodData.h" #include "cache_cf.h" -#include "HttpRequestMethod.h" #include "wordlist.h" int ACLMethodData::ThePurgeCount = 0; ACLMethodData::ACLMethodData() : values (NULL) {} ACLMethodData::ACLMethodData(ACLMethodData const &old) : values (NULL) { assert (!old.values); } ACLMethodData::~ACLMethodData() { if (values) delete values; } /// todo make this a pass-by-reference now that HTTPRequestMethods a full class? bool === 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 @@ -19,41 +19,41 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * * Copyright (c) 2003, Robert Collins */ #ifndef SQUID_ACLMETHODDATA_H #define SQUID_ACLMETHODDATA_H #include "acl/Acl.h" #include "acl/Data.h" #include "CbDataList.h" -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" /// \ingroup ACLAPI class ACLMethodData : public ACLData { public: MEMPROXY_CLASS(ACLMethodData); ACLMethodData(); ACLMethodData(ACLMethodData const &); ACLMethodData &operator= (ACLMethodData const &); virtual ~ACLMethodData(); bool match(HttpRequestMethod); wordlist *dump(); void parse(); bool empty() const; virtual ACLData *clone() const; CbDataList *values; === 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 @@ -36,41 +36,40 @@ #include "acl/AclDenyInfoList.h" #include "acl/AclNameList.h" #include "acl/AclSizeLimit.h" #include "acl/Gadgets.h" #include "acl/MethodData.h" #include "acl/Tree.h" #include "anyp/PortCfg.h" #include "AuthReg.h" #include "base/RunnersRegistry.h" #include "cache_cf.h" #include "CachePeer.h" #include "CachePeerDomainList.h" #include "ConfigParser.h" #include "CpuAffinityMap.h" #include "DiskIO/DiskIOModule.h" #include "eui/Config.h" #include "ExternalACL.h" #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" #include "ip/tools.h" #include "ipc/Kids.h" #include "log/Config.h" #include "log/CustomLog.h" #include "Mem.h" #include "MemBuf.h" #include "mgr/ActionPasswordList.h" #include "mgr/Registration.h" #include "neighbors.h" #include "NeighborTypeDomainList.h" #include "Parsing.h" #include "PeerDigest.h" #include "RefreshPattern.h" #include "rfc1738.h" #include "SquidConfig.h" #include "SquidString.h" #include "ssl/ProxyCerts.h" === modified file 'src/client_side.cc' --- src/client_side.cc 2013-12-06 14:59:47 +0000 +++ src/client_side.cc 2013-12-31 15:59:25 +0000 @@ -87,40 +87,41 @@ #include "ChunkedCodingParser.h" #include "client_db.h" #include "client_side.h" #include "client_side_reply.h" #include "client_side_request.h" #include "ClientRequestContext.h" #include "clientStream.h" #include "comm.h" #include "comm/Connection.h" #include "comm/Loops.h" #include "comm/TcpAcceptor.h" #include "comm/Write.h" #include "CommCalls.h" #include "errorpage.h" #include "fd.h" #include "fde.h" #include "fqdncache.h" #include "FwdState.h" #include "globals.h" #include "http.h" +#include "http/Http1Parser.h" #include "HttpHdrContRange.h" #include "HttpHeaderTools.h" #include "HttpReply.h" #include "HttpRequest.h" #include "ident/Config.h" #include "ident/Ident.h" #include "internal.h" #include "ipc/FdNotes.h" #include "ipc/StartListening.h" #include "log/access_log.h" #include "Mem.h" #include "MemBuf.h" #include "MemObject.h" #include "mime_header.h" #include "profiler/Profiler.h" #include "rfc1738.h" #include "SquidConfig.h" #include "SquidTime.h" #include "StatCounters.h" #include "StatHist.h" @@ -188,41 +189,41 @@ }; static void clientListenerConnectionOpened(AnyP::PortCfg *s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub); /* our socket-related context */ CBDATA_CLASS_INIT(ClientSocketContext); /* Local functions */ /* ClientSocketContext */ static ClientSocketContext *ClientSocketContextNew(const Comm::ConnectionPointer &clientConn, ClientHttpRequest *); /* other */ static IOCB clientWriteComplete; static IOCB clientWriteBodyComplete; static IOACB httpAccept; #if USE_SSL static IOACB httpsAccept; #endif static CTCB clientLifetimeTimeout; static ClientSocketContext *parseHttpRequestAbort(ConnStateData * conn, const char *uri); -static ClientSocketContext *parseHttpRequest(ConnStateData *, HttpParser *, HttpRequestMethod *, Http::ProtocolVersion *); +static ClientSocketContext *parseHttpRequest(ConnStateData *, const Http::Http1ParserPointer &); #if USE_IDENT static IDCB clientIdentDone; #endif static CSCB clientSocketRecipient; static CSD clientSocketDetach; static void clientSetKeepaliveFlag(ClientHttpRequest *); static int clientIsContentLengthValid(HttpRequest * r); static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength); static void clientUpdateStatHistCounters(LogTags logType, int svc_time); static void clientUpdateStatCounters(LogTags logType); static void clientUpdateHierCounters(HierarchyLogEntry *); static bool clientPingHasFinished(ping_data const *aPing); void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &); #ifndef PURIFY static bool connIsUsable(ConnStateData * conn); #endif static int responseFinishedOrFailed(HttpReply * rep, StoreIOBuffer const &receivedData); static void ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData * conn); static void clientUpdateSocketStats(LogTags logType, size_t size); @@ -2062,357 +2063,312 @@ char *tmp_uri = static_cast(xmalloc(strlen(uri) + 1)); char *q = tmp_uri; t = uri; while (*t) { if (!xisspace(*t)) { *q = *t; ++q; } ++t; } *q = '\0'; http->log_uri = xstrndup(rfc1738_escape_unescaped(tmp_uri), MAX_URL); xfree(tmp_uri); } break; } } } static void -prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr) +prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, const Http::Http1ParserPointer &hp) { int vhost = conn->port->vhost; int vport = conn->port->vport; char *host; 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 = hp->requestUri().c_str(); if (*url != '/') { if (conn->port->vhost) return; /* already in good shape */ /* else we need to ignore the host name */ url = strstr(url, "//"); #if SHOULD_REJECT_UNKNOWN_URLS if (!url) { hp->request_parse_status = Http::scBadRequest; return parseHttpRequestAbort(conn, "error:invalid-request"); } #endif if (url) url = strchr(url + 2, '/'); if (!url) url = (char *) "/"; } if (vport < 0) vport = http->getConn()->clientConnection->local.port(); const bool switchedToHttps = conn->switchedToHttps(); const bool tryHostHeader = vhost || switchedToHttps; - if (tryHostHeader && (host = mime_get_header(req_hdr, "Host")) != NULL) { + if (tryHostHeader && (host = hp->getHeaderField("Host")) != NULL) { debugs(33, 5, "ACCEL VHOST REWRITE: vhost=" << host << " + vport=" << vport); char thost[256]; if (vport > 0) { thost[0] = '\0'; char *t = NULL; if (host[strlen(host)] != ']' && (t = strrchr(host,':')) != NULL) { strncpy(thost, host, (t-host)); snprintf(thost+(t-host), sizeof(thost)-(t-host), ":%d", vport); host = thost; } else if (!t) { snprintf(thost, sizeof(thost), "%s:%d",host, vport); host = thost; } } // else nothing to alter port-wise. int url_sz = strlen(url) + 32 + Config.appendDomainLen + strlen(host); http->uri = (char *)xcalloc(url_sz, 1); const char *protocol = switchedToHttps ? "https" : URLScheme(conn->port->transport.protocol).const_str(); snprintf(http->uri, url_sz, "%s://%s%s", protocol, host, url); 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 + + int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen + strlen(conn->port->defaultsite); http->uri = (char *)xcalloc(url_sz, 1); char vportStr[32]; vportStr[0] = '\0'; if (vport > 0) { snprintf(vportStr, sizeof(vportStr),":%d",vport); } snprintf(http->uri, url_sz, "%s://%s%s%s", URLScheme(conn->port->transport.protocol).const_str(), conn->port->defaultsite, vportStr, url); debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: '" << http->uri <<"'"); } 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; + 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(conn->port->transport.protocol).const_str(), ipbuf, vport, url); debugs(33, 5, "ACCEL VPORT REWRITE: '" << http->uri << "'"); } } static void -prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr) +prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, const Http::Http1ParserPointer &hp) { char *host; char ipbuf[MAX_IPSTRLEN]; - if (*url != '/') + if (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 ((host = hp->getHeaderField("Host")) != NULL) { + 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, 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; + 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(), hp->requestUri().c_str()); debugs(33, 5, "TRANSPARENT REWRITE: '" << http->uri << "'"); } } /** Parse an HTTP request * * \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 Http::Http1Parser * \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 * \param[out] http_ver will be set as a side-effect of the parsing * \return NULL on incomplete requests, * a ClientSocketContext structure on success or failure. */ static ClientSocketContext * -parseHttpRequest(ConnStateData *csd, HttpParser *hp, HttpRequestMethod * method_p, Http::ProtocolVersion *http_ver) +parseHttpRequest(ConnStateData *csd, const Http::Http1ParserPointer &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; - } + /* Attempt to parse the first line; this will define where the method, url, version and header begin */ + { + bool parsedOk = hp->parseRequest(); - 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"); + 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); + + if (!parsedOk) + return parseHttpRequestAbort(csd, "error:invalid-request"); } /* We know the whole request is in hp->buf now */ - + req_sz = hp->messageHeaderSize(); 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); - /* 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; 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; return parseHttpRequestAbort(csd, "error:unsupported-request-method"); } /* * 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); + http->req_sz = hp->messageHeaderSize(); result = ClientSocketContextNew(csd->clientConnection, http); tempBuffer.data = result->reqbuf; tempBuffer.length = HTTP_REQBUF_SZ; ClientStreamData newServer = new clientReplyContext(http); ClientStreamData newClient = result; clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach, 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 = hp->requestUri().c_str(); debugs(33,5, HERE << "repare absolute URL from " << (csd->transparent()?"intercept":(csd->port->flags.accelSurrogate ? "accel":""))); /* Rewrite the URL in transparent or accelerator mode */ /* NP: there are several cases to traverse here: * - standard mode (forward proxy) * - transparent mode (TPROXY) * - transparent mode with failures * - intercept mode (NAT) * - intercept mode with failures * - accelerator mode (reverse proxy) * - internal URL * - mixed combos of the above with internal URL */ 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 */ /* prepend our name & port */ http->uri = xstrdup(internalLocalUri(NULL, url)); // We just re-wrote the URL. Must replace the Host: header. // But have not parsed there yet!! flag for local-only handling. http->flags.internal = true; } 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; http->uri = (char *)xcalloc(url_sz, 1); strcpy(http->uri, url); } debugs(33, 5, "parseHttpRequest: Complete request received"); // 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; } int ConnStateData::getAvailableBufferLength() const { assert (in.allocatedSize > in.notYetUsed); // allocated more than used const size_t result = in.allocatedSize - in.notYetUsed - 1; // huge request_header_max_size may lead to more than INT_MAX unused space assert (static_cast(result) <= INT_MAX); return result; } bool ConnStateData::maybeMakeSpaceAvailable() { if (getAvailableBufferLength() < 2) { size_t newSize; if (in.allocatedSize >= Config.maxRequestBufferSize) { debugs(33, 4, "request buffer full: client_request_buffer_max_size=" << Config.maxRequestBufferSize); @@ -2621,124 +2577,126 @@ srvCert, NULL); err->detail = errDetail; // Save the original request for logging purposes. if (!context->http->al->request) { context->http->al->request = request; HTTPMSGLOCK(context->http->al->request); } repContext->setReplyToError(request->method, err); assert(context->http->out.offset == 0); context->pullData(); return true; } } } return false; } #endif // USE_SSL static void -clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, Http::ProtocolVersion http_ver) +clientProcessRequest(ConnStateData *conn, const Http::Http1ParserPointer &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 HttpRequestMethodPointer method = hp->method(); /* We have an initial client stream in place should it be needed */ /* setup our private context */ context->registerWithConn(); 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) { case Http::scHeaderTooLarge: - repContext->setReplyToError(ERR_TOO_BIG, Http::scBadRequest, method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf, NULL); + repContext->setReplyToError(ERR_TOO_BIG, Http::scBadRequest, *method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf, NULL); break; case Http::scMethodNotAllowed: - repContext->setReplyToError(ERR_UNSUP_REQ, Http::scMethodNotAllowed, method, http->uri, + repContext->setReplyToError(ERR_UNSUP_REQ, Http::scMethodNotAllowed, *method, http->uri, 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); context->pullData(); goto finish; } - if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) { + if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, *method)) == NULL) { clientStreamNode *node = context->getClientReplyContext(); debugs(33, 5, "Invalid URL: " << http->uri); 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_INVALID_URL, Http::scBadRequest, method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL); + repContext->setReplyToError(ERR_INVALID_URL, Http::scBadRequest, *method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL); assert(context->http->out.offset == 0); context->pullData(); goto finish; } /* RFC 2616 section 10.5.6 : handle unsupported HTTP major versions cleanly. */ /* We currently only support 0.9, 1.0, 1.1 properly */ if ( (http_ver.major == 0 && http_ver.minor != 9) || (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->rawHeaderBuf()); 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); + repContext->setReplyToError(ERR_UNSUP_HTTPVERSION, Http::scHttpVersionNotSupported, *method, http->uri, + 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); clientReplyContext *repContext = dynamic_cast(node->data.getRaw()); assert (repContext); - repContext->setReplyToError(ERR_INVALID_REQ, Http::scBadRequest, method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL); + repContext->setReplyToError(ERR_INVALID_REQ, Http::scBadRequest, *method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL); assert(context->http->out.offset == 0); context->pullData(); goto finish; } request->clientConnectionManager = conn; request->flags.accelerated = http->flags.accel; request->flags.sslBumped=conn->switchedToHttps(); request->flags.ignoreCc = conn->port->ignore_cc; // TODO: decouple http->flags.accel from request->flags.sslBumped request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ? !conn->port->allow_direct : 0; #if USE_AUTH if (request->flags.sslBumped) { if (conn->getAuth() != NULL) request->auth_user_request = conn->getAuth(); } #endif @@ -2769,55 +2727,59 @@ request->port = getMyPort(); http->flags.internal = true; } } if (http->flags.internal) { request->protocol = AnyP::PROTO_HTTP; request->login[0] = '\0'; } request->flags.internal = http->flags.internal; setLogUri (http, urlCanonicalClean(request.getRaw())); request->client_addr = conn->clientConnection->remote; // XXX: remove reuest->client_addr member. #if FOLLOW_X_FORWARDED_FOR // indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:) // not a details about teh TCP connection itself request->indirect_client_addr = conn->clientConnection->remote; #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. request->clientConnectionManager = conn; if (request->header.chunked()) { chunked = true; } else if (request->header.has(HDR_TRANSFER_ENCODING)) { const String te = request->header.getList(HDR_TRANSFER_ENCODING); // HTTP/1.1 requires chunking to be the last encoding if there is one unsupportedTe = te.size() && te != "identity"; } // else implied identity coding - mustReplyToOptions = (method == Http::METHOD_OPTIONS) && + mustReplyToOptions = (*method == Http::METHOD_OPTIONS) && (request->header.getInt64(HDR_MAX_FORWARDS) == 0); if (!urlCheckRequest(request.getRaw()) || mustReplyToOptions || unsupportedTe) { clientStreamNode *node = context->getClientReplyContext(); conn->quitAfterError(request.getRaw()); clientReplyContext *repContext = dynamic_cast(node->data.getRaw()); assert (repContext); repContext->setReplyToError(ERR_UNSUP_REQ, Http::scNotImplemented, request->method, NULL, conn->clientConnection->remote, request.getRaw(), NULL, NULL); assert(context->http->out.offset == 0); context->pullData(); goto finish; } if (!chunked && !clientIsContentLengthValid(request.getRaw())) { clientStreamNode *node = context->getClientReplyContext(); clientReplyContext *repContext = dynamic_cast(node->data.getRaw()); assert (repContext); conn->quitAfterError(request.getRaw()); repContext->setReplyToError(ERR_INVALID_REQ, Http::scLengthRequired, request->method, NULL, @@ -2941,87 +2903,92 @@ const int concurrentRequestLimit = Config.pipeline_max_prefetch + 1; // when queue filled already we cant add more. if (existingRequestCount >= concurrentRequestLimit) { debugs(33, 3, clientConnection << " max concurrent requests reached (" << concurrentRequestLimit << ")"); debugs(33, 5, clientConnection << " deferring new request until one is done"); return true; } return false; } /** * Attempt to parse one or more requests from the input buffer. * If a request is successfully parsed, even if the next request * is only partially parsed, it will return TRUE. */ bool ConnStateData::clientParseRequests() { - HttpRequestMethod method; bool parsed_req = false; debugs(33, 5, HERE << clientConnection << ": attempting to parse"); // 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) break; /* Limit the number of concurrent requests */ if (concurrentRequestQueueFilled()) break; /* Should not be needed anymore */ /* Terminate the string */ in.buf[in.notYetUsed] = '\0'; /* 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 Http::Http1Parser(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_); 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"); AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout", CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http)); commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall); - clientProcessRequest(this, &parser_, context, method, http_ver); + clientProcessRequest(this, parser_, context); parsed_req = true; // XXX: do we really need to parse everything right NOW ? if (context->mayUseConnection()) { debugs(33, 3, HERE << "Not parsing new requests, as this request may need the connection"); break; } } } /* XXX where to 'finish' the parsing pass? */ return parsed_req; } void ConnStateData::clientReadRequest(const CommIoCbParams &io) { debugs(33,5,HERE << io.conn << " size " << io.size); Must(reading()); reader = NULL; === modified file 'src/client_side.h' --- src/client_side.h 2013-08-22 18:39:41 +0000 +++ src/client_side.h 2013-12-23 11:32:15 +0000 @@ -18,41 +18,41 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #ifndef SQUID_CLIENTSIDE_H #define SQUID_CLIENTSIDE_H #include "comm.h" #include "HttpControlMsg.h" -#include "HttpParser.h" +#include "http/forward.h" #if USE_AUTH #include "auth/UserRequest.h" #endif #if USE_SSL #include "ssl/support.h" #endif class ConnStateData; class ClientHttpRequest; class clientStreamNode; class ChunkedCodingParser; class HelperReply; namespace AnyP { class PortCfg; } // namespace Anyp /** * Badly named. * This is in fact the processing context for a single HTTP request. @@ -381,43 +381,42 @@ protected: void startDechunkingRequest(); void finishDechunkingRequest(bool withSuccess); void abortChunkedRequestBody(const err_type error); err_type handleChunkedRequestBody(size_t &putSize); void startPinnedConnectionMonitoring(); void clientPinnedConnectionRead(const CommIoCbParams &io); private: int connReadWasError(comm_err_t flag, int size, int xerrno); int connFinishedWithConn(int size); void clientAfterReadingRequests(); bool concurrentRequestQueueFilled() const; #if USE_AUTH /// some user details that can be used to perform authentication on this connection 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 + Http::Http1ParserPointer parser_; #if USE_SSL bool switchedToHttps_; /// The SSL server host name appears in CONNECT request or the server ip address for the intercepted requests String sslConnectHostOrIp; ///< The SSL server host name as passed in the CONNECT request String sslCommonName; ///< CN name for SSL certificate generation String sslBumpCertKey; ///< Key to use to store/retrieve generated certificate /// HTTPS server cert. fetching state for bump-ssl-server-first Ssl::ServerBump *sslServerBump; Ssl::CertSignAlgorithm signAlgorithm; ///< The signing algorithm to use #endif /// the reason why we no longer write the response or nil const char *stoppedSending_; /// the reason why we no longer read the request or nil const char *stoppedReceiving_; AsyncCall::Pointer reader; ///< set when we are reading BodyPipe::Pointer bodyPipe; // set when we are reading request body === 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 @@ -16,45 +16,43 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #ifndef SQUID_HTCP_H #define SQUID_HTCP_H #if USE_HTCP #include "HttpHeader.h" -#include "HttpRequestMethod.h" +#include "http/forward.h" #include "ip/forward.h" -class HttpRequest; - /// \ingroup ServerProtocolHTCP class HtcpReplyData { public: HtcpReplyData(); int hit; HttpHeader hdr; uint32_t msg_id; double version; struct cto_t { /* cache-to-origin */ double rtt; int samp; int hops; } cto; }; /// \ingroup ServerProtocolHTCP === renamed file 'src/HttpParser.cc' => 'src/http/Http1Parser.cc' --- src/HttpParser.cc 2013-03-16 04:57:43 +0000 +++ src/http/Http1Parser.cc 2013-12-31 14:47:57 +0000 @@ -1,71 +1,139 @@ #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() +Http::Http1Parser::clear() { - state = HTTP_PARSE_NONE; + completedState_ = HTTP_PARSE_NONE; request_parse_status = Http::scNone; buf = NULL; bufsiz = 0; + parseOffset_ = 0; 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; + msgProtocol_ = AnyP::ProtocolVersion(); + method_ = NULL; + mimeHeaderBlock_.clear(); } void -HttpParser::reset(const char *aBuf, int len) +Http::Http1Parser::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, "Request parse " << Raw("buf", buf, bufsiz)); } -int -HttpParser::parseRequestFirstLine() +/** + * 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 +Http::Http1Parser::skipGarbageLines() { - 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 + req.start = parseOffset_; // avoid re-parsing any portion we managed to complete - debugs(74, 5, HERE << "parsing possible request: " << buf); - - // Single-pass parse: (provided we have the whole line anyways) +#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 - req.start = 0; + /* 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 +Http::Http1Parser::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, "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 = 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) if (buf[i] == ' ') { last_whitespace = i; if (first_whitespace < req.start) first_whitespace = i; } // track next non-SP/non-HT byte after first_whitespace if (second_word < first_whitespace && buf[i] != ' ' && buf[i] != '\t') { second_word = i; } // locate line terminator if (buf[i] == '\n') { req.end = i; line_end = i - 1; break; } @@ -115,180 +183,246 @@ // generating HTTP status for any aborts as we go. // First non-whitespace = beginning of method if (req.start > line_end) { request_parse_status = Http::scBadRequest; return -1; } req.m_start = req.start; // First whitespace = end of method if (first_whitespace > line_end || first_whitespace < req.start) { request_parse_status = Http::scBadRequest; // no method return -1; } req.m_end = first_whitespace - 1; if (req.m_end < req.m_start) { request_parse_status = Http::scBadRequest; // missing URI? return -1; } + /* Set method_ */ + method_ = new 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 return -1; } req.u_start = second_word; // 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. req.u_end = last_whitespace; // crop any trailing whitespace in the area we think of as URI for (; req.u_end >= req.u_start && xisspace(buf[req.u_end]); --req.u_end); } if (req.u_end < req.u_start) { request_parse_status = Http::scBadRequest; // missing URI 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) { request_parse_status = Http::scBadRequest; // missing version return -1; } req.v_start = last_whitespace + 1; req.v_end = line_end; // We only accept HTTP protocol requests right now. // TODO: accept other protocols; RFC 2326 (RTSP protocol) etc if ((req.v_end - req.v_start +1) < 5 || strncasecmp(&buf[req.v_start], "HTTP/", 5) != 0) { #if 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. request_parse_status = Http::scHttpVersionNotSupported; return -1; #endif } + msgProtocol_.protocol = AnyP::PROTO_HTTP; int i = req.v_start + sizeof("HTTP/") -1; /* next should be 1 or more digits */ if (!isdigit(buf[i])) { request_parse_status = Http::scHttpVersionNotSupported; return -1; } int maj = 0; for (; i <= line_end && (isdigit(buf[i])) && maj < 65536; ++i) { maj = maj * 10; maj = maj + (buf[i]) - '0'; } // catch too-big values or missing remainders if (maj >= 65536 || i > line_end) { 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] != '.') { request_parse_status = Http::scHttpVersionNotSupported; return -1; } // catch missing minor part if (++i > line_end) { request_parse_status = Http::scHttpVersionNotSupported; return -1; } /* next should be one or more digits */ if (!isdigit(buf[i])) { request_parse_status = Http::scHttpVersionNotSupported; return -1; } int min = 0; for (; i <= line_end && (isdigit(buf[i])) && min < 65536; ++i) { min = min * 10; min = min + (buf[i]) - '0'; } // catch too-big values or trailing garbage if (min >= 65536 || i < line_end) { 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) +bool +Http::Http1Parser::parseRequest() { - 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; -} + // stage 1: locate the request-line + if (completedState_ == HTTP_PARSE_NEW) { + if (skipGarbageLines() && (size_t)bufsiz < parseOffset_) + return 0; + } + + // 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; + } + } -#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; -} + // 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) { + debugs(33, 5, "Incomplete request, waiting for end of headers"); + return false; + } + mimeHeaderBlock_.assign(&buf[req.end+1], mimeHeaderBytes); -/* - * 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; -} + } else + debugs(33, 3, "Missing HTTP/1.x identifier"); -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; + // 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; + } + + return isDone(); } -int -HttpParserRequestLen(HttpParser *hp) +// arbitrary maximum-length for headers which can be found by Http1Parser::getHeaderField() +#define GET_HDR_SZ 1024 + +char * +Http::Http1Parser::getHeaderField(const char *name) { - return hp->hdr_end - hp->req.start + 1; -} -#endif + 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 2013-12-31 15:54:41 +0000 @@ -1,97 +1,135 @@ -#ifndef _SQUID_SRC_HTTPPARSER_H -#define _SQUID_SRC_HTTPPARSER_H +#ifndef _SQUID_SRC_HTTP_HTTP1PARSER_H +#define _SQUID_SRC_HTTP_HTTP1PARSER_H +#include "base/RefCount.h" +#include "http/forward.h" +#include "http/ProtocolVersion.h" #include "http/StatusCode.h" +#include "SBuf.h" + +namespace Http { // 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: * * \item Request Line (method, URL, protocol, version) * \item Mime header block */ -class HttpParser +class Http1Parser : public RefCountable { public: - HttpParser() { clear(); } + typedef RefCount Pointer; + + Http1Parser() { clear(); } /** Initialize a new parser. * 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); }; + Http1Parser(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(); /// Reset the parser for use on a new buffer. void reset(const char *aBuf, int len); + /** Whether the parser is already done processing the buffer. + * Use to determine between incomplete data and errors results + * from the parse methods. + */ + bool isDone() const {return completedState_==HTTP_PARSE_DONE;} + + /// size in bytes of the first line (request-line) + /// including CRLF terminator + int64_t firstLineSize() const {return req.end - req.start + 1;} + + /// size in bytes of the message headers including CRLF terminator(s) + /// but excluding request-line bytes + int64_t headerBlockSize() const {return mimeHeaderBlock_.length();} + + /// size in bytes of HTTP message block, includes request-line and mime headers + /// excludes any body/entity/payload bytes + /// excludes any garbage prefix before the request-line + int64_t messageHeaderSize() const {return firstLineSize() + headerBlockSize();} + + /// buffer containing HTTP mime headers, excluding request or status line. + const char *rawHeaderBuf() {return mimeHeaderBlock_.c_str();} + + /** Attempt to parse a request. + * \return true if a valid request was parsed. + * \note Use isDone() method to determine between incomplete parse and errors. + */ + bool parseRequest(); + /** - * 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; + /// the protocol label for this message + const AnyP::ProtocolVersion & messageProtocol() const {return msgProtocol_;} + + /// the HTTP method if this is a request method + const HttpRequestMethodPointer & method() const {return method_;} + + /// the request-line URI if this is a request, or an empty string. + SBuf requestUri() const {return uri_;} + + // 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; + +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; + /// byte offset for non-parsed region of the buffer + size_t parseOffset_; - // TODO: Offsets for pieces of the (HTTP reply) Status-Line as per RFC 2616 + /// what stage the parser is currently up to + uint8_t completedState_; - /** 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 protocol label has been found in the first line + AnyP::ProtocolVersion msgProtocol_; + + /// what request method has been found on the first line + HttpRequestMethodPointer method_; + + /// raw copy of the origina client reqeust-line URI field + SBuf uri_; + + /// buffer holding the mime headers + SBuf mimeHeaderBlock_; }; -// 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 +} // namespace Http -#endif /* _SQUID_SRC_HTTPPARSER_H */ +#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 2013-12-31 03:40:37 +0000 @@ -1,19 +1,24 @@ include $(top_srcdir)/src/Common.am include $(top_srcdir)/src/TestHeaders.am 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 \ StatusLine.h MethodType.cc: MethodType.h $(top_srcdir)/src/mk-string-arrays.awk ($(AWK) -f $(top_srcdir)/src/mk-string-arrays.awk < $(srcdir)/MethodType.h | \ sed -e 's%METHOD_%%' -e 's%_C%-C%' >$@) || ($(RM) -f $@ && exit 1) CLEANFILES += MethodType.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 @@ -1,26 +1,26 @@ /* * DEBUG: section 73 HTTP Request */ #include "squid.h" -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" #include "SquidConfig.h" #include "wordlist.h" static Http::MethodType & operator++ (Http::MethodType &aMethod) { int tmp = (int)aMethod; aMethod = (Http::MethodType)(++tmp); return aMethod; } /** * Construct a HttpRequestMethod from a NULL terminated string such as "GET" * or from a range of chars, * such as "GET" from "GETFOOBARBAZ" * (pass in pointer to G and pointer to F.) */ HttpRequestMethod::HttpRequestMethod(char const *begin, char const *end) : theMethod (Http::METHOD_NONE) { if (begin == NULL) return; === 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,45 +1,39 @@ #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 /** * This class represents an HTTP Request METHOD * - i.e. PUT, POST, GET etc. * 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() {} /** \param begin string to convert to request method. \param end end of the method string (relative to begin). Use NULL if this is unknown. * \note DO NOT give end a default (ie NULL). That will cause silent char* conversion clashes. */ HttpRequestMethod(char const * begin, char const * end); HttpRequestMethod & operator = (const HttpRequestMethod& aMethod) { theMethod = aMethod.theMethod; theImage = aMethod.theImage; return *this; } HttpRequestMethod & operator = (Http::MethodType const aMethod) { theMethod = aMethod; theImage.clean(); return *this; === added file 'src/http/forward.h' --- src/http/forward.h 1970-01-01 00:00:00 +0000 +++ src/http/forward.h 2013-12-23 16:22:03 +0000 @@ -0,0 +1,26 @@ +#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 { + +class Http1Parser; +typedef RefCount Http1ParserPointer; + +//class ParserBase; +//typedef RefCount HttpParserPointer; + +} // namespace Http + +#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 @@ -1,29 +1,29 @@ /* * DEBUG: section 16 Cache Manager API * */ #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" namespace Mgr { /// Cache Manager Action parameters extracted from the user request class ActionParams { public: ActionParams(); explicit ActionParams(const Ipc::TypedMsgHdr &msg); ///< load from msg void pack(Ipc::TypedMsgHdr &msg) const; ///< store into msg public: /* details of the client HTTP request that caused the action */ String httpUri; ///< HTTP request URI HttpRequestMethod httpMethod; ///< HTTP request method === 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 @@ -1,30 +1,29 @@ /* * DEBUG: section 16 Cache Manager API * */ #ifndef SQUID_MGR_ACTION_WRITER_H #define SQUID_MGR_ACTION_WRITER_H #include "comm/forward.h" -#include "HttpRequestMethod.h" #include "mgr/StoreToCommWriter.h" namespace Mgr { /// Creates Store entry, fills it using action's fillEntry(), and /// Comm-writes it using parent StoreToCommWriter. class ActionWriter: public StoreToCommWriter { public: ActionWriter(const Action::Pointer &anAction, const Comm::ConnectionPointer &conn); protected: /* AsyncJob API */ virtual void start(); private: Action::Pointer action; ///< action that fills the entry CBDATA_CLASS2(ActionWriter); === 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 @@ -1,30 +1,29 @@ /* * DEBUG: section 16 Cache Manager API * */ #ifndef SQUID_MGR_FILLER_H #define SQUID_MGR_FILLER_H #include "comm/forward.h" -#include "HttpRequestMethod.h" #include "mgr/Action.h" #include "mgr/StoreToCommWriter.h" namespace Mgr { /// provides Coordinator with a local cache manager response class Filler: public StoreToCommWriter { public: Filler(const Action::Pointer &anAction, const Comm::ConnectionPointer &conn, unsigned int aRequestId); protected: /* AsyncJob API */ virtual void start(); virtual void swanSong(); private: Action::Pointer action; ///< action that will run() and sendResponse() unsigned int requestId; ///< the ID of the Request we are responding to === modified file 'src/mime.cc' --- src/mime.cc 2013-03-23 11:47:20 +0000 +++ src/mime.cc 2013-12-31 03:43:08 +0000 @@ -34,42 +34,40 @@ #include "disk.h" #include "fde.h" #include "globals.h" #include "HttpHdrCc.h" #include "HttpReply.h" #include "HttpRequest.h" #include "internal.h" #include "Mem.h" #include "MemBuf.h" #include "MemObject.h" #include "mime.h" #include "RequestFlags.h" #include "SquidConfig.h" #include "Store.h" #include "StoreClient.h" #if HAVE_SYS_STAT_H #include #endif -#define GET_HDR_SZ 1024 - /* forward declarations */ static void mimeFreeMemory(void); static char const *mimeGetIcon(const char *fn); class MimeIcon : public StoreClient { public: explicit MimeIcon(const char *aName); ~MimeIcon(); void setName(char const *); char const * getName() const; void load(); void created(StoreEntry *newEntry); MEMPROXY_CLASS(MimeIcon); private: const char *icon_; char *url_; }; MEMPROXY_CLASS_INLINE(MimeIcon); === 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 @@ -14,125 +14,43 @@ * incorporates software developed and/or copyrighted by other * sources; see the CREDITS file for full details. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #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) { size_t e = 0; int state = 1; PROF_start(headersEnd); while (e < l && state < 3) { switch (state) { case 0: if ('\n' == mime[e]) state = 1; break; case 1: if ('\r' == mime[e]) === 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 @@ -16,25 +16,23 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #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_ */ === 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 2013-12-30 13:53:06 +0000 @@ -1,1101 +1,1232 @@ #define SQUID_UNIT_TEST 1 #include "squid.h" #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) return; Mem::Init(); setup_done = true; } void -testHttpParser::testParseRequestLineProtocols() +testHttp1Parser::testParseRequestLineProtocols() { // ensure MemPools etc exist globalSetup(); MemBuf input; - HttpParser output; + Http::Http1Parser output; input.init(); // TEST: Do we comply with RFC 1945 section 5.1 ? // TEST: Do we comply with RFC 2616 section 5.1 ? // RFC 1945 : HTTP/0.9 simple-request { input.append("GET /\r\n", 7); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(true, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0,memcmp("GET /\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start], (output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start], (output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(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(); } // RFC 1945 : invalid HTTP/0.9 simple-request (only GET is valid) #if 0 { input.append("POST /\r\n", 7); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(true, output.parseRequest()); + 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 // RFC 1945 and 2616 : HTTP/1.0 request { input.append("GET / HTTP/1.0\r\n", 16); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.0\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.0", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // RFC 2616 : HTTP/1.1 request { 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.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.2\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.2", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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.parseRequest()); // 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/10.12\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(15, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/10.12", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // This stage of the parser does not yet accept non-HTTP protocol names. { // violations mode treats them as HTTP/0.9 requests! input.append("GET / FOO/1.0\n", 14); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); #if USE_HTTP_VIOLATIONS - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(true, output.parseRequest()); + 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.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / FOO/1.0\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(12, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("FOO/1.0", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); input.reset(); } // no version { input.append("GET / HTTP/\n", 12); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(10, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // no major version { input.append("GET / HTTP/.1\n", 14); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(12, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // no version dot { input.append("GET / HTTP/11\n", 14); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/11\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(12, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/11", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // negative major version (bug 3062) { input.append("GET / HTTP/-999999.1\n", 21); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/-999999.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(19, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/-999999.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // no minor version { input.append("GET / HTTP/1.\n", 14); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(12, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // negative major version (bug 3062 corollary) { input.append("GET / HTTP/1.-999999\n", 21); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.-999999\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(19, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.-999999", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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; + Http::Http1Parser output; input.init(); // space padded URL { input.append("GET / HTTP/1.1\r\n", 21); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); CPPUNIT_ASSERT_EQUAL(5, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(11, output.req.v_start); CPPUNIT_ASSERT_EQUAL(18, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // whitespace inside URI. (nasty but happens) { 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.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0,memcmp("GET /fo o/ HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(9, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/fo o/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(11, output.req.v_start); CPPUNIT_ASSERT_EQUAL(18, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // additional data in buffer { input.append("GET / HTTP/1.1\nboo!", 23); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); // strangeness generated by following RFC CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(10, output.req.v_start); CPPUNIT_ASSERT_EQUAL(17, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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; + Http::Http1Parser output; input.init(); // alternative EOL sequence: NL-only { input.append("GET / HTTP/1.1\n", 15); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // alternative EOL sequence: double-NL-only { input.append("GET / HTTP/1.1\n\n", 16); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(true, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-2, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // RELAXED alternative EOL sequence: multi-CR-NL { input.append("GET / HTTP/1.1\r\r\r\n", 18); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); Config.onoff.relaxed_header_parser = 1; // Being tolerant we can ignore and elide these apparently benign CR - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\r\r\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(6, output.req.v_start); CPPUNIT_ASSERT_EQUAL(13, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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 { input.append("GET / HTTP/1.1\r\r\r\n", 18); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); // strict mode treats these as several bare-CR in the request line which is explicitly invalid. Config.onoff.relaxed_header_parser = 0; - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL(-1, output.req.end); CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start); CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } // space padded version { // RFC 1945 and 2616 specify version is followed by CRLF. No intermediary bytes. // NP: the terminal whitespace is a special case: invalid for even HTTP/0.9 with no version tag input.append("GET / HTTP/1.1 \n", 16); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1 \n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(13, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/ HTTP/1.1", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } // incomplete line at various positions { input.append("GET", 3); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(0, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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(!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.parseRequest()); + 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(!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.parseRequest()); + 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(!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.parseRequest()); + 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(!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; + Http::Http1Parser output; input.init(); // RFC 2616 : . method { input.append(". / HTTP/1.1\n", 13); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp(". / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(0, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp(".", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(".", NULL), *output.method_); CPPUNIT_ASSERT_EQUAL(2, output.req.u_start); CPPUNIT_ASSERT_EQUAL(2, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(4, output.req.v_start); CPPUNIT_ASSERT_EQUAL(11, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // OPTIONS with * URL { input.append("OPTIONS * HTTP/1.1\n", 19); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("OPTIONS * HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(6, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("OPTIONS", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_OPTIONS), *output.method_); CPPUNIT_ASSERT_EQUAL(8, output.req.u_start); CPPUNIT_ASSERT_EQUAL(8, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("*", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(10, output.req.v_start); CPPUNIT_ASSERT_EQUAL(17, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // unknown method { input.append("HELLOWORLD / HTTP/1.1\n", 22); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("HELLOWORLD / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(9, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HELLOWORLD", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("HELLOWORLD",NULL), *output.method_); CPPUNIT_ASSERT_EQUAL(11, output.req.u_start); CPPUNIT_ASSERT_EQUAL(11, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(13, output.req.v_start); CPPUNIT_ASSERT_EQUAL(20, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // method-only { 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.parseRequest()); + 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(!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\n", 4); { //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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(!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(); } // RELAXED space padded method (in strict mode SP is reserved so invalid as a method byte) { input.append(" GET / HTTP/1.1\n", 16); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); Config.onoff.relaxed_header_parser = 1; - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(1, output.req.m_start); CPPUNIT_ASSERT_EQUAL(3, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); CPPUNIT_ASSERT_EQUAL(5, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(7, output.req.v_start); CPPUNIT_ASSERT_EQUAL(14, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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) { input.append(" GET / HTTP/1.1\n", 16); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); Config.onoff.relaxed_header_parser = 0; - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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(!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(); } // tab padded method (NP: tab is not SP so treated as any other binary) { 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.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("\tGET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(3, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("\tGET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(&output.buf[output.req.m_start],&output.buf[output.req.m_end+1]), *output.method_); CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); CPPUNIT_ASSERT_EQUAL(5, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(7, output.req.v_start); CPPUNIT_ASSERT_EQUAL(14, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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; + Http::Http1Parser output; input.init(); // no method (but in a form which is ambiguous with HTTP/0.9 simple-request) { // XXX: Bug: HTTP/0.9 requires method to be "GET" 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.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/ HTTP/1.0\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(0, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("/",NULL), *output.method_); CPPUNIT_ASSERT_EQUAL(2, output.req.u_start); CPPUNIT_ASSERT_EQUAL(9, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.0", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(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(); } // RELAXED no method (an invalid format) { input.append(" / HTTP/1.0\n", 12); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - // When tolerantly ignoring SP prefix this case becomes ambiguous with HTTP/0.9 simple-request) + // 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.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("/ HTTP/1.0\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(1, output.req.m_start); CPPUNIT_ASSERT_EQUAL(1, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("/",NULL), *output.method_); CPPUNIT_ASSERT_EQUAL(3, output.req.u_start); CPPUNIT_ASSERT_EQUAL(10, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.0", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(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) { 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) Config.onoff.relaxed_header_parser = 0; - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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(!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(); } // binary code in method (strange but ...) { 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.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\x0B / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(3, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\x0B", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); +// CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("GET\0x0B",NULL), *output.method_); CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); CPPUNIT_ASSERT_EQUAL(5, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(7, output.req.v_start); CPPUNIT_ASSERT_EQUAL(14, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // CR in method { // RFC 2616 sec 5.1 prohibits CR other than in terminator. input.append("GET\r / HTTP/1.1\r\n", 16); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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(!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(); } // binary code NUL! in method (strange but ...) { input.append("GET\0 / HTTP/1.1\n", 16); //printf("TEST: %d-%d/%d '%.*s'\n", output.req.start, output.req.end, input.contentSize(), 16, input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\0 / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(3, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\0", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); +// CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("GET\0",NULL), *output.method_); CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); CPPUNIT_ASSERT_EQUAL(5, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(7, output.req.v_start); CPPUNIT_ASSERT_EQUAL(14, output.req.v_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1))); - CPPUNIT_ASSERT_EQUAL(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(); } // no URL (grammer otherwise correct) { 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.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(5, output.req.u_start); CPPUNIT_ASSERT_EQUAL(12, output.req.u_end); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(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(); } // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request) { input.append("GET HTTP/1.1\n", 13); //printf("TEST: '%s'\n",input.content()); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(true, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(2, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), *output.method_); CPPUNIT_ASSERT_EQUAL(4, output.req.u_start); CPPUNIT_ASSERT_EQUAL(11, output.req.u_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1))); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(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(); } // binary line { 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.parseRequest()); + 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(!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(); } // mixed whitespace line { // We accept non-space binary bytes for method so first \t shows up as that // but remaining space and tabs are skipped searching for URI-start input.append("\t \t \t\n", 6); //printf("TEST: mixed whitespace\n"); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + CPPUNIT_ASSERT_EQUAL(true, output.isDone()); CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status); CPPUNIT_ASSERT_EQUAL(0, output.req.start); CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end); CPPUNIT_ASSERT_EQUAL(0, memcmp("\t \t \t\n", &output.buf[output.req.start],(output.req.end-output.req.start+1))); CPPUNIT_ASSERT_EQUAL(0, output.req.m_start); CPPUNIT_ASSERT_EQUAL(0, output.req.m_end); CPPUNIT_ASSERT_EQUAL(0, memcmp("\t", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1))); + CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(&output.buf[output.req.m_start],&output.buf[output.req.m_end+1]), *output.method_); CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_maj); - CPPUNIT_ASSERT_EQUAL(0, output.req.v_min); + CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); input.reset(); } // mixed whitespace line with CR middle { // CR aborts on sight, so even initial \t method is not marked as above // (not when parsing clean with whole line available anyway) input.append("\t \r \n", 6); //printf("TEST: mixed whitespace with CR\n"); output.reset(input.content(), input.contentSize()); - CPPUNIT_ASSERT_EQUAL(-1, HttpParserParseReqLine(&output)); + CPPUNIT_ASSERT_EQUAL(false, output.parseRequest()); + 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(!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); + + Http::Http1Parser 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.parseRequest(); + +#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,27 +1,30 @@ -#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: void globalSetup(); // MemPools init etc. // request-line unit tests void testParseRequestLineTerminators(); // terminator detection correct void testParseRequestLineMethods(); // methoid detection correct 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 @@ -1,26 +1,26 @@ #define SQUID_UNIT_TEST 1 #include "squid.h" #include -#include "HttpRequestMethod.h" +#include "http/RequestMethod.h" #include "Mem.h" #include "SquidConfig.h" #include "testHttpRequestMethod.h" #if HAVE_SSTREAM #include #endif CPPUNIT_TEST_SUITE_REGISTRATION( testHttpRequestMethod ); /* * We should be able to make an HttpRequestMethod straight from a string. */ void testHttpRequestMethod::testConstructCharStart() { /* parse an empty string -> Http::METHOD_NONE */ CPPUNIT_ASSERT(HttpRequestMethod(NULL,NULL) == Http::METHOD_NONE); /* parsing a literal should work */ CPPUNIT_ASSERT(HttpRequestMethod("GET", NULL) == Http::METHOD_GET);