├── requirements.txt ├── pyhaproxy ├── canopy ├── __init__.py ├── haproxy.peg ├── haproxy.cfg ├── render.py ├── test.py ├── parse.py ├── config.py └── pegnode.py ├── .travis.yml ├── setup.py ├── .gitignore ├── LICENSE └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | nose==1.3.7 2 | -------------------------------------------------------------------------------- /pyhaproxy/canopy: -------------------------------------------------------------------------------- 1 | node_modules/canopy/bin/canopy -------------------------------------------------------------------------------- /pyhaproxy/__init__.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # -*- coding: utf8 -*- 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python:2.7 2 | 3 | before_install: 4 | - sudo apt-get -y install python-pip 5 | 6 | install: 7 | - sudo pip install -r requirements.txt 8 | 9 | script: 10 | - sudo python setup.py install 11 | - nosetests -sv pyhaproxy/test.py 12 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='pyhaproxy', 5 | version='0.3.7', 6 | keywords=('haproxy', 'parse'), 7 | description='A Python library to parse haproxy configuration file', 8 | license='MIT License', 9 | install_requires=[], 10 | 11 | include_package_data=True, 12 | package_data={ 13 | 'pyhaproxy': ['*.peg'], 14 | }, 15 | 16 | author='Joey', 17 | author_email='majunjiev@gmail.com', 18 | 19 | url='https://github.com/imjoey/pyhaproxy', 20 | 21 | packages=find_packages(), 22 | platforms='any', 23 | ) 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | # All .cfg files 8 | *.cfg 9 | 10 | # Sublime Text extensions 11 | *.sublime-* 12 | 13 | # VSCode extensions 14 | .vscode/ 15 | 16 | # Nodejs 17 | node_modules 18 | 19 | # Distribution / packaging 20 | .Python 21 | env/ 22 | build/ 23 | develop-eggs/ 24 | dist/ 25 | downloads/ 26 | eggs/ 27 | .eggs/ 28 | lib/ 29 | lib64/ 30 | parts/ 31 | sdist/ 32 | var/ 33 | *.egg-info/ 34 | .installed.cfg 35 | *.egg 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .coverage 51 | .coverage.* 52 | .cache 53 | nosetests.xml 54 | coverage.xml 55 | *,cover 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 iterjpnic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /pyhaproxy/haproxy.peg: -------------------------------------------------------------------------------- 1 | grammar HaproxyConfig 2 | 3 | configuration <- (comment_line / blank_line / global_section / defaults_section / userlist_section / listen_section / frontend_section / backend_section)* 4 | 5 | global_section <- global_header config_block 6 | 7 | defaults_section <- defaults_header config_block 8 | 9 | userlist_section <- userlist_header config_block 10 | 11 | listen_section <- listen_header config_block 12 | 13 | frontend_section <- frontend_header config_block 14 | 15 | backend_section <- backend_header config_block 16 | 17 | global_header <- whitespace "global" whitespace comment_text? line_break 18 | 19 | userlist_header <- whitespace "userlist" whitespace proxy_name comment_text? line_break 20 | 21 | defaults_header <- whitespace "defaults" whitespace proxy_name? whitespace comment_text? line_break 22 | 23 | listen_header <- whitespace "listen" whitespace proxy_name whitespace service_address? value? comment_text? line_break 24 | 25 | frontend_header <- whitespace "frontend" whitespace proxy_name whitespace service_address? value? comment_text? line_break 26 | 27 | backend_header <- whitespace "backend" whitespace proxy_name whitespace value? comment_text? line_break 28 | 29 | config_block <- (server_line / option_line / bind_line / acl_line / backend_line / group_line / user_line / config_line / comment_line / blank_line)* 30 | 31 | server_line <- whitespace "server" whitespace server_name whitespace service_address value? comment_text? line_break 32 | 33 | option_line <- whitespace "option" whitespace keyword whitespace value? comment_text? line_break 34 | 35 | bind_line <- whitespace "bind" whitespaceplus service_address whitespace value? comment_text? line_break 36 | 37 | acl_line <- whitespace "acl" whitespace acl_name whitespace value? comment_text? line_break 38 | 39 | backend_line <- whitespace ("use_backend" / "default_backend") whitespace backend_name whitespace ("if" / "unless")? whitespace backend_condition? comment_text? line_break 40 | 41 | group_line <- whitespace "group" whitespace group_name whitespace ("users" whitespace)? value? comment_text? line_break 42 | 43 | user_line <- whitespace "user" whitespace user_name whitespace ("password" / "insecure-password") whitespace password whitespace ("groups" whitespace)? value? comment_text? line_break 44 | 45 | config_line <- whitespace !("defaults" / "global" / "userlist" / "listen" / "frontend" / "backend") keyword whitespace value? comment_text? line_break 46 | 47 | comment_line <- whitespace comment_text line_break 48 | 49 | blank_line <- whitespace line_break 50 | 51 | comment_text <- "#" char* &line_break 52 | 53 | line_break <- [\n] 54 | 55 | keyword <- (("errorfile" / "timeout") whitespace)? [a-z0-9\-\_\.]+ 56 | 57 | server_name <- [a-zA-z0-9\-\_\.:]+ 58 | 59 | acl_name <- [a-zA-z0-9\-\_\.:]+ 60 | 61 | backend_name <- [a-zA-z0-9\-\_\.:]+ 62 | 63 | group_name <- [a-zA-z0-9\-\_\.:]+ 64 | 65 | user_name <- [a-zA-z0-9\-\_\.:]+ 66 | 67 | password <- [^#\n ]+ 68 | 69 | backend_condition <- [^#\n]+ 70 | 71 | service_address <- host [:]? port 72 | 73 | host <- ipv4_host / dns_host / wildcard_host 74 | 75 | port <- [\d]* 76 | 77 | ipv4_host <- [\d]+ "." [\d]+ "." [\d]+ "." [\d]+ 78 | 79 | dns_host <- [a-zA-Z\-\.\d]+ 80 | 81 | wildcard_host <- "*" 82 | 83 | proxy_name <- [a-zA-Z0-9\-\_\.:]+ 84 | 85 | value <- [^#\n]+ 86 | 87 | char <- ![\n] . 88 | 89 | whitespace <- [ \t]* 90 | 91 | whitespaceplus <- [ \t]+ 92 | -------------------------------------------------------------------------------- /pyhaproxy/haproxy.cfg: -------------------------------------------------------------------------------- 1 | global 2 | maxconn 4096 3 | nbproc 1 4 | debug 5 | daemon 6 | log 127.0.0.1 local0 7 | 8 | defaults 9 | mode http 10 | option httplog 11 | log global 12 | 13 | userlist L1 14 | group G1 users tiger,scott 15 | group G2 users xdb,sc 16 | 17 | user tiger password $6$k6y3o.eP$JlKBx9za9667qe4(...)xHSwRv6J.C0/D7cV91 18 | user scott insecure-password elgato 19 | user xdb insecure-password hello 20 | 21 | userlist L2 22 | group G1 23 | group G2 24 | 25 | user tiger password $6$k6y3o.eP$JlKBx(...)xHSwRv6J.C0/D7cV91 groups G1 26 | user scott insecure-password elgato groups G1,G2 27 | user xdb insecure-password hello groups G2 28 | 29 | frontend unsecured *:80 30 | timeout client 86400000 31 | mode http 32 | option httpclose 33 | option forwardfor #forward’s clients IP to app 34 | bind-process odd 35 | #catch all domains that begin with 'www.' 36 | acl host_www1 hdr_beg(host) -i www. 37 | reqirep ^Host:\ www.(.*)$ Host:\ \1 if host_www 38 | redirect code 301 prefix / if host_www 39 | 40 | # Define hosts 41 | acl host_niftykick hdr(host) -i niftykick.com 42 | acl host_chatleap hdr(host) -i chatleap.com 43 | acl host_trevordev hdr(host) -i trevordev.com 44 | acl host_jokeydoke hdr(host) -i jokeydoke.com 45 | acl host_swiftnifty hdr(host) -i swiftnifty.trevordev.com 46 | 47 | #redirect to https 48 | redirect prefix https://niftykick.com code 301 if host_niftykick 49 | 50 | ## figure out which one to use 51 | use_backend niftykick if host_niftykick 52 | use_backend chatleap if host_chatleap 53 | use_backend trevordev if host_trevordev 54 | use_backend swiftnifty if host_swiftnifty 55 | use_backend jokeydoke if host_jokeydoke 56 | 57 | default_backend devbrick 58 | 59 | frontend secured 60 | timeout client 86400000 61 | mode http 62 | option httpclose 63 | option forwardfor 64 | 65 | #catch all domains that begin with 'www.' 66 | acl host_www2 hdr_beg(host) -i www. 67 | reqirep ^Host:\ www.(.*)$ Host:\ \1 if host_www 68 | redirect code 301 prefix / if host_www 69 | 70 | acl host_coyote path_beg /fileServer/ 71 | acl host_niftykick hdr(host) -i niftykick.com 72 | 73 | bind 0.0.0.0:443 ssl crt /etc/haproxy/niftykickCert.pem 74 | 75 | use_backend fileServer if host_coyote 76 | use_backend niftykick if host_niftykick 77 | 78 | backend devbrick 79 | mode http 80 | option forwardfor #this sets X-Forwarded-For 81 | timeout server 30000 82 | timeout connect 4000 83 | server server1 localhost:3000 weight 1 maxconn 1024 check 84 | 85 | backend chatleap 86 | mode http 87 | option forwardfor #this sets X-Forwarded-For 88 | timeout server 30000 89 | timeout connect 4000 90 | server server1 localhost:3001 weight 1 maxconn 1024 check 91 | 92 | backend jokeydoke 93 | mode http 94 | option forwardfor #this sets X-Forwarded-For 95 | timeout server 30000 96 | timeout connect 4000 97 | server server1 localhost:3002 weight 1 maxconn 1024 check 98 | 99 | backend trevordev 100 | mode http 101 | option forwardfor #this sets X-Forwarded-For 102 | timeout server 30000 103 | timeout connect 4000 104 | server server1 localhost:3003 weight 1 maxconn 1024 check 105 | 106 | backend niftykick 107 | mode http 108 | option forwardfor #this sets X-Forwarded-For 109 | timeout server 30000 110 | timeout connect 4000 111 | server server1 localhost:3005 weight 1 maxconn 1024 check 112 | 113 | backend fileServer 114 | mode http 115 | option forwardfor #this sets X-Forwarded-For 116 | timeout server 30000 117 | timeout connect 4000 118 | reqrep ^([^\ :]*)\ /fileServer/(.*) \1\ /\2 119 | server server1 localhost:3008 weight 1 maxconn 1024 check 120 | 121 | backend swiftnifty 122 | mode http 123 | option forwardfor #this sets X-Forwarded-For 124 | timeout server 30000 125 | timeout connect 4000 126 | server server1 localhost:3007 weight 1 maxconn 1024 check 127 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pyhaproxy ![PyPi](https://img.shields.io/pypi/v/pyhaproxy.svg) ![Build Status](https://travis-ci.org/imjoey/pyhaproxy.svg?branch=master) 2 | It's a Python library to parse haproxy config file. Thanks to [canopy](https://github.com/jcoglan/canopy), which I use for auto-generating Python codes by PEG grammar. But the 'Extension methods for node' feature in canopy seems broken, I always encounter the MRO errors when running `parse` function. So I modify the generated codes which mainly rename the `TreeNode*` to specified treenode name, eg: GlobalSection, GlobalHeader, BackendHeader, and also complement missing attributes. 3 | 4 | 5 | # Install 6 | This project uses nose for unit testing, but with no more Python libraries dependencies for running. 7 | 8 | * Suppose that you have virtualenv installed, if not, please go [here](https://virtualenv.readthedocs.org/en/latest/installation.html) to install 9 | * Create a virutalenv and activate it, 10 | ```bash 11 | $ virtualenv --no-site-packages pyhaproxy 12 | $ source pyhaproxy/bin/activate 13 | ``` 14 | * User pip (`pip install pyhaproxy`) or setuptools (`easy_install pyhaproxy`) to install it 15 | * Install nose dependency for unittest 16 | ```bash 17 | (pyhaproxy)$ pip install -r requirements.txt 18 | ``` 19 | 20 | 21 | # Example 22 | Here is the simple example to show how to use it. See unittest file [test.py](https://github.com/imjoey/pyhaproxy/blob/master/pyhaproxy/test.py) for more usage details. 23 | 24 | ```python 25 | #!/usr/bin/env python 26 | # -*- coding: utf8 -*- 27 | 28 | from pyhaproxy.parse import Parser 29 | from pyhaproxy.render import Render 30 | import pyhaproxy.config as config 31 | 32 | 33 | # Build the configuration instance by calling Parser('config_file').build_configuration() 34 | cfg_parser = Parser('haproxy.cfg') 35 | configuration = cfg_parser.build_configuration() 36 | 37 | # Get the global section 38 | print configuration.globall # the `global` is keyword of Python, so name it `globall` 39 | print configuration.globall.options() # get the 'option ...' config lines 40 | print configuration.globall.configs() # get config lines except 'option ...' ones 41 | 42 | 43 | # Get all the frontend sections 44 | frontend_sections = configuration.frontends 45 | 46 | # Get frontend sections specifics 47 | for fe_section in frontend_sections: 48 | # Get the name, host, port of the frontend section 49 | print fe_section.name, fe_section.host, fe_section.port 50 | print fe_section.options() 51 | print fe_section.configs() 52 | 53 | 54 | # Find the frontend by name 55 | the_fe_section = configuration.frontend(the_fe_section_name) 56 | 57 | '''To get other sections is ditto. 58 | ''' 59 | 60 | 61 | # Operates the ACL in a frontend, it much likes operating a list 62 | 63 | # Get all the ACLs defined in the frontend section 64 | acls = the_fe_section.acls() # return list(config.Acl) 65 | 66 | # Find the specified ACL 67 | acl_instance = the_fe_section.acl(the_acl_name) # return config.Acl 68 | 69 | # Modify existing ACL 70 | acl_instance.value = 'hdr(host) -i modified.example.com' 71 | 72 | # Append the ACL into the frontend section 73 | # for version <= 0.2.4 74 | the_fe_section.acls().append(config.Acl(acl_name, acl_value)) 75 | # for version > 0.2.4 76 | the_fe_section.add_acl(config.Acl(acl_name, acl_value)) 77 | 78 | # Remove ACL 79 | # for version <= 0.2.4 80 | acl_instance = the_fe_section.acl(the_acl_name) 81 | the_fe_section.acls().remove(acl_instance) 82 | # for version > 0.2.4 83 | the_fe_section.remove_acl(the_acl_name) 84 | 85 | 86 | # Operates the use_backend / default_backend configs in a frontend 87 | 88 | # Get all the backend configs 89 | usebackends = the_fe_section.usebackends() # return list(config.UseBackend) 90 | for usebe in usebackends: 91 | # Get the using backend name, operator, condition 92 | print usebe.backend_name, usebe.operator, usebe.backend_condition 93 | # Determine if it's `default_backend` line 94 | print usebe.is_default 95 | # Add a new `use_backend` or `default_backend` line 96 | # for version <= 0.2.4 97 | the_fe_section.usebackends().append(config.UseBackend(backend_name, operator, backend_condition, is_default)) 98 | # for version > 0.2.4 99 | the_fe_section.add_usebackend(config.UseBackend(backend_name, operator, backend_condition, is_default)) 100 | 101 | 102 | # Operates the Server in a backend 103 | the_be_section = configuration.backend(the_be_section_name) 104 | # Get all the Server lines in backend section 105 | servers = the_be_section.servers() # return list(config.Server) 106 | # Find the specified Server 107 | the_server = the_be_section.server(the_server_name) # return config.Server 108 | # Get the Server name, host, port 109 | print the_server.name, the_server.host, the_server.port 110 | # Get the Server attributes, for line: `server web_server_1 10.1.1.2:80 cookie 1 check inter 2000 rise 3` 111 | print the_server.attributes # it's is ['cookie', 1, 'check', 'inter', 2000, 'rise', 3] 112 | # Remove the Server by name 113 | # for version <= 0.2.4 114 | the_be_section.servers().remove(the_server) 115 | # for version > 0.2.4 116 | the_be_section.remove_server(server_name) 117 | 118 | 119 | # Render out to the cfg file 120 | cfg_render = Render(configuration) 121 | cfg_render.dumps_to('./hatest.cfg') # you will see hatest.cfg which is same to the `haproxy.cfg` parsed previously 122 | 123 | ``` 124 | 125 | 126 | # Finished 127 | - [x] Parse `global` section 128 | - [x] Parse `frontend` sections 129 | - [x] Parse `bind` config lines 130 | - [x] Parse `backend` sections 131 | - [x] Parse `defaults` sections 132 | - [x] Parse `userlist` sections 133 | - [x] Parse `listen` sections 134 | - [x] Parse `acl` config lines 135 | - [x] Parse `use_backend` and `default_backend` config lines 136 | - [x] Render `global` section 137 | - [x] Render `frontend` section 138 | - [x] Render `backend` sections 139 | - [x] Render `defaults` sections 140 | - [x] Render `userlist` sections 141 | - [x] Render `listen` sections 142 | 143 | 144 | # TODO 145 | - [ ] Link `backend` with `frontend` by `acl` 146 | 147 | 148 | # Unittest 149 | Use nose unit test framework 150 | ```bash 151 | (pyhaproxy)$ nosetests -sv test.py 152 | ``` 153 | 154 | 155 | # Thanks 156 | * Inspired by @subakva 's [haproxy-tools](https://github.com/subakva/haproxy-tools) 157 | * Use [canopy](https://github.com/jcoglan/canopy) of @jcoglan for PEG parsing and Python code auto-generating 158 | -------------------------------------------------------------------------------- /pyhaproxy/render.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import pyhaproxy.config as config 5 | 6 | 7 | class Render(object): 8 | """Do rendering the config.Config object to a str 9 | 10 | Attributes: 11 | configuration (config.Configuration): 12 | """ 13 | def __init__(self, configuration): 14 | self.configuration = configuration 15 | 16 | def render_configuration(self): 17 | config_str = '' 18 | # render global section 19 | if self.configuration.globall: 20 | config_str = self.render_global(self.configuration.globall) 21 | # render defaults sections 22 | for defaults_section in self.configuration.defaults: 23 | config_str = config_str + self.render_defaults(defaults_section) 24 | # render userlists sections 25 | for userlist_section in self.configuration.userlists: 26 | config_str = config_str + self.render_userlist(userlist_section) 27 | # render listen sections 28 | for listen_section in self.configuration.listens: 29 | config_str = config_str + self.render_listen(listen_section) 30 | # render frontend sections 31 | for frontend_section in self.configuration.frontends: 32 | config_str = config_str + self.render_frontend(frontend_section) 33 | # render backend sections 34 | for backend_section in self.configuration.backends: 35 | config_str = config_str + self.render_backend(backend_section) 36 | 37 | return config_str 38 | 39 | def dumps_to(self, filepath): 40 | with open(filepath, 'w') as f: 41 | f.write(self.render_configuration()) 42 | 43 | def render_global(self, globall): 44 | globall_str = ''' 45 | global 46 | %s 47 | ''' 48 | return globall_str % self.__render_config_block( 49 | globall.config_block) 50 | 51 | def render_defaults(self, defaults): 52 | defaults_str = ''' 53 | defaults %s 54 | %s 55 | ''' 56 | return defaults_str % (defaults.name, self.__render_config_block( 57 | defaults.config_block)) 58 | 59 | def render_userlist(self, userlist): 60 | userlist_str = ''' 61 | userlist %s 62 | %s 63 | ''' 64 | 65 | return userlist_str % ( 66 | userlist.name, 67 | self.__render_config_block(userlist.config_block)) 68 | 69 | def render_listen(self, listen): 70 | listen_str = ''' 71 | listen %s %s 72 | %s 73 | ''' 74 | host_port = '' 75 | if not len(listen.binds()): 76 | host_port = '%s:%s' % (listen.host, listen.port) 77 | 78 | return listen_str % ( 79 | listen.name, host_port, 80 | self.__render_config_block(listen.config_block)) 81 | 82 | def render_frontend(self, frontend): 83 | frontend_str = ''' 84 | frontend %s %s 85 | %s 86 | ''' 87 | host_port = '' 88 | if not len(frontend.binds()): 89 | host_port = '%s:%s' % (frontend.host, frontend.port) 90 | 91 | return frontend_str % ( 92 | frontend.name, host_port, 93 | self.__render_config_block(frontend.config_block)) 94 | 95 | def render_backend(self, backend): 96 | backend_str = ''' 97 | backend %s 98 | %s 99 | ''' 100 | return backend_str % (backend.name, self.__render_config_block( 101 | backend.config_block)) 102 | 103 | def __render_config_block(self, config_block): 104 | """Summary 105 | 106 | Args: 107 | config_block [config.Item, ...]: config lines 108 | 109 | Returns: 110 | str: config block str 111 | """ 112 | config_block_str = '' 113 | for line in config_block: 114 | if isinstance(line, config.Option): 115 | line_str = self.__render_option(line) 116 | elif isinstance(line, config.Config): 117 | line_str = self.__render_config(line) 118 | elif isinstance(line, config.Server): 119 | line_str = self.__render_server(line) 120 | elif isinstance(line, config.Bind): 121 | line_str = self.__render_bind(line) 122 | elif isinstance(line, config.Acl): 123 | line_str = self.__render_acl(line) 124 | elif isinstance(line, config.UseBackend): 125 | line_str = self.__render_usebackend(line) 126 | elif isinstance(line, config.User): 127 | line_str = self.__render_user(line) 128 | elif isinstance(line, config.Group): 129 | line_str = self.__render_group(line) 130 | # append line str 131 | config_block_str = config_block_str + line_str 132 | 133 | return config_block_str 134 | 135 | def __render_usebackend(self, usebackend): 136 | usebackend_line = ' %s %s %s %s\n' 137 | backendtype = 'use_backend' 138 | if usebackend.is_default: 139 | backendtype = 'default_backend' 140 | 141 | return usebackend_line % ( 142 | backendtype, usebackend.backend_name, 143 | usebackend.operator, usebackend.backend_condition) 144 | 145 | def __render_user(self, user): 146 | user_line = ' user %s %s %s %s\n' 147 | group_fragment = '' 148 | if user.group_names: 149 | group_fragment = 'groups ' + ','.join(user.group_names) 150 | return user_line % ( 151 | user.name, user.passwd_type, user.passwd, group_fragment) 152 | 153 | def __render_group(self, group): 154 | group_line = ' group %s %s\n' 155 | user_fragment = '' 156 | if group.user_names: 157 | user_fragment = 'users ' + ','.join(group.user_names) 158 | return group_line % (group.name, user_fragment) 159 | 160 | def __render_server(self, server): 161 | server_line = ' server %s %s:%s %s\n' 162 | return server_line % ( 163 | server.name, server.host, server.port, ' '.join(server.attributes)) 164 | 165 | def __render_acl(self, acl): 166 | acl_line = ' acl %s %s\n' 167 | return acl_line % (acl.name, acl.value) 168 | 169 | def __render_bind(self, bind): 170 | bind_line = ' bind %s:%s %s\n' 171 | return bind_line % ( 172 | bind.host, bind.port, ' '.join(bind.attributes)) 173 | 174 | def __render_option(self, option): 175 | option_line = ' option %s %s\n' 176 | return option_line % (option.keyword, option.value) 177 | 178 | def __render_config(self, config): 179 | config_line = ' %s %s\n' 180 | return config_line % (config.keyword, config.value) 181 | -------------------------------------------------------------------------------- /pyhaproxy/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from __future__ import absolute_import, print_function, unicode_literals 4 | 5 | import pyhaproxy.parse as parse 6 | import pyhaproxy.render as render 7 | import pyhaproxy.config as config 8 | 9 | 10 | class TestParse(object): 11 | 12 | @classmethod 13 | def setup_class(cls): 14 | pass 15 | 16 | @classmethod 17 | def teardown_class(cls): 18 | pass 19 | 20 | def setup(self): 21 | filestring = r""" 22 | global 23 | maxconn 4096 24 | nbproc 1 25 | debug 26 | daemon 27 | log 127.0.0.1 local0 28 | 29 | defaults 30 | mode http 31 | option httplog 32 | log global 33 | 34 | userlist L1 35 | group G1 users tiger,scott 36 | group G2 users xdb,sc 37 | 38 | user tiger password $6$k6y3o.eP$JlKBx9za9667qe4(...)xHSwRv6J.C0/D7cV91 39 | user scott insecure-password elgato 40 | user xdb insecure-password hello 41 | 42 | userlist L2 43 | group G1 44 | group G2 45 | 46 | user tiger password $6$k6y3o.eP$JlKBx(...)xHSwRv6J.C0/D7cV91 groups G1 47 | user scott insecure-password elgato groups G1,G2 48 | user xdb insecure-password hello groups G2 49 | 50 | frontend unsecured *:80 51 | timeout client 86400000 52 | mode http 53 | option httpclose 54 | option forwardfor #forward’s clients IP to app 55 | bind-process odd 56 | #catch all domains that begin with 'www.' 57 | acl host_www1 hdr_beg(host) -i www. 58 | reqirep ^Host:\ www.(.*)$ Host:\ \1 if host_www 59 | redirect code 301 prefix / if host_www 60 | 61 | # Define hosts 62 | acl host_niftykick hdr(host) -i niftykick.com 63 | acl host_chatleap hdr(host) -i chatleap.com 64 | acl host_trevordev hdr(host) -i trevordev.com 65 | acl host_jokeydoke hdr(host) -i jokeydoke.com 66 | acl host_swiftnifty hdr(host) -i swiftnifty.trevordev.com 67 | 68 | #redirect to https 69 | redirect prefix https://niftykick.com code 301 if host_niftykick 70 | 71 | ## figure out which one to use 72 | use_backend niftykick if host_niftykick 73 | use_backend chatleap if host_chatleap 74 | use_backend trevordev if host_trevordev 75 | use_backend swiftnifty if host_swiftnifty 76 | use_backend jokeydoke if host_jokeydoke 77 | 78 | default_backend devbrick 79 | 80 | frontend secured 81 | timeout client 86400000 82 | mode http 83 | option httpclose 84 | option forwardfor 85 | 86 | #catch all domains that begin with 'www.' 87 | acl host_www2 hdr_beg(host) -i www. 88 | reqirep ^Host:\ www.(.*)$ Host:\ \1 if host_www 89 | redirect code 301 prefix / if host_www 90 | 91 | acl host_coyote path_beg /fileServer/ 92 | acl host_niftykick hdr(host) -i niftykick.com 93 | 94 | bind 0.0.0.0:443 ssl crt /etc/haproxy/niftykickCert.pem 95 | 96 | use_backend fileServer if host_coyote 97 | use_backend niftykick if host_niftykick 98 | 99 | backend devbrick 100 | mode http 101 | option forwardfor #this sets X-Forwarded-For 102 | timeout server 30000 103 | timeout connect 4000 104 | server server1 localhost:3000 weight 1 maxconn 1024 check 105 | 106 | backend chatleap 107 | mode http 108 | option forwardfor #this sets X-Forwarded-For 109 | timeout server 30000 110 | timeout connect 4000 111 | server server1 localhost:3001 weight 1 maxconn 1024 check 112 | 113 | backend jokeydoke 114 | mode http 115 | option forwardfor #this sets X-Forwarded-For 116 | timeout server 30000 117 | timeout connect 4000 118 | server server1 localhost:3002 weight 1 maxconn 1024 check 119 | 120 | backend trevordev 121 | mode http 122 | option forwardfor #this sets X-Forwarded-For 123 | timeout server 30000 124 | timeout connect 4000 125 | server server1 localhost:3003 weight 1 maxconn 1024 check 126 | 127 | backend niftykick 128 | mode http 129 | option forwardfor #this sets X-Forwarded-For 130 | timeout server 30000 131 | timeout connect 4000 132 | server server1 localhost:3005 weight 1 maxconn 1024 check 133 | 134 | backend fileServer 135 | mode http 136 | option forwardfor #this sets X-Forwarded-For 137 | timeout server 30000 138 | timeout connect 4000 139 | reqrep ^([^\ :]*)\ /fileServer/(.*) \1\ /\2 140 | server server1 localhost:3008 weight 1 maxconn 1024 check 141 | 142 | backend swiftnifty 143 | mode http 144 | option forwardfor #this sets X-Forwarded-For 145 | timeout server 30000 146 | timeout connect 4000 147 | server server1 localhost:3007 weight 1 maxconn 1024 check 148 | 149 | """ 150 | self.parser = parse.Parser(filestring=filestring) 151 | self.configration = self.parser.build_configuration() 152 | 153 | def teardown(self): 154 | pass 155 | 156 | def test_parse_global_section(self): 157 | print(self.configration) 158 | print(self.configration.globall.configs()) 159 | print('-' * 30) 160 | print(self.configration.globall.options()) 161 | 162 | def test_parse_frontend_section(self): 163 | for frontend in self.configration.frontends: 164 | print('-' * 15) 165 | print(frontend.name, frontend.host, frontend.port) 166 | for acl in frontend.acls(): 167 | print(acl) 168 | 169 | def test_parse_defaults_section(self): 170 | for defaults in self.configration.defaults: 171 | print(defaults.name) 172 | print(defaults.options()) 173 | print(defaults.configs()) 174 | 175 | def test_parse_listen_section(self): 176 | for listen in self.configration.listens: 177 | print(listen.name, listen.host, listen.port) 178 | print(listen.options()) 179 | print(listen.configs()) 180 | 181 | def test_parse_backend_section(self): 182 | for backend in self.configration.backends: 183 | print(backend.name) 184 | print(backend.options()) 185 | print(backend.configs()) 186 | 187 | def test_parse_userlist_section(self): 188 | for userlist in self.configration.userlists: 189 | print(userlist.name) 190 | print('groups:\n') 191 | for group in userlist.groups(): 192 | print(group.name) 193 | print(group.user_names) 194 | print('users:\n') 195 | for user in userlist.users(): 196 | print(user.name, '-', user.passwd, '-', user.passwd_type) 197 | print(user.group_names) 198 | 199 | def test_update_config_block(self): 200 | backend = self.configration.backend('chatleap') 201 | new_server = config.Server('newly1', '8.8.8.8', 1234) 202 | backend.add_server(new_server) 203 | server1 = backend.server('server1') 204 | server1.name = 'server2' 205 | backend.add_config(config.Config('conf_key_1', 'conf_key_2')) 206 | self.render = render.Render(self.configration) 207 | self.render.dumps_to('./hatest.cfg') 208 | -------------------------------------------------------------------------------- /pyhaproxy/parse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf8 -*- 3 | 4 | import os 5 | 6 | import pyhaproxy.pegnode as pegnode 7 | import pyhaproxy.config as config 8 | 9 | 10 | class Parser(object): 11 | """Do parsing the peg-tree and build the objects in config module 12 | 13 | Attributes: 14 | filepath (str): the absolute path of haproxy config file 15 | filestring (str): the content of haproxy config file 16 | """ 17 | def __init__(self, filepath='/etc/haproxy/haproxy.cfg', filestring=None): 18 | if filestring: 19 | self.filestring = filestring 20 | elif filepath: 21 | self.filestring = self.__read_string_from_file(filepath) 22 | else: 23 | raise Exception('please validate your input') 24 | 25 | def build_configuration(self): 26 | """Parse the haproxy config file 27 | 28 | Raises: 29 | Exception: when there are unsupported section 30 | 31 | Returns: 32 | config.Configuration: haproxy config object 33 | """ 34 | configuration = config.Configuration() 35 | pegtree = pegnode.parse(self.filestring) 36 | for section_node in pegtree: 37 | if isinstance(section_node, pegnode.GlobalSection): 38 | configuration.globall = self.build_global(section_node) 39 | elif isinstance(section_node, pegnode.FrontendSection): 40 | configuration.frontends.append( 41 | self.build_frontend(section_node)) 42 | elif isinstance(section_node, pegnode.DefaultsSection): 43 | configuration.defaults.append( 44 | self.build_defaults(section_node)) 45 | elif isinstance(section_node, pegnode.ListenSection): 46 | configuration.listens.append( 47 | self.build_listen(section_node)) 48 | elif isinstance(section_node, pegnode.UserlistSection): 49 | configuration.userlists.append( 50 | self.build_userlist(section_node)) 51 | elif isinstance(section_node, pegnode.BackendSection): 52 | configuration.backends.append( 53 | self.build_backend(section_node)) 54 | 55 | return configuration 56 | 57 | def build_global(self, global_node): 58 | 59 | """parse `global` section, and return the config.Global 60 | 61 | Args: 62 | global_node (TreeNode): `global` section treenode 63 | 64 | Returns: 65 | config.Global: an object 66 | """ 67 | config_block_lines = self.__build_config_block( 68 | global_node.config_block) 69 | return config.Global(config_block=config_block_lines) 70 | 71 | def __build_config_block(self, config_block_node): 72 | """parse `config_block` in each section 73 | 74 | Args: 75 | config_block_node (TreeNode): Description 76 | 77 | Returns: 78 | [line_node1, line_node2, ...] 79 | """ 80 | node_lists = [] 81 | 82 | for line_node in config_block_node: 83 | if isinstance(line_node, pegnode.ConfigLine): 84 | node_lists.append(self.__build_config(line_node)) 85 | elif isinstance(line_node, pegnode.OptionLine): 86 | node_lists.append(self.__build_option(line_node)) 87 | elif isinstance(line_node, pegnode.ServerLine): 88 | node_lists.append( 89 | self.__build_server(line_node)) 90 | elif isinstance(line_node, pegnode.BindLine): 91 | node_lists.append( 92 | self.__build_bind(line_node)) 93 | elif isinstance(line_node, pegnode.AclLine): 94 | node_lists.append( 95 | self.__build_acl(line_node)) 96 | elif isinstance(line_node, pegnode.BackendLine): 97 | node_lists.append( 98 | self.__build_usebackend(line_node)) 99 | elif isinstance(line_node, pegnode.UserLine): 100 | node_lists.append( 101 | self.__build_user(line_node)) 102 | elif isinstance(line_node, pegnode.GroupLine): 103 | node_lists.append( 104 | self.__build_group(line_node)) 105 | else: 106 | # may blank_line, comment_line 107 | pass 108 | return node_lists 109 | 110 | def build_defaults(self, defaults_node): 111 | """parse `defaults` sections, and return a config.Defaults 112 | 113 | Args: 114 | defaults_node (TreeNode): Description 115 | 116 | Returns: 117 | config.Defaults: an object 118 | """ 119 | proxy_name = defaults_node.defaults_header.proxy_name.text 120 | config_block_lines = self.__build_config_block( 121 | defaults_node.config_block) 122 | return config.Defaults( 123 | name=proxy_name, 124 | config_block=config_block_lines) 125 | 126 | def build_userlist(self, userlist_node): 127 | """parse `userlist` sections, and return a config.Userlist""" 128 | proxy_name = userlist_node.userlist_header.proxy_name.text 129 | config_block_lines = self.__build_config_block( 130 | userlist_node.config_block) 131 | return config.Userlist( 132 | name=proxy_name, 133 | config_block=config_block_lines) 134 | 135 | def build_listen(self, listen_node): 136 | """parse `listen` sections, and return a config.Listen 137 | 138 | Args: 139 | listen_node (TreeNode): Description 140 | 141 | Returns: 142 | config.Listen: an object 143 | """ 144 | proxy_name = listen_node.listen_header.proxy_name.text 145 | service_address_node = listen_node.listen_header.service_address 146 | 147 | # parse the config block 148 | config_block_lines = self.__build_config_block( 149 | listen_node.config_block) 150 | 151 | # parse host and port 152 | host, port = '', '' 153 | if isinstance(service_address_node, pegnode.ServiceAddress): 154 | host = service_address_node.host.text 155 | port = service_address_node.port.text 156 | else: 157 | # use `bind` in config lines to fill in host and port 158 | # just use the first 159 | for line in config_block_lines: 160 | if isinstance(line, config.Bind): 161 | host, port = line.host, line.port 162 | break 163 | else: 164 | raise Exception( 165 | 'Not specify host and port in `listen` definition') 166 | return config.Listen( 167 | name=proxy_name, host=host, port=port, 168 | config_block=config_block_lines) 169 | 170 | def build_frontend(self, frontend_node): 171 | """parse `frontend` sections, and return a config.Frontend 172 | 173 | Args: 174 | frontend_node (TreeNode): Description 175 | 176 | Raises: 177 | Exception: Description 178 | 179 | Returns: 180 | config.Frontend: an object 181 | """ 182 | proxy_name = frontend_node.frontend_header.proxy_name.text 183 | service_address_node = frontend_node.frontend_header.service_address 184 | 185 | # parse the config block 186 | config_block_lines = self.__build_config_block( 187 | frontend_node.config_block) 188 | 189 | # parse host and port 190 | host, port = '', '' 191 | if isinstance(service_address_node, pegnode.ServiceAddress): 192 | host = service_address_node.host.text 193 | port = service_address_node.port.text 194 | else: 195 | # use `bind` in config lines to fill in host and port 196 | # just use the first 197 | for line in config_block_lines: 198 | if isinstance(line, config.Bind): 199 | host, port = line.host, line.port 200 | break 201 | else: 202 | raise Exception( 203 | 'Not specify host and port in `frontend` definition') 204 | return config.Frontend( 205 | name=proxy_name, host=host, port=port, 206 | config_block=config_block_lines) 207 | 208 | def build_backend(self, backend_node): 209 | """parse `backend` sections 210 | 211 | Args: 212 | backend_node (TreeNode): Description 213 | 214 | Returns: 215 | config.Backend: an object 216 | """ 217 | proxy_name = backend_node.backend_header.proxy_name.text 218 | config_block_lines = self.__build_config_block( 219 | backend_node.config_block) 220 | return config.Backend(name=proxy_name, config_block=config_block_lines) 221 | 222 | def __build_server(self, server_node): 223 | server_name = server_node.server_name.text 224 | host = server_node.service_address.host.text 225 | port = server_node.service_address.port.text 226 | 227 | # parse server attributes, value is similar to \ 228 | # 'maxconn 1024 weight 3 check inter 2000 rise 2 fall 3' 229 | server_attributes = server_node.value.text.split(' \t') 230 | return config.Server( 231 | name=server_name, host=host, port=port, 232 | attributes=server_attributes) 233 | 234 | def __build_config(self, config_node): 235 | return config.Config(keyword=config_node.keyword.text, 236 | value=config_node.value.text) 237 | 238 | def __build_option(self, option_node): 239 | return config.Option(keyword=option_node.keyword.text, 240 | value=option_node.value.text) 241 | 242 | def __build_bind(self, bind_node): 243 | service_address = bind_node.service_address 244 | return config.Bind( 245 | host=service_address.host.text, 246 | port=service_address.port.text, 247 | attributes=bind_node.value.text.split(' \t')) 248 | 249 | def __build_acl(self, acl_node): 250 | acl_name = acl_node.acl_name.text 251 | acl_value = acl_node.value.text 252 | return config.Acl(name=acl_name, value=acl_value) 253 | 254 | def __build_usebackend(self, usebackend_node): 255 | operator = usebackend_node.operator.text 256 | backendtype = usebackend_node.backendtype.text 257 | 258 | return config.UseBackend( 259 | backend_name=usebackend_node.backend_name.text, 260 | operator=operator, 261 | backend_condition=usebackend_node.backend_condition.text, 262 | is_default=(backendtype == 'default_backend')) 263 | 264 | def __build_user(self, user_node): 265 | groups_fragment = user_node.groups_fragment.text 266 | group_names = groups_fragment.split(',') if groups_fragment else [] 267 | return config.User( 268 | name=user_node.user_name.text, 269 | passwd=user_node.password.text, 270 | passwd_type=user_node.passwd_type.text, 271 | group_names=group_names) 272 | 273 | def __build_group(self, group_node): 274 | users_fragment = group_node.users_fragment.text 275 | user_names = users_fragment.split(',') if users_fragment else [] 276 | return config.Group( 277 | name=group_node.group_name.text, 278 | user_names=user_names) 279 | 280 | def __read_string_from_file(self, filepath): 281 | filestring = '' 282 | if os.path.exists(filepath): 283 | with open(filepath) as f: 284 | filestring = f.read() 285 | return filestring 286 | -------------------------------------------------------------------------------- /pyhaproxy/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | class Configuration(object): 6 | """Represents a whole haproxy config file 7 | 8 | Attributes: 9 | 10 | """ 11 | def __init__(self): 12 | self.__defaults = [] 13 | self.__backends = [] 14 | self.__frontends = [] 15 | self.__listens = [] 16 | self.__userlists = [] 17 | self.__globall = None 18 | 19 | @property 20 | def globall(self): 21 | return self.__globall 22 | 23 | @globall.setter 24 | def globall(self, globall): 25 | self.__globall = globall 26 | 27 | @property 28 | def userlists(self): 29 | return self.__userlists 30 | 31 | def userlist(self, name): 32 | for userlist in self.__userlists: 33 | if userlist.name == name: 34 | return userlist 35 | 36 | @property 37 | def listens(self): 38 | return self.__listens 39 | 40 | def listen(self, name): 41 | for listen in self.__listens: 42 | if listen.name == name: 43 | return listen 44 | 45 | @property 46 | def defaults(self): 47 | return self.__defaults 48 | 49 | def default(self, name): 50 | for default in self.__defaults: 51 | if default.name == name: 52 | return default 53 | 54 | @property 55 | def backends(self): 56 | return self.__backends 57 | 58 | def backend(self, name): 59 | for backend in self.__backends: 60 | if backend.name == name: 61 | return backend 62 | 63 | @property 64 | def frontends(self): 65 | return self.__frontends 66 | 67 | def frontend(self, name): 68 | for frontend in self.__frontends: 69 | if frontend.name == name: 70 | return frontend 71 | 72 | 73 | class HasConfigBlock(object): 74 | 75 | def __init__(self, config_block): 76 | super(HasConfigBlock, self).__init__() 77 | self.config_block = config_block 78 | 79 | def __find_configs(self, config_type): 80 | configs = [] 81 | for line in self.config_block: 82 | if isinstance(line, config_type): 83 | configs.append(line) 84 | return configs 85 | 86 | def __add_node(self, node, node_type): 87 | if not isinstance(node, node_type): 88 | raise Exception('config.%s is only supported' % node_type) 89 | self.config_block.append(node) 90 | 91 | def options(self): 92 | return self.__find_configs(Option) 93 | 94 | def option(self, keyword, value): 95 | for line in self.config_block: 96 | if isinstance(line, Option): 97 | if line.keyword == keyword and line.value == value: 98 | return line 99 | 100 | def add_option(self, option): 101 | self.__add_node(option, Option) 102 | 103 | def remove_option(self, keyword, value): 104 | option = self.option(keyword, value) 105 | if option: 106 | self.config_block.remove(option) 107 | 108 | def configs(self): 109 | return self.__find_configs(Config) 110 | 111 | def config(self, keyword, value): 112 | for line in self.config_block: 113 | if isinstance(line, Config): 114 | if line.keyword == keyword and line.value == value: 115 | return line 116 | 117 | def add_config(self, config): 118 | self.__add_node(config, Config) 119 | 120 | def remove_config(self, keyword, value): 121 | config = self.config(keyword, value) 122 | if config: 123 | self.config_block.remove(config) 124 | 125 | def servers(self): 126 | return self.__find_configs(Server) 127 | 128 | def server(self, name): 129 | for line in self.config_block: 130 | if isinstance(line, Server): 131 | if line.name == name: 132 | return line 133 | 134 | def add_server(self, server): 135 | self.__add_node(server, Server) 136 | 137 | def remove_server(self, name): 138 | server = self.server(name) 139 | if server: 140 | self.config_block.remove(server) 141 | 142 | def binds(self): 143 | return self.__find_configs(Bind) 144 | 145 | def bind(self, host, port): 146 | for line in self.config_block: 147 | if isinstance(line, Bind): 148 | if line.host == host and line.port == port: 149 | return line 150 | 151 | def add_bind(self, bind): 152 | self.__add_node(bind, Bind) 153 | 154 | def remove_bind(self, host, port): 155 | bind = self.bind(host, port) 156 | if bind: 157 | self.config_block.remove(bind) 158 | 159 | def acls(self): 160 | return self.__find_configs(Acl) 161 | 162 | def acl(self, name): 163 | for line in self.config_block: 164 | if isinstance(line, Acl): 165 | if line.name == name: 166 | return line 167 | 168 | def add_acl(self, acl): 169 | self.__add_node(acl, Acl) 170 | 171 | def remove_acl(self, name): 172 | acl = self.acl(name) 173 | if acl: 174 | self.config_block.remove(acl) 175 | 176 | def users(self): 177 | return self.__find_configs(User) 178 | 179 | def user(self, name): 180 | for line in self.config_block: 181 | if isinstance(line, User): 182 | if line.name == name: 183 | return line 184 | 185 | def add_user(self, user): 186 | self.__add_node(user, User) 187 | 188 | def remove_user(self, name): 189 | user = self.user(name) 190 | if user: 191 | self.config_block.remove(user) 192 | 193 | def groups(self): 194 | return self.__find_configs(Group) 195 | 196 | def group(self, name): 197 | for line in self.config_block: 198 | if isinstance(line, Group): 199 | if line.name == name: 200 | return line 201 | 202 | def add_group(self, group): 203 | self.__add_node(group, Group) 204 | 205 | def remove_group(self, name): 206 | group = self.group(name) 207 | if group: 208 | self.config_block.remove(group) 209 | 210 | def usebackends(self): 211 | return self.__find_configs(UseBackend) 212 | 213 | def usebackend(self, name): 214 | for line in self.config_block: 215 | if isinstance(line, UseBackend): 216 | if line.backend_name == name: 217 | return line 218 | 219 | def add_usebackend(self, usebackend): 220 | self.__add_node(usebackend, UseBackend) 221 | 222 | def remove_usebackend(self, name): 223 | usebackend = self.usebackend(name) 224 | if usebackend: 225 | self.config_block.remove(usebackend) 226 | 227 | 228 | class Global(HasConfigBlock): 229 | """Represens a `global` section 230 | """ 231 | pass 232 | 233 | 234 | class Defaults(HasConfigBlock): 235 | 236 | def __init__(self, name, config_block): 237 | super(Defaults, self).__init__(config_block) 238 | self.name = name 239 | 240 | 241 | class Backend(HasConfigBlock): 242 | 243 | def __init__(self, name, config_block): 244 | super(Backend, self).__init__(config_block) 245 | self.name = name 246 | 247 | 248 | class Listen(HasConfigBlock): 249 | def __init__(self, name, host, port, config_block): 250 | super(Listen, self).__init__(config_block) 251 | self.name = name 252 | self.host = host 253 | self.port = port 254 | 255 | 256 | class Frontend(HasConfigBlock): 257 | def __init__(self, name, host, port, config_block): 258 | super(Frontend, self).__init__(config_block) 259 | self.name = name 260 | self.host = host 261 | self.port = port 262 | 263 | 264 | class Userlist(HasConfigBlock): 265 | """Represents the `userlist` section 266 | 267 | Attributes: 268 | name (str): Description 269 | """ 270 | def __init__(self, name, config_block): 271 | super(Userlist, self).__init__(config_block) 272 | self.name = name 273 | 274 | 275 | class Server(object): 276 | """Represents the `server` line in config block 277 | 278 | Attributes: 279 | name (str): Description 280 | host (str): Description 281 | port (str): Description 282 | attributes (list): Description 283 | """ 284 | def __init__(self, name, host, port, attributes=[]): 285 | super(Server, self).__init__() 286 | self.name = name 287 | self.host = host 288 | self.port = port 289 | self.attributes = [attr.strip() for attr in attributes] 290 | 291 | def __str__(self): 292 | return '' % ( 293 | self.name, self.host, self.port, ' '.join(self.attributes)) 294 | 295 | 296 | class Config(object): 297 | """Represents the `config` line in config block 298 | 299 | Attributes: 300 | keyword (srt): 301 | value (str): 302 | """ 303 | def __init__(self, keyword, value): 304 | self.keyword = keyword 305 | self.value = value 306 | 307 | def __str__(self): 308 | return '' % ( 309 | self.keyword, self.value) 310 | 311 | 312 | class Option(object): 313 | """Represents the `option` line in config block 314 | 315 | Attributes: 316 | keyword (srt): 317 | value (str): 318 | """ 319 | def __init__(self, keyword, value): 320 | self.keyword = keyword 321 | self.value = value 322 | 323 | def __str__(self): 324 | return '' % ( 325 | self.keyword, self.value) 326 | 327 | 328 | class Bind(object): 329 | """Represents the `bind` line in config block 330 | 331 | Attributes: 332 | host (srt): 333 | port (list): 334 | attributes (str): 335 | """ 336 | def __init__(self, host, port, attributes): 337 | self.host = host 338 | self.port = port 339 | self.attributes = attributes or [] 340 | 341 | def __str__(self): 342 | return '' % ( 343 | self.host, self.port, ' '.join(self.attributes)) 344 | 345 | 346 | class Acl(object): 347 | """Represents the `acl` line in config block 348 | 349 | Attributes: 350 | name (str): 351 | value (str): 352 | """ 353 | def __init__(self, name, value): 354 | self.name = name 355 | self.value = value 356 | 357 | def __str__(self): 358 | return '' % (self.name, self.value) 359 | 360 | 361 | class User(object): 362 | """Represents the `user` line in config block 363 | 364 | Attributes: 365 | name (str): Description 366 | passwd (str): Description 367 | passwd_type ('password' or 'insecure-password'): Description 368 | group_names (list(str)): Description 369 | """ 370 | def __init__(self, name, passwd, passwd_type, group_names): 371 | super(User, self).__init__() 372 | self.name = name 373 | self.passwd = passwd 374 | self.passwd_type = passwd_type 375 | self.group_names = group_names or [] 376 | 377 | def __str__(self): 378 | if self.group_names: 379 | group_fragment = 'groups ' + ','.join(self.group_names) 380 | else: 381 | group_fragment = '' 382 | return '' % ( 383 | self.name, self.passwd_type, self.passwd, group_fragment) 384 | 385 | 386 | class Group(object): 387 | """Represents the `group` line in config block 388 | 389 | Attributes: 390 | name (str): Description 391 | user_names (list(str)): Description 392 | """ 393 | def __init__(self, name, user_names): 394 | super(Group, self).__init__() 395 | self.name = name 396 | self.user_names = user_names or [] 397 | 398 | def __str__(self): 399 | if self.user_names: 400 | user_fragment = 'users ' + ', '.join(self.user_names) 401 | else: 402 | user_fragment = '' 403 | 404 | return '' % (self.name, user_fragment) 405 | 406 | 407 | class UseBackend(object): 408 | """Represents the `use_backend` or `default_backend` line in config block 409 | 410 | Attributes: 411 | backend_condition (str): Description 412 | backend_name (str): Description 413 | is_default (bool): Description 414 | operator (str): Description 415 | """ 416 | def __init__(self, backend_name, operator, 417 | backend_condition, is_default=False): 418 | self.backend_name = backend_name 419 | self.operator = operator 420 | self.backend_condition = backend_condition 421 | self.is_default = is_default 422 | 423 | def __str__(self): 424 | backendtype = 'default_backend' if self.is_default else 'use_backend' 425 | return '' % ( 426 | backendtype, self.backend_name, 427 | self.operator, self.backend_condition) 428 | -------------------------------------------------------------------------------- /pyhaproxy/pegnode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from collections import defaultdict 5 | import re 6 | 7 | 8 | class TreeNode(object): 9 | def __init__(self, text, offset, elements=None): 10 | self.text = text 11 | self.offset = offset 12 | self.elements = elements or [] 13 | 14 | def __iter__(self): 15 | for el in self.elements: 16 | yield el 17 | 18 | 19 | class GlobalSection(TreeNode): 20 | def __init__(self, text, offset, elements): 21 | super(GlobalSection, self).__init__(text, offset, elements) 22 | self.global_header = elements[0] 23 | self.config_block = elements[1] 24 | 25 | 26 | class DefaultsSection(TreeNode): 27 | def __init__(self, text, offset, elements): 28 | super(DefaultsSection, self).__init__(text, offset, elements) 29 | self.defaults_header = elements[0] 30 | self.config_block = elements[1] 31 | 32 | 33 | class UserlistSection(TreeNode): 34 | def __init__(self, text, offset, elements): 35 | super(UserlistSection, self).__init__(text, offset, elements) 36 | self.userlist_header = elements[0] 37 | self.config_block = elements[1] 38 | 39 | 40 | class ListenSection(TreeNode): 41 | def __init__(self, text, offset, elements): 42 | super(ListenSection, self).__init__(text, offset, elements) 43 | self.listen_header = elements[0] 44 | self.config_block = elements[1] 45 | 46 | 47 | class FrontendSection(TreeNode): 48 | def __init__(self, text, offset, elements): 49 | super(FrontendSection, self).__init__(text, offset, elements) 50 | self.frontend_header = elements[0] 51 | self.config_block = elements[1] 52 | 53 | 54 | class BackendSection(TreeNode): 55 | def __init__(self, text, offset, elements): 56 | super(BackendSection, self).__init__(text, offset, elements) 57 | self.backend_header = elements[0] 58 | self.config_block = elements[1] 59 | 60 | 61 | class GlobalHeader(TreeNode): 62 | def __init__(self, text, offset, elements): 63 | super(GlobalHeader, self).__init__(text, offset, elements) 64 | self.whitespace = elements[2] 65 | self.line_break = elements[4] 66 | 67 | 68 | class UserlistHeader(TreeNode): 69 | def __init__(self, text, offset, elements): 70 | super(UserlistHeader, self).__init__(text, offset, elements) 71 | self.whitespace = elements[2] 72 | self.proxy_name = elements[3] 73 | self.line_break = elements[5] 74 | 75 | 76 | class DefaultsHeader(TreeNode): 77 | def __init__(self, text, offset, elements): 78 | super(DefaultsHeader, self).__init__(text, offset, elements) 79 | self.whitespace = elements[4] 80 | self.line_break = elements[6] 81 | self.proxy_name = elements[3] 82 | 83 | 84 | class ListenHeader(TreeNode): 85 | def __init__(self, text, offset, elements): 86 | super(ListenHeader, self).__init__(text, offset, elements) 87 | self.whitespace = elements[4] 88 | self.proxy_name = elements[3] 89 | self.line_break = elements[8] 90 | self.service_address = elements[5] 91 | 92 | 93 | class FrontendHeader(TreeNode): 94 | def __init__(self, text, offset, elements): 95 | super(FrontendHeader, self).__init__(text, offset, elements) 96 | self.whitespace = elements[4] 97 | self.proxy_name = elements[3] 98 | self.line_break = elements[8] 99 | self.service_address = elements[5] 100 | 101 | 102 | class BackendHeader(TreeNode): 103 | def __init__(self, text, offset, elements): 104 | super(BackendHeader, self).__init__(text, offset, elements) 105 | self.whitespace = elements[4] 106 | self.proxy_name = elements[3] 107 | self.line_break = elements[7] 108 | 109 | 110 | class ServerLine(TreeNode): 111 | def __init__(self, text, offset, elements): 112 | super(ServerLine, self).__init__(text, offset, elements) 113 | self.whitespace = elements[4] 114 | self.server_name = elements[3] 115 | self.service_address = elements[5] 116 | self.line_break = elements[8] 117 | self.value = elements[6] 118 | 119 | 120 | class OptionLine(TreeNode): 121 | def __init__(self, text, offset, elements): 122 | super(OptionLine, self).__init__(text, offset, elements) 123 | self.whitespace = elements[4] 124 | self.whitespaceplus = elements[2] 125 | self.keyword = elements[3] 126 | self.line_break = elements[7] 127 | self.value = elements[5] 128 | 129 | 130 | class BindLine(TreeNode): 131 | def __init__(self, text, offset, elements): 132 | super(BindLine, self).__init__(text, offset, elements) 133 | self.whitespace = elements[4] 134 | self.service_address = elements[3] 135 | self.line_break = elements[7] 136 | self.value = elements[5] 137 | 138 | 139 | class AclLine(TreeNode): 140 | def __init__(self, text, offset, elements): 141 | super(AclLine, self).__init__(text, offset, elements) 142 | self.whitespace = elements[4] 143 | self.acl_name = elements[3] 144 | self.line_break = elements[7] 145 | self.value = elements[5] 146 | 147 | 148 | class BackendLine(TreeNode): 149 | def __init__(self, text, offset, elements): 150 | super(BackendLine, self).__init__(text, offset, elements) 151 | self.whitespace = elements[6] 152 | self.backend_name = elements[3] 153 | self.line_break = elements[9] 154 | self.operator = elements[5] 155 | self.backend_condition = elements[7] 156 | self.backendtype = elements[1] 157 | 158 | 159 | class GroupLine(TreeNode): 160 | def __init__(self, text, offset, elements): 161 | super(GroupLine, self).__init__(text, offset, elements) 162 | self.whitespace = elements[4] 163 | self.group_name = elements[3] 164 | self.line_break = elements[8] 165 | self.users_fragment = elements[6] 166 | 167 | 168 | class TreeNode19(TreeNode): 169 | def __init__(self, text, offset, elements): 170 | super(TreeNode19, self).__init__(text, offset, elements) 171 | self.whitespace = elements[1] 172 | 173 | 174 | class UserLine(TreeNode): 175 | def __init__(self, text, offset, elements): 176 | super(UserLine, self).__init__(text, offset, elements) 177 | self.whitespace = elements[8] 178 | self.user_name = elements[3] 179 | self.password = elements[7] 180 | self.line_break = elements[12] 181 | self.groups_fragment = elements[10] 182 | self.passwd_type = elements[5] 183 | 184 | 185 | class TreeNode21(TreeNode): 186 | def __init__(self, text, offset, elements): 187 | super(TreeNode21, self).__init__(text, offset, elements) 188 | self.whitespace = elements[1] 189 | 190 | 191 | class ConfigLine(TreeNode): 192 | def __init__(self, text, offset, elements): 193 | super(ConfigLine, self).__init__(text, offset, elements) 194 | self.whitespace = elements[3] 195 | self.keyword = elements[2] 196 | self.line_break = elements[6] 197 | self.value = elements[4] 198 | 199 | 200 | class CommentLine(TreeNode): 201 | def __init__(self, text, offset, elements): 202 | super(CommentLine, self).__init__(text, offset, elements) 203 | self.whitespace = elements[0] 204 | self.comment_text = elements[1] 205 | self.line_break = elements[2] 206 | 207 | 208 | class BlankLine(TreeNode): 209 | def __init__(self, text, offset, elements): 210 | super(BlankLine, self).__init__(text, offset, elements) 211 | self.whitespace = elements[0] 212 | self.line_break = elements[1] 213 | 214 | 215 | class Keyword(TreeNode): 216 | def __init__(self, text, offset, elements): 217 | super(Keyword, self).__init__(text, offset, elements) 218 | self.whitespace = elements[1] 219 | 220 | 221 | class ServiceAddress(TreeNode): 222 | def __init__(self, text, offset, elements): 223 | super(ServiceAddress, self).__init__(text, offset, elements) 224 | self.host = elements[0] 225 | self.port = elements[2] 226 | 227 | 228 | class ParseError(SyntaxError): 229 | pass 230 | 231 | 232 | FAILURE = object() 233 | 234 | 235 | class Grammar(object): 236 | REGEX_1 = re.compile('^[\\n]') 237 | REGEX_2 = re.compile('^[a-z0-9\\-\\_\\.]') 238 | REGEX_3 = re.compile('^[a-zA-z0-9\\-\\_\\.:]') 239 | REGEX_4 = re.compile('^[a-zA-z0-9\\-\\_\\.:]') 240 | REGEX_5 = re.compile('^[a-zA-z0-9\\-\\_\\.:]') 241 | REGEX_6 = re.compile('^[a-zA-z0-9\\-\\_\\.:]') 242 | REGEX_7 = re.compile('^[a-zA-z0-9\\-\\_\\.:]') 243 | REGEX_8 = re.compile('^[^#\\n ]') 244 | REGEX_9 = re.compile('^[^#\\n]') 245 | REGEX_10 = re.compile('^[:]') 246 | REGEX_11 = re.compile('^[\\d]') 247 | REGEX_12 = re.compile('^[\\d]') 248 | REGEX_13 = re.compile('^[\\d]') 249 | REGEX_14 = re.compile('^[\\d]') 250 | REGEX_15 = re.compile('^[\\d]') 251 | REGEX_16 = re.compile('^[a-zA-Z\\-\\.\\d]') 252 | REGEX_17 = re.compile('^[a-zA-Z0-9\\-\\_\\.:]') 253 | REGEX_18 = re.compile('^[^#\\n]') 254 | REGEX_19 = re.compile('^[\\n]') 255 | REGEX_20 = re.compile('^[ \\t]') 256 | REGEX_21 = re.compile('^[ \\t]') 257 | 258 | def _read_configuration(self): 259 | address0, index0 = FAILURE, self._offset 260 | cached = self._cache['configuration'].get(index0) 261 | if cached: 262 | self._offset = cached[1] 263 | return cached[0] 264 | remaining0, index1, elements0, address1 = 0, self._offset, [], True 265 | while address1 is not FAILURE: 266 | index2 = self._offset 267 | address1 = self._read_comment_line() 268 | if address1 is FAILURE: 269 | self._offset = index2 270 | address1 = self._read_blank_line() 271 | if address1 is FAILURE: 272 | self._offset = index2 273 | address1 = self._read_global_section() 274 | if address1 is FAILURE: 275 | self._offset = index2 276 | address1 = self._read_defaults_section() 277 | if address1 is FAILURE: 278 | self._offset = index2 279 | address1 = self._read_userlist_section() 280 | if address1 is FAILURE: 281 | self._offset = index2 282 | address1 = self._read_listen_section() 283 | if address1 is FAILURE: 284 | self._offset = index2 285 | address1 = self._read_frontend_section() 286 | if address1 is FAILURE: 287 | self._offset = index2 288 | address1 = self._read_backend_section() 289 | if address1 is FAILURE: 290 | self._offset = index2 291 | if address1 is not FAILURE: 292 | elements0.append(address1) 293 | remaining0 -= 1 294 | if remaining0 <= 0: 295 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 296 | self._offset = self._offset 297 | else: 298 | address0 = FAILURE 299 | self._cache['configuration'][index0] = (address0, self._offset) 300 | return address0 301 | 302 | def _read_global_section(self): 303 | address0, index0 = FAILURE, self._offset 304 | cached = self._cache['global_section'].get(index0) 305 | if cached: 306 | self._offset = cached[1] 307 | return cached[0] 308 | index1, elements0 = self._offset, [] 309 | address1 = FAILURE 310 | address1 = self._read_global_header() 311 | if address1 is not FAILURE: 312 | elements0.append(address1) 313 | address2 = FAILURE 314 | address2 = self._read_config_block() 315 | if address2 is not FAILURE: 316 | elements0.append(address2) 317 | else: 318 | elements0 = None 319 | self._offset = index1 320 | else: 321 | elements0 = None 322 | self._offset = index1 323 | if elements0 is None: 324 | address0 = FAILURE 325 | else: 326 | address0 = GlobalSection(self._input[index1:self._offset], index1, elements0) 327 | self._offset = self._offset 328 | self._cache['global_section'][index0] = (address0, self._offset) 329 | return address0 330 | 331 | def _read_defaults_section(self): 332 | address0, index0 = FAILURE, self._offset 333 | cached = self._cache['defaults_section'].get(index0) 334 | if cached: 335 | self._offset = cached[1] 336 | return cached[0] 337 | index1, elements0 = self._offset, [] 338 | address1 = FAILURE 339 | address1 = self._read_defaults_header() 340 | if address1 is not FAILURE: 341 | elements0.append(address1) 342 | address2 = FAILURE 343 | address2 = self._read_config_block() 344 | if address2 is not FAILURE: 345 | elements0.append(address2) 346 | else: 347 | elements0 = None 348 | self._offset = index1 349 | else: 350 | elements0 = None 351 | self._offset = index1 352 | if elements0 is None: 353 | address0 = FAILURE 354 | else: 355 | address0 = DefaultsSection(self._input[index1:self._offset], index1, elements0) 356 | self._offset = self._offset 357 | self._cache['defaults_section'][index0] = (address0, self._offset) 358 | return address0 359 | 360 | def _read_userlist_section(self): 361 | address0, index0 = FAILURE, self._offset 362 | cached = self._cache['userlist_section'].get(index0) 363 | if cached: 364 | self._offset = cached[1] 365 | return cached[0] 366 | index1, elements0 = self._offset, [] 367 | address1 = FAILURE 368 | address1 = self._read_userlist_header() 369 | if address1 is not FAILURE: 370 | elements0.append(address1) 371 | address2 = FAILURE 372 | address2 = self._read_config_block() 373 | if address2 is not FAILURE: 374 | elements0.append(address2) 375 | else: 376 | elements0 = None 377 | self._offset = index1 378 | else: 379 | elements0 = None 380 | self._offset = index1 381 | if elements0 is None: 382 | address0 = FAILURE 383 | else: 384 | address0 = UserlistSection(self._input[index1:self._offset], index1, elements0) 385 | self._offset = self._offset 386 | self._cache['userlist_section'][index0] = (address0, self._offset) 387 | return address0 388 | 389 | def _read_listen_section(self): 390 | address0, index0 = FAILURE, self._offset 391 | cached = self._cache['listen_section'].get(index0) 392 | if cached: 393 | self._offset = cached[1] 394 | return cached[0] 395 | index1, elements0 = self._offset, [] 396 | address1 = FAILURE 397 | address1 = self._read_listen_header() 398 | if address1 is not FAILURE: 399 | elements0.append(address1) 400 | address2 = FAILURE 401 | address2 = self._read_config_block() 402 | if address2 is not FAILURE: 403 | elements0.append(address2) 404 | else: 405 | elements0 = None 406 | self._offset = index1 407 | else: 408 | elements0 = None 409 | self._offset = index1 410 | if elements0 is None: 411 | address0 = FAILURE 412 | else: 413 | address0 = ListenSection(self._input[index1:self._offset], index1, elements0) 414 | self._offset = self._offset 415 | self._cache['listen_section'][index0] = (address0, self._offset) 416 | return address0 417 | 418 | def _read_frontend_section(self): 419 | address0, index0 = FAILURE, self._offset 420 | cached = self._cache['frontend_section'].get(index0) 421 | if cached: 422 | self._offset = cached[1] 423 | return cached[0] 424 | index1, elements0 = self._offset, [] 425 | address1 = FAILURE 426 | address1 = self._read_frontend_header() 427 | if address1 is not FAILURE: 428 | elements0.append(address1) 429 | address2 = FAILURE 430 | address2 = self._read_config_block() 431 | if address2 is not FAILURE: 432 | elements0.append(address2) 433 | else: 434 | elements0 = None 435 | self._offset = index1 436 | else: 437 | elements0 = None 438 | self._offset = index1 439 | if elements0 is None: 440 | address0 = FAILURE 441 | else: 442 | address0 = FrontendSection(self._input[index1:self._offset], index1, elements0) 443 | self._offset = self._offset 444 | self._cache['frontend_section'][index0] = (address0, self._offset) 445 | return address0 446 | 447 | def _read_backend_section(self): 448 | address0, index0 = FAILURE, self._offset 449 | cached = self._cache['backend_section'].get(index0) 450 | if cached: 451 | self._offset = cached[1] 452 | return cached[0] 453 | index1, elements0 = self._offset, [] 454 | address1 = FAILURE 455 | address1 = self._read_backend_header() 456 | if address1 is not FAILURE: 457 | elements0.append(address1) 458 | address2 = FAILURE 459 | address2 = self._read_config_block() 460 | if address2 is not FAILURE: 461 | elements0.append(address2) 462 | else: 463 | elements0 = None 464 | self._offset = index1 465 | else: 466 | elements0 = None 467 | self._offset = index1 468 | if elements0 is None: 469 | address0 = FAILURE 470 | else: 471 | address0 = BackendSection(self._input[index1:self._offset], index1, elements0) 472 | self._offset = self._offset 473 | self._cache['backend_section'][index0] = (address0, self._offset) 474 | return address0 475 | 476 | def _read_global_header(self): 477 | address0, index0 = FAILURE, self._offset 478 | cached = self._cache['global_header'].get(index0) 479 | if cached: 480 | self._offset = cached[1] 481 | return cached[0] 482 | index1, elements0 = self._offset, [] 483 | address1 = FAILURE 484 | address1 = self._read_whitespace() 485 | if address1 is not FAILURE: 486 | elements0.append(address1) 487 | address2 = FAILURE 488 | chunk0 = None 489 | if self._offset < self._input_size: 490 | chunk0 = self._input[self._offset:self._offset + 6] 491 | if chunk0 == 'global': 492 | address2 = TreeNode(self._input[self._offset:self._offset + 6], self._offset) 493 | self._offset = self._offset + 6 494 | else: 495 | address2 = FAILURE 496 | if self._offset > self._failure: 497 | self._failure = self._offset 498 | self._expected = [] 499 | if self._offset == self._failure: 500 | self._expected.append('"global"') 501 | if address2 is not FAILURE: 502 | elements0.append(address2) 503 | address3 = FAILURE 504 | address3 = self._read_whitespace() 505 | if address3 is not FAILURE: 506 | elements0.append(address3) 507 | address4 = FAILURE 508 | index2 = self._offset 509 | address4 = self._read_comment_text() 510 | if address4 is FAILURE: 511 | address4 = TreeNode(self._input[index2:index2], index2) 512 | self._offset = index2 513 | if address4 is not FAILURE: 514 | elements0.append(address4) 515 | address5 = FAILURE 516 | address5 = self._read_line_break() 517 | if address5 is not FAILURE: 518 | elements0.append(address5) 519 | else: 520 | elements0 = None 521 | self._offset = index1 522 | else: 523 | elements0 = None 524 | self._offset = index1 525 | else: 526 | elements0 = None 527 | self._offset = index1 528 | else: 529 | elements0 = None 530 | self._offset = index1 531 | else: 532 | elements0 = None 533 | self._offset = index1 534 | if elements0 is None: 535 | address0 = FAILURE 536 | else: 537 | address0 = GlobalHeader(self._input[index1:self._offset], index1, elements0) 538 | self._offset = self._offset 539 | self._cache['global_header'][index0] = (address0, self._offset) 540 | return address0 541 | 542 | def _read_userlist_header(self): 543 | address0, index0 = FAILURE, self._offset 544 | cached = self._cache['userlist_header'].get(index0) 545 | if cached: 546 | self._offset = cached[1] 547 | return cached[0] 548 | index1, elements0 = self._offset, [] 549 | address1 = FAILURE 550 | address1 = self._read_whitespace() 551 | if address1 is not FAILURE: 552 | elements0.append(address1) 553 | address2 = FAILURE 554 | chunk0 = None 555 | if self._offset < self._input_size: 556 | chunk0 = self._input[self._offset:self._offset + 8] 557 | if chunk0 == 'userlist': 558 | address2 = TreeNode(self._input[self._offset:self._offset + 8], self._offset) 559 | self._offset = self._offset + 8 560 | else: 561 | address2 = FAILURE 562 | if self._offset > self._failure: 563 | self._failure = self._offset 564 | self._expected = [] 565 | if self._offset == self._failure: 566 | self._expected.append('"userlist"') 567 | if address2 is not FAILURE: 568 | elements0.append(address2) 569 | address3 = FAILURE 570 | address3 = self._read_whitespace() 571 | if address3 is not FAILURE: 572 | elements0.append(address3) 573 | address4 = FAILURE 574 | address4 = self._read_proxy_name() 575 | if address4 is not FAILURE: 576 | elements0.append(address4) 577 | address5 = FAILURE 578 | index2 = self._offset 579 | address5 = self._read_comment_text() 580 | if address5 is FAILURE: 581 | address5 = TreeNode(self._input[index2:index2], index2) 582 | self._offset = index2 583 | if address5 is not FAILURE: 584 | elements0.append(address5) 585 | address6 = FAILURE 586 | address6 = self._read_line_break() 587 | if address6 is not FAILURE: 588 | elements0.append(address6) 589 | else: 590 | elements0 = None 591 | self._offset = index1 592 | else: 593 | elements0 = None 594 | self._offset = index1 595 | else: 596 | elements0 = None 597 | self._offset = index1 598 | else: 599 | elements0 = None 600 | self._offset = index1 601 | else: 602 | elements0 = None 603 | self._offset = index1 604 | else: 605 | elements0 = None 606 | self._offset = index1 607 | if elements0 is None: 608 | address0 = FAILURE 609 | else: 610 | address0 = UserlistHeader(self._input[index1:self._offset], index1, elements0) 611 | self._offset = self._offset 612 | self._cache['userlist_header'][index0] = (address0, self._offset) 613 | return address0 614 | 615 | def _read_defaults_header(self): 616 | address0, index0 = FAILURE, self._offset 617 | cached = self._cache['defaults_header'].get(index0) 618 | if cached: 619 | self._offset = cached[1] 620 | return cached[0] 621 | index1, elements0 = self._offset, [] 622 | address1 = FAILURE 623 | address1 = self._read_whitespace() 624 | if address1 is not FAILURE: 625 | elements0.append(address1) 626 | address2 = FAILURE 627 | chunk0 = None 628 | if self._offset < self._input_size: 629 | chunk0 = self._input[self._offset:self._offset + 8] 630 | if chunk0 == 'defaults': 631 | address2 = TreeNode(self._input[self._offset:self._offset + 8], self._offset) 632 | self._offset = self._offset + 8 633 | else: 634 | address2 = FAILURE 635 | if self._offset > self._failure: 636 | self._failure = self._offset 637 | self._expected = [] 638 | if self._offset == self._failure: 639 | self._expected.append('"defaults"') 640 | if address2 is not FAILURE: 641 | elements0.append(address2) 642 | address3 = FAILURE 643 | address3 = self._read_whitespace() 644 | if address3 is not FAILURE: 645 | elements0.append(address3) 646 | address4 = FAILURE 647 | index2 = self._offset 648 | address4 = self._read_proxy_name() 649 | if address4 is FAILURE: 650 | address4 = TreeNode(self._input[index2:index2], index2) 651 | self._offset = index2 652 | if address4 is not FAILURE: 653 | elements0.append(address4) 654 | address5 = FAILURE 655 | address5 = self._read_whitespace() 656 | if address5 is not FAILURE: 657 | elements0.append(address5) 658 | address6 = FAILURE 659 | index3 = self._offset 660 | address6 = self._read_comment_text() 661 | if address6 is FAILURE: 662 | address6 = TreeNode(self._input[index3:index3], index3) 663 | self._offset = index3 664 | if address6 is not FAILURE: 665 | elements0.append(address6) 666 | address7 = FAILURE 667 | address7 = self._read_line_break() 668 | if address7 is not FAILURE: 669 | elements0.append(address7) 670 | else: 671 | elements0 = None 672 | self._offset = index1 673 | else: 674 | elements0 = None 675 | self._offset = index1 676 | else: 677 | elements0 = None 678 | self._offset = index1 679 | else: 680 | elements0 = None 681 | self._offset = index1 682 | else: 683 | elements0 = None 684 | self._offset = index1 685 | else: 686 | elements0 = None 687 | self._offset = index1 688 | else: 689 | elements0 = None 690 | self._offset = index1 691 | if elements0 is None: 692 | address0 = FAILURE 693 | else: 694 | address0 = DefaultsHeader(self._input[index1:self._offset], index1, elements0) 695 | self._offset = self._offset 696 | self._cache['defaults_header'][index0] = (address0, self._offset) 697 | return address0 698 | 699 | def _read_listen_header(self): 700 | address0, index0 = FAILURE, self._offset 701 | cached = self._cache['listen_header'].get(index0) 702 | if cached: 703 | self._offset = cached[1] 704 | return cached[0] 705 | index1, elements0 = self._offset, [] 706 | address1 = FAILURE 707 | address1 = self._read_whitespace() 708 | if address1 is not FAILURE: 709 | elements0.append(address1) 710 | address2 = FAILURE 711 | chunk0 = None 712 | if self._offset < self._input_size: 713 | chunk0 = self._input[self._offset:self._offset + 6] 714 | if chunk0 == 'listen': 715 | address2 = TreeNode(self._input[self._offset:self._offset + 6], self._offset) 716 | self._offset = self._offset + 6 717 | else: 718 | address2 = FAILURE 719 | if self._offset > self._failure: 720 | self._failure = self._offset 721 | self._expected = [] 722 | if self._offset == self._failure: 723 | self._expected.append('"listen"') 724 | if address2 is not FAILURE: 725 | elements0.append(address2) 726 | address3 = FAILURE 727 | address3 = self._read_whitespace() 728 | if address3 is not FAILURE: 729 | elements0.append(address3) 730 | address4 = FAILURE 731 | address4 = self._read_proxy_name() 732 | if address4 is not FAILURE: 733 | elements0.append(address4) 734 | address5 = FAILURE 735 | address5 = self._read_whitespace() 736 | if address5 is not FAILURE: 737 | elements0.append(address5) 738 | address6 = FAILURE 739 | index2 = self._offset 740 | address6 = self._read_service_address() 741 | if address6 is FAILURE: 742 | address6 = TreeNode(self._input[index2:index2], index2) 743 | self._offset = index2 744 | if address6 is not FAILURE: 745 | elements0.append(address6) 746 | address7 = FAILURE 747 | index3 = self._offset 748 | address7 = self._read_value() 749 | if address7 is FAILURE: 750 | address7 = TreeNode(self._input[index3:index3], index3) 751 | self._offset = index3 752 | if address7 is not FAILURE: 753 | elements0.append(address7) 754 | address8 = FAILURE 755 | index4 = self._offset 756 | address8 = self._read_comment_text() 757 | if address8 is FAILURE: 758 | address8 = TreeNode(self._input[index4:index4], index4) 759 | self._offset = index4 760 | if address8 is not FAILURE: 761 | elements0.append(address8) 762 | address9 = FAILURE 763 | address9 = self._read_line_break() 764 | if address9 is not FAILURE: 765 | elements0.append(address9) 766 | else: 767 | elements0 = None 768 | self._offset = index1 769 | else: 770 | elements0 = None 771 | self._offset = index1 772 | else: 773 | elements0 = None 774 | self._offset = index1 775 | else: 776 | elements0 = None 777 | self._offset = index1 778 | else: 779 | elements0 = None 780 | self._offset = index1 781 | else: 782 | elements0 = None 783 | self._offset = index1 784 | else: 785 | elements0 = None 786 | self._offset = index1 787 | else: 788 | elements0 = None 789 | self._offset = index1 790 | else: 791 | elements0 = None 792 | self._offset = index1 793 | if elements0 is None: 794 | address0 = FAILURE 795 | else: 796 | address0 = ListenHeader(self._input[index1:self._offset], index1, elements0) 797 | self._offset = self._offset 798 | self._cache['listen_header'][index0] = (address0, self._offset) 799 | return address0 800 | 801 | def _read_frontend_header(self): 802 | address0, index0 = FAILURE, self._offset 803 | cached = self._cache['frontend_header'].get(index0) 804 | if cached: 805 | self._offset = cached[1] 806 | return cached[0] 807 | index1, elements0 = self._offset, [] 808 | address1 = FAILURE 809 | address1 = self._read_whitespace() 810 | if address1 is not FAILURE: 811 | elements0.append(address1) 812 | address2 = FAILURE 813 | chunk0 = None 814 | if self._offset < self._input_size: 815 | chunk0 = self._input[self._offset:self._offset + 8] 816 | if chunk0 == 'frontend': 817 | address2 = TreeNode(self._input[self._offset:self._offset + 8], self._offset) 818 | self._offset = self._offset + 8 819 | else: 820 | address2 = FAILURE 821 | if self._offset > self._failure: 822 | self._failure = self._offset 823 | self._expected = [] 824 | if self._offset == self._failure: 825 | self._expected.append('"frontend"') 826 | if address2 is not FAILURE: 827 | elements0.append(address2) 828 | address3 = FAILURE 829 | address3 = self._read_whitespace() 830 | if address3 is not FAILURE: 831 | elements0.append(address3) 832 | address4 = FAILURE 833 | address4 = self._read_proxy_name() 834 | if address4 is not FAILURE: 835 | elements0.append(address4) 836 | address5 = FAILURE 837 | address5 = self._read_whitespace() 838 | if address5 is not FAILURE: 839 | elements0.append(address5) 840 | address6 = FAILURE 841 | index2 = self._offset 842 | address6 = self._read_service_address() 843 | if address6 is FAILURE: 844 | address6 = TreeNode(self._input[index2:index2], index2) 845 | self._offset = index2 846 | if address6 is not FAILURE: 847 | elements0.append(address6) 848 | address7 = FAILURE 849 | index3 = self._offset 850 | address7 = self._read_value() 851 | if address7 is FAILURE: 852 | address7 = TreeNode(self._input[index3:index3], index3) 853 | self._offset = index3 854 | if address7 is not FAILURE: 855 | elements0.append(address7) 856 | address8 = FAILURE 857 | index4 = self._offset 858 | address8 = self._read_comment_text() 859 | if address8 is FAILURE: 860 | address8 = TreeNode(self._input[index4:index4], index4) 861 | self._offset = index4 862 | if address8 is not FAILURE: 863 | elements0.append(address8) 864 | address9 = FAILURE 865 | address9 = self._read_line_break() 866 | if address9 is not FAILURE: 867 | elements0.append(address9) 868 | else: 869 | elements0 = None 870 | self._offset = index1 871 | else: 872 | elements0 = None 873 | self._offset = index1 874 | else: 875 | elements0 = None 876 | self._offset = index1 877 | else: 878 | elements0 = None 879 | self._offset = index1 880 | else: 881 | elements0 = None 882 | self._offset = index1 883 | else: 884 | elements0 = None 885 | self._offset = index1 886 | else: 887 | elements0 = None 888 | self._offset = index1 889 | else: 890 | elements0 = None 891 | self._offset = index1 892 | else: 893 | elements0 = None 894 | self._offset = index1 895 | if elements0 is None: 896 | address0 = FAILURE 897 | else: 898 | address0 = FrontendHeader(self._input[index1:self._offset], index1, elements0) 899 | self._offset = self._offset 900 | self._cache['frontend_header'][index0] = (address0, self._offset) 901 | return address0 902 | 903 | def _read_backend_header(self): 904 | address0, index0 = FAILURE, self._offset 905 | cached = self._cache['backend_header'].get(index0) 906 | if cached: 907 | self._offset = cached[1] 908 | return cached[0] 909 | index1, elements0 = self._offset, [] 910 | address1 = FAILURE 911 | address1 = self._read_whitespace() 912 | if address1 is not FAILURE: 913 | elements0.append(address1) 914 | address2 = FAILURE 915 | chunk0 = None 916 | if self._offset < self._input_size: 917 | chunk0 = self._input[self._offset:self._offset + 7] 918 | if chunk0 == 'backend': 919 | address2 = TreeNode(self._input[self._offset:self._offset + 7], self._offset) 920 | self._offset = self._offset + 7 921 | else: 922 | address2 = FAILURE 923 | if self._offset > self._failure: 924 | self._failure = self._offset 925 | self._expected = [] 926 | if self._offset == self._failure: 927 | self._expected.append('"backend"') 928 | if address2 is not FAILURE: 929 | elements0.append(address2) 930 | address3 = FAILURE 931 | address3 = self._read_whitespace() 932 | if address3 is not FAILURE: 933 | elements0.append(address3) 934 | address4 = FAILURE 935 | address4 = self._read_proxy_name() 936 | if address4 is not FAILURE: 937 | elements0.append(address4) 938 | address5 = FAILURE 939 | address5 = self._read_whitespace() 940 | if address5 is not FAILURE: 941 | elements0.append(address5) 942 | address6 = FAILURE 943 | index2 = self._offset 944 | address6 = self._read_value() 945 | if address6 is FAILURE: 946 | address6 = TreeNode(self._input[index2:index2], index2) 947 | self._offset = index2 948 | if address6 is not FAILURE: 949 | elements0.append(address6) 950 | address7 = FAILURE 951 | index3 = self._offset 952 | address7 = self._read_comment_text() 953 | if address7 is FAILURE: 954 | address7 = TreeNode(self._input[index3:index3], index3) 955 | self._offset = index3 956 | if address7 is not FAILURE: 957 | elements0.append(address7) 958 | address8 = FAILURE 959 | address8 = self._read_line_break() 960 | if address8 is not FAILURE: 961 | elements0.append(address8) 962 | else: 963 | elements0 = None 964 | self._offset = index1 965 | else: 966 | elements0 = None 967 | self._offset = index1 968 | else: 969 | elements0 = None 970 | self._offset = index1 971 | else: 972 | elements0 = None 973 | self._offset = index1 974 | else: 975 | elements0 = None 976 | self._offset = index1 977 | else: 978 | elements0 = None 979 | self._offset = index1 980 | else: 981 | elements0 = None 982 | self._offset = index1 983 | else: 984 | elements0 = None 985 | self._offset = index1 986 | if elements0 is None: 987 | address0 = FAILURE 988 | else: 989 | address0 = BackendHeader(self._input[index1:self._offset], index1, elements0) 990 | self._offset = self._offset 991 | self._cache['backend_header'][index0] = (address0, self._offset) 992 | return address0 993 | 994 | def _read_config_block(self): 995 | address0, index0 = FAILURE, self._offset 996 | cached = self._cache['config_block'].get(index0) 997 | if cached: 998 | self._offset = cached[1] 999 | return cached[0] 1000 | remaining0, index1, elements0, address1 = 0, self._offset, [], True 1001 | while address1 is not FAILURE: 1002 | index2 = self._offset 1003 | address1 = self._read_server_line() 1004 | if address1 is FAILURE: 1005 | self._offset = index2 1006 | address1 = self._read_option_line() 1007 | if address1 is FAILURE: 1008 | self._offset = index2 1009 | address1 = self._read_bind_line() 1010 | if address1 is FAILURE: 1011 | self._offset = index2 1012 | address1 = self._read_acl_line() 1013 | if address1 is FAILURE: 1014 | self._offset = index2 1015 | address1 = self._read_backend_line() 1016 | if address1 is FAILURE: 1017 | self._offset = index2 1018 | address1 = self._read_group_line() 1019 | if address1 is FAILURE: 1020 | self._offset = index2 1021 | address1 = self._read_user_line() 1022 | if address1 is FAILURE: 1023 | self._offset = index2 1024 | address1 = self._read_config_line() 1025 | if address1 is FAILURE: 1026 | self._offset = index2 1027 | address1 = self._read_comment_line() 1028 | if address1 is FAILURE: 1029 | self._offset = index2 1030 | address1 = self._read_blank_line() 1031 | if address1 is FAILURE: 1032 | self._offset = index2 1033 | if address1 is not FAILURE: 1034 | elements0.append(address1) 1035 | remaining0 -= 1 1036 | if remaining0 <= 0: 1037 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 1038 | self._offset = self._offset 1039 | else: 1040 | address0 = FAILURE 1041 | self._cache['config_block'][index0] = (address0, self._offset) 1042 | return address0 1043 | 1044 | def _read_server_line(self): 1045 | address0, index0 = FAILURE, self._offset 1046 | cached = self._cache['server_line'].get(index0) 1047 | if cached: 1048 | self._offset = cached[1] 1049 | return cached[0] 1050 | index1, elements0 = self._offset, [] 1051 | address1 = FAILURE 1052 | address1 = self._read_whitespace() 1053 | if address1 is not FAILURE: 1054 | elements0.append(address1) 1055 | address2 = FAILURE 1056 | chunk0 = None 1057 | if self._offset < self._input_size: 1058 | chunk0 = self._input[self._offset:self._offset + 7] 1059 | if chunk0 == 'server ': 1060 | address2 = TreeNode(self._input[self._offset:self._offset + 6], self._offset) 1061 | self._offset = self._offset + 6 1062 | else: 1063 | address2 = FAILURE 1064 | if self._offset > self._failure: 1065 | self._failure = self._offset 1066 | self._expected = [] 1067 | if self._offset == self._failure: 1068 | self._expected.append('"server"') 1069 | if address2 is not FAILURE: 1070 | elements0.append(address2) 1071 | address3 = FAILURE 1072 | address3 = self._read_whitespace() 1073 | if address3 is not FAILURE: 1074 | elements0.append(address3) 1075 | address4 = FAILURE 1076 | address4 = self._read_server_name() 1077 | if address4 is not FAILURE: 1078 | elements0.append(address4) 1079 | address5 = FAILURE 1080 | address5 = self._read_whitespace() 1081 | if address5 is not FAILURE: 1082 | elements0.append(address5) 1083 | address6 = FAILURE 1084 | address6 = self._read_service_address() 1085 | if address6 is not FAILURE: 1086 | elements0.append(address6) 1087 | address7 = FAILURE 1088 | index2 = self._offset 1089 | address7 = self._read_value() 1090 | if address7 is FAILURE: 1091 | address7 = TreeNode(self._input[index2:index2], index2) 1092 | self._offset = index2 1093 | if address7 is not FAILURE: 1094 | elements0.append(address7) 1095 | address8 = FAILURE 1096 | index3 = self._offset 1097 | address8 = self._read_comment_text() 1098 | if address8 is FAILURE: 1099 | address8 = TreeNode(self._input[index3:index3], index3) 1100 | self._offset = index3 1101 | if address8 is not FAILURE: 1102 | elements0.append(address8) 1103 | address9 = FAILURE 1104 | address9 = self._read_line_break() 1105 | if address9 is not FAILURE: 1106 | elements0.append(address9) 1107 | else: 1108 | elements0 = None 1109 | self._offset = index1 1110 | else: 1111 | elements0 = None 1112 | self._offset = index1 1113 | else: 1114 | elements0 = None 1115 | self._offset = index1 1116 | else: 1117 | elements0 = None 1118 | self._offset = index1 1119 | else: 1120 | elements0 = None 1121 | self._offset = index1 1122 | else: 1123 | elements0 = None 1124 | self._offset = index1 1125 | else: 1126 | elements0 = None 1127 | self._offset = index1 1128 | else: 1129 | elements0 = None 1130 | self._offset = index1 1131 | else: 1132 | elements0 = None 1133 | self._offset = index1 1134 | if elements0 is None: 1135 | address0 = FAILURE 1136 | else: 1137 | address0 = ServerLine(self._input[index1:self._offset], index1, elements0) 1138 | self._offset = self._offset 1139 | self._cache['server_line'][index0] = (address0, self._offset) 1140 | return address0 1141 | 1142 | def _read_option_line(self): 1143 | address0, index0 = FAILURE, self._offset 1144 | cached = self._cache['option_line'].get(index0) 1145 | if cached: 1146 | self._offset = cached[1] 1147 | return cached[0] 1148 | index1, elements0 = self._offset, [] 1149 | address1 = FAILURE 1150 | address1 = self._read_whitespace() 1151 | if address1 is not FAILURE: 1152 | elements0.append(address1) 1153 | address2 = FAILURE 1154 | chunk0 = None 1155 | if self._offset < self._input_size: 1156 | chunk0 = self._input[self._offset:self._offset + 6] 1157 | if chunk0 == 'option': 1158 | address2 = TreeNode(self._input[self._offset:self._offset + 6], self._offset) 1159 | self._offset = self._offset + 6 1160 | else: 1161 | address2 = FAILURE 1162 | if self._offset > self._failure: 1163 | self._failure = self._offset 1164 | self._expected = [] 1165 | if self._offset == self._failure: 1166 | self._expected.append('"option"') 1167 | if address2 is not FAILURE: 1168 | elements0.append(address2) 1169 | address3 = FAILURE 1170 | address3 = self._read_whitespace() 1171 | if address3 is not FAILURE: 1172 | elements0.append(address3) 1173 | address4 = FAILURE 1174 | address4 = self._read_keyword() 1175 | if address4 is not FAILURE: 1176 | elements0.append(address4) 1177 | address5 = FAILURE 1178 | address5 = self._read_whitespace() 1179 | if address5 is not FAILURE: 1180 | elements0.append(address5) 1181 | address6 = FAILURE 1182 | index2 = self._offset 1183 | address6 = self._read_value() 1184 | if address6 is FAILURE: 1185 | address6 = TreeNode(self._input[index2:index2], index2) 1186 | self._offset = index2 1187 | if address6 is not FAILURE: 1188 | elements0.append(address6) 1189 | address7 = FAILURE 1190 | index3 = self._offset 1191 | address7 = self._read_comment_text() 1192 | if address7 is FAILURE: 1193 | address7 = TreeNode(self._input[index3:index3], index3) 1194 | self._offset = index3 1195 | if address7 is not FAILURE: 1196 | elements0.append(address7) 1197 | address8 = FAILURE 1198 | address8 = self._read_line_break() 1199 | if address8 is not FAILURE: 1200 | elements0.append(address8) 1201 | else: 1202 | elements0 = None 1203 | self._offset = index1 1204 | else: 1205 | elements0 = None 1206 | self._offset = index1 1207 | else: 1208 | elements0 = None 1209 | self._offset = index1 1210 | else: 1211 | elements0 = None 1212 | self._offset = index1 1213 | else: 1214 | elements0 = None 1215 | self._offset = index1 1216 | else: 1217 | elements0 = None 1218 | self._offset = index1 1219 | else: 1220 | elements0 = None 1221 | self._offset = index1 1222 | else: 1223 | elements0 = None 1224 | self._offset = index1 1225 | if elements0 is None: 1226 | address0 = FAILURE 1227 | else: 1228 | address0 = OptionLine(self._input[index1:self._offset], index1, elements0) 1229 | self._offset = self._offset 1230 | self._cache['option_line'][index0] = (address0, self._offset) 1231 | return address0 1232 | 1233 | def _read_bind_line(self): 1234 | address0, index0 = FAILURE, self._offset 1235 | cached = self._cache['bind_line'].get(index0) 1236 | if cached: 1237 | self._offset = cached[1] 1238 | return cached[0] 1239 | index1, elements0 = self._offset, [] 1240 | address1 = FAILURE 1241 | address1 = self._read_whitespace() 1242 | if address1 is not FAILURE: 1243 | elements0.append(address1) 1244 | address2 = FAILURE 1245 | chunk0 = None 1246 | if self._offset < self._input_size: 1247 | chunk0 = self._input[self._offset:self._offset + 4] 1248 | if chunk0 == 'bind': 1249 | address2 = TreeNode(self._input[self._offset:self._offset + 4], self._offset) 1250 | self._offset = self._offset + 4 1251 | else: 1252 | address2 = FAILURE 1253 | if self._offset > self._failure: 1254 | self._failure = self._offset 1255 | self._expected = [] 1256 | if self._offset == self._failure: 1257 | self._expected.append('"bind"') 1258 | if address2 is not FAILURE: 1259 | elements0.append(address2) 1260 | address3 = FAILURE 1261 | address3 = self._read_whitespaceplus() 1262 | if address3 is not FAILURE: 1263 | elements0.append(address3) 1264 | address4 = FAILURE 1265 | address4 = self._read_service_address() 1266 | if address4 is not FAILURE: 1267 | elements0.append(address4) 1268 | address5 = FAILURE 1269 | address5 = self._read_whitespace() 1270 | if address5 is not FAILURE: 1271 | elements0.append(address5) 1272 | address6 = FAILURE 1273 | index2 = self._offset 1274 | address6 = self._read_value() 1275 | if address6 is FAILURE: 1276 | address6 = TreeNode(self._input[index2:index2], index2) 1277 | self._offset = index2 1278 | if address6 is not FAILURE: 1279 | elements0.append(address6) 1280 | address7 = FAILURE 1281 | index3 = self._offset 1282 | address7 = self._read_comment_text() 1283 | if address7 is FAILURE: 1284 | address7 = TreeNode(self._input[index3:index3], index3) 1285 | self._offset = index3 1286 | if address7 is not FAILURE: 1287 | elements0.append(address7) 1288 | address8 = FAILURE 1289 | address8 = self._read_line_break() 1290 | if address8 is not FAILURE: 1291 | elements0.append(address8) 1292 | else: 1293 | elements0 = None 1294 | self._offset = index1 1295 | else: 1296 | elements0 = None 1297 | self._offset = index1 1298 | else: 1299 | elements0 = None 1300 | self._offset = index1 1301 | else: 1302 | elements0 = None 1303 | self._offset = index1 1304 | else: 1305 | elements0 = None 1306 | self._offset = index1 1307 | else: 1308 | elements0 = None 1309 | self._offset = index1 1310 | else: 1311 | elements0 = None 1312 | self._offset = index1 1313 | else: 1314 | elements0 = None 1315 | self._offset = index1 1316 | if elements0 is None: 1317 | address0 = FAILURE 1318 | else: 1319 | address0 = BindLine(self._input[index1:self._offset], index1, elements0) 1320 | self._offset = self._offset 1321 | self._cache['bind_line'][index0] = (address0, self._offset) 1322 | return address0 1323 | 1324 | def _read_acl_line(self): 1325 | address0, index0 = FAILURE, self._offset 1326 | cached = self._cache['acl_line'].get(index0) 1327 | if cached: 1328 | self._offset = cached[1] 1329 | return cached[0] 1330 | index1, elements0 = self._offset, [] 1331 | address1 = FAILURE 1332 | address1 = self._read_whitespace() 1333 | if address1 is not FAILURE: 1334 | elements0.append(address1) 1335 | address2 = FAILURE 1336 | chunk0 = None 1337 | if self._offset < self._input_size: 1338 | chunk0 = self._input[self._offset:self._offset + 3] 1339 | if chunk0 == 'acl': 1340 | address2 = TreeNode(self._input[self._offset:self._offset + 3], self._offset) 1341 | self._offset = self._offset + 3 1342 | else: 1343 | address2 = FAILURE 1344 | if self._offset > self._failure: 1345 | self._failure = self._offset 1346 | self._expected = [] 1347 | if self._offset == self._failure: 1348 | self._expected.append('"acl"') 1349 | if address2 is not FAILURE: 1350 | elements0.append(address2) 1351 | address3 = FAILURE 1352 | address3 = self._read_whitespace() 1353 | if address3 is not FAILURE: 1354 | elements0.append(address3) 1355 | address4 = FAILURE 1356 | address4 = self._read_acl_name() 1357 | if address4 is not FAILURE: 1358 | elements0.append(address4) 1359 | address5 = FAILURE 1360 | address5 = self._read_whitespace() 1361 | if address5 is not FAILURE: 1362 | elements0.append(address5) 1363 | address6 = FAILURE 1364 | index2 = self._offset 1365 | address6 = self._read_value() 1366 | if address6 is FAILURE: 1367 | address6 = TreeNode(self._input[index2:index2], index2) 1368 | self._offset = index2 1369 | if address6 is not FAILURE: 1370 | elements0.append(address6) 1371 | address7 = FAILURE 1372 | index3 = self._offset 1373 | address7 = self._read_comment_text() 1374 | if address7 is FAILURE: 1375 | address7 = TreeNode(self._input[index3:index3], index3) 1376 | self._offset = index3 1377 | if address7 is not FAILURE: 1378 | elements0.append(address7) 1379 | address8 = FAILURE 1380 | address8 = self._read_line_break() 1381 | if address8 is not FAILURE: 1382 | elements0.append(address8) 1383 | else: 1384 | elements0 = None 1385 | self._offset = index1 1386 | else: 1387 | elements0 = None 1388 | self._offset = index1 1389 | else: 1390 | elements0 = None 1391 | self._offset = index1 1392 | else: 1393 | elements0 = None 1394 | self._offset = index1 1395 | else: 1396 | elements0 = None 1397 | self._offset = index1 1398 | else: 1399 | elements0 = None 1400 | self._offset = index1 1401 | else: 1402 | elements0 = None 1403 | self._offset = index1 1404 | else: 1405 | elements0 = None 1406 | self._offset = index1 1407 | if elements0 is None: 1408 | address0 = FAILURE 1409 | else: 1410 | address0 = AclLine(self._input[index1:self._offset], index1, elements0) 1411 | self._offset = self._offset 1412 | self._cache['acl_line'][index0] = (address0, self._offset) 1413 | return address0 1414 | 1415 | def _read_backend_line(self): 1416 | address0, index0 = FAILURE, self._offset 1417 | cached = self._cache['backend_line'].get(index0) 1418 | if cached: 1419 | self._offset = cached[1] 1420 | return cached[0] 1421 | index1, elements0 = self._offset, [] 1422 | address1 = FAILURE 1423 | address1 = self._read_whitespace() 1424 | if address1 is not FAILURE: 1425 | elements0.append(address1) 1426 | address2 = FAILURE 1427 | index2 = self._offset 1428 | chunk0 = None 1429 | if self._offset < self._input_size: 1430 | chunk0 = self._input[self._offset:self._offset + 11] 1431 | if chunk0 == 'use_backend': 1432 | address2 = TreeNode(self._input[self._offset:self._offset + 11], self._offset) 1433 | self._offset = self._offset + 11 1434 | else: 1435 | address2 = FAILURE 1436 | if self._offset > self._failure: 1437 | self._failure = self._offset 1438 | self._expected = [] 1439 | if self._offset == self._failure: 1440 | self._expected.append('"use_backend"') 1441 | if address2 is FAILURE: 1442 | self._offset = index2 1443 | chunk1 = None 1444 | if self._offset < self._input_size: 1445 | chunk1 = self._input[self._offset:self._offset + 15] 1446 | if chunk1 == 'default_backend': 1447 | address2 = TreeNode(self._input[self._offset:self._offset + 15], self._offset) 1448 | self._offset = self._offset + 15 1449 | else: 1450 | address2 = FAILURE 1451 | if self._offset > self._failure: 1452 | self._failure = self._offset 1453 | self._expected = [] 1454 | if self._offset == self._failure: 1455 | self._expected.append('"default_backend"') 1456 | if address2 is FAILURE: 1457 | self._offset = index2 1458 | if address2 is not FAILURE: 1459 | elements0.append(address2) 1460 | address3 = FAILURE 1461 | address3 = self._read_whitespace() 1462 | if address3 is not FAILURE: 1463 | elements0.append(address3) 1464 | address4 = FAILURE 1465 | address4 = self._read_backend_name() 1466 | if address4 is not FAILURE: 1467 | elements0.append(address4) 1468 | address5 = FAILURE 1469 | address5 = self._read_whitespace() 1470 | if address5 is not FAILURE: 1471 | elements0.append(address5) 1472 | address6 = FAILURE 1473 | index3 = self._offset 1474 | index4 = self._offset 1475 | chunk2 = None 1476 | if self._offset < self._input_size: 1477 | chunk2 = self._input[self._offset:self._offset + 2] 1478 | if chunk2 == 'if': 1479 | address6 = TreeNode(self._input[self._offset:self._offset + 2], self._offset) 1480 | self._offset = self._offset + 2 1481 | else: 1482 | address6 = FAILURE 1483 | if self._offset > self._failure: 1484 | self._failure = self._offset 1485 | self._expected = [] 1486 | if self._offset == self._failure: 1487 | self._expected.append('"if"') 1488 | if address6 is FAILURE: 1489 | self._offset = index4 1490 | chunk3 = None 1491 | if self._offset < self._input_size: 1492 | chunk3 = self._input[self._offset:self._offset + 6] 1493 | if chunk3 == 'unless': 1494 | address6 = TreeNode(self._input[self._offset:self._offset + 6], self._offset) 1495 | self._offset = self._offset + 6 1496 | else: 1497 | address6 = FAILURE 1498 | if self._offset > self._failure: 1499 | self._failure = self._offset 1500 | self._expected = [] 1501 | if self._offset == self._failure: 1502 | self._expected.append('"unless"') 1503 | if address6 is FAILURE: 1504 | self._offset = index4 1505 | if address6 is FAILURE: 1506 | address6 = TreeNode(self._input[index3:index3], index3) 1507 | self._offset = index3 1508 | if address6 is not FAILURE: 1509 | elements0.append(address6) 1510 | address7 = FAILURE 1511 | address7 = self._read_whitespace() 1512 | if address7 is not FAILURE: 1513 | elements0.append(address7) 1514 | address8 = FAILURE 1515 | index5 = self._offset 1516 | address8 = self._read_backend_condition() 1517 | if address8 is FAILURE: 1518 | address8 = TreeNode(self._input[index5:index5], index5) 1519 | self._offset = index5 1520 | if address8 is not FAILURE: 1521 | elements0.append(address8) 1522 | address9 = FAILURE 1523 | index6 = self._offset 1524 | address9 = self._read_comment_text() 1525 | if address9 is FAILURE: 1526 | address9 = TreeNode(self._input[index6:index6], index6) 1527 | self._offset = index6 1528 | if address9 is not FAILURE: 1529 | elements0.append(address9) 1530 | address10 = FAILURE 1531 | address10 = self._read_line_break() 1532 | if address10 is not FAILURE: 1533 | elements0.append(address10) 1534 | else: 1535 | elements0 = None 1536 | self._offset = index1 1537 | else: 1538 | elements0 = None 1539 | self._offset = index1 1540 | else: 1541 | elements0 = None 1542 | self._offset = index1 1543 | else: 1544 | elements0 = None 1545 | self._offset = index1 1546 | else: 1547 | elements0 = None 1548 | self._offset = index1 1549 | else: 1550 | elements0 = None 1551 | self._offset = index1 1552 | else: 1553 | elements0 = None 1554 | self._offset = index1 1555 | else: 1556 | elements0 = None 1557 | self._offset = index1 1558 | else: 1559 | elements0 = None 1560 | self._offset = index1 1561 | else: 1562 | elements0 = None 1563 | self._offset = index1 1564 | if elements0 is None: 1565 | address0 = FAILURE 1566 | else: 1567 | address0 = BackendLine(self._input[index1:self._offset], index1, elements0) 1568 | self._offset = self._offset 1569 | self._cache['backend_line'][index0] = (address0, self._offset) 1570 | return address0 1571 | 1572 | def _read_group_line(self): 1573 | address0, index0 = FAILURE, self._offset 1574 | cached = self._cache['group_line'].get(index0) 1575 | if cached: 1576 | self._offset = cached[1] 1577 | return cached[0] 1578 | index1, elements0 = self._offset, [] 1579 | address1 = FAILURE 1580 | address1 = self._read_whitespace() 1581 | if address1 is not FAILURE: 1582 | elements0.append(address1) 1583 | address2 = FAILURE 1584 | chunk0 = None 1585 | if self._offset < self._input_size: 1586 | chunk0 = self._input[self._offset:self._offset + 5] 1587 | if chunk0 == 'group': 1588 | address2 = TreeNode(self._input[self._offset:self._offset + 5], self._offset) 1589 | self._offset = self._offset + 5 1590 | else: 1591 | address2 = FAILURE 1592 | if self._offset > self._failure: 1593 | self._failure = self._offset 1594 | self._expected = [] 1595 | if self._offset == self._failure: 1596 | self._expected.append('"group"') 1597 | if address2 is not FAILURE: 1598 | elements0.append(address2) 1599 | address3 = FAILURE 1600 | address3 = self._read_whitespace() 1601 | if address3 is not FAILURE: 1602 | elements0.append(address3) 1603 | address4 = FAILURE 1604 | address4 = self._read_group_name() 1605 | if address4 is not FAILURE: 1606 | elements0.append(address4) 1607 | address5 = FAILURE 1608 | address5 = self._read_whitespace() 1609 | if address5 is not FAILURE: 1610 | elements0.append(address5) 1611 | address6 = FAILURE 1612 | index2 = self._offset 1613 | index3, elements1 = self._offset, [] 1614 | address7 = FAILURE 1615 | chunk1 = None 1616 | if self._offset < self._input_size: 1617 | chunk1 = self._input[self._offset:self._offset + 5] 1618 | if chunk1 == 'users': 1619 | address7 = TreeNode(self._input[self._offset:self._offset + 5], self._offset) 1620 | self._offset = self._offset + 5 1621 | else: 1622 | address7 = FAILURE 1623 | if self._offset > self._failure: 1624 | self._failure = self._offset 1625 | self._expected = [] 1626 | if self._offset == self._failure: 1627 | self._expected.append('"users"') 1628 | if address7 is not FAILURE: 1629 | elements1.append(address7) 1630 | address8 = FAILURE 1631 | address8 = self._read_whitespace() 1632 | if address8 is not FAILURE: 1633 | elements1.append(address8) 1634 | else: 1635 | elements1 = None 1636 | self._offset = index3 1637 | else: 1638 | elements1 = None 1639 | self._offset = index3 1640 | if elements1 is None: 1641 | address6 = FAILURE 1642 | else: 1643 | address6 = TreeNode19(self._input[index3:self._offset], index3, elements1) 1644 | self._offset = self._offset 1645 | if address6 is FAILURE: 1646 | address6 = TreeNode(self._input[index2:index2], index2) 1647 | self._offset = index2 1648 | if address6 is not FAILURE: 1649 | elements0.append(address6) 1650 | address9 = FAILURE 1651 | index4 = self._offset 1652 | address9 = self._read_value() 1653 | if address9 is FAILURE: 1654 | address9 = TreeNode(self._input[index4:index4], index4) 1655 | self._offset = index4 1656 | if address9 is not FAILURE: 1657 | elements0.append(address9) 1658 | address10 = FAILURE 1659 | index5 = self._offset 1660 | address10 = self._read_comment_text() 1661 | if address10 is FAILURE: 1662 | address10 = TreeNode(self._input[index5:index5], index5) 1663 | self._offset = index5 1664 | if address10 is not FAILURE: 1665 | elements0.append(address10) 1666 | address11 = FAILURE 1667 | address11 = self._read_line_break() 1668 | if address11 is not FAILURE: 1669 | elements0.append(address11) 1670 | else: 1671 | elements0 = None 1672 | self._offset = index1 1673 | else: 1674 | elements0 = None 1675 | self._offset = index1 1676 | else: 1677 | elements0 = None 1678 | self._offset = index1 1679 | else: 1680 | elements0 = None 1681 | self._offset = index1 1682 | else: 1683 | elements0 = None 1684 | self._offset = index1 1685 | else: 1686 | elements0 = None 1687 | self._offset = index1 1688 | else: 1689 | elements0 = None 1690 | self._offset = index1 1691 | else: 1692 | elements0 = None 1693 | self._offset = index1 1694 | else: 1695 | elements0 = None 1696 | self._offset = index1 1697 | if elements0 is None: 1698 | address0 = FAILURE 1699 | else: 1700 | address0 = GroupLine(self._input[index1:self._offset], index1, elements0) 1701 | self._offset = self._offset 1702 | self._cache['group_line'][index0] = (address0, self._offset) 1703 | return address0 1704 | 1705 | def _read_user_line(self): 1706 | address0, index0 = FAILURE, self._offset 1707 | cached = self._cache['user_line'].get(index0) 1708 | if cached: 1709 | self._offset = cached[1] 1710 | return cached[0] 1711 | index1, elements0 = self._offset, [] 1712 | address1 = FAILURE 1713 | address1 = self._read_whitespace() 1714 | if address1 is not FAILURE: 1715 | elements0.append(address1) 1716 | address2 = FAILURE 1717 | chunk0 = None 1718 | if self._offset < self._input_size: 1719 | chunk0 = self._input[self._offset:self._offset + 4] 1720 | if chunk0 == 'user': 1721 | address2 = TreeNode(self._input[self._offset:self._offset + 4], self._offset) 1722 | self._offset = self._offset + 4 1723 | else: 1724 | address2 = FAILURE 1725 | if self._offset > self._failure: 1726 | self._failure = self._offset 1727 | self._expected = [] 1728 | if self._offset == self._failure: 1729 | self._expected.append('"user"') 1730 | if address2 is not FAILURE: 1731 | elements0.append(address2) 1732 | address3 = FAILURE 1733 | address3 = self._read_whitespace() 1734 | if address3 is not FAILURE: 1735 | elements0.append(address3) 1736 | address4 = FAILURE 1737 | address4 = self._read_user_name() 1738 | if address4 is not FAILURE: 1739 | elements0.append(address4) 1740 | address5 = FAILURE 1741 | address5 = self._read_whitespace() 1742 | if address5 is not FAILURE: 1743 | elements0.append(address5) 1744 | address6 = FAILURE 1745 | index2 = self._offset 1746 | chunk1 = None 1747 | if self._offset < self._input_size: 1748 | chunk1 = self._input[self._offset:self._offset + 8] 1749 | if chunk1 == 'password': 1750 | address6 = TreeNode(self._input[self._offset:self._offset + 8], self._offset) 1751 | self._offset = self._offset + 8 1752 | else: 1753 | address6 = FAILURE 1754 | if self._offset > self._failure: 1755 | self._failure = self._offset 1756 | self._expected = [] 1757 | if self._offset == self._failure: 1758 | self._expected.append('"password"') 1759 | if address6 is FAILURE: 1760 | self._offset = index2 1761 | chunk2 = None 1762 | if self._offset < self._input_size: 1763 | chunk2 = self._input[self._offset:self._offset + 17] 1764 | if chunk2 == 'insecure-password': 1765 | address6 = TreeNode(self._input[self._offset:self._offset + 17], self._offset) 1766 | self._offset = self._offset + 17 1767 | else: 1768 | address6 = FAILURE 1769 | if self._offset > self._failure: 1770 | self._failure = self._offset 1771 | self._expected = [] 1772 | if self._offset == self._failure: 1773 | self._expected.append('"insecure-password"') 1774 | if address6 is FAILURE: 1775 | self._offset = index2 1776 | if address6 is not FAILURE: 1777 | elements0.append(address6) 1778 | address7 = FAILURE 1779 | address7 = self._read_whitespace() 1780 | if address7 is not FAILURE: 1781 | elements0.append(address7) 1782 | address8 = FAILURE 1783 | address8 = self._read_password() 1784 | if address8 is not FAILURE: 1785 | elements0.append(address8) 1786 | address9 = FAILURE 1787 | address9 = self._read_whitespace() 1788 | if address9 is not FAILURE: 1789 | elements0.append(address9) 1790 | address10 = FAILURE 1791 | index3 = self._offset 1792 | index4, elements1 = self._offset, [] 1793 | address11 = FAILURE 1794 | chunk3 = None 1795 | if self._offset < self._input_size: 1796 | chunk3 = self._input[self._offset:self._offset + 6] 1797 | if chunk3 == 'groups': 1798 | address11 = TreeNode(self._input[self._offset:self._offset + 6], self._offset) 1799 | self._offset = self._offset + 6 1800 | else: 1801 | address11 = FAILURE 1802 | if self._offset > self._failure: 1803 | self._failure = self._offset 1804 | self._expected = [] 1805 | if self._offset == self._failure: 1806 | self._expected.append('"groups"') 1807 | if address11 is not FAILURE: 1808 | elements1.append(address11) 1809 | address12 = FAILURE 1810 | address12 = self._read_whitespace() 1811 | if address12 is not FAILURE: 1812 | elements1.append(address12) 1813 | else: 1814 | elements1 = None 1815 | self._offset = index4 1816 | else: 1817 | elements1 = None 1818 | self._offset = index4 1819 | if elements1 is None: 1820 | address10 = FAILURE 1821 | else: 1822 | address10 = TreeNode21(self._input[index4:self._offset], index4, elements1) 1823 | self._offset = self._offset 1824 | if address10 is FAILURE: 1825 | address10 = TreeNode(self._input[index3:index3], index3) 1826 | self._offset = index3 1827 | if address10 is not FAILURE: 1828 | elements0.append(address10) 1829 | address13 = FAILURE 1830 | index5 = self._offset 1831 | address13 = self._read_value() 1832 | if address13 is FAILURE: 1833 | address13 = TreeNode(self._input[index5:index5], index5) 1834 | self._offset = index5 1835 | if address13 is not FAILURE: 1836 | elements0.append(address13) 1837 | address14 = FAILURE 1838 | index6 = self._offset 1839 | address14 = self._read_comment_text() 1840 | if address14 is FAILURE: 1841 | address14 = TreeNode(self._input[index6:index6], index6) 1842 | self._offset = index6 1843 | if address14 is not FAILURE: 1844 | elements0.append(address14) 1845 | address15 = FAILURE 1846 | address15 = self._read_line_break() 1847 | if address15 is not FAILURE: 1848 | elements0.append(address15) 1849 | else: 1850 | elements0 = None 1851 | self._offset = index1 1852 | else: 1853 | elements0 = None 1854 | self._offset = index1 1855 | else: 1856 | elements0 = None 1857 | self._offset = index1 1858 | else: 1859 | elements0 = None 1860 | self._offset = index1 1861 | else: 1862 | elements0 = None 1863 | self._offset = index1 1864 | else: 1865 | elements0 = None 1866 | self._offset = index1 1867 | else: 1868 | elements0 = None 1869 | self._offset = index1 1870 | else: 1871 | elements0 = None 1872 | self._offset = index1 1873 | else: 1874 | elements0 = None 1875 | self._offset = index1 1876 | else: 1877 | elements0 = None 1878 | self._offset = index1 1879 | else: 1880 | elements0 = None 1881 | self._offset = index1 1882 | else: 1883 | elements0 = None 1884 | self._offset = index1 1885 | else: 1886 | elements0 = None 1887 | self._offset = index1 1888 | if elements0 is None: 1889 | address0 = FAILURE 1890 | else: 1891 | address0 = UserLine(self._input[index1:self._offset], index1, elements0) 1892 | self._offset = self._offset 1893 | self._cache['user_line'][index0] = (address0, self._offset) 1894 | return address0 1895 | 1896 | def _read_config_line(self): 1897 | address0, index0 = FAILURE, self._offset 1898 | cached = self._cache['config_line'].get(index0) 1899 | if cached: 1900 | self._offset = cached[1] 1901 | return cached[0] 1902 | index1, elements0 = self._offset, [] 1903 | address1 = FAILURE 1904 | address1 = self._read_whitespace() 1905 | if address1 is not FAILURE: 1906 | elements0.append(address1) 1907 | address2 = FAILURE 1908 | index2 = self._offset 1909 | index3 = self._offset 1910 | chunk0 = None 1911 | if self._offset < self._input_size: 1912 | chunk0 = self._input[self._offset:self._offset + 8] 1913 | if chunk0 == 'defaults': 1914 | address2 = TreeNode(self._input[self._offset:self._offset + 8], self._offset) 1915 | self._offset = self._offset + 8 1916 | else: 1917 | address2 = FAILURE 1918 | if self._offset > self._failure: 1919 | self._failure = self._offset 1920 | self._expected = [] 1921 | if self._offset == self._failure: 1922 | self._expected.append('"defaults"') 1923 | if address2 is FAILURE: 1924 | self._offset = index3 1925 | chunk1 = None 1926 | if self._offset < self._input_size: 1927 | chunk1 = self._input[self._offset:self._offset + 6] 1928 | if chunk1 == 'global': 1929 | address2 = TreeNode(self._input[self._offset:self._offset + 6], self._offset) 1930 | self._offset = self._offset + 6 1931 | else: 1932 | address2 = FAILURE 1933 | if self._offset > self._failure: 1934 | self._failure = self._offset 1935 | self._expected = [] 1936 | if self._offset == self._failure: 1937 | self._expected.append('"global"') 1938 | if address2 is FAILURE: 1939 | self._offset = index3 1940 | chunk2 = None 1941 | if self._offset < self._input_size: 1942 | chunk2 = self._input[self._offset:self._offset + 8] 1943 | if chunk2 == 'userlist': 1944 | address2 = TreeNode(self._input[self._offset:self._offset + 8], self._offset) 1945 | self._offset = self._offset + 8 1946 | else: 1947 | address2 = FAILURE 1948 | if self._offset > self._failure: 1949 | self._failure = self._offset 1950 | self._expected = [] 1951 | if self._offset == self._failure: 1952 | self._expected.append('"userlist"') 1953 | if address2 is FAILURE: 1954 | self._offset = index3 1955 | chunk3 = None 1956 | if self._offset < self._input_size: 1957 | chunk3 = self._input[self._offset:self._offset + 6] 1958 | if chunk3 == 'listen': 1959 | address2 = TreeNode(self._input[self._offset:self._offset + 6], self._offset) 1960 | self._offset = self._offset + 6 1961 | else: 1962 | address2 = FAILURE 1963 | if self._offset > self._failure: 1964 | self._failure = self._offset 1965 | self._expected = [] 1966 | if self._offset == self._failure: 1967 | self._expected.append('"listen"') 1968 | if address2 is FAILURE: 1969 | self._offset = index3 1970 | chunk4 = None 1971 | if self._offset < self._input_size: 1972 | chunk4 = self._input[self._offset:self._offset + 8] 1973 | if chunk4 == 'frontend': 1974 | address2 = TreeNode(self._input[self._offset:self._offset + 8], self._offset) 1975 | self._offset = self._offset + 8 1976 | else: 1977 | address2 = FAILURE 1978 | if self._offset > self._failure: 1979 | self._failure = self._offset 1980 | self._expected = [] 1981 | if self._offset == self._failure: 1982 | self._expected.append('"frontend"') 1983 | if address2 is FAILURE: 1984 | self._offset = index3 1985 | chunk5 = None 1986 | if self._offset < self._input_size: 1987 | chunk5 = self._input[self._offset:self._offset + 7] 1988 | if chunk5 == 'backend': 1989 | address2 = TreeNode(self._input[self._offset:self._offset + 7], self._offset) 1990 | self._offset = self._offset + 7 1991 | else: 1992 | address2 = FAILURE 1993 | if self._offset > self._failure: 1994 | self._failure = self._offset 1995 | self._expected = [] 1996 | if self._offset == self._failure: 1997 | self._expected.append('"backend"') 1998 | if address2 is FAILURE: 1999 | self._offset = index3 2000 | self._offset = index2 2001 | if address2 is FAILURE: 2002 | address2 = TreeNode(self._input[self._offset:self._offset], self._offset) 2003 | self._offset = self._offset 2004 | else: 2005 | address2 = FAILURE 2006 | if address2 is not FAILURE: 2007 | elements0.append(address2) 2008 | address3 = FAILURE 2009 | address3 = self._read_keyword() 2010 | if address3 is not FAILURE: 2011 | elements0.append(address3) 2012 | address4 = FAILURE 2013 | address4 = self._read_whitespace() 2014 | if address4 is not FAILURE: 2015 | elements0.append(address4) 2016 | address5 = FAILURE 2017 | index4 = self._offset 2018 | address5 = self._read_value() 2019 | if address5 is FAILURE: 2020 | address5 = TreeNode(self._input[index4:index4], index4) 2021 | self._offset = index4 2022 | if address5 is not FAILURE: 2023 | elements0.append(address5) 2024 | address6 = FAILURE 2025 | index5 = self._offset 2026 | address6 = self._read_comment_text() 2027 | if address6 is FAILURE: 2028 | address6 = TreeNode(self._input[index5:index5], index5) 2029 | self._offset = index5 2030 | if address6 is not FAILURE: 2031 | elements0.append(address6) 2032 | address7 = FAILURE 2033 | address7 = self._read_line_break() 2034 | if address7 is not FAILURE: 2035 | elements0.append(address7) 2036 | else: 2037 | elements0 = None 2038 | self._offset = index1 2039 | else: 2040 | elements0 = None 2041 | self._offset = index1 2042 | else: 2043 | elements0 = None 2044 | self._offset = index1 2045 | else: 2046 | elements0 = None 2047 | self._offset = index1 2048 | else: 2049 | elements0 = None 2050 | self._offset = index1 2051 | else: 2052 | elements0 = None 2053 | self._offset = index1 2054 | else: 2055 | elements0 = None 2056 | self._offset = index1 2057 | if elements0 is None: 2058 | address0 = FAILURE 2059 | else: 2060 | address0 = ConfigLine(self._input[index1:self._offset], index1, elements0) 2061 | self._offset = self._offset 2062 | self._cache['config_line'][index0] = (address0, self._offset) 2063 | return address0 2064 | 2065 | def _read_comment_line(self): 2066 | address0, index0 = FAILURE, self._offset 2067 | cached = self._cache['comment_line'].get(index0) 2068 | if cached: 2069 | self._offset = cached[1] 2070 | return cached[0] 2071 | index1, elements0 = self._offset, [] 2072 | address1 = FAILURE 2073 | address1 = self._read_whitespace() 2074 | if address1 is not FAILURE: 2075 | elements0.append(address1) 2076 | address2 = FAILURE 2077 | address2 = self._read_comment_text() 2078 | if address2 is not FAILURE: 2079 | elements0.append(address2) 2080 | address3 = FAILURE 2081 | address3 = self._read_line_break() 2082 | if address3 is not FAILURE: 2083 | elements0.append(address3) 2084 | else: 2085 | elements0 = None 2086 | self._offset = index1 2087 | else: 2088 | elements0 = None 2089 | self._offset = index1 2090 | else: 2091 | elements0 = None 2092 | self._offset = index1 2093 | if elements0 is None: 2094 | address0 = FAILURE 2095 | else: 2096 | address0 = CommentLine(self._input[index1:self._offset], index1, elements0) 2097 | self._offset = self._offset 2098 | self._cache['comment_line'][index0] = (address0, self._offset) 2099 | return address0 2100 | 2101 | def _read_blank_line(self): 2102 | address0, index0 = FAILURE, self._offset 2103 | cached = self._cache['blank_line'].get(index0) 2104 | if cached: 2105 | self._offset = cached[1] 2106 | return cached[0] 2107 | index1, elements0 = self._offset, [] 2108 | address1 = FAILURE 2109 | address1 = self._read_whitespace() 2110 | if address1 is not FAILURE: 2111 | elements0.append(address1) 2112 | address2 = FAILURE 2113 | address2 = self._read_line_break() 2114 | if address2 is not FAILURE: 2115 | elements0.append(address2) 2116 | else: 2117 | elements0 = None 2118 | self._offset = index1 2119 | else: 2120 | elements0 = None 2121 | self._offset = index1 2122 | if elements0 is None: 2123 | address0 = FAILURE 2124 | else: 2125 | address0 = BlankLine(self._input[index1:self._offset], index1, elements0) 2126 | self._offset = self._offset 2127 | self._cache['blank_line'][index0] = (address0, self._offset) 2128 | return address0 2129 | 2130 | def _read_comment_text(self): 2131 | address0, index0 = FAILURE, self._offset 2132 | cached = self._cache['comment_text'].get(index0) 2133 | if cached: 2134 | self._offset = cached[1] 2135 | return cached[0] 2136 | index1, elements0 = self._offset, [] 2137 | address1 = FAILURE 2138 | chunk0 = None 2139 | if self._offset < self._input_size: 2140 | chunk0 = self._input[self._offset:self._offset + 1] 2141 | if chunk0 == '#': 2142 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2143 | self._offset = self._offset + 1 2144 | else: 2145 | address1 = FAILURE 2146 | if self._offset > self._failure: 2147 | self._failure = self._offset 2148 | self._expected = [] 2149 | if self._offset == self._failure: 2150 | self._expected.append('"#"') 2151 | if address1 is not FAILURE: 2152 | elements0.append(address1) 2153 | address2 = FAILURE 2154 | remaining0, index2, elements1, address3 = 0, self._offset, [], True 2155 | while address3 is not FAILURE: 2156 | address3 = self._read_char() 2157 | if address3 is not FAILURE: 2158 | elements1.append(address3) 2159 | remaining0 -= 1 2160 | if remaining0 <= 0: 2161 | address2 = TreeNode(self._input[index2:self._offset], index2, elements1) 2162 | self._offset = self._offset 2163 | else: 2164 | address2 = FAILURE 2165 | if address2 is not FAILURE: 2166 | elements0.append(address2) 2167 | address4 = FAILURE 2168 | index3 = self._offset 2169 | address4 = self._read_line_break() 2170 | self._offset = index3 2171 | if address4 is not FAILURE: 2172 | address4 = TreeNode(self._input[self._offset:self._offset], self._offset) 2173 | self._offset = self._offset 2174 | else: 2175 | address4 = FAILURE 2176 | if address4 is not FAILURE: 2177 | elements0.append(address4) 2178 | else: 2179 | elements0 = None 2180 | self._offset = index1 2181 | else: 2182 | elements0 = None 2183 | self._offset = index1 2184 | else: 2185 | elements0 = None 2186 | self._offset = index1 2187 | if elements0 is None: 2188 | address0 = FAILURE 2189 | else: 2190 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2191 | self._offset = self._offset 2192 | self._cache['comment_text'][index0] = (address0, self._offset) 2193 | return address0 2194 | 2195 | def _read_line_break(self): 2196 | address0, index0 = FAILURE, self._offset 2197 | cached = self._cache['line_break'].get(index0) 2198 | if cached: 2199 | self._offset = cached[1] 2200 | return cached[0] 2201 | chunk0 = None 2202 | if self._offset < self._input_size: 2203 | chunk0 = self._input[self._offset:self._offset + 1] 2204 | if chunk0 is not None and Grammar.REGEX_1.search(chunk0): 2205 | address0 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2206 | self._offset = self._offset + 1 2207 | else: 2208 | address0 = FAILURE 2209 | if self._offset > self._failure: 2210 | self._failure = self._offset 2211 | self._expected = [] 2212 | if self._offset == self._failure: 2213 | self._expected.append('[\\n]') 2214 | self._cache['line_break'][index0] = (address0, self._offset) 2215 | return address0 2216 | 2217 | def _read_keyword(self): 2218 | address0, index0 = FAILURE, self._offset 2219 | cached = self._cache['keyword'].get(index0) 2220 | if cached: 2221 | self._offset = cached[1] 2222 | return cached[0] 2223 | index1, elements0 = self._offset, [] 2224 | address1 = FAILURE 2225 | index2 = self._offset 2226 | index3, elements1 = self._offset, [] 2227 | address2 = FAILURE 2228 | index4 = self._offset 2229 | chunk0 = None 2230 | if self._offset < self._input_size: 2231 | chunk0 = self._input[self._offset:self._offset + 9] 2232 | if chunk0 == 'errorfile': 2233 | address2 = TreeNode(self._input[self._offset:self._offset + 9], self._offset) 2234 | self._offset = self._offset + 9 2235 | else: 2236 | address2 = FAILURE 2237 | if self._offset > self._failure: 2238 | self._failure = self._offset 2239 | self._expected = [] 2240 | if self._offset == self._failure: 2241 | self._expected.append('"errorfile"') 2242 | if address2 is FAILURE: 2243 | self._offset = index4 2244 | chunk1 = None 2245 | if self._offset < self._input_size: 2246 | chunk1 = self._input[self._offset:self._offset + 7] 2247 | if chunk1 == 'timeout': 2248 | address2 = TreeNode(self._input[self._offset:self._offset + 7], self._offset) 2249 | self._offset = self._offset + 7 2250 | else: 2251 | address2 = FAILURE 2252 | if self._offset > self._failure: 2253 | self._failure = self._offset 2254 | self._expected = [] 2255 | if self._offset == self._failure: 2256 | self._expected.append('"timeout"') 2257 | if address2 is FAILURE: 2258 | self._offset = index4 2259 | if address2 is not FAILURE: 2260 | elements1.append(address2) 2261 | address3 = FAILURE 2262 | address3 = self._read_whitespace() 2263 | if address3 is not FAILURE: 2264 | elements1.append(address3) 2265 | else: 2266 | elements1 = None 2267 | self._offset = index3 2268 | else: 2269 | elements1 = None 2270 | self._offset = index3 2271 | if elements1 is None: 2272 | address1 = FAILURE 2273 | else: 2274 | address1 = Keyword(self._input[index3:self._offset], index3, elements1) 2275 | self._offset = self._offset 2276 | if address1 is FAILURE: 2277 | address1 = TreeNode(self._input[index2:index2], index2) 2278 | self._offset = index2 2279 | if address1 is not FAILURE: 2280 | elements0.append(address1) 2281 | address4 = FAILURE 2282 | remaining0, index5, elements2, address5 = 1, self._offset, [], True 2283 | while address5 is not FAILURE: 2284 | chunk2 = None 2285 | if self._offset < self._input_size: 2286 | chunk2 = self._input[self._offset:self._offset + 1] 2287 | if chunk2 is not None and Grammar.REGEX_2.search(chunk2): 2288 | address5 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2289 | self._offset = self._offset + 1 2290 | else: 2291 | address5 = FAILURE 2292 | if self._offset > self._failure: 2293 | self._failure = self._offset 2294 | self._expected = [] 2295 | if self._offset == self._failure: 2296 | self._expected.append('[a-z0-9\\-\\_\\.]') 2297 | if address5 is not FAILURE: 2298 | elements2.append(address5) 2299 | remaining0 -= 1 2300 | if remaining0 <= 0: 2301 | address4 = TreeNode(self._input[index5:self._offset], index5, elements2) 2302 | self._offset = self._offset 2303 | else: 2304 | address4 = FAILURE 2305 | if address4 is not FAILURE: 2306 | elements0.append(address4) 2307 | else: 2308 | elements0 = None 2309 | self._offset = index1 2310 | else: 2311 | elements0 = None 2312 | self._offset = index1 2313 | if elements0 is None: 2314 | address0 = FAILURE 2315 | else: 2316 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2317 | self._offset = self._offset 2318 | self._cache['keyword'][index0] = (address0, self._offset) 2319 | return address0 2320 | 2321 | def _read_server_name(self): 2322 | address0, index0 = FAILURE, self._offset 2323 | cached = self._cache['server_name'].get(index0) 2324 | if cached: 2325 | self._offset = cached[1] 2326 | return cached[0] 2327 | remaining0, index1, elements0, address1 = 1, self._offset, [], True 2328 | while address1 is not FAILURE: 2329 | chunk0 = None 2330 | if self._offset < self._input_size: 2331 | chunk0 = self._input[self._offset:self._offset + 1] 2332 | if chunk0 is not None and Grammar.REGEX_3.search(chunk0): 2333 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2334 | self._offset = self._offset + 1 2335 | else: 2336 | address1 = FAILURE 2337 | if self._offset > self._failure: 2338 | self._failure = self._offset 2339 | self._expected = [] 2340 | if self._offset == self._failure: 2341 | self._expected.append('[a-zA-z0-9\\-\\_\\.:]') 2342 | if address1 is not FAILURE: 2343 | elements0.append(address1) 2344 | remaining0 -= 1 2345 | if remaining0 <= 0: 2346 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2347 | self._offset = self._offset 2348 | else: 2349 | address0 = FAILURE 2350 | self._cache['server_name'][index0] = (address0, self._offset) 2351 | return address0 2352 | 2353 | def _read_acl_name(self): 2354 | address0, index0 = FAILURE, self._offset 2355 | cached = self._cache['acl_name'].get(index0) 2356 | if cached: 2357 | self._offset = cached[1] 2358 | return cached[0] 2359 | remaining0, index1, elements0, address1 = 1, self._offset, [], True 2360 | while address1 is not FAILURE: 2361 | chunk0 = None 2362 | if self._offset < self._input_size: 2363 | chunk0 = self._input[self._offset:self._offset + 1] 2364 | if chunk0 is not None and Grammar.REGEX_4.search(chunk0): 2365 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2366 | self._offset = self._offset + 1 2367 | else: 2368 | address1 = FAILURE 2369 | if self._offset > self._failure: 2370 | self._failure = self._offset 2371 | self._expected = [] 2372 | if self._offset == self._failure: 2373 | self._expected.append('[a-zA-z0-9\\-\\_\\.:]') 2374 | if address1 is not FAILURE: 2375 | elements0.append(address1) 2376 | remaining0 -= 1 2377 | if remaining0 <= 0: 2378 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2379 | self._offset = self._offset 2380 | else: 2381 | address0 = FAILURE 2382 | self._cache['acl_name'][index0] = (address0, self._offset) 2383 | return address0 2384 | 2385 | def _read_backend_name(self): 2386 | address0, index0 = FAILURE, self._offset 2387 | cached = self._cache['backend_name'].get(index0) 2388 | if cached: 2389 | self._offset = cached[1] 2390 | return cached[0] 2391 | remaining0, index1, elements0, address1 = 1, self._offset, [], True 2392 | while address1 is not FAILURE: 2393 | chunk0 = None 2394 | if self._offset < self._input_size: 2395 | chunk0 = self._input[self._offset:self._offset + 1] 2396 | if chunk0 is not None and Grammar.REGEX_5.search(chunk0): 2397 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2398 | self._offset = self._offset + 1 2399 | else: 2400 | address1 = FAILURE 2401 | if self._offset > self._failure: 2402 | self._failure = self._offset 2403 | self._expected = [] 2404 | if self._offset == self._failure: 2405 | self._expected.append('[a-zA-z0-9\\-\\_\\.:]') 2406 | if address1 is not FAILURE: 2407 | elements0.append(address1) 2408 | remaining0 -= 1 2409 | if remaining0 <= 0: 2410 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2411 | self._offset = self._offset 2412 | else: 2413 | address0 = FAILURE 2414 | self._cache['backend_name'][index0] = (address0, self._offset) 2415 | return address0 2416 | 2417 | def _read_group_name(self): 2418 | address0, index0 = FAILURE, self._offset 2419 | cached = self._cache['group_name'].get(index0) 2420 | if cached: 2421 | self._offset = cached[1] 2422 | return cached[0] 2423 | remaining0, index1, elements0, address1 = 1, self._offset, [], True 2424 | while address1 is not FAILURE: 2425 | chunk0 = None 2426 | if self._offset < self._input_size: 2427 | chunk0 = self._input[self._offset:self._offset + 1] 2428 | if chunk0 is not None and Grammar.REGEX_6.search(chunk0): 2429 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2430 | self._offset = self._offset + 1 2431 | else: 2432 | address1 = FAILURE 2433 | if self._offset > self._failure: 2434 | self._failure = self._offset 2435 | self._expected = [] 2436 | if self._offset == self._failure: 2437 | self._expected.append('[a-zA-z0-9\\-\\_\\.:]') 2438 | if address1 is not FAILURE: 2439 | elements0.append(address1) 2440 | remaining0 -= 1 2441 | if remaining0 <= 0: 2442 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2443 | self._offset = self._offset 2444 | else: 2445 | address0 = FAILURE 2446 | self._cache['group_name'][index0] = (address0, self._offset) 2447 | return address0 2448 | 2449 | def _read_user_name(self): 2450 | address0, index0 = FAILURE, self._offset 2451 | cached = self._cache['user_name'].get(index0) 2452 | if cached: 2453 | self._offset = cached[1] 2454 | return cached[0] 2455 | remaining0, index1, elements0, address1 = 1, self._offset, [], True 2456 | while address1 is not FAILURE: 2457 | chunk0 = None 2458 | if self._offset < self._input_size: 2459 | chunk0 = self._input[self._offset:self._offset + 1] 2460 | if chunk0 is not None and Grammar.REGEX_7.search(chunk0): 2461 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2462 | self._offset = self._offset + 1 2463 | else: 2464 | address1 = FAILURE 2465 | if self._offset > self._failure: 2466 | self._failure = self._offset 2467 | self._expected = [] 2468 | if self._offset == self._failure: 2469 | self._expected.append('[a-zA-z0-9\\-\\_\\.:]') 2470 | if address1 is not FAILURE: 2471 | elements0.append(address1) 2472 | remaining0 -= 1 2473 | if remaining0 <= 0: 2474 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2475 | self._offset = self._offset 2476 | else: 2477 | address0 = FAILURE 2478 | self._cache['user_name'][index0] = (address0, self._offset) 2479 | return address0 2480 | 2481 | def _read_password(self): 2482 | address0, index0 = FAILURE, self._offset 2483 | cached = self._cache['password'].get(index0) 2484 | if cached: 2485 | self._offset = cached[1] 2486 | return cached[0] 2487 | remaining0, index1, elements0, address1 = 1, self._offset, [], True 2488 | while address1 is not FAILURE: 2489 | chunk0 = None 2490 | if self._offset < self._input_size: 2491 | chunk0 = self._input[self._offset:self._offset + 1] 2492 | if chunk0 is not None and Grammar.REGEX_8.search(chunk0): 2493 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2494 | self._offset = self._offset + 1 2495 | else: 2496 | address1 = FAILURE 2497 | if self._offset > self._failure: 2498 | self._failure = self._offset 2499 | self._expected = [] 2500 | if self._offset == self._failure: 2501 | self._expected.append('[^#\\n]') 2502 | if address1 is not FAILURE: 2503 | elements0.append(address1) 2504 | remaining0 -= 1 2505 | if remaining0 <= 0: 2506 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2507 | self._offset = self._offset 2508 | else: 2509 | address0 = FAILURE 2510 | self._cache['password'][index0] = (address0, self._offset) 2511 | return address0 2512 | 2513 | def _read_backend_condition(self): 2514 | address0, index0 = FAILURE, self._offset 2515 | cached = self._cache['backend_condition'].get(index0) 2516 | if cached: 2517 | self._offset = cached[1] 2518 | return cached[0] 2519 | remaining0, index1, elements0, address1 = 1, self._offset, [], True 2520 | while address1 is not FAILURE: 2521 | chunk0 = None 2522 | if self._offset < self._input_size: 2523 | chunk0 = self._input[self._offset:self._offset + 1] 2524 | if chunk0 is not None and Grammar.REGEX_9.search(chunk0): 2525 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2526 | self._offset = self._offset + 1 2527 | else: 2528 | address1 = FAILURE 2529 | if self._offset > self._failure: 2530 | self._failure = self._offset 2531 | self._expected = [] 2532 | if self._offset == self._failure: 2533 | self._expected.append('[^#\\n]') 2534 | if address1 is not FAILURE: 2535 | elements0.append(address1) 2536 | remaining0 -= 1 2537 | if remaining0 <= 0: 2538 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2539 | self._offset = self._offset 2540 | else: 2541 | address0 = FAILURE 2542 | self._cache['backend_condition'][index0] = (address0, self._offset) 2543 | return address0 2544 | 2545 | def _read_service_address(self): 2546 | address0, index0 = FAILURE, self._offset 2547 | cached = self._cache['service_address'].get(index0) 2548 | if cached: 2549 | self._offset = cached[1] 2550 | return cached[0] 2551 | index1, elements0 = self._offset, [] 2552 | address1 = FAILURE 2553 | address1 = self._read_host() 2554 | if address1 is not FAILURE: 2555 | elements0.append(address1) 2556 | address2 = FAILURE 2557 | index2 = self._offset 2558 | chunk0 = None 2559 | if self._offset < self._input_size: 2560 | chunk0 = self._input[self._offset:self._offset + 1] 2561 | if chunk0 is not None and Grammar.REGEX_10.search(chunk0): 2562 | address2 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2563 | self._offset = self._offset + 1 2564 | else: 2565 | address2 = FAILURE 2566 | if self._offset > self._failure: 2567 | self._failure = self._offset 2568 | self._expected = [] 2569 | if self._offset == self._failure: 2570 | self._expected.append('[:]') 2571 | if address2 is FAILURE: 2572 | address2 = TreeNode(self._input[index2:index2], index2) 2573 | self._offset = index2 2574 | if address2 is not FAILURE: 2575 | elements0.append(address2) 2576 | address3 = FAILURE 2577 | address3 = self._read_port() 2578 | if address3 is not FAILURE: 2579 | elements0.append(address3) 2580 | else: 2581 | elements0 = None 2582 | self._offset = index1 2583 | else: 2584 | elements0 = None 2585 | self._offset = index1 2586 | else: 2587 | elements0 = None 2588 | self._offset = index1 2589 | if elements0 is None: 2590 | address0 = FAILURE 2591 | else: 2592 | address0 = ServiceAddress(self._input[index1:self._offset], index1, elements0) 2593 | self._offset = self._offset 2594 | self._cache['service_address'][index0] = (address0, self._offset) 2595 | return address0 2596 | 2597 | def _read_host(self): 2598 | address0, index0 = FAILURE, self._offset 2599 | cached = self._cache['host'].get(index0) 2600 | if cached: 2601 | self._offset = cached[1] 2602 | return cached[0] 2603 | index1 = self._offset 2604 | address0 = self._read_ipv4_host() 2605 | if address0 is FAILURE: 2606 | self._offset = index1 2607 | address0 = self._read_dns_host() 2608 | if address0 is FAILURE: 2609 | self._offset = index1 2610 | address0 = self._read_wildcard_host() 2611 | if address0 is FAILURE: 2612 | self._offset = index1 2613 | self._cache['host'][index0] = (address0, self._offset) 2614 | return address0 2615 | 2616 | def _read_port(self): 2617 | address0, index0 = FAILURE, self._offset 2618 | cached = self._cache['port'].get(index0) 2619 | if cached: 2620 | self._offset = cached[1] 2621 | return cached[0] 2622 | remaining0, index1, elements0, address1 = 0, self._offset, [], True 2623 | while address1 is not FAILURE: 2624 | chunk0 = None 2625 | if self._offset < self._input_size: 2626 | chunk0 = self._input[self._offset:self._offset + 1] 2627 | if chunk0 is not None and Grammar.REGEX_11.search(chunk0): 2628 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2629 | self._offset = self._offset + 1 2630 | else: 2631 | address1 = FAILURE 2632 | if self._offset > self._failure: 2633 | self._failure = self._offset 2634 | self._expected = [] 2635 | if self._offset == self._failure: 2636 | self._expected.append('[\\d]') 2637 | if address1 is not FAILURE: 2638 | elements0.append(address1) 2639 | remaining0 -= 1 2640 | if remaining0 <= 0: 2641 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2642 | self._offset = self._offset 2643 | else: 2644 | address0 = FAILURE 2645 | self._cache['port'][index0] = (address0, self._offset) 2646 | return address0 2647 | 2648 | def _read_ipv4_host(self): 2649 | address0, index0 = FAILURE, self._offset 2650 | cached = self._cache['ipv4_host'].get(index0) 2651 | if cached: 2652 | self._offset = cached[1] 2653 | return cached[0] 2654 | index1, elements0 = self._offset, [] 2655 | address1 = FAILURE 2656 | remaining0, index2, elements1, address2 = 1, self._offset, [], True 2657 | while address2 is not FAILURE: 2658 | chunk0 = None 2659 | if self._offset < self._input_size: 2660 | chunk0 = self._input[self._offset:self._offset + 1] 2661 | if chunk0 is not None and Grammar.REGEX_12.search(chunk0): 2662 | address2 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2663 | self._offset = self._offset + 1 2664 | else: 2665 | address2 = FAILURE 2666 | if self._offset > self._failure: 2667 | self._failure = self._offset 2668 | self._expected = [] 2669 | if self._offset == self._failure: 2670 | self._expected.append('[\\d]') 2671 | if address2 is not FAILURE: 2672 | elements1.append(address2) 2673 | remaining0 -= 1 2674 | if remaining0 <= 0: 2675 | address1 = TreeNode(self._input[index2:self._offset], index2, elements1) 2676 | self._offset = self._offset 2677 | else: 2678 | address1 = FAILURE 2679 | if address1 is not FAILURE: 2680 | elements0.append(address1) 2681 | address3 = FAILURE 2682 | chunk1 = None 2683 | if self._offset < self._input_size: 2684 | chunk1 = self._input[self._offset:self._offset + 1] 2685 | if chunk1 == '.': 2686 | address3 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2687 | self._offset = self._offset + 1 2688 | else: 2689 | address3 = FAILURE 2690 | if self._offset > self._failure: 2691 | self._failure = self._offset 2692 | self._expected = [] 2693 | if self._offset == self._failure: 2694 | self._expected.append('"."') 2695 | if address3 is not FAILURE: 2696 | elements0.append(address3) 2697 | address4 = FAILURE 2698 | remaining1, index3, elements2, address5 = 1, self._offset, [], True 2699 | while address5 is not FAILURE: 2700 | chunk2 = None 2701 | if self._offset < self._input_size: 2702 | chunk2 = self._input[self._offset:self._offset + 1] 2703 | if chunk2 is not None and Grammar.REGEX_13.search(chunk2): 2704 | address5 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2705 | self._offset = self._offset + 1 2706 | else: 2707 | address5 = FAILURE 2708 | if self._offset > self._failure: 2709 | self._failure = self._offset 2710 | self._expected = [] 2711 | if self._offset == self._failure: 2712 | self._expected.append('[\\d]') 2713 | if address5 is not FAILURE: 2714 | elements2.append(address5) 2715 | remaining1 -= 1 2716 | if remaining1 <= 0: 2717 | address4 = TreeNode(self._input[index3:self._offset], index3, elements2) 2718 | self._offset = self._offset 2719 | else: 2720 | address4 = FAILURE 2721 | if address4 is not FAILURE: 2722 | elements0.append(address4) 2723 | address6 = FAILURE 2724 | chunk3 = None 2725 | if self._offset < self._input_size: 2726 | chunk3 = self._input[self._offset:self._offset + 1] 2727 | if chunk3 == '.': 2728 | address6 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2729 | self._offset = self._offset + 1 2730 | else: 2731 | address6 = FAILURE 2732 | if self._offset > self._failure: 2733 | self._failure = self._offset 2734 | self._expected = [] 2735 | if self._offset == self._failure: 2736 | self._expected.append('"."') 2737 | if address6 is not FAILURE: 2738 | elements0.append(address6) 2739 | address7 = FAILURE 2740 | remaining2, index4, elements3, address8 = 1, self._offset, [], True 2741 | while address8 is not FAILURE: 2742 | chunk4 = None 2743 | if self._offset < self._input_size: 2744 | chunk4 = self._input[self._offset:self._offset + 1] 2745 | if chunk4 is not None and Grammar.REGEX_14.search(chunk4): 2746 | address8 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2747 | self._offset = self._offset + 1 2748 | else: 2749 | address8 = FAILURE 2750 | if self._offset > self._failure: 2751 | self._failure = self._offset 2752 | self._expected = [] 2753 | if self._offset == self._failure: 2754 | self._expected.append('[\\d]') 2755 | if address8 is not FAILURE: 2756 | elements3.append(address8) 2757 | remaining2 -= 1 2758 | if remaining2 <= 0: 2759 | address7 = TreeNode(self._input[index4:self._offset], index4, elements3) 2760 | self._offset = self._offset 2761 | else: 2762 | address7 = FAILURE 2763 | if address7 is not FAILURE: 2764 | elements0.append(address7) 2765 | address9 = FAILURE 2766 | chunk5 = None 2767 | if self._offset < self._input_size: 2768 | chunk5 = self._input[self._offset:self._offset + 1] 2769 | if chunk5 == '.': 2770 | address9 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2771 | self._offset = self._offset + 1 2772 | else: 2773 | address9 = FAILURE 2774 | if self._offset > self._failure: 2775 | self._failure = self._offset 2776 | self._expected = [] 2777 | if self._offset == self._failure: 2778 | self._expected.append('"."') 2779 | if address9 is not FAILURE: 2780 | elements0.append(address9) 2781 | address10 = FAILURE 2782 | remaining3, index5, elements4, address11 = 1, self._offset, [], True 2783 | while address11 is not FAILURE: 2784 | chunk6 = None 2785 | if self._offset < self._input_size: 2786 | chunk6 = self._input[self._offset:self._offset + 1] 2787 | if chunk6 is not None and Grammar.REGEX_15.search(chunk6): 2788 | address11 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2789 | self._offset = self._offset + 1 2790 | else: 2791 | address11 = FAILURE 2792 | if self._offset > self._failure: 2793 | self._failure = self._offset 2794 | self._expected = [] 2795 | if self._offset == self._failure: 2796 | self._expected.append('[\\d]') 2797 | if address11 is not FAILURE: 2798 | elements4.append(address11) 2799 | remaining3 -= 1 2800 | if remaining3 <= 0: 2801 | address10 = TreeNode(self._input[index5:self._offset], index5, elements4) 2802 | self._offset = self._offset 2803 | else: 2804 | address10 = FAILURE 2805 | if address10 is not FAILURE: 2806 | elements0.append(address10) 2807 | else: 2808 | elements0 = None 2809 | self._offset = index1 2810 | else: 2811 | elements0 = None 2812 | self._offset = index1 2813 | else: 2814 | elements0 = None 2815 | self._offset = index1 2816 | else: 2817 | elements0 = None 2818 | self._offset = index1 2819 | else: 2820 | elements0 = None 2821 | self._offset = index1 2822 | else: 2823 | elements0 = None 2824 | self._offset = index1 2825 | else: 2826 | elements0 = None 2827 | self._offset = index1 2828 | if elements0 is None: 2829 | address0 = FAILURE 2830 | else: 2831 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2832 | self._offset = self._offset 2833 | self._cache['ipv4_host'][index0] = (address0, self._offset) 2834 | return address0 2835 | 2836 | def _read_dns_host(self): 2837 | address0, index0 = FAILURE, self._offset 2838 | cached = self._cache['dns_host'].get(index0) 2839 | if cached: 2840 | self._offset = cached[1] 2841 | return cached[0] 2842 | remaining0, index1, elements0, address1 = 1, self._offset, [], True 2843 | while address1 is not FAILURE: 2844 | chunk0 = None 2845 | if self._offset < self._input_size: 2846 | chunk0 = self._input[self._offset:self._offset + 1] 2847 | if chunk0 is not None and Grammar.REGEX_16.search(chunk0): 2848 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2849 | self._offset = self._offset + 1 2850 | else: 2851 | address1 = FAILURE 2852 | if self._offset > self._failure: 2853 | self._failure = self._offset 2854 | self._expected = [] 2855 | if self._offset == self._failure: 2856 | self._expected.append('[a-zA-Z\\-\\.\\d]') 2857 | if address1 is not FAILURE: 2858 | elements0.append(address1) 2859 | remaining0 -= 1 2860 | if remaining0 <= 0: 2861 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2862 | self._offset = self._offset 2863 | else: 2864 | address0 = FAILURE 2865 | self._cache['dns_host'][index0] = (address0, self._offset) 2866 | return address0 2867 | 2868 | def _read_wildcard_host(self): 2869 | address0, index0 = FAILURE, self._offset 2870 | cached = self._cache['wildcard_host'].get(index0) 2871 | if cached: 2872 | self._offset = cached[1] 2873 | return cached[0] 2874 | chunk0 = None 2875 | if self._offset < self._input_size: 2876 | chunk0 = self._input[self._offset:self._offset + 1] 2877 | if chunk0 == '*': 2878 | address0 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2879 | self._offset = self._offset + 1 2880 | else: 2881 | address0 = FAILURE 2882 | if self._offset > self._failure: 2883 | self._failure = self._offset 2884 | self._expected = [] 2885 | if self._offset == self._failure: 2886 | self._expected.append('"*"') 2887 | self._cache['wildcard_host'][index0] = (address0, self._offset) 2888 | return address0 2889 | 2890 | def _read_proxy_name(self): 2891 | address0, index0 = FAILURE, self._offset 2892 | cached = self._cache['proxy_name'].get(index0) 2893 | if cached: 2894 | self._offset = cached[1] 2895 | return cached[0] 2896 | remaining0, index1, elements0, address1 = 1, self._offset, [], True 2897 | while address1 is not FAILURE: 2898 | chunk0 = None 2899 | if self._offset < self._input_size: 2900 | chunk0 = self._input[self._offset:self._offset + 1] 2901 | if chunk0 is not None and Grammar.REGEX_17.search(chunk0): 2902 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2903 | self._offset = self._offset + 1 2904 | else: 2905 | address1 = FAILURE 2906 | if self._offset > self._failure: 2907 | self._failure = self._offset 2908 | self._expected = [] 2909 | if self._offset == self._failure: 2910 | self._expected.append('[a-zA-Z0-9\\-\\_\\.:]') 2911 | if address1 is not FAILURE: 2912 | elements0.append(address1) 2913 | remaining0 -= 1 2914 | if remaining0 <= 0: 2915 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2916 | self._offset = self._offset 2917 | else: 2918 | address0 = FAILURE 2919 | self._cache['proxy_name'][index0] = (address0, self._offset) 2920 | return address0 2921 | 2922 | def _read_value(self): 2923 | address0, index0 = FAILURE, self._offset 2924 | cached = self._cache['value'].get(index0) 2925 | if cached: 2926 | self._offset = cached[1] 2927 | return cached[0] 2928 | remaining0, index1, elements0, address1 = 1, self._offset, [], True 2929 | while address1 is not FAILURE: 2930 | chunk0 = None 2931 | if self._offset < self._input_size: 2932 | chunk0 = self._input[self._offset:self._offset + 1] 2933 | if chunk0 is not None and Grammar.REGEX_18.search(chunk0): 2934 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2935 | self._offset = self._offset + 1 2936 | else: 2937 | address1 = FAILURE 2938 | if self._offset > self._failure: 2939 | self._failure = self._offset 2940 | self._expected = [] 2941 | if self._offset == self._failure: 2942 | self._expected.append('[^#\\n]') 2943 | if address1 is not FAILURE: 2944 | elements0.append(address1) 2945 | remaining0 -= 1 2946 | if remaining0 <= 0: 2947 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 2948 | self._offset = self._offset 2949 | else: 2950 | address0 = FAILURE 2951 | self._cache['value'][index0] = (address0, self._offset) 2952 | return address0 2953 | 2954 | def _read_char(self): 2955 | address0, index0 = FAILURE, self._offset 2956 | cached = self._cache['char'].get(index0) 2957 | if cached: 2958 | self._offset = cached[1] 2959 | return cached[0] 2960 | index1, elements0 = self._offset, [] 2961 | address1 = FAILURE 2962 | index2 = self._offset 2963 | chunk0 = None 2964 | if self._offset < self._input_size: 2965 | chunk0 = self._input[self._offset:self._offset + 1] 2966 | if chunk0 is not None and Grammar.REGEX_19.search(chunk0): 2967 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2968 | self._offset = self._offset + 1 2969 | else: 2970 | address1 = FAILURE 2971 | if self._offset > self._failure: 2972 | self._failure = self._offset 2973 | self._expected = [] 2974 | if self._offset == self._failure: 2975 | self._expected.append('[\\n]') 2976 | self._offset = index2 2977 | if address1 is FAILURE: 2978 | address1 = TreeNode(self._input[self._offset:self._offset], self._offset) 2979 | self._offset = self._offset 2980 | else: 2981 | address1 = FAILURE 2982 | if address1 is not FAILURE: 2983 | elements0.append(address1) 2984 | address2 = FAILURE 2985 | if self._offset < self._input_size: 2986 | address2 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 2987 | self._offset = self._offset + 1 2988 | else: 2989 | address2 = FAILURE 2990 | if self._offset > self._failure: 2991 | self._failure = self._offset 2992 | self._expected = [] 2993 | if self._offset == self._failure: 2994 | self._expected.append('') 2995 | if address2 is not FAILURE: 2996 | elements0.append(address2) 2997 | else: 2998 | elements0 = None 2999 | self._offset = index1 3000 | else: 3001 | elements0 = None 3002 | self._offset = index1 3003 | if elements0 is None: 3004 | address0 = FAILURE 3005 | else: 3006 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 3007 | self._offset = self._offset 3008 | self._cache['char'][index0] = (address0, self._offset) 3009 | return address0 3010 | 3011 | def _read_whitespace(self): 3012 | address0, index0 = FAILURE, self._offset 3013 | cached = self._cache['whitespace'].get(index0) 3014 | if cached: 3015 | self._offset = cached[1] 3016 | return cached[0] 3017 | remaining0, index1, elements0, address1 = 0, self._offset, [], True 3018 | while address1 is not FAILURE: 3019 | chunk0 = None 3020 | if self._offset < self._input_size: 3021 | chunk0 = self._input[self._offset:self._offset + 1] 3022 | if chunk0 is not None and Grammar.REGEX_20.search(chunk0): 3023 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 3024 | self._offset = self._offset + 1 3025 | else: 3026 | address1 = FAILURE 3027 | if self._offset > self._failure: 3028 | self._failure = self._offset 3029 | self._expected = [] 3030 | if self._offset == self._failure: 3031 | self._expected.append('[ \\t]') 3032 | if address1 is not FAILURE: 3033 | elements0.append(address1) 3034 | remaining0 -= 1 3035 | if remaining0 <= 0: 3036 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 3037 | self._offset = self._offset 3038 | else: 3039 | address0 = FAILURE 3040 | self._cache['whitespace'][index0] = (address0, self._offset) 3041 | return address0 3042 | 3043 | def _read_whitespaceplus(self): 3044 | address0, index0 = FAILURE, self._offset 3045 | cached = self._cache['whitespaceplus'].get(index0) 3046 | if cached: 3047 | self._offset = cached[1] 3048 | return cached[0] 3049 | remaining0, index1, elements0, address1 = 1, self._offset, [], True 3050 | while address1 is not FAILURE: 3051 | chunk0 = None 3052 | if self._offset < self._input_size: 3053 | chunk0 = self._input[self._offset:self._offset + 1] 3054 | if chunk0 is not None and Grammar.REGEX_21.search(chunk0): 3055 | address1 = TreeNode(self._input[self._offset:self._offset + 1], self._offset) 3056 | self._offset = self._offset + 1 3057 | else: 3058 | address1 = FAILURE 3059 | if self._offset > self._failure: 3060 | self._failure = self._offset 3061 | self._expected = [] 3062 | if self._offset == self._failure: 3063 | self._expected.append('[ \\t]') 3064 | if address1 is not FAILURE: 3065 | elements0.append(address1) 3066 | remaining0 -= 1 3067 | if remaining0 <= 0: 3068 | address0 = TreeNode(self._input[index1:self._offset], index1, elements0) 3069 | self._offset = self._offset 3070 | else: 3071 | address0 = FAILURE 3072 | self._cache['whitespaceplus'][index0] = (address0, self._offset) 3073 | return address0 3074 | 3075 | 3076 | 3077 | class Parser(Grammar): 3078 | def __init__(self, input, actions, types): 3079 | self._input = input 3080 | self._input_size = len(input) 3081 | self._actions = actions 3082 | self._types = types 3083 | self._offset = 0 3084 | self._cache = defaultdict(dict) 3085 | self._failure = 0 3086 | self._expected = [] 3087 | 3088 | def parse(self): 3089 | tree = self._read_configuration() 3090 | if tree is not FAILURE and self._offset == self._input_size: 3091 | return tree 3092 | if not self._expected: 3093 | self._failure = self._offset 3094 | self._expected.append('') 3095 | raise ParseError(format_error(self._input, self._failure, self._expected)) 3096 | 3097 | 3098 | def format_error(input, offset, expected): 3099 | lines, line_no, position = input.split('\n'), 0, 0 3100 | while position <= offset: 3101 | position += len(lines[line_no]) + 1 3102 | line_no += 1 3103 | message, line = 'Line ' + str(line_no) + ': expected ' + ', '.join(expected) + '\n', lines[line_no - 1] 3104 | message += line + '\n' 3105 | position -= len(line) + 1 3106 | message += ' ' * (offset - position) 3107 | return message + '^' 3108 | 3109 | def parse(input, actions=None, types=None): 3110 | parser = Parser(input, actions, types) 3111 | return parser.parse() 3112 | --------------------------------------------------------------------------------