Browse Source

feat: add Filter support

master
dnomd343 2 years ago
parent
commit
15a7ac72d5
  1. 109
      Basis/Filter.py
  2. 12
      Filter/Shadowsocks.py
  3. 7
      demo.py

109
Basis/Filter.py

@ -1,85 +1,120 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import copy
filterObject = { filterObject = {
'optional': { 'optional': {
'type': bool,
'optional': True, # `optional` is not force require 'optional': True, # `optional` is not force require
'default': False, # disable `optional` option in default 'default': False, # disable `optional` option in default
'allowNone': False, # `optional` couldn't be None 'allowNone': False, # `optional` couldn't be None
'type': bool,
'format': lambda x: x, # return same value
'filter': lambda b: True, # always return True
'errMsg': 'Invalid `optional` key'
}, },
'default': { 'default': {
'type': any, # skip type check
'optional': True, # `default` is not force require 'optional': True, # `default` is not force require
'default': None, 'default': None,
'allowNone': True, # `default` can be None 'allowNone': True, # `default` can be None
'type': any, # skip type check
'format': lambda x: x, # return same value
'filter': lambda b: True, # always return True
'errMsg': 'Invalid `default` key'
}, },
'allowNone': { 'allowNone': {
'type': bool,
'optional': True, # `allowNone` is not force require 'optional': True, # `allowNone` is not force require
'default': False, # disable `allowNone` option in default 'default': False, # disable `allowNone` option in default
'allowNone': False, # `allowNone` couldn't be None 'allowNone': False, # `allowNone` couldn't be None
'type': bool,
'format': lambda x: x, # return same value
'filter': lambda b: True, # always return True
'errMsg': 'Invalid `allowNone` key'
}, },
'type': { 'type': {
'type': [any, type, list, dict],
'optional': False, # `type` is force require 'optional': False, # `type` is force require
'allowNone': False, # `type` couldn't be None 'allowNone': False, # `type` couldn't be None
'type': [any, type, list, dict],
'format': lambda x: x, # return same value
'filter': lambda b: True, # always return True
'errMsg': 'Invalid `type` key'
}, },
'multiSub': { 'multiSub': {
'type': bool,
'optional': True, # `multiSub` is not force require 'optional': True, # `multiSub` is not force require
'default': False, # disable `multiSub` option in default 'default': False, # disable `multiSub` option in default
'allowNone': False, # `multiSub` couldn't be None 'allowNone': False, # `multiSub` couldn't be None
'type': bool,
'format': lambda x: x, # return same value
'filter': lambda b: True, # always return True
'errMsg': 'Invalid `multiSub` key'
}, },
'indexKey': { 'indexKey': {
'type': str,
'optional': True, # `indexKey` is not force require 'optional': True, # `indexKey` is not force require
'default': 'type', 'default': 'type',
'allowNone': False, # `indexKey` couldn't be None 'allowNone': False, # `indexKey` couldn't be None
'type': str,
'format': lambda x: x, # return same value
'filter': lambda b: True, # always return True
'errMsg': 'Invalid `indexKey` key'
}, },
'format': { 'format': {
'type': any,
'optional': True, # `format` is not force require 'optional': True, # `format` is not force require
'default': lambda x: x, # don't change anything 'default': lambda x: x, # don't change anything
'allowNone': False, # `format` couldn't be None 'allowNone': False, # `format` couldn't be None
'type': any,
'format': lambda x: x, # return same value
'filter': lambda b: True, # always return True
'errMsg': 'Invalid `format` key'
}, },
'filter': { 'filter': {
'type': any,
'optional': True, # `filter` is not force require 'optional': True, # `filter` is not force require
'default': lambda x: True, # always pass filter 'default': lambda x: True, # always pass filter
'allowNone': False, # `filter` couldn't be None 'allowNone': False, # `filter` couldn't be None
'type': any,
'format': lambda x: x, # return same value
'filter': lambda b: True, # always return True
'errMsg': 'Invalid `filter` key'
}, },
'errMsg': { 'errMsg': {
'type': str,
'optional': True, # `errMsg` is not force require 'optional': True, # `errMsg` is not force require
'default': 'filter error', 'default': 'filter error',
'allowNone': False, # `errMsg` couldn't be None 'allowNone': False, # `errMsg` couldn't be None
'type': str, }
'format': lambda x: x, # return same value
'filter': lambda b: True, # always return True
'errMsg': 'Invalid `errMsg` key'
},
} }
for field in filterObject:
filterObject[field]['errMsg'] = 'Invalid `%s` key' % field
filterObject[field]['format'] = filterObject['format']['default'] # return same value
filterObject[field]['filter'] = filterObject['filter']['default'] # always return True
def Filter(raw: dict, rules: dict) -> dict:
if type(raw) != dict:
raise RuntimeError('Invalid input for filter')
data = {}
raw = copy.deepcopy(raw)
rules = copy.deepcopy(rules)
for key, rule in rules.items():
# pretreatment process (raw --[copy / default value]--> data)
if key not in raw: # key not exist
if not rule['optional']: # force require key not exist
raise RuntimeError('Miss `%s` field' % key)
data[key] = rule['default'] # set default value
else: # key exist
data[key] = raw[key]
# format process (data --[format]--> data)
try:
data[key] = rule['format'](data[key]) # run format
except:
raise RuntimeError(rule['errMsg']) # format error
if data[key] is None: # key content is None
if not rule['allowNone']: # key is not allow None
raise RuntimeError('Field `%s` shouldn\'t be None' % key)
continue # skip following process
# filter process (data --[type check (& filter check)]--> pass / non-pass)
if type(rule['type']) == type: # str / int / bool / ...
rule['type'] = [rule['type']] # str -> [str] / int -> [int] / ...
if type(rule['type']) == list: # [str, int, bool, ...]
if data[key] == any and any in rule['type']: # special case -> skip type filter
pass
elif type(data[key]) not in rule['type']: # type not in allow list
raise RuntimeError('Invalid `%s` field' % key)
elif type(rule['type']) == dict: # check subObject
if not rule['multiSub']: # single subObject
data[key] = Filter(data[key], rule['type'])
else: # multi subObject
# TODO: multi sub filter
pass
continue
elif rule['type'] != any: # type == any -> skip type filter
raise RuntimeError('Unknown `type` in rules')
if not rule['filter'](data[key]): # run filter
raise RuntimeError(rule['errMsg'])
return data
def rulesFilter(rules: dict) -> dict:
result = {}
for key, rule in rules.items(): # filter by basic rules
result[key] = Filter(rule, filterObject)
return result
filterObject = rulesFilter(filterObject) # format itself

