github.com/xmidt-org/webpa-common@v1.11.9/xhttp/fanout/endpoints.go (about) 1 package fanout 2 3 import ( 4 "errors" 5 "net/http" 6 "net/url" 7 8 "github.com/xmidt-org/webpa-common/xhttp" 9 ) 10 11 var ( 12 errNoConfiguredEndpoints = errors.New("No configured endpoints") 13 ) 14 15 // Endpoints is a strategy interface for determining the set of HTTP URL endpoints that a fanout 16 // should use. 17 type Endpoints interface { 18 // FanoutURLs determines the URLs that an original request should be dispatched 19 // to as part of a fanout. Each returned URL will be associated with a single http.Request 20 // object and transaction. 21 FanoutURLs(*http.Request) ([]*url.URL, error) 22 } 23 24 type EndpointsFunc func(*http.Request) ([]*url.URL, error) 25 26 func (ef EndpointsFunc) FanoutURLs(original *http.Request) ([]*url.URL, error) { 27 return ef(original) 28 } 29 30 // MustFanoutURLs invokes FanoutURLs on the given Endpoints instance, and panics if there's an error. 31 func MustFanoutURLs(e Endpoints, original *http.Request) []*url.URL { 32 endpointURLs, err := e.FanoutURLs(original) 33 if err != nil { 34 panic(err) 35 } 36 37 return endpointURLs 38 } 39 40 // FixedEndpoints represents a set of URLs that act as base URLs for a fanout. 41 type FixedEndpoints []*url.URL 42 43 // ParseURLs parses each URL to produce a FixedEndpoints. Each supplied URL should have a scheme 44 // instead of being abbreviated, e.g. "http://hostname" or "http://hostname:1234" instead of "hostname" or "hostname:1234" 45 func ParseURLs(values ...string) (FixedEndpoints, error) { 46 urls, err := xhttp.ApplyURLParser(url.Parse, values...) 47 if err != nil { 48 return nil, err 49 } 50 51 return FixedEndpoints(urls), nil 52 } 53 54 // MustParseURLs is like ParseURLs, except that it panics instead of returning an error. 55 func MustParseURLs(urls ...string) FixedEndpoints { 56 fe, err := ParseURLs(urls...) 57 if err != nil { 58 panic(err) 59 } 60 61 return fe 62 } 63 64 func (fe FixedEndpoints) FanoutURLs(original *http.Request) ([]*url.URL, error) { 65 endpoints := make([]*url.URL, len(fe)) 66 for i := 0; i < len(fe); i++ { 67 endpoints[i] = new(url.URL) 68 *endpoints[i] = *fe[i] 69 70 endpoints[i].Path = original.URL.Path 71 endpoints[i].RawPath = original.URL.RawPath 72 endpoints[i].RawQuery = original.URL.RawQuery 73 endpoints[i].Fragment = original.URL.Fragment 74 } 75 76 return endpoints, nil 77 } 78 79 // NewEndpoints accepts a Configuration, typically injected via configuration, and an alternate function 80 // that can create an Endpoints. If the Configuration has a fixed set of endpoints, this function returns a 81 // FixedEndpoints built from those URLs. Otherwise, the alternate function is invoked to produce 82 // and Endpoints instance to return. 83 // 84 // This function allows an application-layer Endpoints, returned by alternate, to be used when injected 85 // endpoints are not present. 86 func NewEndpoints(c Configuration, alternate func() (Endpoints, error)) (Endpoints, error) { 87 if endpoints := c.endpoints(); len(endpoints) > 0 { 88 return ParseURLs(endpoints...) 89 } 90 91 if alternate != nil { 92 return alternate() 93 } 94 95 return nil, errNoConfiguredEndpoints 96 } 97 98 // MustNewEndpoints is like NewEndpoints, save that it panics upon any error. 99 func MustNewEndpoints(c Configuration, alternate func() (Endpoints, error)) Endpoints { 100 e, err := NewEndpoints(c, alternate) 101 if err != nil { 102 panic(err) 103 } 104 105 return e 106 }