Skip to content

Commit 46d4091

Browse files
qianlongztJoeingarm
authored andcommitted
feat: support inline rule provider (MetaCubeX#1731)
1 parent 5dd971d commit 46d4091

File tree

4 files changed

+135
-31
lines changed

4 files changed

+135
-31
lines changed

constant/provider/interface.go

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const (
1313
File VehicleType = iota
1414
HTTP
1515
Compatible
16+
Inline
1617
)
1718

1819
// VehicleType defined
@@ -26,6 +27,8 @@ func (v VehicleType) String() string {
2627
return "HTTP"
2728
case Compatible:
2829
return "Compatible"
30+
case Inline:
31+
return "Inline"
2932
default:
3033
return "Unknown"
3134
}

docs/config.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,14 @@ rule-providers:
10141014
format: mrs
10151015
behavior: domain
10161016
path: /path/to/save/file.mrs
1017+
rule4:
1018+
type: inline
1019+
behavior: domain # classical / ipcidr
1020+
payload:
1021+
- '.blogger.com'
1022+
- '*.*.microsoft.com'
1023+
- 'books.itunes.apple.com'
1024+
10171025
rules:
10181026
- RULE-SET,rule1,REJECT
10191027
- IP-ASN,1,PROXY

rules/provider/parse.go

+14-9
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,20 @@ var (
1616
)
1717

1818
type ruleProviderSchema struct {
19-
Type string `provider:"type"`
20-
Behavior string `provider:"behavior"`
21-
Path string `provider:"path,omitempty"`
22-
URL string `provider:"url,omitempty"`
23-
Proxy string `provider:"proxy,omitempty"`
24-
Format string `provider:"format,omitempty"`
25-
Interval int `provider:"interval,omitempty"`
26-
SizeLimit int64 `provider:"size-limit,omitempty"`
19+
Type string `provider:"type"`
20+
Behavior string `provider:"behavior"`
21+
Path string `provider:"path,omitempty"`
22+
URL string `provider:"url,omitempty"`
23+
Proxy string `provider:"proxy,omitempty"`
24+
Format string `provider:"format,omitempty"`
25+
Interval int `provider:"interval,omitempty"`
26+
SizeLimit int64 `provider:"size-limit,omitempty"`
27+
Payload []string `provider:"payload,omitempty"`
2728
}
2829

29-
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) {
30+
type parseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)
31+
32+
func ParseRuleProvider(name string, mapping map[string]any, parse parseRuleFunc) (P.RuleProvider, error) {
3033
schema := &ruleProviderSchema{}
3134
decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true})
3235
if err := decoder.Decode(mapping, schema); err != nil {
@@ -55,6 +58,8 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t
5558
}
5659
}
5760
vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil, resource.DefaultHttpTimeout, schema.SizeLimit)
61+
case "inline":
62+
return newInlineProvider(name, behavior, schema.Payload, parse), nil
5863
default:
5964
return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type)
6065
}

rules/provider/provider.go

+110-22
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ func SetTunnel(t P.Tunnel) {
2424
}
2525

2626
type ruleSetProvider struct {
27+
ruleSetProviderBase
2728
*resource.Fetcher[ruleStrategy]
29+
format P.RuleFormat
30+
}
31+
32+
type ruleSetProviderBase struct {
2833
behavior P.RuleBehavior
29-
format P.RuleFormat
3034
strategy ruleStrategy
3135
}
3236

@@ -61,7 +65,7 @@ type mrsRuleStrategy interface {
6165
DumpMrs(f func(key string) bool)
6266
}
6367

