forked from asecurityteam/transport
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrotate.go
66 lines (59 loc) · 2 KB
/
rotate.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
package transport
import (
"net/http"
"sync"
)
// Rotator contains multiple instances of a RoundTripper and rotates through
// them per-request. This is useful when dealing with HTTP/2 in situations where
// more than one TCP connection per host is required.
type Rotator struct {
numberOfInstances int
currentOffset int
instances []http.RoundTripper
factory Factory
lock *sync.Mutex
}
// RotatorOption is a configuration for the Rotator decorator
type RotatorOption func(*Rotator) *Rotator
// RotatorOptionInstances configurs a rotator with the number of internal
// RoundTripper instances it should maintain for the rotation.
func RotatorOptionInstances(number int) RotatorOption {
return func(r *Rotator) *Rotator {
r.numberOfInstances = number
return r
}
}
// NewRotator uses the given factory as a source and generates a number of
// instances based on the options given. The instances are called in a naive,
// round-robin manner.
func NewRotator(factory Factory, opts ...RotatorOption) *Rotator {
var r = &Rotator{factory: factory, lock: &sync.Mutex{}}
for _, opt := range opts {
r = opt(r)
}
for x := 0; x < r.numberOfInstances; x = x + 1 {
r.instances = append(r.instances, r.factory())
}
// Defensively maintain at least one in the set at all times.
if len(r.instances) < 1 {
r.instances = append(r.instances, r.factory())
r.numberOfInstances = 1
}
return r
}
// NewRotatorFactory is a counterpart for NewRotator that generates a Factory
// function for use with other decorators.
func NewRotatorFactory(factory Factory, opts ...RotatorOption) Factory {
return func() http.RoundTripper {
return NewRotator(factory, opts...)
}
}
// RoundTrip round-robins the outgoing requests against all of the internal
// instances.
func (c *Rotator) RoundTrip(r *http.Request) (*http.Response, error) {
c.lock.Lock()
c.currentOffset = (c.currentOffset + 1) % c.numberOfInstances
var offset = c.currentOffset
c.lock.Unlock()
return c.instances[offset].RoundTrip(r)
}