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