64-
func (rp *ruleSetProvider) Type() P.ProviderType {
68+
func (rp *ruleSetProviderBase) Type() P.ProviderType {
6569
return P.Rule
6670
}
6771

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

78-
func (rp *ruleSetProvider) Behavior() P.RuleBehavior {
82+
func (rp *ruleSetProviderBase) Behavior() P.RuleBehavior {
7983
return rp.behavior
8084
}
8185

82-
func (rp *ruleSetProvider) Count() int {
86+
func (rp *ruleSetProviderBase) Count() int {
8387
return rp.strategy.Count()
8488
}
8589

86-
func (rp *ruleSetProvider) Match(metadata *C.Metadata) bool {
90+
func (rp *ruleSetProviderBase) Match(metadata *C.Metadata) bool {
8791
return rp.strategy != nil && rp.strategy.Match(metadata)
8892
}
8993

90-
func (rp *ruleSetProvider) ShouldResolveIP() bool {
94+
func (rp *ruleSetProviderBase) ShouldResolveIP() bool {
9195
return rp.strategy.ShouldResolveIP()
9296
}
9397

94-
func (rp *ruleSetProvider) ShouldFindProcess() bool {
98+
func (rp *ruleSetProviderBase) ShouldFindProcess() bool {
9599
return rp.strategy.ShouldFindProcess()
96100
}
97101

98-
func (rp *ruleSetProvider) Strategy() any {
102+
func (rp *ruleSetProviderBase) Strategy() any {
99103
return rp.strategy
100104
}
101105

106+
type providerForApi struct {
107+
Behavior string `json:"behavior"`
108+
Format string `json:"format"`
109+
Name string `json:"name"`
110+
RuleCount int `json:"ruleCount"`
111+
Type string `json:"type"`
112+
VehicleType string `json:"vehicleType"`
113+
UpdatedAt time.Time `json:"updatedAt"`
114+
Payload []string `json:"payload,omitempty"`
115+
}
116+
102117
func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) {
103118
return json.Marshal(
104-
map[string]interface{}{
105-
"behavior": rp.behavior.String(),
106-
"format": rp.format.String(),
107-
"name": rp.Name(),
108-
"ruleCount": rp.strategy.Count(),
109-
"type": rp.Type().String(),
110-
"updatedAt": rp.UpdatedAt(),
111-
"vehicleType": rp.VehicleType().String(),
119+
providerForApi{
120+
Behavior: rp.behavior.String(),
121+
Format: rp.format.String(),
122+
Name: rp.Fetcher.Name(),
123+
RuleCount: rp.strategy.Count(),
124+
Type: rp.Type().String(),
125+
UpdatedAt: rp.UpdatedAt(),
126+
VehicleType: rp.VehicleType().String(),
112127
})
113128
}
114129

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

120135
func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleFormat, interval time.Duration, vehicle P.Vehicle,
121-
parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) P.RuleProvider {
136+
parse parseRuleFunc,
137+
) P.RuleProvider {
122138
rp := &ruleSetProvider{
123-
behavior: behavior,
124-
format: format,
139+
ruleSetProviderBase: ruleSetProviderBase{
140+
behavior: behavior,
141+
},
142+
format: format,
125143
}
126144

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

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

161-
var ErrNoPayload = errors.New("file must have a `payload` field")
162-
var ErrInvalidFormat = errors.New("invalid format")
179+
var (
180+
ErrNoPayload = errors.New("file must have a `payload` field")
181+
ErrInvalidFormat = errors.New("invalid format")
182+
)
163183

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

255275
return strategy, nil
256276
}
277+
278+
func rulesParseInline(rs []string, strategy ruleStrategy) ruleStrategy {
279+
strategy.Reset()
280+
for _, r := range rs {
281+
if r != "" {
282+
strategy.Insert(r)
283+
}
284+
}
285+
strategy.FinishInsert()
286+
return strategy
287+
}
288+
289+
type inlineProvider struct {
290+
ruleSetProviderBase
291+
name string
292+
updateTime time.Time
293+
payload []string
294+
}
295+
296+
func (i *inlineProvider) Name() string {
297+
return i.name
298+
}
299+
300+
func (i *inlineProvider) Initial() error {
301+
return nil
302+
}
303+
304+
func (i *inlineProvider) Update() error {
305+
// make api update happy
306+
i.updateTime = time.Now()
307+
return nil
308+
}
309+
310+
func (i *inlineProvider) VehicleType() P.VehicleType {
311+
return P.Inline
312+
}
313+
314+
func (i *inlineProvider) MarshalJSON() ([]byte, error) {
315+
return json.Marshal(
316+
providerForApi{
317+
Behavior: i.behavior.String(),
318+
Name: i.Name(),
319+
RuleCount: i.strategy.Count(),
320+
Type: i.Type().String(),
321+
VehicleType: i.VehicleType().String(),
322+
UpdatedAt: i.updateTime,
323+
Payload: i.payload,
324+
})
325+
}
326+
327+
func newInlineProvider(
328+
name string,
329+
behavior P.RuleBehavior,
330+
payload []string,
331+
parse parseRuleFunc,
332+
) P.RuleProvider {
333+
rp := &inlineProvider{
334+
ruleSetProviderBase: ruleSetProviderBase{
335+
behavior: behavior,
336+
strategy: newStrategy(behavior, parse),
337+
},
338+
payload: payload,
339+
name: name,
340+
updateTime: time.Now(),
341+
}
342+
rp.strategy = rulesParseInline(payload, rp.strategy)
343+
return rp
344+
}

0 commit comments

Comments
 (0)