-
Notifications
You must be signed in to change notification settings - Fork 445
/
slicer.go
85 lines (75 loc) · 2.09 KB
/
slicer.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
package toxics
import (
"math/rand"
"time"
"github.com/Shopify/toxiproxy/v2/stream"
)
// The SlicerToxic slices data into multiple smaller packets
// to simulate real-world TCP behavior.
type SlicerToxic struct {
// Average number of bytes to slice at
AverageSize int `json:"average_size"`
// +/- bytes to vary sliced amounts. Must be less than
// the average size
SizeVariation int `json:"size_variation"`
// Microseconds to delay each packet. May be useful since there's
// usually some kind of buffering of network data
Delay int `json:"delay"`
}
// Returns a list of chunk offsets to slice up a packet of the
// given total size. For example, for a size of 100, output might be:
//
// | []int{0, 18, 18, 43, 43, 67, 67, 77, 77, 100}
// | ^---^ ^----^ ^----^ ^----^ ^-----^
//
// This tries to get fairly evenly-varying chunks (no tendency
// to have a small/large chunk at the start/end).
func (t *SlicerToxic) chunk(start int, end int) []int {
// Base case:
// If the size is within the random varation, _or already
// less than the average size_, just return it.
// Otherwise split the chunk in about two, and recurse.
if (end-start)-t.AverageSize <= t.SizeVariation {
return []int{start, end}
}
mid := start + (end-start)/2
//#nosec
if t.SizeVariation > 0 {
mid += rand.Intn(t.SizeVariation*2) - t.SizeVariation
}
left := t.chunk(start, mid)
right := t.chunk(mid, end)
return append(left, right...)
}
func (t *SlicerToxic) Pipe(stub *ToxicStub) {
for {
select {
case <-stub.Interrupt:
return
case c := <-stub.Input:
if c == nil {
stub.Close()
return
}
chunks := t.chunk(0, len(c.Data))
for i := 1; i < len(chunks); i += 2 {
stub.Output <- &stream.StreamChunk{
Data: c.Data[chunks[i-1]:chunks[i]],
Timestamp: c.Timestamp,
}
select {
case <-stub.Interrupt:
stub.Output <- &stream.StreamChunk{
Data: c.Data[chunks[i]:],
Timestamp: c.Timestamp,
}
return
case <-time.After(time.Duration(t.Delay) * time.Microsecond):
}
}
}
}
}
func init() {
Register("slicer", new(SlicerToxic))
}