Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,31 @@ func ChainHandlers(handlers ...ResponseHandler) ResponseHandler {
}
}

// KeepRespBodyHandlers Combine multiple ResponseHandler and ensure that each processor has access to the original response body
func KeepRespBodyHandlers(handlers ...ResponseHandler) ResponseHandler {
return func(r *http.Response) error {
for _, h := range handlers {
if h == nil {
continue
}

var dup io.ReadCloser
r.Body, dup = dupReadCloser(r.Body)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but I think if one of the handlers only consumed part of the response, the subsequent handlers would only get a partial buffer.

Instead, what if on a new line between line 35 and line 36, it read the whole body into a buffer and reset the buffer on each loop?

if err := h(r); err != nil {
return err
}
r.Body = dup
}
return nil
}
}

func dupReadCloser(reader io.ReadCloser) (io.ReadCloser, io.ReadCloser) {
var buf bytes.Buffer
tee := io.TeeReader(reader, &buf)
return io.NopCloser(tee), io.NopCloser(&buf)
}

func consumeBody(res *http.Response) (err error) {
const maxDiscardSize = 640 * 1 << 10
if _, err = io.CopyN(io.Discard, res.Body, maxDiscardSize); err == io.EOF {
Expand Down
36 changes: 36 additions & 0 deletions handler_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package requests_test

import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
Expand Down Expand Up @@ -32,3 +34,37 @@ func BenchmarkBuilder_ToFile(b *testing.B) {
be.NilErr(b, err)
}
}

// TestKeepRespBodyHandlers tests the KeepRespBodyHandlers function.
func TestKeepRespBodyHandlers(t *testing.T) {
type Common struct {
ID int `json:"id"`
}

type Book struct {
Common
Name string `json:"name"`
}

var (
book Book
common Common
str string
)

handler := requests.KeepRespBodyHandlers(
requests.ToJSON(&common),
requests.ToJSON(&book),
requests.ToString(&str),
)

err := handler(&http.Response{
Body: io.NopCloser(bytes.NewReader([]byte(`{"id":1, "name":"孙子兵法"}`))),
})

be.NilErr(t, err)
be.Equal(t, 1, common.ID)
be.Equal(t, 1, book.ID)
be.Equal(t, "孙子兵法", book.Name)
be.Equal(t, `{"id":1, "name":"孙子兵法"}`, str)
}