Repository: Shopify/toxiproxy (Go)

Issue:
panic in slicer
Running on `2.1.1` with a `slicer` toxic:

```
~/P/shopify (master|✚1…) $ /Users/kir/Downloads/toxiproxy-server-darwin-amd64
INFO[0000] API HTTP server starting                      host=localhost port=8474 version=2.1.1
INFO[0053] Started proxy                                 name=dumb proxy=127.0.0.1:8182 upstream=localhost:8181
INFO[0177] Accepted client                               client=127.0.0.1:62635 name=dumb proxy=127.0.0.1:8182 upstream=localhost:8181
WARN[0177] Source terminated                             bytes=655582 err=read tcp 127.0.0.1:8182->127.0.0.1:62635: use of closed network connection name=dumb
INFO[0179] Accepted client                               client=127.0.0.1:62671 name=dumb proxy=127.0.0.1:8182 upstream=localhost:8181
WARN[0179] Source terminated                             bytes=655582 err=read tcp 127.0.0.1:8182->127.0.0.1:62671: use of closed network connection name=dumb
INFO[0184] Accepted client                               client=127.0.0.1:62739 name=dumb proxy=127.0.0.1:8182 upstream=localhost:8181
WARN[0184] Source terminated                             bytes=655582 err=read tcp 127.0.0.1:8182->127.0.0.1:62739: use of closed network connection name=dumb
INFO[0238] Accepted client                               client=127.0.0.1:63609 name=dumb proxy=127.0.0.1:8182 upstream=localhost:8181
WARN[0238] Source terminated                             bytes=655582 err=read tcp 127.0.0.1:8182->127.0.0.1:63609: use of closed network connection name=dumb
INFO[0271] Accepted client                               client=127.0.0.1:64135 name=dumb proxy=127.0.0.1:8182 upstream=localhost:8181
panic: invalid argument to Intn

goroutine 49 [running]:
panic(0x261fa0, 0xc420162050)
        /usr/local/Cellar/go/1.7.1/libexec/src/runtime/panic.go:500 +0x1a1
math/rand.(*Rand).Intn(0xc42008a080, 0x0, 0x2)
        /usr/local/Cellar/go/1.7.1/libexec/src/math/rand/rand.go:116 +0xd0
math/rand.Intn(0x0, 0x0)
        /usr/local/Cellar/go/1.7.1/libexec/src/math/rand/rand.go:247 +0x37
github.com/Shopify/toxiproxy/toxics.(*SlicerToxic).chunk(0xc4200eeb40, 0x0, 0x97, 0x1, 0x0, 0x0)
        /Users/jpittis/Code/go/src/github.com/Shopify/toxiproxy/toxics/slicer.go:42 +0xcc
github.com/Shopify/toxiproxy/toxics.(*SlicerToxic).Pipe(0xc4200eeb40, 0xc420180640)
        /Users/jpittis/Code/go/src/github.com/Shopify/toxiproxy/toxics/slicer.go:60 +0x139
github.com/Shopify/toxiproxy/toxics.(*ToxicStub).Run(0xc420180640, 0xc420074360)
        /Users/jpittis/Code/go/src/github.com/Shopify/toxiproxy/toxics/toxic.go:82 +0xb5
created by github.com/Shopify/toxiproxy.(*ToxicLink).Start
        /Users/jpittis/Code/go/src/github.com/Shopify/toxiproxy/link.go:73 +0x143
```

cc @jpittis 


Discussion:
It looks like this is due to `size_variation` not being set, and defaulting to 0.
A variation of 0 should definitely be supported, so that will need to be fixed. A workaround for now would be to set the variation to >= 1.
Thanks for looking into it. I didn't set `size_variation` when I added the toxic because I assumed it would use a default value. The help didn't mention that value for `size_variation` is required.
Still an issue, ran into this today on the latest version published to dockerhub. 
Looking at this.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab5ca1bf..7aa786fc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
 * Added verbose error on proxy upstream dialing (#355, @f-dg)
 * Improve server startup message (#358, @areveny)
 * Introduce yaml linter. (#362, @miry)
+* Handle slicer toxic with zero `SizeVariation` and fix slicing randomization (#359, @areveny)
 
 # [2.3.0] - 2021-12-23
 
diff --git a/toxics/slicer.go b/toxics/slicer.go
index 12c7c33f..31b61308 100644
--- a/toxics/slicer.go
+++ b/toxics/slicer.go
@@ -37,10 +37,11 @@ func (t *SlicerToxic) chunk(start int, end int) []int {
 		return []int{start, end}
 	}
 
-	// +1 in the size variation to offset favoring of smaller
-	// numbers by integer division
+	mid := start + (end-start)/2
 	//#nosec
-	mid := start + (end-start)/2 + (rand.Intn(t.SizeVariation*2) - t.SizeVariation) + rand.Intn(1)
+	if t.SizeVariation > 0 {
+		mid += rand.Intn(t.SizeVariation*2) - t.SizeVariation
+	}
 	left := t.chunk(start, mid)
 	right := t.chunk(mid, end)
 


Test patch:
diff --git a/toxics/slicer_test.go b/toxics/slicer_test.go
index 3a2a2c09..7b396893 100644
--- a/toxics/slicer_test.go
+++ b/toxics/slicer_test.go
@@ -38,14 +38,14 @@ func TestSlicerToxic(t *testing.T) {
 
 	buf := make([]byte, 0, len(data))
 	reads := 0
-L:
-	for {
+
+	for timeout := false; !timeout; {
 		select {
 		case c := <-output:
 			reads++
 			buf = append(buf, c.Data...)
 		case <-time.After(10 * time.Millisecond):
-			break L
+			timeout = true
 		}
 	}
 
@@ -56,3 +56,51 @@ L:
 		t.Errorf("Server did not read correct buffer from client!")
 	}
 }
+
+func TestSlicerToxicZeroSizeVariation(t *testing.T) {
+	data := []byte(strings.Repeat("hello world ", 2)) // 24 bytes
+	// SizeVariation: 0 by default
+	slicer := &toxics.SlicerToxic{AverageSize: 1, Delay: 10}
+
+	input := make(chan *stream.StreamChunk)
+	output := make(chan *stream.StreamChunk)
+	stub := toxics.NewToxicStub(input, output)
+
+	done := make(chan bool)
+	go func() {
+		slicer.Pipe(stub)
+		done <- true
+	}()
+	defer func() {
+		close(input)
+		for {
+			select {
+			case <-done:
+				return
+			case <-output:
+			}
+		}
+	}()
+
+	input <- &stream.StreamChunk{Data: data}
+
+	buf := make([]byte, 0, len(data))
+	reads := 0
+
+	for timeout := false; !timeout; {
+		select {
+		case c := <-output:
+			reads++
+			buf = append(buf, c.Data...)
+		case <-time.After(10 * time.Millisecond):
+			timeout = true
+		}
+	}
+
+	if reads != 24 {
+		t.Errorf("Expected to read 24 times, but read %d times.", reads)
+	}
+	if !bytes.Equal(buf, data) {
+		t.Errorf("Server did not read correct buffer from client!")
+	}
+}