-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
interceptor tripperware and options pattern
- Loading branch information
1 parent
c5152bc
commit 3c1e8aa
Showing
4 changed files
with
192 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package tripperware | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/gol4ng/httpware/v2" | ||
"github.com/gol4ng/httpware/v2/interceptor" | ||
) | ||
|
||
// Interceptor tripperware allow multiple req.Body read and allow to set callback before and after roundtrip | ||
func Interceptor(options ...Option) httpware.Tripperware { | ||
config := NewConfig(options...) | ||
return func(next http.RoundTripper) http.RoundTripper { | ||
return httpware.RoundTripFunc(func(req *http.Request) (resp *http.Response, err error) { | ||
req.Body = interceptor.NewCopyReadCloser(req.Body) | ||
config.CallbackBefore(req) | ||
defer func() { | ||
config.CallbackAfter(resp, req) | ||
}() | ||
|
||
return next.RoundTrip(req) | ||
}) | ||
} | ||
} | ||
|
||
type Config struct { | ||
CallbackBefore func(*http.Request) | ||
CallbackAfter func(*http.Response, *http.Request) | ||
} | ||
|
||
func (c *Config) apply(options ...Option) *Config { | ||
for _, option := range options { | ||
option(c) | ||
} | ||
return c | ||
} | ||
|
||
// NewConfig returns a new interceptor configuration with all options applied | ||
func NewConfig(options ...Option) *Config { | ||
config := &Config{ | ||
CallbackBefore: func(_ *http.Request) {}, | ||
CallbackAfter: func(_ *http.Response, _ *http.Request) {}, | ||
} | ||
return config.apply(options...) | ||
} | ||
|
||
// Option defines a interceptor tripperware configuration option | ||
type Option func(*Config) | ||
|
||
// WithAfter will configure CallbackAfter interceptor option | ||
func WithBefore(callbackBefore func(*http.Request)) Option { | ||
return func(config *Config) { | ||
config.CallbackBefore = callbackBefore | ||
} | ||
} | ||
|
||
// WithAfter will configure CallbackAfter interceptor option | ||
func WithAfter(callbackAfter func(*http.Response, *http.Request)) Option { | ||
return func(config *Config) { | ||
config.CallbackAfter = callbackAfter | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package tripperware_test | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/mock" | ||
|
||
"github.com/gol4ng/httpware/v2" | ||
"github.com/gol4ng/httpware/v2/mocks" | ||
"github.com/gol4ng/httpware/v2/tripperware" | ||
) | ||
|
||
func TestInterceptor(t *testing.T) { | ||
roundTripperMock := &mocks.RoundTripper{} | ||
req := httptest.NewRequest(http.MethodPost, "http://fake-addr", bytes.NewBufferString("my_fake_body")) | ||
resp := &http.Response{ | ||
Status: "OK", | ||
StatusCode: http.StatusOK, | ||
ContentLength: 30, | ||
} | ||
|
||
roundTripperMock.On("RoundTrip", mock.AnythingOfType("*http.Request")).Return(resp, nil).Run(func(args mock.Arguments) { | ||
innerReq := args.Get(0).(*http.Request) | ||
reqData, err := ioutil.ReadAll(innerReq.Body) | ||
assert.Nil(t, err) | ||
assert.Equal(t, "my_fake_body", string(reqData)) | ||
}) | ||
|
||
resp2, err := tripperware.Interceptor( | ||
tripperware.WithBefore(func(request *http.Request) { | ||
reqData, err := ioutil.ReadAll(request.Body) | ||
assert.Nil(t, err) | ||
assert.Equal(t, "my_fake_body", string(reqData)) | ||
}), | ||
tripperware.WithAfter(func(response *http.Response, request *http.Request) { | ||
|
||
}), | ||
)(roundTripperMock).RoundTrip(req) | ||
assert.Nil(t, err) | ||
assert.Equal(t, resp, resp2) | ||
|
||
reqData, err := ioutil.ReadAll(req.Body) | ||
assert.Nil(t, err) | ||
assert.Equal(t, "my_fake_body", string(reqData)) | ||
} | ||
|
||
// ===================================================================================================================== | ||
// ========================================= EXAMPLES ================================================================== | ||
// ===================================================================================================================== | ||
|
||
func ExampleInterceptor() { | ||
// we recommend to use TripperwareStack to simplify managing all wanted tripperware | ||
// caution tripperware order matter | ||
stack := httpware.TripperwareStack( | ||
tripperware.Interceptor( | ||
tripperware.WithBefore(func(request *http.Request) { | ||
reqData, err := ioutil.ReadAll(request.Body) | ||
fmt.Println("before callback", string(reqData), err) | ||
}), | ||
tripperware.WithAfter(func(response *http.Response, request *http.Request) { | ||
reqData, err := ioutil.ReadAll(request.Body) | ||
fmt.Println("after callback", string(reqData), err) | ||
}), | ||
), | ||
) | ||
|
||
// create http client using the tripperwareStack as RoundTripper | ||
client := http.Client{ | ||
Transport: stack, | ||
} | ||
|
||
_, _ = client.Post("fake-address.foo", "plain/text", bytes.NewBufferString("my_fake_body")) | ||
|
||
//Output: | ||
//before callback my_fake_body <nil> | ||
//after callback my_fake_body <nil> | ||
} |