Add str and fromStr for every condition type.

This commit is contained in:
FelisCatus 2015-02-08 20:53:13 +08:00
parent 6e7b5150c1
commit 56457519ab
2 changed files with 179 additions and 14 deletions

View File

@ -34,6 +34,54 @@ module.exports = exports =
return cache.compiled if cache.compiled return cache.compiled if cache.compiled
handler = exports._handler(condition.conditionType) handler = exports._handler(condition.conditionType)
cache.compiled = handler.compile.call(exports, condition, cache) cache.compiled = handler.compile.call(exports, condition, cache)
str: (condition, {abbr} = {abbr: -1}) ->
handler = exports._handler(condition.conditionType)
if handler.abbrs[0].length == 0
endCode = condition.pattern.charCodeAt(condition.pattern.length - 1)
if endCode != exports.colonCharCode and condition.pattern.indexOf(' ') < 0
return condition.pattern
str = handler.str
typeStr =
if typeof abbr == 'number'
handler.abbrs[(handler.abbrs.length + abbr) % handler.abbrs.length]
else
condition.conditionType
result = typeStr + ':'
part = if str then str.call(exports, condition) else condition.pattern
result += ' ' + part if part
return result
colonCharCode: ':'.charCodeAt(0)
fromStr: (str) ->
str = str.trim()
i = str.indexOf(' ')
i = str.length if i < 0
if str.charCodeAt(i - 1) == exports.colonCharCode
conditionType = str.substr(0, i - 1)
str = str.substr(i + 1).trim()
else
conditionType = ''
conditionType = exports.typeFromAbbr(conditionType)
return null unless conditionType
condition = {conditionType: conditionType}
fromStr = exports._handler(condition.conditionType).fromStr
if fromStr
return fromStr.call(exports, str, condition)
else
condition.pattern = str
return condition
_abbrs: null
typeFromAbbr: (abbr) ->
if not exports._abbrs
exports._abbrs = {}
for own type, {abbrs} of exports._conditionTypes
exports._abbrs[type.toUpperCase()] = type
for ab in abbrs
exports._abbrs[ab.toUpperCase()] = type
return exports._abbrs[abbr.toUpperCase()]
comment: (comment, node) -> comment: (comment, node) ->
return unless comment return unless comment
@ -148,8 +196,11 @@ module.exports = exports =
localHosts: ["127.0.0.1", "[::1]", "localhost"] localHosts: ["127.0.0.1", "[::1]", "localhost"]
_condCache: new AttachedCache (condition) -> _condCache: new AttachedCache (condition) ->
condition.conditionType + '$' + tag = exports._handler(condition.conditionType).tag
exports._handler(condition.conditionType).tag.apply(exports, arguments) result =
if tag then tag.apply(exports, arguments) else exports.str(condition)
condition.conditionType + '$' + result
_setProp: (obj, prop, value) -> _setProp: (obj, prop, value) ->
if not Object::hasOwnProperty.call obj, prop if not Object::hasOwnProperty.call obj, prop
@ -169,17 +220,25 @@ module.exports = exports =
# These functions are .call()-ed with `this` set to module.exports. # These functions are .call()-ed with `this` set to module.exports.
# coffeelint: disable=missing_fat_arrows # coffeelint: disable=missing_fat_arrows
'TrueCondition': 'TrueCondition':
tag: (condition) -> '' abbrs: ['True']
analyze: (condition) -> null analyze: (condition) -> null
match: -> true match: -> true
compile: (condition) -> new U2.AST_True compile: (condition) -> new U2.AST_True
str: (condition) -> ''
fromStr: (str, condition) -> condition
'FalseCondition': 'FalseCondition':
tag: (condition) -> '' abbrs: ['False', 'Disabled']
analyze: (condition) -> null analyze: (condition) -> null
match: -> false match: -> false
compile: (condition) -> new U2.AST_False compile: (condition) -> new U2.AST_False
fromStr: (str, condition) ->
if str.length > 0
condition.pattern = str
condition
'UrlRegexCondition': 'UrlRegexCondition':
tag: (condition) -> condition.pattern abbrs: ['UR', 'URegex', 'UrlR', 'UrlRegex']
analyze: (condition) -> @safeRegex escapeSlash condition.pattern analyze: (condition) -> @safeRegex escapeSlash condition.pattern
match: (condition, request, cache) -> match: (condition, request, cache) ->
return cache.analyzed.test(request.url) return cache.analyzed.test(request.url)
@ -187,7 +246,8 @@ module.exports = exports =
@regTest 'url', cache.analyzed @regTest 'url', cache.analyzed
'UrlWildcardCondition': 'UrlWildcardCondition':
tag: (condition) -> condition.pattern abbrs: ['U', 'UW', 'Url', 'UrlW', 'UWild', 'UWildcard', 'UrlWild',
'UrlWildcard']
analyze: (condition) -> analyze: (condition) ->
parts = for pattern in condition.pattern.split('|') when pattern parts = for pattern in condition.pattern.split('|') when pattern
shExp2RegExp pattern, trimAsterisk: true shExp2RegExp pattern, trimAsterisk: true
@ -198,7 +258,7 @@ module.exports = exports =
@regTest 'url', cache.analyzed @regTest 'url', cache.analyzed
'HostRegexCondition': 'HostRegexCondition':
tag: (condition) -> condition.pattern abbrs: ['R', 'HR', 'Regex', 'HostR', 'HRegex', 'HostRegex']
analyze: (condition) -> @safeRegex escapeSlash condition.pattern analyze: (condition) -> @safeRegex escapeSlash condition.pattern
match: (condition, request, cache) -> match: (condition, request, cache) ->
return cache.analyzed.test(request.host) return cache.analyzed.test(request.host)
@ -206,7 +266,8 @@ module.exports = exports =
@regTest 'host', cache.analyzed @regTest 'host', cache.analyzed
'HostWildcardCondition': 'HostWildcardCondition':
tag: (condition) -> condition.pattern abbrs: ['', 'H', 'W', 'HW', 'Wild', 'Wildcard', 'Host', 'HostW', 'HWild',
'HWildcard', 'HostWild', 'HostWildcard']
analyze: (condition) -> analyze: (condition) ->
parts = for pattern in condition.pattern.split('|') when pattern parts = for pattern in condition.pattern.split('|') when pattern
# Get the magical regex of this pattern. See # Get the magical regex of this pattern. See
@ -229,7 +290,7 @@ module.exports = exports =
@regTest 'host', cache.analyzed @regTest 'host', cache.analyzed
'BypassCondition': 'BypassCondition':
tag: (condition) -> condition.pattern abbrs: ['B', 'Bypass']
analyze: (condition) -> analyze: (condition) ->
# See https://developer.chrome.com/extensions/proxy#bypass_list # See https://developer.chrome.com/extensions/proxy#bypass_list
cache = cache =
@ -335,7 +396,7 @@ module.exports = exports =
right: conditions[1] right: conditions[1]
) )
'KeywordCondition': 'KeywordCondition':
tag: (condition) -> condition.pattern abbrs: ['K', 'KW', 'Keyword']
analyze: (condition) -> null analyze: (condition) -> null
match: (condition, request) -> match: (condition, request) ->
request.scheme == 'http' and request.url.indexOf(condition.pattern) >= 0 request.scheme == 'http' and request.url.indexOf(condition.pattern) >= 0
@ -361,7 +422,7 @@ module.exports = exports =
) )
'IpCondition': 'IpCondition':
tag: (condition) -> condition.ip + '/' + condition.prefixLength abbrs: ['Ip']
analyze: (condition) -> analyze: (condition) ->
cache = cache =
addr: null addr: null
@ -429,8 +490,16 @@ module.exports = exports =
consequent: isInNetExCall consequent: isInNetExCall
alternative: alternative alternative: alternative
) )
str: (condition) -> condition.ip + '/' + condition.prefixLength
fromStr: (str, condition) ->
[ip, prefixLength] = str.split('/')
condition.ip = ip
condition.prefixLength = parseInt(prefixLength)
condition
'HostLevelsCondition': 'HostLevelsCondition':
tag: (condition) -> condition.minValue + '~' + condition.maxValue abbrs: ['Lv', 'Level', 'Levels', 'HL', 'HLv', 'HLevel', 'HLevels',
'HostL', 'HostLv', 'HostLevel', 'HostLevels']
analyze: (condition) -> '.'.charCodeAt 0 analyze: (condition) -> '.'.charCodeAt 0
match: (condition, request, cache) -> match: (condition, request, cache) ->
dotCharCode = cache.analyzed dotCharCode = cache.analyzed
@ -453,8 +522,15 @@ module.exports = exports =
) )
@between(val, condition.minValue + 1, condition.maxValue + 1, @between(val, condition.minValue + 1, condition.maxValue + 1,
"#{condition.minValue} <= hostLevels <= #{condition.maxValue}") "#{condition.minValue} <= hostLevels <= #{condition.maxValue}")
str: (condition) -> condition.minValue + '~' + condition.maxValue
fromStr: (str, condition) ->
[minValue, maxValue] = str.split('~')
condition.minValue = minValue
condition.maxValue = maxValue
condition
'WeekdayCondition': 'WeekdayCondition':
tag: (condition) -> condition.startDay + '~' + condition.endDay abbrs: ['WD', 'Week', 'Day', 'Weekday']
analyze: (condition) -> null analyze: (condition) -> null
match: (condition, request) -> match: (condition, request) ->
day = new Date().getDay() day = new Date().getDay()
@ -471,8 +547,14 @@ module.exports = exports =
) )
) )
@between val, condition.startDay, condition.endDay @between val, condition.startDay, condition.endDay
str: (condition) -> condition.startDay + '~' + condition.endDay
fromStr: (str, condition) ->
[startDay, endDay] = str.split('~')
condition.startDay = startDay
condition.endDay = endDay
condition
'TimeCondition': 'TimeCondition':
tag: (condition) -> condition.startHour + '~' + condition.endHour abbrs: ['T', 'Time', 'Hour']
analyze: (condition) -> null analyze: (condition) -> null
match: (condition, request) -> match: (condition, request) ->
hour = new Date().getHours() hour = new Date().getHours()
@ -489,4 +571,10 @@ module.exports = exports =
) )
) )
@between val, condition.startHour, condition.endHour @between val, condition.startHour, condition.endHour
str: (condition) -> condition.startHour + '~' + condition.endHour
fromStr: (str, condition) ->
[startHour, endHour] = str.split('~')
condition.startHour = startHour
condition.endHour = endHour
condition
# coffeelint: enable=missing_fat_arrows # coffeelint: enable=missing_fat_arrows

