diff --git a/internal/net/http/serve_mux.go b/internal/net/http/serve_mux.go new file mode 100644 index 0000000..863c8d9 --- /dev/null +++ b/internal/net/http/serve_mux.go @@ -0,0 +1,21 @@ +package http + +import "net/http" + +type ServeMux struct { + *http.ServeMux +} + +func NewServeMux() ServeMux { + return ServeMux{http.NewServeMux()} +} + +func (mux ServeMux) HandleFunc(pattern string, handler http.HandlerFunc) (err error) { + defer func() { + if r := recover(); r != nil { + err = r.(error) + } + }() + mux.ServeMux.HandleFunc(pattern, handler) + return +} diff --git a/internal/route/entry/reverse_proxy.go b/internal/route/entry/reverse_proxy.go index 4f8a8a8..67caaaf 100644 --- a/internal/route/entry/reverse_proxy.go +++ b/internal/route/entry/reverse_proxy.go @@ -20,7 +20,7 @@ type ReverseProxyEntry struct { // real model after validation Scheme route.Scheme `json:"scheme"` URL net.URL `json:"url"` NoTLSVerify bool `json:"no_tls_verify,omitempty"` - PathPatterns route.PathPatterns `json:"path_patterns,omitempty"` + PathPatterns []string `json:"path_patterns,omitempty"` HealthCheck *health.HealthCheckConfig `json:"healthcheck,omitempty"` LoadBalance *loadbalance.Config `json:"load_balance,omitempty"` Middlewares map[string]docker.LabelMap `json:"middlewares,omitempty"` @@ -66,7 +66,6 @@ func validateRPEntry(m *route.RawEntry, s route.Scheme, errs *E.Builder) *Revers host := E.Collect(errs, route.ValidateHost, m.Host) port := E.Collect(errs, route.ValidatePort, m.Port) - pathPats := E.Collect(errs, route.ValidatePathPatterns, m.PathPatterns) url := E.Collect(errs, url.Parse, fmt.Sprintf("%s://%s:%d", s, host, port)) iwCfg := E.Collect(errs, idlewatcher.ValidateConfig, cont) @@ -80,7 +79,7 @@ func validateRPEntry(m *route.RawEntry, s route.Scheme, errs *E.Builder) *Revers Scheme: s, URL: net.NewURL(url), NoTLSVerify: m.NoTLSVerify, - PathPatterns: pathPats, + PathPatterns: m.PathPatterns, HealthCheck: m.HealthCheck, LoadBalance: lb, Middlewares: m.Middlewares, diff --git a/internal/route/http.go b/internal/route/http.go index d470b51..f98d157 100755 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -102,9 +102,13 @@ func (r *HTTPRoute) Start(providerSubtask task.Task) E.Error { case len(r.PathPatterns) == 1 && r.PathPatterns[0] == "/": r.handler = r.rp default: - mux := http.NewServeMux() + mux := gphttp.NewServeMux() + patErrs := E.NewBuilder("invalid path pattern(s)") for _, p := range r.PathPatterns { - mux.HandleFunc(string(p), r.rp.HandlerFunc) + patErrs.Add(mux.HandleFunc(p, r.rp.HandlerFunc)) + } + if err := patErrs.Error(); err != nil { + return err } r.handler = mux } diff --git a/internal/route/types/path_pattern.go b/internal/route/types/path_pattern.go deleted file mode 100644 index b1f412d..0000000 --- a/internal/route/types/path_pattern.go +++ /dev/null @@ -1,48 +0,0 @@ -package types - -import ( - "errors" - "fmt" - "regexp" - - E "github.com/yusing/go-proxy/internal/error" -) - -type ( - PathPattern string - PathPatterns = []PathPattern -) - -var pathPattern = regexp.MustCompile(`^(/[-\w./]*({\$\})?|((GET|POST|DELETE|PUT|HEAD|OPTION) /[-\w./]*({\$\})?))$`) - -var ( - ErrEmptyPathPattern = errors.New("path must not be empty") - ErrInvalidPathPattern = errors.New("invalid path pattern") -) - -func ValidatePathPattern(s string) (PathPattern, error) { - if len(s) == 0 { - return "", ErrEmptyPathPattern - } - if !pathPattern.MatchString(s) { - return "", fmt.Errorf("%w %q", ErrInvalidPathPattern, s) - } - return PathPattern(s), nil -} - -func ValidatePathPatterns(s []string) (PathPatterns, E.Error) { - if len(s) == 0 { - return nil, nil - } - errs := E.NewBuilder("invalid path patterns") - pp := make(PathPatterns, len(s)) - for i, v := range s { - pattern, err := ValidatePathPattern(v) - if err != nil { - errs.Add(err) - } else { - pp[i] = pattern - } - } - return pp, errs.Error() -} diff --git a/internal/route/types/path_pattern_test.go b/internal/route/types/path_pattern_test.go deleted file mode 100644 index 0285e7e..0000000 --- a/internal/route/types/path_pattern_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package types - -import ( - "errors" - "testing" - - U "github.com/yusing/go-proxy/internal/utils/testing" -) - -var validPatterns = []string{ - "/", - "/index.html", - "/somepage/", - "/drive/abc.mp4", - "/{$}", - "/some-page/{$}", - "GET /", - "GET /static/{$}", - "GET /drive/abc.mp4", - "GET /drive/abc.mp4/{$}", - "POST /auth", - "DELETE /user/", - "PUT /storage/id/", -} - -var invalidPatterns = []string{ - "/$", - "/{$}{$}", - "/{$}/{$}", - "/index.html$", - "get /", - "GET/", - "GET /$", - "GET /drive/{$}/abc.mp4/", - "OPTION /config/{$}/abc.conf/{$}", -} - -func TestPathPatternRegex(t *testing.T) { - for _, pattern := range validPatterns { - _, err := ValidatePathPattern(pattern) - U.ExpectNoError(t, err) - } - for _, pattern := range invalidPatterns { - _, err := ValidatePathPattern(pattern) - U.ExpectTrue(t, errors.Is(err, ErrInvalidPathPattern)) - } -}