Skip to content

Commit

Permalink
Add bidirectional toxic info to the docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacob Wirth committed Sep 14, 2016
1 parent fb86b43 commit ac78131
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# 2.1.0 (Unreleased)

* Add bidirectional toxics #132
* Add `/populate` endpoint to server #111
* Change error responses from `title` to `error`
* Allow hostname to be specified in CLI #129
Expand Down
81 changes: 81 additions & 0 deletions CREATING_TOXICS.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,15 @@ transferred (See `limit_data` toxic), a state object can be created by implement
`StatefulToxic` interface. This interface defines the `NewState()` function that can create
a new state object with default values set.

```go
func (t *ExampleToxic) NewState() interface{} {
return &ExampleToxicState{
BytesRemaining: t.BytesAllowed,
SomeOtherState: true,
}
}
```

When a stateful toxic is created, the state object will be stored on the `ToxicStub` and
can be accessed from `toxic.Pipe()`:

Expand All @@ -133,6 +142,78 @@ If necessary, some global state can be stored in the toxic struct, which will no
instanced per-connection. These fields cannot have a custom default value set and will
not be thread-safe, so proper locking or atomic operations will need to be used.

## Bidirectional toxics

Regular toxics are limited to data flowing in a single direction, so they can't make decisions
for the `downstream` based on a request in the `upstream`. For things like protocol aware toxics
this is a problem.

Bidirectional toxics allow state to be shared for the `upstream` and `downstream` pipes in a single
toxic implementation. They also ensure direction-specific code is always run on the correct pipe
(a toxic that only works on the `upstream` can't be added to the `downstream`).

Creating a bidirectional toxic is done by implementing a second `Pipe()` function called `PipeRequest()`.
The implementation is same as a regular toxic, and can be paired with other types such as a stateful toxic.

One use case of a bidirectional toxic is to mock out the backend server entirely, which is shown below:

```go
type EchoToxic struct {}

type EchoToxicState struct {
Request chan *stream.StreamChunk
}

// PipeRequest handles the upstream direction
func (t *EchoToxic) PipeRequest(stub *toxics.ToxicStub) {
state := stub.State.(*EchoToxicState)

for {
select {
case <-stub.Interrupt:
return
case c := <-stub.Input:
if c == nil {
// Close the downstream when the client closes
close(state.Request)
stub.Close()
return
}
// Send the data to the downstream through the state object
state.Request <- c
}
}
}

// Pipe() will only handle the downstream on a bidirectional toxic
func (t *EchoToxic) Pipe(stub *toxics.ToxicStub) {
state := stub.State.(*EchoToxicState)

for {
select {
case <-stub.Interrupt:
return
case c := <-state.Request: // Read from the upstream instead of the server
if c == nil {
stub.Close()
return
}
stub.Output <- c
}
}
}

func (t *EchoToxic) NewState() interface{} {
return &EchoToxicState{
Request: make(chan *stream.StreamChunk),
}
}
```

This example will loop back all data send to the server back to the client. Another use case seen
within toxiproxy is to filter http response modifications based on the request URL (See the
[http toxic](https://github.com/Shopify/toxiproxy/tree/master/toxics/http.go)).

## Using `io.Reader` and `io.Writer`

If your toxic involves modifying the data going through a proxy, you can use the `ChanReader`
Expand Down

0 comments on commit ac78131

Please sign in to comment.