View File

@ -274,3 +274,80 @@ describe 'Conditions', ->
it 'should not match HTTPS requests', -> it 'should not match HTTPS requests', ->
testCond(cond, 'https://example.com/', not 'match') testCond(cond, 'https://example.com/', not 'match')
testCond(cond, 'https://example.net/', not 'match') testCond(cond, 'https://example.net/', not 'match')
describe '#typeFromAbbr', ->
it 'should get condition types by abbrs', ->
Conditions.typeFromAbbr('True').should.equal('TrueCondition')
Conditions.typeFromAbbr('HR').should.equal('HostRegexCondition')
describe '#str and #fromStr', ->
it 'should encode & decode TrueCondition correctly', ->
condition =
conditionType: 'TrueCondition'
result = Conditions.str(condition)
result.should.equal('True:')
cond = Conditions.fromStr(result)
cond.should.eql(condition)
it 'should encode & decode conditions with pattern correctly', ->
condition =
conditionType: 'UrlWildcardCondition'
pattern: '*://*.example.com/*'
result = Conditions.str(condition)
result.should.equal('UrlWildcard: ' + condition.pattern)
cond = Conditions.fromStr(result)
cond.should.eql(condition)
it 'should encode & decode False while preserving pattern', ->
condition =
conditionType: 'FalseCondition'
pattern: 'a b c'
result = Conditions.str(condition)
result.should.equal('Disabled: a b c')
cond = Conditions.fromStr(result)
cond.should.eql(condition)
it 'should encode & decode FalseCondition without any pattern', ->
condition =
conditionType: 'FalseCondition'
result = Conditions.str(condition)
result.should.equal('Disabled:')
cond = Conditions.fromStr(result)
cond.should.eql(condition)
it 'should encode & decode HostWildcardCondition using shorthand syntax', ->
condition =
conditionType: 'HostWildcardCondition'
pattern: '*.example.com'
result = Conditions.str(condition)
result.should.equal(condition.pattern)
cond = Conditions.fromStr(result)
cond.should.eql(condition)
it 'should encode & decode HostWildcardCondition ending with colon', ->
condition =
conditionType: 'HostWildcardCondition'
pattern: 'bogus:'
result = Conditions.str(condition)
result.should.equal('HostWildcard: ' + condition.pattern)
cond = Conditions.fromStr(result)
cond.should.eql(condition)
it 'should encode & decode IpCondition correctly', ->
condition =
conditionType: 'IpCondition'
ip: '127.0.0.1'
prefixLength: 16
result = Conditions.str(condition)
result.should.equal('Ip: 127.0.0.1/16')
cond = Conditions.fromStr(result)
cond.should.eql(condition)
it 'should parse conditions with extra spaces correctly', ->
Conditions.fromStr('url: *abcde* ').should.eql({
conditionType: 'UrlWildcardCondition'
pattern: '*abcde*'
})
it 'should parse abbreviated condition types correctly', ->
Conditions.fromStr('url: *://*.example.com/*').should.eql({
conditionType: 'UrlWildcardCondition'
pattern: '*://*.example.com/*'
})
it 'should parse escaped HostWildcardCondition starting with colon', ->
Conditions.fromStr(': :bogus:').should.eql({
conditionType: 'HostWildcardCondition'
pattern: ':bogus:'
})