Skip to content

Commit

Permalink
feat(rule): support inline rule provider
Browse files Browse the repository at this point in the history
  • Loading branch information
qianlongzt committed Dec 18, 2024
1 parent 3f6823b commit e139331
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 31 deletions.
3 changes: 3 additions & 0 deletions constant/provider/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const (
File VehicleType = iota
HTTP
Compatible
Inline
)

// VehicleType defined
Expand All @@ -26,6 +27,8 @@ func (v VehicleType) String() string {
return "HTTP"
case Compatible:
return "Compatible"
case Inline:
return "Inline"
default:
return "Unknown"
}
Expand Down
8 changes: 8 additions & 0 deletions docs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,14 @@ rule-providers:
format: mrs
behavior: domain
path: /path/to/save/file.mrs
rule4:
type: inline
behavior: domain # classical / ipcidr
payload:
- '.blogger.com'
- '*.*.microsoft.com'
- 'books.itunes.apple.com'

rules:
- RULE-SET,rule1,REJECT
- IP-ASN,1,PROXY
Expand Down
23 changes: 14 additions & 9 deletions rules/provider/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@ var (
)

type ruleProviderSchema struct {
Type string `provider:"type"`
Behavior string `provider:"behavior"`
Path string `provider:"path,omitempty"`
URL string `provider:"url,omitempty"`
Proxy string `provider:"proxy,omitempty"`
Format string `provider:"format,omitempty"`
Interval int `provider:"interval,omitempty"`
SizeLimit int64 `provider:"size-limit,omitempty"`
Type string `provider:"type"`
Behavior string `provider:"behavior"`
Path string `provider:"path,omitempty"`
URL string `provider:"url,omitempty"`
Proxy string `provider:"proxy,omitempty"`
Format string `provider:"format,omitempty"`
Interval int `provider:"interval,omitempty"`
SizeLimit int64 `provider:"size-limit,omitempty"`
Payload []string `provider:"payload,omitempty"`
}

func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (P.RuleProvider, error) {
type parseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)

func ParseRuleProvider(name string, mapping map[string]any, parse parseRuleFunc) (P.RuleProvider, error) {
schema := &ruleProviderSchema{}
decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true})
if err := decoder.Decode(mapping, schema); err != nil {
Expand Down Expand Up @@ -55,6 +58,8 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t
}
}
vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil, resource.DefaultHttpTimeout, schema.SizeLimit)
case "inline":
return newInlineProvider(name, behavior, schema.Payload, parse), nil
default:
return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type)
}
Expand Down
132 changes: 110 additions & 22 deletions rules/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ func SetTunnel(t P.Tunnel) {
}

type ruleSetProvider struct {
ruleSetProviderBase
*resource.Fetcher[ruleStrategy]
format P.RuleFormat
}

type ruleSetProviderBase struct {
behavior P.RuleBehavior
format P.RuleFormat
strategy ruleStrategy
}

Expand Down Expand Up @@ -61,7 +65,7 @@ type mrsRuleStrategy interface {
DumpMrs(f func(key string) bool)
}

