@@ -118,9 +118,18 @@ and how much memory you are comfortable with using.
118118## Stateful toxics
119119
120120If a toxic needs to store extra information for a connection such as the number of bytes
121- transferred (See ` limit_data ` toxic), a state object can be created by implementing the
122- ` StatefulToxic ` interface. This interface defines the ` NewState() ` function that can create
123- a new state object with default values set.
121+ transferred (See the [ limit_data toxic] ( https://github.com/Shopify/toxiproxy/blob/master/toxics/limit_data.go ) ),
122+ a state object can be created by implementing the ` StatefulToxic ` interface. This interface
123+ defines the ` NewState() ` function that can create a new state object with default values set.
124+
125+ ``` go
126+ func (t *ExampleToxic ) NewState () interface {} {
127+ return &ExampleToxicState{
128+ BytesRemaining: t.BytesAllowed ,
129+ SomeOtherState: true ,
130+ }
131+ }
132+ ```
124133
125134When a stateful toxic is created, the state object will be stored on the ` ToxicStub ` and
126135can be accessed from ` toxic.Pipe() ` :
@@ -133,6 +142,78 @@ If necessary, some global state can be stored in the toxic struct, which will no
133142instanced per-connection. These fields cannot have a custom default value set and will
134143not be thread-safe, so proper locking or atomic operations will need to be used.
135144
145+ ## Bidirectional toxics
146+
147+ Regular toxics are limited to data flowing in a single direction, so they can't make decisions
148+ for the ` downstream ` based on a request in the ` upstream ` . For things like protocol aware toxics
149+ this is a problem.
150+
151+ Bidirectional toxics allow state to be shared for the ` upstream ` and ` downstream ` pipes in a single
152+ toxic implementation. They also ensure direction-specific code is always run on the correct pipe
153+ (a toxic that only works on the ` upstream ` can't be added to the ` downstream ` ).
154+
155+ Creating a bidirectional toxic is done by implementing a second ` Pipe() ` function called ` PipeRequest() ` .
156+ The implementation is same as a regular toxic, and can be paired with other types such as a stateful toxic.
157+
158+ One use case of a bidirectional toxic is to mock out the backend server entirely, which is shown below:
159+
160+ ``` go
161+ type EchoToxic struct {}
162+
163+ type EchoToxicState struct {
164+ Request chan *stream.StreamChunk
165+ }
166+
167+ // PipeRequest handles the upstream direction
168+ func (t *EchoToxic ) PipeRequest (stub *toxics .ToxicStub ) {
169+ state := stub.State .(*EchoToxicState)
170+
171+ for {
172+ select {
173+ case <- stub.Interrupt :
174+ return
175+ case c := <- stub.Input :
176+ if c == nil {
177+ // Close the downstream when the client closes
178+ close (state.Request )
179+ stub.Close ()
180+ return
181+ }
182+ // Send the data to the downstream through the state object
183+ state.Request <- c
184+ }
185+ }
186+ }
187+
188+ // Pipe() will only handle the downstream on a bidirectional toxic
189+ func (t *EchoToxic ) Pipe (stub *toxics .ToxicStub ) {
190+ state := stub.State .(*EchoToxicState)
191+
192+ for {
193+ select {
194+ case <- stub.Interrupt :
195+ return
196+ case c := <- state.Request : // Read from the upstream instead of the server
197+ if c == nil {
198+ stub.Close ()
199+ return
200+ }
201+ stub.Output <- c
202+ }
203+ }
204+ }
205+
206+ func (t *EchoToxic ) NewState () interface {} {
207+ return &EchoToxicState{
208+ Request: make (chan *stream.StreamChunk ),
209+ }
210+ }
211+ ```
212+
213+ This example will loop back all data send to the server back to the client. Another use case seen
214+ within toxiproxy is to filter http response modifications based on the request URL (See the
215+ [ http toxic] ( https://github.com/Shopify/toxiproxy/tree/master/toxics/http.go ) ).
216+
136217## Using ` io.Reader ` and ` io.Writer `
137218
138219If your toxic involves modifying the data going through a proxy, you can use the ` ChanReader `
0 commit comments