#
#
#
from ..equality import EqualityTupleMixin
from .base import Record, ValuesMixin, unquote
from .rr import RrParseError
from .validator import ValueValidator
[docs]
class UrlfwdValueValidator(ValueValidator):
'''
Validates URLFWD rdata: ``code`` is a valid HTTP redirect code,
``masking`` and ``query`` are in the recognized enum sets, and
``path`` and ``target`` are present.
'''
[docs]
def validate(self, value_cls, data, _type):
reasons = []
for value in data:
try:
code = int(value['code'])
if code not in value_cls.VALID_CODES:
reasons.append(f'unrecognized return code "{code}"')
except KeyError:
reasons.append('missing code')
except ValueError:
reasons.append(f'invalid return code "{value["code"]}"')
try:
masking = int(value['masking'])
if masking not in value_cls.VALID_MASKS:
reasons.append(f'unrecognized masking setting "{masking}"')
except KeyError:
reasons.append('missing masking')
except ValueError:
reasons.append(f'invalid masking setting "{value["masking"]}"')
try:
query = int(value['query'])
if query not in value_cls.VALID_QUERY:
reasons.append(f'unrecognized query setting "{query}"')
except KeyError:
reasons.append('missing query')
except ValueError:
reasons.append(f'invalid query setting "{value["query"]}"')
for k in ('path', 'target'):
if k not in value:
reasons.append(f'missing {k}')
return reasons
[docs]
class UrlfwdValue(EqualityTupleMixin, dict):
VALID_CODES = (301, 302)
VALID_MASKS = (0, 1, 2)
VALID_QUERY = (0, 1)
VALIDATORS = [
UrlfwdValueValidator('urlfwd-value', sets={'legacy', 'strict'})
]
[docs]
@classmethod
def _schema(cls):
return {
'type': 'object',
'required': ['path', 'target', 'code', 'masking', 'query'],
'properties': {
'path': {'type': 'string'},
'target': {'type': 'string'},
'code': {'type': 'integer', 'enum': list(cls.VALID_CODES)},
'masking': {'type': 'integer', 'enum': list(cls.VALID_MASKS)},
'query': {'type': 'integer', 'enum': list(cls.VALID_QUERY)},
},
}
[docs]
@classmethod
def parse_rdata_text(self, value):
try:
path, target, code, masking, query = value.split(' ')
except ValueError:
raise RrParseError()
try:
code = int(code)
except ValueError:
pass
try:
masking = int(masking)
except ValueError:
pass
try:
query = int(query)
except ValueError:
pass
path = unquote(path)
target = unquote(target)
return {
'path': path,
'target': target,
'code': code,
'masking': masking,
'query': query,
}
[docs]
@classmethod
def process(cls, values):
return [cls(v) for v in values]
[docs]
def __init__(self, value):
super().__init__(
{
'path': value['path'],
'target': value['target'],
'code': int(value['code']),
'masking': int(value['masking']),
'query': int(value['query']),
}
)
@property
def path(self):
return self['path']
@path.setter
def path(self, value):
self['path'] = value
@property
def target(self):
return self['target']
@target.setter
def target(self, value):
self['target'] = value
@property
def code(self):
return self['code']
@code.setter
def code(self, value):
self['code'] = value
@property
def masking(self):
return self['masking']
@masking.setter
def masking(self, value):
self['masking'] = value
@property
def query(self):
return self['query']
@query.setter
def query(self, value):
self['query'] = value
@property
def rdata_text(self):
return f'"{self.path}" "{self.target}" {self.code} {self.masking} {self.query}'
[docs]
def template(self, params):
if '{' not in self.path and '{' not in self.target:
return self
new = self.__class__(self)
new.path = new.path.format(**params)
new.target = new.target.format(**params)
return new
[docs]
def _equality_tuple(self):
return (self.path, self.target, self.code, self.masking, self.query)
def __hash__(self):
return hash(
(self.path, self.target, self.code, self.masking, self.query)
)
[docs]
def __repr__(self):
return f'"{self.path}" "{self.target}" {self.code} {self.masking} {self.query}'
[docs]
class UrlfwdRecord(ValuesMixin, Record):
REFERENCES = ()
_type = 'URLFWD'
_value_type = UrlfwdValue
Record.register_type(UrlfwdRecord)