12
Filter/Shadowsocks.py

@ -4,9 +4,10 @@
from Filter.Plugin import pluginFormat from Filter.Plugin import pluginFormat
from Basis.Functions import toInt, toStr from Basis.Functions import toInt, toStr
from Basis.Functions import isHost, isPort from Basis.Functions import isHost, isPort
from Basis.Filter import Filter, rulesFilter
from Basis.Constant import ssMethods, pluginClients from Basis.Constant import ssMethods, pluginClients
pluginObject = { pluginObject = rulesFilter({
'type': { 'type': {
'type': str, 'type': str,
'format': lambda s: pluginFormat(toStr(s).strip().lower()), 'format': lambda s: pluginFormat(toStr(s).strip().lower()),
@ -20,9 +21,9 @@ pluginObject = {
'format': toStr, 'format': toStr,
'errMsg': 'Invalid SIP003 param' 'errMsg': 'Invalid SIP003 param'
} }
} })
ssObject = { ssObject = rulesFilter({
'server': { 'server': {
'type': str, 'type': str,
'format': toStr, 'format': toStr,
@ -53,4 +54,7 @@ ssObject = {
'type': pluginObject, 'type': pluginObject,
'errMsg': 'Invalid pluginObject' 'errMsg': 'Invalid pluginObject'
} }
} })
from pprint import pprint
pprint(ssObject, sort_dicts = False)

7
demo.py

@ -0,0 +1,7 @@
#!/usr/bin/env python
import Filter.Shadowsocks
# from pprint import pprint
# from Basis.Filter import filterObject
# pprint(filterObject, sort_dicts = False)
Loading…
Cancel
Save