Skip to content

Commit

Permalink
TCP Reset peer toxic documentation.
Browse files Browse the repository at this point in the history
Update documentation to mention about reset_peer toxic.
Add e2e smoke tests to check format of reset_peer toxic.
Client show the toxic in the help usage output.

Example of usage:

```shell
$ toxiproxy-cli -h http://localhost:8474 create -l localhost:20000 -u localhost:20002 http
$ toxiproxy-cli toxic add --type reset_peer --toxicName "reset_peer" \
                       --attribute "timeout=2000" \
                       --toxicity 1.0 http
$ time curl 127.0.0.1:20000
curl: (56) Recv failure: Connection reset by peer
curl 127.0.0.1:20000  0.00s user 0.00s system 0% cpu 2.010 total
```
  • Loading branch information
miry committed Oct 17, 2021
1 parent d457170 commit 0ac5d83
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
/usr/bin/toxiproxy-server
```
(#331, @miry)
* A new toxic to simulate TCP RESET (Connection reset by peer) on the connections by closing
the stub Input immediately or after a timeout. (#247 and #333, @chaosbox)

# [2.1.7]

Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ stopping you from creating a client in any other language (see
3. [Bandwidth](#bandwidth)
4. [Slow close](#slow_close)
5. [Timeout](#timeout)
6. [Slicer](#slicer)
6. [Reset peer](#reset_peer)
7. [Slicer](#slicer)
6. [HTTP API](#http-api)
1. [Proxy fields](#proxy-fields)
2. [Toxic fields](#toxic-fields)
Expand Down Expand Up @@ -404,6 +405,15 @@ Stops all data from getting through, and closes the connection after `timeout`.
`timeout` is 0, the connection won't close, and data will be delayed until the
toxic is removed.

Attributes:

- `timeout`: time in milliseconds

#### reset_peer

Simulate TCP RESET (Connection reset by peer) on the connections by closing the stub Input
immediately or after a `timeout`.

Attributes:

- `timeout`: time in milliseconds
Expand Down
10 changes: 10 additions & 0 deletions bin/e2e
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,16 @@ go test -bench=. ./testing -v

echo -e "-----------------\n"

echo "=== Reset peer toxic"

./dist/toxiproxy-cli toxic add --type reset_peer --toxicName "reset_peer" \
--attribute "timeout=2000" \
--toxicity 1.0 shopify_http
./dist/toxiproxy-cli inspect shopify_http
./dist/toxiproxy-cli toxic delete --toxicName "reset_peer" shopify_http

echo -e "-----------------\n"

echo "== Teardown"

./dist/toxiproxy-cli delete shopify_http
Expand Down
4 changes: 4 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ var toxicDescription = `
timeout: stop all data and close after timeout
timeout=<ms>
reset_peer: simulate TCP RESET (Connection reset by peer) on the connections by closing
the stub Input immediately or after a timeout
timeout=<ms>
slicer: slice data into bits with optional delay
average_size=<bytes>,size_variation=<bytes>,delay=<microseconds>
Expand Down
11 changes: 6 additions & 5 deletions link.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ func NewToxicLink(

// Start the link with the specified toxics.
func (link *ToxicLink) Start(name string, source io.Reader, dest io.WriteCloser) {

go func() {
bytes, err := io.Copy(link.input, source)
if err != nil {
Expand All @@ -76,31 +75,33 @@ func (link *ToxicLink) Start(name string, source io.Reader, dest io.WriteCloser)
}
link.input.Close()
}()

for i, toxic := range link.toxics.chain[link.direction] {
if stateful, ok := toxic.Toxic.(toxics.StatefulToxic); ok {
link.stubs[i].State = stateful.NewState()
}

if _, ok := toxic.Toxic.(*toxics.ResetToxic); ok {
if err := source.(*net.TCPConn).SetLinger(0); err != nil {
logrus.WithFields(logrus.Fields{
"name": link.proxy.Name,
"toxic": toxic.Type,
"err": err,
}).Error("source: Unable to setLinger(ms)")

}).Error("source: Unable to setLinger(ms)")
}

if err := dest.(*net.TCPConn).SetLinger(0); err != nil {
logrus.WithFields(logrus.Fields{
"name": link.proxy.Name,
"toxic": toxic.Type,
"err": err,
}).Error("dest: Unable to setLinger(ms)")

}).Error("dest: Unable to setLinger(ms)")
}
}

go link.stubs[i].Run(toxic)
}

go func() {
bytes, err := io.Copy(dest, link.output)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion toxics/reset_peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import (
)

/*
The ResetToxic sends closes the connection abruptly after a timeout (in ms). The behaviour of Close is set to discard any unsent/unacknowledged data by setting SetLinger to 0,
The ResetToxic sends closes the connection abruptly after a timeout (in ms).
The behavior of Close is set to discard any unsent/unacknowledged data by setting SetLinger to 0,
~= sets TCP RST flag and resets the connection.
If the timeout is set to 0, then the connection will be reset immediately.
Expand Down
23 changes: 18 additions & 5 deletions toxics/reset_peer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package toxics_test

import (
"bufio"
"github.com/Shopify/toxiproxy/toxics"
"io"
"net"
"os"
"syscall"
"testing"
"time"

"github.com/Shopify/toxiproxy/v2/toxics"
)

const msg = "reset toxic payload\n"
Expand All @@ -21,15 +22,24 @@ func TestResetToxicWithTimeout(t *testing.T) {
start := time.Now()
resetToxic := toxics.ResetToxic{Timeout: 100}
resetTCPHelper(t, ToxicToJson(t, "resettcp", "reset_peer", "upstream", &resetToxic))
AssertDeltaTime(t, "Reset after timeout", time.Since(start), time.Duration(resetToxic.Timeout)*time.Millisecond, time.Duration(resetToxic.Timeout+10)*time.Millisecond)
AssertDeltaTime(t,
"Reset after timeout",
time.Since(start),
time.Duration(resetToxic.Timeout)*time.Millisecond,
time.Duration(resetToxic.Timeout+10)*time.Millisecond,
)
}

func TestResetToxicWithTimeoutDownstream(t *testing.T) {
start := time.Now()
resetToxic := toxics.ResetToxic{Timeout: 100}
resetTCPHelper(t, ToxicToJson(t, "resettcp", "reset_peer", "downstream", &resetToxic))
AssertDeltaTime(t, "Reset after timeout", time.Since(start), time.Duration(resetToxic.Timeout)*time.Millisecond, time.Duration(resetToxic.Timeout+10)*time.Millisecond)

AssertDeltaTime(t,
"Reset after timeout",
time.Since(start),
time.Duration(resetToxic.Timeout)*time.Millisecond,
time.Duration(resetToxic.Timeout+10)*time.Millisecond,
)
}

func checkConnectionState(t *testing.T, listenAddress string) {
Expand All @@ -49,7 +59,10 @@ func checkConnectionState(t *testing.T, listenAddress string) {
t.Error("Expected: connection reset by peer. Got:", err)
}
} else {
t.Error("Expected: connection reset by peer. Got:", err, "conn:", conn.RemoteAddr(), conn.LocalAddr())
t.Error(
"Expected: connection reset by peer. Got:",
err, "conn:", conn.RemoteAddr(), conn.LocalAddr(),
)
}
_, err = conn.Read(tmp)
if err != io.EOF {
Expand Down

0 comments on commit 0ac5d83

Please sign in to comment.