pkg/model/router.go (111 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package model import ( stdHttp "net/http" "regexp" "strings" ) import ( "github.com/pkg/errors" ) import ( "github.com/apache/dubbo-go-pixiu/pkg/common/router/trie" "github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil" ) // Router struct type ( Router struct { ID string `yaml:"id" json:"id" mapstructure:"id"` Match RouterMatch `yaml:"match" json:"match" mapstructure:"match"` Route RouteAction `yaml:"route" json:"route" mapstructure:"route"` } // RouterMatch RouterMatch struct { Prefix string `yaml:"prefix" json:"prefix" mapstructure:"prefix"` Path string `yaml:"path" json:"path" mapstructure:"path"` // Regex string `yaml:"regex" json:"regex" mapstructure:"regex"` TODO: next version Methods []string `yaml:"methods" json:"methods" mapstructure:"methods"` Headers []HeaderMatcher `yaml:"headers,omitempty" json:"headers,omitempty" mapstructure:"headers"` // pathRE *regexp.Regexp } // RouteAction match route should do RouteAction struct { Cluster string `yaml:"cluster" json:"cluster" mapstructure:"cluster"` ClusterNotFoundResponseCode int `yaml:"cluster_not_found_response_code" json:"cluster_not_found_response_code" mapstructure:"cluster_not_found_response_code"` } // RouteConfiguration RouteConfiguration struct { RouteTrie trie.Trie `yaml:"-" json:"-" mapstructure:"-"` Routes []*Router `yaml:"routes" json:"routes" mapstructure:"routes"` Dynamic bool `yaml:"dynamic" json:"dynamic" mapstructure:"dynamic"` } // HeaderMatcher include Name header key, Values header value, Regex regex value HeaderMatcher struct { Name string `yaml:"name" json:"name" mapstructure:"name"` Values []string `yaml:"values" json:"values" mapstructure:"values"` Regex bool `yaml:"regex" json:"regex" mapstructure:"regex"` valueRE *regexp.Regexp } ) func NewRouterMatchPrefix(name string) RouterMatch { return RouterMatch{Prefix: "/" + name + "/"} } func (rc *RouteConfiguration) RouteByPathAndMethod(path, method string) (*RouteAction, error) { if rc.RouteTrie.IsEmpty() { return nil, errors.Errorf("router configuration is empty") } node, _, _ := rc.RouteTrie.Match(stringutil.GetTrieKey(method, path)) if node == nil { return nil, errors.Errorf("route failed for %s, no rules matched.", stringutil.GetTrieKey(method, path)) } if node.GetBizInfo() == nil { return nil, errors.Errorf("action is nil. please check your configuration.") } ret := (node.GetBizInfo()).(RouteAction) return &ret, nil } func (rc *RouteConfiguration) Route(req *stdHttp.Request) (*RouteAction, error) { return rc.RouteByPathAndMethod(req.URL.Path, req.Method) } // MatchHeader used when there's only headers to match func (rm *RouterMatch) MatchHeader(req *stdHttp.Request) bool { if len(rm.Methods) > 0 { for _, method := range rm.Methods { if method == req.Method { goto HEADER } } return false } HEADER: for _, header := range rm.Headers { if val := req.Header.Get(header.Name); len(val) > 0 { if header.MatchValues(val) { return true } } } return false } // MatchValues match values in header, including regex type func (hm *HeaderMatcher) MatchValues(dst string) bool { if hm.Regex && hm.valueRE != nil { return hm.valueRE.MatchString(dst) } for _, src := range hm.Values { if src == dst { return true } } return false } // SetValueRegex compile the regex, disable regex if it failed func (hm *HeaderMatcher) SetValueRegex(regex string) error { r, err := regexp.Compile(regex) if err == nil { hm.valueRE = r return nil } hm.Regex = false return err } func (r *Router) String() string { var builder strings.Builder builder.WriteString("[" + strings.Join(r.Match.Methods, ",") + "] ") if r.Match.Prefix != "" { builder.WriteString("prefix " + r.Match.Prefix) } else { builder.WriteString("path " + r.Match.Path) } builder.WriteString(" to cluster " + r.Route.Cluster) return builder.String() }