func (rp *ruleSetProvider) Type() P.ProviderType {
func (rp *ruleSetProviderBase) Type() P.ProviderType {
return P.Rule
}

Expand All @@ -75,40 +79,51 @@ func (rp *ruleSetProvider) Update() error {
return err
}

func (rp *ruleSetProvider) Behavior() P.RuleBehavior {
func (rp *ruleSetProviderBase) Behavior() P.RuleBehavior {
return rp.behavior
}

func (rp *ruleSetProvider) Count() int {
func (rp *ruleSetProviderBase) Count() int {
return rp.strategy.Count()
}

func (rp *ruleSetProvider) Match(metadata *C.Metadata) bool {
func (rp *ruleSetProviderBase) Match(metadata *C.Metadata) bool {
return rp.strategy != nil && rp.strategy.Match(metadata)
}

func (rp *ruleSetProvider) ShouldResolveIP() bool {
func (rp *ruleSetProviderBase) ShouldResolveIP() bool {
return rp.strategy.ShouldResolveIP()
}

func (rp *ruleSetProvider) ShouldFindProcess() bool {
func (rp *ruleSetProviderBase) ShouldFindProcess() bool {
return rp.strategy.ShouldFindProcess()
}

func (rp *ruleSetProvider) Strategy() any {
func (rp *ruleSetProviderBase) Strategy() any {
return rp.strategy
}

type providerForApi struct {
Behavior string `json:"behavior"`
Format string `json:"format"`
Name string `json:"name"`
RuleCount int `json:"ruleCount"`
Type string `json:"type"`
VehicleType string `json:"vehicleType"`
UpdatedAt time.Time `json:"updatedAt"`
Payload []string `json:"payload,omitempty"`
}

func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) {
return json.Marshal(
map[string]interface{}{
"behavior": rp.behavior.String(),
"format": rp.format.String(),
"name": rp.Name(),
"ruleCount": rp.strategy.Count(),
"type": rp.Type().String(),
"updatedAt": rp.UpdatedAt(),
"vehicleType": rp.VehicleType().String(),
providerForApi{
Behavior: rp.behavior.String(),
Format: rp.format.String(),
Name: rp.Fetcher.Name(),
RuleCount: rp.strategy.Count(),
Type: rp.Type().String(),
UpdatedAt: rp.UpdatedAt(),
VehicleType: rp.VehicleType().String(),
})
}

Expand All @@ -118,10 +133,13 @@ func (rp *RuleSetProvider) Close() error {
}

func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleFormat, interval time.Duration, vehicle P.Vehicle,
parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) P.RuleProvider {
parse parseRuleFunc,
) P.RuleProvider {
rp := &ruleSetProvider{
behavior: behavior,
format: format,
ruleSetProviderBase: ruleSetProviderBase{
behavior: behavior,
},
format: format,
}

onUpdate := func(strategy ruleStrategy) {
Expand All @@ -142,7 +160,7 @@ func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleForma
return wrapper
}

func newStrategy(behavior P.RuleBehavior, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) ruleStrategy {
func newStrategy(behavior P.RuleBehavior, parse parseRuleFunc) ruleStrategy {
switch behavior {
case P.Domain:
strategy := NewDomainStrategy()
Expand All @@ -158,8 +176,10 @@ func newStrategy(behavior P.RuleBehavior, parse func(tp, payload, target string,
}
}

var ErrNoPayload = errors.New("file must have a `payload` field")
var ErrInvalidFormat = errors.New("invalid format")
var (
ErrNoPayload = errors.New("file must have a `payload` field")
ErrInvalidFormat = errors.New("invalid format")
)

func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStrategy, error) {
strategy.Reset()
Expand Down Expand Up @@ -254,3 +274,71 @@ func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStr

return strategy, nil
}

func rulesParseInline(rs []string, strategy ruleStrategy) ruleStrategy {
strategy.Reset()
for _, r := range rs {
if r != "" {
strategy.Insert(r)
}
}
strategy.FinishInsert()
return strategy
}

type inlineProvider struct {
ruleSetProviderBase
name string
updateTime time.Time
payload []string
}

func (i *inlineProvider) Name() string {
return i.name
}

func (i *inlineProvider) Initial() error {
return nil
}

func (i *inlineProvider) Update() error {
// make api update happy
i.updateTime = time.Now()
return nil
}

func (i *inlineProvider) VehicleType() P.VehicleType {
return P.Inline
}

func (i *inlineProvider) MarshalJSON() ([]byte, error) {
return json.Marshal(
providerForApi{
Behavior: i.behavior.String(),
Name: i.Name(),
RuleCount: i.strategy.Count(),
Type: i.Type().String(),
VehicleType: i.VehicleType().String(),
UpdatedAt: i.updateTime,
Payload: i.payload,
})
}

func newInlineProvider(
name string,
behavior P.RuleBehavior,
payload []string,
parse parseRuleFunc,
) P.RuleProvider {
rp := &inlineProvider{
ruleSetProviderBase: ruleSetProviderBase{
behavior: behavior,
strategy: newStrategy(behavior, parse),
},
payload: payload,
name: name,
updateTime: time.Now(),
}
rp.strategy = rulesParseInline(payload, rp.strategy)
return rp
}

0 comments on commit e139331

Please sign in to comment.