Skip to content

Commit

Permalink
Add fasthttpexpect.Binder (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
gavv committed May 19, 2016
1 parent 916e4b9 commit e042182
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 51 deletions.
15 changes: 15 additions & 0 deletions expect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/http/httptest"
"testing"

"github.com/valyala/fasthttp/fasthttpadaptor"
"github.com/gavv/httpexpect/fasthttpexpect"
)

Expand Down Expand Up @@ -295,3 +296,17 @@ func BenchmarkExpectBinderStandard(b *testing.B) {
testHandler(e)
}
}

func BenchmarkExpectBinderFast(b *testing.B) {
handler := fasthttpadaptor.NewFastHTTPHandler(createHandler())

e := WithConfig(Config{
BaseURL: "http://example.com",
Client: fasthttpexpect.NewBinder(handler),
Reporter: NewRequireReporter(b),
})

for i := 0; i < b.N; i++ {
testHandler(e)
}
}
36 changes: 36 additions & 0 deletions fasthttpexpect/binder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package fasthttpexpect

import (
"github.com/valyala/fasthttp"
"net/http"
)

// Binder implements networkless httpexpect.Client attached directly to
// fasthttp.RequestHandler.
type Binder struct {
handler fasthttp.RequestHandler
}

// NewBinder returns a new Binder given fasthttp.RequestHandler.
func NewBinder(handler fasthttp.RequestHandler) *Binder {
return &Binder{handler}
}

// Do implements httpexpect.Client.Do.
func (binder *Binder) Do(stdreq *http.Request) (*http.Response, error) {
var fastreq fasthttp.Request

convertRequest(stdreq, &fastreq)

var ctx fasthttp.RequestCtx

ctx.Init(&fastreq, nil, nil)

if stdreq.Body != nil {
ctx.Request.SetBodyStream(stdreq.Body, -1)
}

binder.handler(&ctx)

return convertResponse(stdreq, &ctx.Response), nil
}
51 changes: 4 additions & 47 deletions fasthttpexpect/client.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
// Package fasthttpexpect provides fasthttp adapter for httpexpect.
package fasthttpexpect

import (
"bytes"
"github.com/valyala/fasthttp"
"io"
"net/http"
)

Expand Down Expand Up @@ -37,55 +34,15 @@ func WithClient(backend ClientBackend) ClientAdapter {

// Do implements httpexpect.Client.Do.
func (adapter ClientAdapter) Do(stdreq *http.Request) (stdresp *http.Response, err error) {
fastreq := fasthttp.AcquireRequest()
var fastreq fasthttp.Request

if stdreq.Body != nil {
fastreq.SetBodyStream(stdreq.Body, -1)
}

fastreq.SetRequestURI(stdreq.URL.String())

fastreq.Header.SetMethod(stdreq.Method)

for k, a := range stdreq.Header {
for _, v := range a {
fastreq.Header.Add(k, v)
}
}
convertRequest(stdreq, &fastreq)

var fastresp fasthttp.Response

if err = adapter.backend.Do(fastreq, &fastresp); err == nil {
status := fastresp.Header.StatusCode()
body := fastresp.Body()

stdresp = &http.Response{
Request: stdreq,
StatusCode: status,
Status: http.StatusText(status),
}

fastresp.Header.VisitAll(func(k, v []byte) {
if stdresp.Header == nil {
stdresp.Header = make(http.Header)
}
stdresp.Header.Add(string(k), string(v))
})

if body != nil {
stdresp.Body = readCloserAdapter{bytes.NewReader(body)}
}
if err = adapter.backend.Do(&fastreq, &fastresp); err == nil {
stdresp = convertResponse(stdreq, &fastresp)
}

fasthttp.ReleaseRequest(fastreq)

return
}

type readCloserAdapter struct {
io.Reader
}

func (b readCloserAdapter) Close() error {
return nil
}
56 changes: 56 additions & 0 deletions fasthttpexpect/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package fasthttpexpect

import (
"bytes"
"github.com/valyala/fasthttp"
"io"
"net/http"
)

func convertRequest(stdreq *http.Request, fastreq *fasthttp.Request) {
if stdreq.Body != nil {
fastreq.SetBodyStream(stdreq.Body, -1)
}

fastreq.SetRequestURI(stdreq.URL.String())

fastreq.Header.SetMethod(stdreq.Method)

for k, a := range stdreq.Header {
for _, v := range a {
fastreq.Header.Add(k, v)
}
}
}

func convertResponse(stdreq *http.Request, fastresp *fasthttp.Response) *http.Response {
status := fastresp.Header.StatusCode()
body := fastresp.Body()

stdresp := &http.Response{
Request: stdreq,
StatusCode: status,
Status: http.StatusText(status),
}

fastresp.Header.VisitAll(func(k, v []byte) {
if stdresp.Header == nil {
stdresp.Header = make(http.Header)
}
stdresp.Header.Add(string(k), string(v))
})

if body != nil {
stdresp.Body = readCloserAdapter{bytes.NewReader(body)}
}

return stdresp
}

type readCloserAdapter struct {
io.Reader
}

func (b readCloserAdapter) Close() error {
return nil
}
2 changes: 2 additions & 0 deletions fasthttpexpect/fasthttpexpect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package fasthttpexpect provides fasthttp adapter for httpexpect.
package fasthttpexpect
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (
"testing"
)

type testClient interface {
Do(*http.Request) (*http.Response, error)
}

type mockBackend struct {
t *testing.T
}
Expand All @@ -24,17 +28,15 @@ func (c mockBackend) Do(req *fasthttp.Request, resp *fasthttp.Response) error {
return nil
}

func TestClientAdapter(t *testing.T) {
adapter := WithClient(mockBackend{t})

func runTest(t *testing.T, client testClient) {
req, err := http.NewRequest(
"GET", "http://example.com", bytes.NewReader([]byte("body")))

if err != nil {
t.Fatal(err)
}

resp, err := adapter.Do(req)
resp, err := client.Do(req)
if err != nil {
t.Fatal(err)
}
Expand All @@ -51,3 +53,19 @@ func TestClientAdapter(t *testing.T) {
assert.Equal(t, header, resp.Header)
assert.Equal(t, `{"hello":"world"}`, string(b))
}

func TestClientAdapter(t *testing.T) {
adapter := WithClient(mockBackend{t})

runTest(t, adapter)
}

func TestBinder(t *testing.T) {
backend := mockBackend{t}

binder := NewBinder(func(ctx *fasthttp.RequestCtx) {
backend.Do(&ctx.Request, &ctx.Response)
})

runTest(t, binder)
}

0 comments on commit e042182

Please sign in to comment.