@@ -42,14 +42,15 @@ const (
4242// (i.e. this library is falling behind or requests volumes are too large),
4343// then some individual requests will be dropped.
4444type Tracker struct {
45- mu sync.RWMutex
46- buffer []* request
47- size int
48- cap int
49- pos int
50- ch chan * request
51- done chan struct {}
52- resp Responder
45+ mu sync.RWMutex
46+ buffer []* request
47+ size int
48+ cap int
49+ pos int
50+ ch chan * request
51+ done chan struct {}
52+ resp Responder
53+ maxLatencyMs uint64
5354}
5455
5556type request struct {
@@ -67,18 +68,28 @@ func newRequest(start, end time.Time, headerSize, bodySize uint64) *request {
6768}
6869
6970// New creates a new tracker with the `DefaultCapacity`.
70- func New () * Tracker {
71- t , _ := NewTracker (& PlainResponder {}, DefaultCapacity )
71+ func New (opts ... Option ) * Tracker {
72+ t , _ := NewTracker (& PlainResponder {}, DefaultCapacity , opts ... )
7273 return t
7374}
7475
76+ // Option defines a method for applying options when configuring a new tracker.
77+ type Option func (* Tracker )
78+
79+ // WithMaxLatency puts a cap on the tunnel latency.
80+ func WithMaxLatency (maxLatencyMs uint64 ) Option {
81+ return func (t * Tracker ) {
82+ t .maxLatencyMs = maxLatencyMs
83+ }
84+ }
85+
7586// NewTracker creates a tracker with custom capacity.
7687// Launches a goroutine to update the request metrics.
7788// To shut this down, use the .Close() method.
7889// The Responder parameter is used to write the output. If non is specified,
7990// the tracker will default to the "PlainResponder" which just writes the raw
8091// chaff bytes.
81- func NewTracker (resp Responder , cap int ) (* Tracker , error ) {
92+ func NewTracker (resp Responder , cap int , opts ... Option ) (* Tracker , error ) {
8293 if cap < 1 || cap > DefaultCapacity {
8394 return nil , fmt .Errorf ("cap must be 1 <= cap <= 100, got: %v" , cap )
8495 }
@@ -88,14 +99,21 @@ func NewTracker(resp Responder, cap int) (*Tracker, error) {
8899 }
89100
90101 t := & Tracker {
91- buffer : make ([]* request , 0 , int (cap )),
92- size : 0 ,
93- cap : cap ,
94- pos : 0 ,
95- ch : make (chan * request , cap ),
96- done : make (chan struct {}),
97- resp : resp ,
102+ buffer : make ([]* request , 0 , int (cap )),
103+ size : 0 ,
104+ cap : cap ,
105+ pos : 0 ,
106+ ch : make (chan * request , cap ),
107+ done : make (chan struct {}),
108+ resp : resp ,
109+ maxLatencyMs : 0 ,
98110 }
111+
112+ // Apply options.
113+ for _ , opt := range opts {
114+ opt (t )
115+ }
116+
99117 go t .updater ()
100118 return t , nil
101119}
@@ -152,8 +170,13 @@ func (t *Tracker) CalculateProfile() *request {
152170 }
153171 divisor := uint64 (t .size )
154172
173+ latencyMs := latency / divisor
174+ if max := t .maxLatencyMs ; max > 0 && latencyMs > max {
175+ latencyMs = max
176+ }
177+
155178 return & request {
156- latencyMs : latency / divisor ,
179+ latencyMs : latencyMs ,
157180 headerSize : uint64 (hSize / divisor ),
158181 bodySize : uint64 (bSize / divisor ),
159182 }
0 commit comments