]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
SourceLayout: refactor regex pattern objects
authorAmos Jeffries <squid3@treenet.co.nz>
Wed, 29 Jul 2015 07:11:17 +0000 (00:11 -0700)
committerAmos Jeffries <squid3@treenet.co.nz>
Wed, 29 Jul 2015 07:11:17 +0000 (00:11 -0700)
* moves the regex pattern state storage to class RegexPattern
  in base/RegexPattern.h which is MEMPROXY_CLASS pooled and
  constructed with flags and pattern preset.
 - for now the regcomp generated data is set separately.

* Replaces ACL storage class RegexList with a std::list

* converts refresh_pattern regex data to class RegexPattern for
  its pattern and -i/+i flag details.

1  2 
src/Makefile.am
src/RefreshPattern.h
src/acl/RegexData.cc
src/base/Makefile.am
src/base/RegexPattern.cc
src/base/RegexPattern.h
src/cache_cf.cc
src/refresh.cc
test-suite/squidconf/regex

diff --cc src/Makefile.am
Simple merge
index e835dd6be9d55dfd71a89d9a0c1438935a731f34,e835dd6be9d55dfd71a89d9a0c1438935a731f34..8812253474908c022641b316479fcf4f6f70ebec
@@@ -9,21 -9,21 +9,48 @@@
  #ifndef SQUID_REFRESHPATTERN_H_
  #define SQUID_REFRESHPATTERN_H_
  
--#include "compat/GnuRegex.h"
++#include "base/RegexPattern.h"
  
