From 458400c16718ee2cd9c3750355c45b99e0343ca3 Mon Sep 17 00:00:00 2001 From: fulder Date: Wed, 28 Mar 2018 12:55:52 +0200 Subject: [PATCH 1/7] Simplified regex for key --- nginx.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/nginx.py b/nginx.py index 19b4798..1c86180 100755 --- a/nginx.py +++ b/nginx.py @@ -477,19 +477,11 @@ def loads(data, conf=True): index += m.end() continue - key_with_quoted = r'^\s*(\S*?)\s*"([^"]+)";?|\'([^\']+)\';?|\\S+;?' - key_wo_quoted = r'^\s*([a-zA-Z0-9-_]+?)\s+(.+?);' - m1 = re.compile(key_with_quoted, re.S).search(data[index:]) - m2 = re.compile(key_wo_quoted, re.S).search(data[index:]) - if m1 and m2: - if m1.start() <= m2.start(): - m = m1 - else: - m = m2 - else: - m = m1 or m2 + string_combos = r'[^;]+|"([^"]+)"|\'([^\']+)\'' + key = r'^\s*({})\s+({})\s*([^;]*?);'.format(string_combos, string_combos) + m = re.compile(key, re.S).search(data[index:]) if m: - k = Key(m.group(1), m.group(2)) + k = Key(m.group(1), m.group(4) + m.group(7)) if lopen and isinstance(lopen[0], (Container, Server)): lopen[0].add(k) else: From ea22751f4f45de4d62d415f557f37ff68183ff32 Mon Sep 17 00:00:00 2001 From: fulder Date: Wed, 28 Mar 2018 23:53:12 +0200 Subject: [PATCH 2/7] Make all test pass --- nginx.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/nginx.py b/nginx.py index 1c86180..b75295e 100755 --- a/nginx.py +++ b/nginx.py @@ -382,7 +382,7 @@ def as_strings(self): """Return key as nginx config string.""" if self.value == '' or self.value is None: return '{0};\n'.format(self.name) - if ';' in self.value or '#' in self.value: + if '"' not in self.value and (';' in self.value or '#' in self.value and '"' not in self.value): return '{0} "{1}";\n'.format(self.name, self.value) return '{0} {1};\n'.format(self.name, self.value) @@ -477,11 +477,13 @@ def loads(data, conf=True): index += m.end() continue - string_combos = r'[^;]+|"([^"]+)"|\'([^\']+)\'' - key = r'^\s*({})\s+({})\s*([^;]*?);'.format(string_combos, string_combos) + s1 = r'("[^"]+"|\'[^\']+\'|[^\s;]+)' + s2 = r'("[^"]*"|\'[^\']*\'|[^\s;]*)' + s3 = r'(\s*[^;]*?)' + key = r'^\s*{}\s*{}{};'.format(s1, s2, s3) m = re.compile(key, re.S).search(data[index:]) if m: - k = Key(m.group(1), m.group(4) + m.group(7)) + k = Key(m.group(1), m.group(2) + m.group(3)) if lopen and isinstance(lopen[0], (Container, Server)): lopen[0].add(k) else: From 2b9dedeb17cf87f3c92ee397120c286451eb3433 Mon Sep 17 00:00:00 2001 From: fulder Date: Wed, 28 Mar 2018 23:53:27 +0200 Subject: [PATCH 3/7] Correct test cases with qotes --- tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests.py b/tests.py index 6b14d85..13bcdc8 100755 --- a/tests.py +++ b/tests.py @@ -124,7 +124,7 @@ def test_key_parse(self): self.assertEqual(firstKey.name, 'listen') self.assertEqual(firstKey.value, '80') self.assertEqual(thirdKey.name, 'mykey') - self.assertEqual(thirdKey.value, 'myvalue; #notme myothervalue') + self.assertEqual(thirdKey.value, '"myvalue; #notme myothervalue"') def test_key_parse_complex(self): data = nginx.loads(TESTBLOCK_CASE_2) @@ -134,7 +134,7 @@ def test_key_parse_complex(self): self.assertEqual(firstKey.name, 'listen') self.assertEqual(firstKey.value, '80') self.assertEqual(thirdKey.name, 'mykey') - self.assertEqual(thirdKey.value, 'myvalue; #notme myothervalue') + self.assertEqual(thirdKey.value, '"myvalue; #notme myothervalue"') self.assertEqual( data.server.locations[-1].keys[0].value, "301 $scheme://$host:$server_port${request_uri}bitbucket/" From bb6f5141fe4519260de6bc8a06412fd28565d09f Mon Sep 17 00:00:00 2001 From: fulder Date: Wed, 28 Mar 2018 23:59:38 +0200 Subject: [PATCH 4/7] Add test for quoted key name/val --- tests.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests.py b/tests.py index 13bcdc8..eae3ffd 100755 --- a/tests.py +++ b/tests.py @@ -45,6 +45,7 @@ server_name localhost 127.0.0.1; root /srv/http; # And also this one mykey "myvalue; #notme myothervalue"; + "quoted_key" "quoted_value"; # This one too index index.php; if (!-e $request_filename) @@ -128,9 +129,10 @@ def test_key_parse(self): def test_key_parse_complex(self): data = nginx.loads(TESTBLOCK_CASE_2) - self.assertEqual(len(data.server.keys), 5) + self.assertEqual(len(data.server.keys), 6) firstKey = data.server.keys[0] thirdKey = data.server.keys[3] + fourthKey = data.server.keys[4] self.assertEqual(firstKey.name, 'listen') self.assertEqual(firstKey.value, '80') self.assertEqual(thirdKey.name, 'mykey') @@ -139,6 +141,8 @@ def test_key_parse_complex(self): data.server.locations[-1].keys[0].value, "301 $scheme://$host:$server_port${request_uri}bitbucket/" ) + self.assertEqual(fourthKey.name, '"quoted_key"') + self.assertEqual(fourthKey.value, '"quoted_value"') def test_location_parse(self): data = nginx.loads(TESTBLOCK_CASE_1) From 406612f9ee374233641574746c6d8c8877aaf0c8 Mon Sep 17 00:00:00 2001 From: fulder Date: Fri, 30 Mar 2018 15:51:08 +0200 Subject: [PATCH 5/7] Add test from PR #21 and issue #20 --- tests.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests.py b/tests.py index eae3ffd..1aa908d 100755 --- a/tests.py +++ b/tests.py @@ -102,6 +102,43 @@ """ +TESTBLOCK_CASE_5 = """ +upstream test0 { + server 1.1.1.1:8080; + send "some request"; +} + +upstream test1 { + server 1.1.1.1:8080; + send 'some request'; +} + +server { + server_name "www.example.com"; + + location / { + root html; + } +} +""" + + +TESTBLOCK_CASE_6 = """ +upstream test0 { + server 1.1.1.1:8080; + check interval=3000 rise=2 fall=3 timeout=3000 type=http; + check_http_send "GET /alive.html HTTP/1.0\r\n\r\n"; + check_http_expect_alive http_2xx http_3xx; +} + +upstream test1 { + ip_hash; + server 2.2.2.2:9000; + check_http_send 'GET /alive.html HTTP/1.0\r\n\r\n'; +} +""" + + class TestPythonNginx(unittest.TestCase): def test_basic_load(self): self.assertTrue(nginx.loads(TESTBLOCK_CASE_1) is not None) @@ -166,6 +203,16 @@ def test_reflection(self): out_data = '\n' + nginx.dumps(inp_data) self.assertEqual(TESTBLOCK_CASE_1, out_data) + def test_quoted_key_value(self): + data = nginx.loads(TESTBLOCK_CASE_5) + out_data = '\n' + nginx.dumps(data) + self.assertEqual(out_data, TESTBLOCK_CASE_5) + + def test_complex_upstream(self): + inp_data = nginx.loads(TESTBLOCK_CASE_6) + out_data = '\n' + nginx.dumps(inp_data) + self.assertEqual(TESTBLOCK_CASE_6, out_data) + def test_filtering(self): data = nginx.loads(TESTBLOCK_CASE_1) self.assertEqual(len(data.server.filter('Key', 'mykey')), 1) From 7a9f1cb07db8071808648f9870783ecdc7b38e0f Mon Sep 17 00:00:00 2001 From: fulder Date: Fri, 30 Mar 2018 16:21:13 +0200 Subject: [PATCH 6/7] Add testcase for issue #19 --- tests.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests.py b/tests.py index 1aa908d..126c32b 100755 --- a/tests.py +++ b/tests.py @@ -138,6 +138,24 @@ } """ +TESTBLOCK_CASE_7 = """ +upstream xx.com_backend { + server 10.193.2.2:9061 weight=1 max_fails=2 fail_timeout=30s; + server 10.193.2.1:9061 weight=1 max_fails=2 fail_timeout=30s; + session_sticky; +} + +server { + listen 80; + + location / { + set $xlocation 'test'; + proxy_pass http://xx.com_backend; + } +} +""" + + class TestPythonNginx(unittest.TestCase): def test_basic_load(self): @@ -213,6 +231,11 @@ def test_complex_upstream(self): out_data = '\n' + nginx.dumps(inp_data) self.assertEqual(TESTBLOCK_CASE_6, out_data) + def test_session_sticky(self): + inp_data = nginx.loads(TESTBLOCK_CASE_7) + out_data = '\n' + nginx.dumps(inp_data) + self.assertEqual(TESTBLOCK_CASE_7, out_data) + def test_filtering(self): data = nginx.loads(TESTBLOCK_CASE_1) self.assertEqual(len(data.server.filter('Key', 'mykey')), 1) From ff5a0aa94a7eb8bfa8b80fe544a2116003703823 Mon Sep 17 00:00:00 2001 From: fulder Date: Sat, 31 Mar 2018 15:31:43 +0200 Subject: [PATCH 7/7] Incorrect if statement --- nginx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.py b/nginx.py index b75295e..b183f8f 100755 --- a/nginx.py +++ b/nginx.py @@ -382,7 +382,7 @@ def as_strings(self): """Return key as nginx config string.""" if self.value == '' or self.value is None: return '{0};\n'.format(self.name) - if '"' not in self.value and (';' in self.value or '#' in self.value and '"' not in self.value): + if '"' not in self.value and (';' in self.value or '#' in self.value): return '{0} "{1}";\n'.format(self.name, self.value) return '{0} {1};\n'.format(self.name, self.value)