fix: bypass rules should not check first

This commit is contained in:
yusing 2025-01-09 18:17:05 +08:00
parent 8109c9ac4f
commit 4ebe0abba0

View file

@ -2,8 +2,6 @@ package rules
import (
"net/http"
"github.com/yusing/go-proxy/internal/logging"
)
type (
@ -48,76 +46,58 @@ type (
// BuildHandler returns a http.HandlerFunc that implements the rules.
//
// Bypass rules are executed first
// if a bypass rule matches,
// the request is passed to the upstream and no more rules are executed.
//
// Other rules are executed later
// if no rule matches, the default rule is executed
// if no rule matches and default rule is not set,
// the request is passed to the upstream.
func (rules Rules) BuildHandler(up http.Handler) http.HandlerFunc {
// move bypass rules to the front.
bypassRules := make(Rules, 0, len(rules))
otherRules := make(Rules, 0, len(rules))
var (
defaultRule Rule
defaultRuleIndex int
)
var defaultRule Rule
for _, rule := range rules {
switch {
case rule.Do.isBypass():
bypassRules = append(bypassRules, rule)
case rule.Name == "default":
for i, rule := range rules {
if rule.Name == "default" {
defaultRule = rule
default:
otherRules = append(otherRules, rule)
defaultRuleIndex = i
break
}
}
rules = append(rules[:defaultRuleIndex], rules[defaultRuleIndex+1:]...)
// free allocated empty slices
// before encapsulating them into the handlerFunc.
if len(bypassRules) == 0 {
bypassRules = []Rule{}
}
if len(otherRules) == 0 {
otherRules = []Rule{}
if len(rules) == 0 {
if defaultRule.Do.isBypass() {
return up.ServeHTTP
}
rules = []Rule{}
}
return func(w http.ResponseWriter, r *http.Request) {
for _, rule := range bypassRules {
if rule.On.check(r) {
logging.Debug().
Str("rule", rule.Name).
Msg("matched: bypass")
up.ServeHTTP(w, r)
return
}
}
hasMatch := false
for _, rule := range otherRules {
for _, rule := range rules {
if rule.On.check(r) {
logging.Debug().
Str("rule", rule.Name).
Msgf("matched proceed=%t", rule.Do.exec.proceed)
hasMatch = true
if rule.Do.isBypass() {
up.ServeHTTP(w, r)
return
}
rule.Do.exec.HandlerFunc(w, r)
if !rule.Do.exec.proceed {
return
}
hasMatch = true
}
}
if hasMatch || defaultRule.Do.isBypass() {
logging.Debug().
Str("rule", defaultRule.Name).
Msg("matched: bypass")
up.ServeHTTP(w, r)
return
}
logging.Debug().
Str("rule", defaultRule.Name).
Msg("matched: default")
defaultRule.Do.exec.HandlerFunc(w, r)
}
}