Skip to content

Commit

Permalink
Use PCRE instead of Python's re for RedirectMatch tests
Browse files Browse the repository at this point in the history
Previously, RedirectMatch rules were evaluated using Python's re regexp
module. However, Apache httpd uses the PCRE library for evaluating
regular expressions,[1] and there are subtle differences between the
Python and PCRE implementations. Using the PCRE library itself (via the
pcre-python binding) provides more representative results, and hence
more confidence to the user that any rules that pass whereto's tests
will work in the same way in a real Apache configuration.

[1] https://httpd.apache.org/docs/trunk/glossary.html#regex

Change-Id: Ibef3376d9da0688d0c97f5837dacc5b7cc52431c
  • Loading branch information
zaneb committed Nov 9, 2017
1 parent 006ffc0 commit 797cb67
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 2 deletions.
3 changes: 3 additions & 0 deletions bindep.txt
@@ -0,0 +1,3 @@
libpcre3-dev [platform:dpkg]
pcre-devel [platform:rpm]
dev-libs/libpcre [platform:gentoo]
4 changes: 4 additions & 0 deletions releasenotes/notes/pcre-9c6e76b391620545.yaml
@@ -0,0 +1,4 @@
---
features:
- whereto now uses the PCRE library - the same regex library used by
mod_alias - for compiling regular expressions.
1 change: 1 addition & 0 deletions requirements.txt
Expand Up @@ -3,3 +3,4 @@
# process, which may cause wedges in the gate later.

pbr>=2.0 # Apache-2.0
python-pcre
4 changes: 4 additions & 0 deletions tox.ini
Expand Up @@ -25,6 +25,10 @@ commands = flake8 {posargs}
basepython = python3
commands = {posargs}

[testenv:bindep]
deps = bindep
commands = bindep test

[testenv:cover]
commands = python setup.py test --coverage --testr-args='{posargs}'

Expand Down
6 changes: 4 additions & 2 deletions whereto/rules.py
Expand Up @@ -16,6 +16,7 @@
# under the License.

import logging
import pcre
import re


Expand Down Expand Up @@ -74,14 +75,15 @@ class RedirectMatch(Rule):

def __init__(self, linenum, *params):
super(RedirectMatch, self).__init__(linenum, *params)
self.regex = re.compile(self.pattern)
self.regex = pcre.compile(self.pattern)
if self.target:
self.target_repl = self._get_target_repl()
else:
self.target_repl = None

def _get_target_repl(self):
return self._group_subst.sub(r'\\1', self.target).replace(r'\$', '$')
escaped = pcre.escape_template(self.target)
return self._group_subst.sub(r'{\1}', escaped).replace(r'\$', '$')

def match(self, path):
m = self.regex.search(path)
Expand Down
19 changes: 19 additions & 0 deletions whereto/tests/test_rules.py
Expand Up @@ -96,6 +96,15 @@ def test_match(self):
rule.match('/user/'),
)

def test_match_pcre_syntax(self):
rule = rules.RedirectMatch(
1,
'redirectmatch', '301', '^/((?i)pike)/user/.*$', '/pike/user/',
)
self.assertIsNone(
rule.match('/Pike/USER/')
)

def test_match_with_group(self):
rule = rules.RedirectMatch(
1,
Expand All @@ -106,6 +115,16 @@ def test_match_with_group(self):
rule.match('/user/foo'),
)

def test_match_with_group_braces(self):
rule = rules.RedirectMatch(
1,
'redirectmatch', '301', '^/user/(.*)$', '/pike/user/{1}/$1',
)
self.assertEqual(
('301', '/pike/user/{1}/foo'),
rule.match('/user/foo'),
)

def test_match_with_no_group_dollar(self):
rule = rules.RedirectMatch(
1,
Expand Down

0 comments on commit 797cb67

Please sign in to comment.