--/// a representation of a refresh pattern. Currently a POD.
++/// a representation of a refresh pattern.
  class RefreshPattern
  {
++    MEMPROXY_CLASS(RefreshPattern);
++
  public:
--    const char *pattern;
--    regex_t compiled_pattern;
++
++    /*
++     * Defaults:
++     *      MIN     NONE
++     *      PCT     20%
++     *      MAX     3 days
++     */
++#define REFRESH_DEFAULT_MAX static_cast<time_t>(259200)
++
++    RefreshPattern(const char *aPattern, const decltype(RegexPattern::flags) &reFlags) :
++        pattern(reFlags, aPattern),
++        min(0), pct(0.20), max(REFRESH_DEFAULT_MAX),
++        next(NULL),
++        max_stale(0)
++    {
++        memset(&flags, 0, sizeof(flags));
++    }
++
++    ~RefreshPattern() {
++        while (RefreshPattern *t = next) {
++            next = t->next;
++            t->next = nullptr;
++            delete t;
++        }
++    }
++    // ~RefreshPattern() default destructor is fine
++
++    RegexPattern pattern;
      time_t min;
      double pct;
      time_t max;
      RefreshPattern *next;
  
      struct {
--        bool icase;
          bool refresh_ims;
          bool store_stale;
  #if USE_HTTP_VIOLATIONS
@@@ -39,6 -39,6 +66,8 @@@
  
      // statistics about how many matches this pattern has had
      mutable struct stats_ {
++        stats_() : matchTests(0), matchCount(0) {}
++
          uint64_t matchTests;
          uint64_t matchCount;
          // TODO: some stats to indicate how useful/less the flags are would be nice.
index 3103b3c8e0c3e22eca6d59ce0b715ceed9dcb0e9,951da42b017b9fef3b124d9c835261d185265023..d48cf01867f650059f4677030ac8933fefc5db6a
@@@ -44,37 -30,19 +30,19 @@@ ACLRegexData::~ACLRegexData(
  bool
  ACLRegexData::match(char const *word)
  {
-     if (word == NULL)
+     if (!word)
          return 0;
  
-     debugs(28, 3, "aclRegexData::match: checking '" << word << "'");
-     RegexList *first, *prev;
-     first = data;
-     prev = NULL;
+     debugs(28, 3, "checking '" << word << "'");
  
-     RegexList *current = first;
-     while (current) {
-         debugs(28, 3, "aclRegexData::match: looking for '" << current->pattern << "'");
-         if (regexec(&current->regex, word, 0, 0, 0) == 0) {
-             if (prev != NULL) {
-                 /* shift the element just found to the second position
-                  * in the list */
-                 prev->next = current->next;
-                 current->next = first->next;
-                 first->next = current;
-             }
-             debugs(28, 2, "aclRegexData::match: match '" << current->pattern << "' found in '" << word << "'");
+     // walk the list of patterns to see if one matches
+     for (auto &i : data) {
 -        if (regexec(&i.regex, word, 0, 0, 0) == 0) {
 -            debugs(28, 2, "'" << i.pattern << "' found in '" << word << "'");
++        if (i.match(word)) {
++            debugs(28, 2, "'" << i.c_str() << "' found in '" << word << "'");
+             // TODO: old code also popped the pattern to second place of the list
+             // in order to reduce patterns search times.
              return 1;
          }
-         prev = current;
-         current = current->next;
      }
  
      return 0;
@@@ -94,11 -63,10 +63,10 @@@ ACLRegexData::dump() cons
              } else {
                  sl.push_back(SBuf("+i"));
              }
-             flags = temp->flags;
+             flags = i.flags;
          }
  
-         sl.push_back(SBuf(temp->pattern));
-         temp = temp->next;
 -        sl.push_back(SBuf(i.pattern));
++        sl.push_back(SBuf(i.c_str()));
      }
  
      return sl;
@@@ -162,7 -123,7 +123,7 @@@ compileRE(std::list<RegexPattern> &curl
  }
  
  /** Compose and compile one large RE from a set of (small) REs.
-- * The ultimate goal is to have only one RE per ACL so that regexec() is
++ * The ultimate goal is to have only one RE per ACL so that match() is
   * called only once per ACL.
   */
  static int
index f0a3a23fe35a1028de3996f40d6fbb3668eec673,72643db253150291cbe02eafa55397dfa249ef74..dc6811c0caadd9c59aad485a9b3a7c5e29409027
@@@ -25,9 -25,10 +25,11 @@@ libbase_la_SOURCES = 
        CharacterSet.cc \
        InstanceId.h \
        Lock.h \
 +      LookupTable.h \
        LruMap.h \
        Packable.h \
+       RegexPattern.cc \
+       RegexPattern.h \
        RunnersRegistry.cc \
        RunnersRegistry.h \
        Subscription.h \
index 71f520d32217974a0ba08e6589e7eb143afbd88a,e70879ac4d306ea2314077d766ffe3c239e52398..355e1e98133b4035229bf7db94cb55f7186b759d
@@@ -7,5 -7,10 +7,17 @@@
   */
  
  #include "squid.h"
- #include "RegexList.h"
+ #include "base/RegexPattern.h"
  
++RegexPattern::RegexPattern(int aFlags, const char *aPattern) :
++        flags(aFlags),
++        pattern(xstrdup(aPattern))
++{
++    memset(&regex, 0, sizeof(regex));
++}
++
+ RegexPattern::~RegexPattern()
+ {
+     xfree(pattern);
+     regfree(&regex);
+ }
index 0000000000000000000000000000000000000000,180488b1477db8dd713721826badda93d93c8223..ad83063c882bafaba077a75b12d2954d3375d154
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,35 +1,42 @@@
 -    RegexPattern(int aFlags, const char *aPattern) : flags(aFlags), pattern(xstrdup(aPattern)) {}
+ /*
+  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+  *
+  * Squid software is distributed under GPLv2+ license and includes
+  * contributions from numerous individuals and organizations.
+  * Please see the COPYING and CONTRIBUTORS files for details.
+  */
+ #ifndef SQUID_SRC_BASE_REGEXPATTERN_H
+ #define SQUID_SRC_BASE_REGEXPATTERN_H
++#include "compat/GnuRegex.h"
+ #include "mem/forward.h"
+ /**
+  * A regular expression,
+  * plain text and compiled representations
+  */
+ class RegexPattern
+ {
+     MEMPROXY_CLASS(RegexPattern);
+ public:
+     RegexPattern() = delete;
 -    char *pattern;
++    RegexPattern(int aFlags, const char *aPattern);
+     RegexPattern(const RegexPattern &) = delete;
+     RegexPattern(RegexPattern &&) = default;
+     ~RegexPattern();
++    const char * c_str() const {return pattern;}
++    bool match(const char *str) const {return regexec(&regex,str,0,NULL,0)==0;}
++
++public:
+     int flags;
+     regex_t regex;
++
++private:
++    char *pattern;
+ };
+ #endif /* SQUID_SRC_BASE_REGEXPATTERN_H */
diff --cc src/cache_cf.cc
index 5eafe104503847ef0550dd569b5028247866ff6a,5eafe104503847ef0550dd569b5028247866ff6a..59cb9a41b974d653ea9e2e660c53fa55afea7648
@@@ -2557,8 -2557,8 +2557,8 @@@ dump_refreshpattern(StoreEntry * entry
      while (head != NULL) {
          storeAppendPrintf(entry, "%s%s %s %d %d%% %d",
                            name,
--                          head->flags.icase ? " -i" : null_string,
--                          head->pattern,
++                          head->pattern.flags&REG_ICASE ? " -i" : null_string,
++                          head->pattern.c_str(),
                            (int) head->min / 60,
                            (int) (100.0 * head->pct + 0.5),
                            (int) head->max / 60);
@@@ -2728,16 -2728,16 +2728,12 @@@ parse_refreshpattern(RefreshPattern ** 
  
      pct = pct < 0.0 ? 0.0 : pct;
      max = max < 0 ? 0 : max;
--    t = static_cast<RefreshPattern *>(xcalloc(1, sizeof(RefreshPattern)));
--    t->pattern = (char *) xstrdup(pattern);
--    t->compiled_pattern = comp;
++    t = new RefreshPattern(pattern, flags);
++    t->pattern.regex = comp;
      t->min = min;
      t->pct = pct;
      t->max = max;
  
--    if (flags & REG_ICASE)
--        t->flags.icase = true;
--
      if (refresh_ims)
          t->flags.refresh_ims = true;
  
  
      *head = t;
  
--    safe_free(pattern);
++    xfree(pattern);
  }
  
  static void
  free_refreshpattern(RefreshPattern ** head)
  {
--    RefreshPattern *t;
--
--    while ((t = *head) != NULL) {
--        *head = t->next;
--        safe_free(t->pattern);
--        regfree(&t->compiled_pattern);
--        safe_free(t);
--    }
++    delete *head;
++    *head = nullptr;
  
  #if USE_HTTP_VIOLATIONS
      refresh_nocache_hack = 0;
diff --cc src/refresh.cc
index 2dcbff8b58e609dac06157c4ee4e14923367e67f,2dcbff8b58e609dac06157c4ee4e14923367e67f..e558224160dcc8c72919c39bcd3c44ce392dc857
@@@ -79,42 -79,42 +79,28 @@@ static struct RefreshCounts 
      int status[STALE_DEFAULT + 1];
  } refreshCounts[rcCount];
  
--/*
-- * Defaults:
-- *      MIN     NONE
-- *      PCT     20%
-- *      MAX     3 days
-- */
--#define REFRESH_DEFAULT_MIN (time_t)0
--#define REFRESH_DEFAULT_PCT 0.20
--#define REFRESH_DEFAULT_MAX (time_t)259200
--
  static const RefreshPattern *refreshUncompiledPattern(const char *);
  static OBJH refreshStats;
  static int refreshStaleness(const StoreEntry * entry, time_t check_time, const time_t age, const RefreshPattern * R, stale_flags * sf);
  
--static RefreshPattern DefaultRefresh;
++static RefreshPattern DefaultRefresh("<none>", 0);
  
  /** Locate the first refresh_pattern rule that matches the given URL by regex.
   *
-- * \note regexec() returns 0 if matched, and REG_NOMATCH otherwise
-- *
-- * \return A pointer to the refresh_pattern parameters to use, or NULL if there is no match.
++ * \return A pointer to the refresh_pattern parameters to use, or nullptr if there is no match.
   */
  const RefreshPattern *
  refreshLimits(const char *url)
  {
--    const RefreshPattern *R;
--
--    for (R = Config.Refresh; R; R = R->next) {
++    for (auto R = Config.Refresh; R; R = R->next) {
          ++(R->stats.matchTests);
--        if (!regexec(&(R->compiled_pattern), url, 0, 0, 0)) {
++        if (R->pattern.match(url)) {
              ++(R->stats.matchCount);
              return R;
          }
      }
  
--    return NULL;
++    return nullptr;
  }
  
  /** Locate the first refresh_pattern rule that has the given uncompiled regex.
   * This function is only ever called if there is no URI. Because a regex match is impossible, Squid
   * forces the "." rule to apply (if it exists)
   *
-- * \return A pointer to the refresh_pattern parameters to use, or NULL if there is no match.
++ * \return A pointer to the refresh_pattern parameters to use, or nullptr if there is no match.
   */
  static const RefreshPattern *
  refreshUncompiledPattern(const char *pat)
  {
--    const RefreshPattern *R;
--
--    for (R = Config.Refresh; R; R = R->next) {
--        if (0 == strcmp(R->pattern, pat))
++    for (auto R = Config.Refresh; R; R = R->next) {
++        if (0 == strcmp(R->pattern.c_str(), pat))
              return R;
      }
  
--    return NULL;
++    return nullptr;
  }
  
  /**
@@@ -318,7 -318,7 +302,7 @@@ refreshCheck(const StoreEntry * entry, 
      if (NULL == R)
          R = &DefaultRefresh;
  
--    debugs(22, 3, "Matched '" << R->pattern << " " <<
++    debugs(22, 3, "Matched '" << R->pattern.c_str() << " " <<
             (int) R->min << " " << (int) (100.0 * R->pct) << "%% " <<
             (int) R->max << "'");
  
@@@ -709,8 -709,8 +693,8 @@@ refreshStats(StoreEntry * sentry
                            R->stats.matchCount,
                            R->stats.matchTests,
                            xpercent(R->stats.matchCount, R->stats.matchTests),
--                          (R->flags.icase ? "-i " : ""),
--                          R->pattern);
++                          (R->pattern.flags&REG_ICASE ? "-i " : ""),
++                          R->pattern.c_str());
      }
  
      int i;
@@@ -762,12 -762,12 +746,6 @@@ refreshInit(void
      refreshCounts[rcCDigest].proto = "Cache Digests";
  #endif
  
--    memset(&DefaultRefresh, '\0', sizeof(DefaultRefresh));
--    DefaultRefresh.pattern = "<none>";
--    DefaultRefresh.min = REFRESH_DEFAULT_MIN;
--    DefaultRefresh.pct = REFRESH_DEFAULT_PCT;
--    DefaultRefresh.max = REFRESH_DEFAULT_MAX;
--
      refreshRegisterWithCacheManager();
  }
  
index 0000000000000000000000000000000000000000,3eca26e204fa2d7f8ff54f5afd0d855e5d4457ea..3f85acca39c142c95d489c81d34bb455e238c4aa
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,20 +1,24 @@@
+ ## Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ ##
+ ## Squid software is distributed under GPLv2+ license and includes
+ ## contributions from numerous individuals and organizations.
+ ## Please see the COPYING and CONTRIBUTORS files for details.
+ ##
+ #
+ # This file contains the list of regular expression syntaxes used
+ # it covers various uses of:
+ #     unoptimized multi-line patterns
+ #
+ # Some other regression related patterns are tested in regressions-3.4.0.1
+ #
+ acl G dstdom_regex \.g...l\.com$
+ acl G dstdom_regex \.g...le\.com$
+ acl B browser ^Mozilla
+ acl B browser ^Java/[0-9]+(\.[0-9]+)?
++
++# invalid pattern - this should ERROR
++acl foo browser *
++