# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: kinkie@squid-cache.org-20140106212057-vcewa3telo4kq2er # target_branch: file:///home/kinkie/squid/workspace/trunk/ # testament_sha1: 202894c2b71d744c260b7b8798102f295e879d8e # timestamp: 2014-01-06 22:21:02 +0100 # base_revision_id: kinkie@squid-cache.org-20140105130744-\ # gqkbrnvpk1feg3bm # # Begin patch === modified file 'configure.ac' --- configure.ac 2013-12-12 09:41:39 +0000 +++ configure.ac 2013-12-19 16:00:30 +0000 @@ -3429,6 +3429,7 @@ src/ipc/Makefile src/ssl/Makefile src/mgr/Makefile + src/parser/Makefile src/snmp/Makefile contrib/Makefile icons/Makefile === modified file 'src/Makefile.am' --- src/Makefile.am 2014-01-03 10:32:53 +0000 +++ src/Makefile.am 2014-01-05 16:57:44 +0000 @@ -46,8 +46,8 @@ LoadableModules.h \ LoadableModules.cc -SUBDIRS = base anyp comm eui acl format fs repl -DIST_SUBDIRS = base anyp comm eui acl format fs repl +SUBDIRS = base anyp parser comm eui acl format fs repl +DIST_SUBDIRS = base anyp parser comm eui acl format fs repl if ENABLE_AUTH SUBDIRS += auth @@ -646,6 +646,7 @@ $(ESI_LIBS) \ $(SSL_LIBS) \ $(SNMP_LIBS) \ + parser/libsquid-parser.la \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ === added directory 'src/parser' === added file 'src/parser/Makefile.am' --- src/parser/Makefile.am 1970-01-01 00:00:00 +0000 +++ src/parser/Makefile.am 2013-12-31 10:42:11 +0000 @@ -0,0 +1,49 @@ +include $(top_srcdir)/src/Common.am +include $(top_srcdir)/src/TestHeaders.am + +EXTRA_PROGRAMS = \ + testTokenizer + +check_PROGRAMS += testTokenizer +TESTS += testTokenizer + +noinst_LTLIBRARIES = libsquid-parser.la + +libsquid_parser_la_SOURCES = \ + Tokenizer.h \ + Tokenizer.cc + +SBUF_SOURCE= \ + $(top_srcdir)/src/base/CharacterSet.h \ + $(top_srcdir)/src/SBuf.h \ + $(top_srcdir)/src/SBuf.cc \ + $(top_srcdir)/src/MemBlob.h \ + $(top_srcdir)/src/MemBlob.cc \ + $(top_srcdir)/src/OutOfBoundsException.h \ + $(top_srcdir)/src/SBufExceptions.h \ + $(top_srcdir)/src/SBufExceptions.cc \ + $(top_srcdir)/src/String.cc \ + $(top_srcdir)/src/SquidString.h \ + $(top_srcdir)/src/base/TextException.h \ + $(top_srcdir)/src/base/TextException.cc + +testTokenizer_SOURCES = \ + $(SBUF_SOURCE) \ + testTokenizer.h \ + testTokenizer.cc \ + Tokenizer.h +nodist_testTokenizer_SOURCES = \ + $(top_srcdir)/src/tests/testMain.cc \ + $(top_srcdir)/src/tests/stub_mem.cc \ + $(top_srcdir)/src/tests/stub_debug.cc \ + $(top_srcdir)/src/tests/stub_time.cc \ + $(top_srcdir)/src/tests/stub_SBufDetailedStats.cc +testTokenizer_LDFLAGS = $(LIBADD_DL) +testTokenizer_LDADD = \ + libsquid-parser.la \ + $(top_builddir)/lib/libmiscutil.la \ + $(top_builddir)/src/base/libbase.la \ + $(SQUID_CPPUNIT_LIBS) \ + $(SQUID_CPPUNIT_LA) \ + $(COMPAT_LIB) +testTokenizer_DEPENDENCIES = $(SQUID_CPPUNIT_LA) === added file 'src/parser/Tokenizer.cc' --- src/parser/Tokenizer.cc 1970-01-01 00:00:00 +0000 +++ src/parser/Tokenizer.cc 2014-01-06 21:20:57 +0000 @@ -0,0 +1,60 @@ +#include "squid.h" +#include "Tokenizer.h" + +namespace Parser { + +bool +Tokenizer::token(SBuf &returnedToken, const CharacterSet &whitespace) +{ + SBuf savebuf(buf_); + SBuf saveRetVal(returnedToken); + skip(whitespace); + if (!(prefix(returnedToken,whitespace))) { + buf_=savebuf; + returnedToken=saveRetVal; + return false; + } + skip(whitespace); + return true; +} + +bool +Tokenizer::prefix(SBuf &returnedToken, const CharacterSet &tokenChars) +{ + SBuf::size_type prefixLen = buf_.findFirstNotOf(tokenChars); + if (prefixLen == 0) + return false; + returnedToken = buf_.consume(prefixLen); + return true; +} + +bool +Tokenizer::skip(const CharacterSet &tokenChars) +{ + SBuf::size_type prefixLen = buf_.findFirstNotOf(tokenChars); + if (prefixLen == 0) + return false; + buf_.consume(prefixLen); + return true; +} + +bool +Tokenizer::skip(const SBuf &tokenToSkip) +{ + if (buf_.startsWith(tokenToSkip)) { + buf_.consume(tokenToSkip.length()); + return true; + } + return false; +} + +bool +Tokenizer::skip(const char tokenChar) +{ + if (buf_[0] == tokenChar) { + buf_.consume(1); + return true; + } + return false; +} +} /* namespace Parser */ === added file 'src/parser/Tokenizer.h' --- src/parser/Tokenizer.h 1970-01-01 00:00:00 +0000 +++ src/parser/Tokenizer.h 2013-12-19 16:00:30 +0000 @@ -0,0 +1,47 @@ +#ifndef SQUID_PARSER_TOKENIZER_H_ +#define SQUID_PARSER_TOKENIZER_H_ + +#include "base/CharacterSet.h" +#include "SBuf.h" + +namespace Parser { + +class Tokenizer { +public: + explicit Tokenizer(const SBuf &inBuf) : buf_(inBuf) {} + + bool atEnd() const { return !buf_.length(); } + const SBuf& remaining() const { return buf_; } + void reset(const SBuf &newBuf) { buf_ = newBuf; } + + /* The following methods start from the beginning of the input buffer. + * They return true and consume parsed chars if a non-empty token is found. + * Otherwise, they return false without any side-effects. */ + + /** Basic strtok(3): + * Skips all leading delimiters (if any), + * accumulates all characters up to the first delimiter (a token), and + * skips all trailing delimiters (if any). + * Want to extract delimiters? Use three prefix() calls instead. + */ + bool token(SBuf &returnedToken, const CharacterSet &whitespace); + + /// Accumulates all sequential permitted characters (a token). + bool prefix(SBuf &returnedToken, const CharacterSet &tokenChars); + + /// Skips all sequential permitted characters (a token). + bool skip(const CharacterSet &tokenChars); + + /// Skips a given token. + bool skip(const SBuf &tokenToSkip); + + /// Skips a given character (a token). + bool skip(const char tokenChar); + +private: + SBuf buf_; ///< yet unparsed input +}; + + +} /* namespace Parser */ +#endif /* SQUID_PARSER_TOKENIZER_H_ */ === added file 'src/parser/testTokenizer.cc' --- src/parser/testTokenizer.cc 1970-01-01 00:00:00 +0000 +++ src/parser/testTokenizer.cc 2014-01-05 16:57:44 +0000 @@ -0,0 +1,109 @@ +#include "squid.h" + +#include "testTokenizer.h" +#include "base/CharacterSet.h" +#include "Tokenizer.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( testTokenizer ); + +SBuf text("GET http://resource.com/path HTTP/1.1\r\n" + "Host: resource.com\r\n" + "Cookie: laijkpk3422r j1noin \r\n" + "\r\n"); +const CharacterSet alpha("alpha","abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); +const CharacterSet whitespace("whitespace"," \r\n"); +const CharacterSet crlf("crlf","\r\n"); +const CharacterSet tab("tab","\t"); +const CharacterSet numbers("numbers","0123456789"); + +void +testTokenizer::testTokenizerPrefix() +{ + Parser::Tokenizer t(text); + SBuf s; + + // successful prefix tokenization + CPPUNIT_ASSERT(t.prefix(s,alpha)); + CPPUNIT_ASSERT_EQUAL(SBuf("GET"),s); + CPPUNIT_ASSERT(t.prefix(s,whitespace)); + CPPUNIT_ASSERT_EQUAL(SBuf(" "),s); + + //no match (first char is not in the prefix set) + CPPUNIT_ASSERT(!t.prefix(s,whitespace)); + CPPUNIT_ASSERT_EQUAL(SBuf(" "),s); + + // one more match to set S to something meaningful + CPPUNIT_ASSERT(t.prefix(s,alpha)); + CPPUNIT_ASSERT_EQUAL(SBuf("http"),s); + + //no match (no characters from the character set in the prefix) + CPPUNIT_ASSERT(!t.prefix(s,tab)); + CPPUNIT_ASSERT_EQUAL(SBuf("http"),s); //output SBuf left untouched + + // match until the end of the sample + CharacterSet all(whitespace); + all += alpha; + all += crlf; + all += numbers; + all.add(':').add('.').add('/'); + CPPUNIT_ASSERT(t.prefix(s,all)); + CPPUNIT_ASSERT_EQUAL(SBuf(),t.remaining()); +} + +void +testTokenizer::testTokenizerSkip() +{ + Parser::Tokenizer t(text); + SBuf s; + + // first scenario: patterns match + // prep for test + CPPUNIT_ASSERT(t.prefix(s,alpha)); + CPPUNIT_ASSERT_EQUAL(SBuf("GET"),s); + + // test skip testing character set + CPPUNIT_ASSERT(t.skip(whitespace)); + // check that skip was right + CPPUNIT_ASSERT(t.prefix(s,alpha)); + CPPUNIT_ASSERT_EQUAL(SBuf("http"),s); + + //check skip prefix + CPPUNIT_ASSERT(t.skip(SBuf("://"))); + // verify + CPPUNIT_ASSERT(t.prefix(s,alpha)); + CPPUNIT_ASSERT_EQUAL(SBuf("resource"),s); + + // no skip + CPPUNIT_ASSERT(!t.skip(alpha)); + CPPUNIT_ASSERT(!t.skip(SBuf("://"))); + CPPUNIT_ASSERT(!t.skip('a')); + +} + +void +testTokenizer::testTokenizerToken() +{ + Parser::Tokenizer t(text); + SBuf s; + + // first scenario: patterns match + CPPUNIT_ASSERT(t.token(s,whitespace)); + CPPUNIT_ASSERT_EQUAL(SBuf("GET"),s); + CPPUNIT_ASSERT(t.token(s,whitespace)); + CPPUNIT_ASSERT_EQUAL(SBuf("http://resource.com/path"),s); + CPPUNIT_ASSERT(t.token(s,whitespace)); + CPPUNIT_ASSERT_EQUAL(SBuf("HTTP/1.1"),s); + CPPUNIT_ASSERT(t.token(s,whitespace)); + CPPUNIT_ASSERT_EQUAL(SBuf("Host:"),s); + + SBuf s2(s); + //no separator found + CPPUNIT_ASSERT(!t.token(s,tab)); + CPPUNIT_ASSERT_EQUAL(s2,s); // check that the output parameter was untouched +} + +void +testTokenizer::testCharacterSet() +{ + +} === added file 'src/parser/testTokenizer.h' --- src/parser/testTokenizer.h 1970-01-01 00:00:00 +0000 +++ src/parser/testTokenizer.h 2013-12-19 16:00:30 +0000 @@ -0,0 +1,22 @@ +#ifndef SQUID_TESTTOKENIZER_H_ +#define SQUID_TESTTOKENIZER_H_ + +#include + +class testTokenizer : public CPPUNIT_NS::TestFixture +{ + CPPUNIT_TEST_SUITE( testTokenizer ); + CPPUNIT_TEST ( testCharacterSet ); + CPPUNIT_TEST ( testTokenizerPrefix ); + CPPUNIT_TEST ( testTokenizerSkip ); + CPPUNIT_TEST ( testTokenizerToken ); + CPPUNIT_TEST_SUITE_END(); + +protected: + void testTokenizerPrefix(); + void testTokenizerSkip(); + void testTokenizerToken(); + void testCharacterSet(); +}; + +#endif /* SQUID_TESTTOKENIZER_H_ */ # Begin bundle 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 /JJ4mmUIj/xdyRThQkMxtpas