implement io.ReaderFrom for gin.ResponseWriter

This causes io.Copy, which calls io.copyBuffer, to automatically use the
sendfile syscall by calling the underlying http.ResponseWriter's
ReadFrom implementation [1]. This can be a significant performance improvement.

[1] https://github.com/golang/go/blob/7eeec1f6e4/src/io/io.go#L410-L413

Co-authored-by: Alex Guerra <alex@heyimalex.com>
This commit is contained in:
Aaron Janse 2022-06-13 14:20:02 -04:00
parent 6c3a1d7063
commit fdb1c440e5
2 changed files with 32 additions and 0 deletions

View File

@ -22,6 +22,7 @@ type ResponseWriter interface {
http.Hijacker
http.Flusher
http.CloseNotifier
io.ReaderFrom
// Status returns the HTTP response status code of the current request.
Status() int
@ -87,6 +88,15 @@ func (w *responseWriter) WriteString(s string) (n int, err error) {
return
}
// ReadFrom implements the io.ReaderFrom interface, allowing Go to automatically
// use the sendfile syscall in methods such as http.ServeFile
func (w *responseWriter) ReadFrom(src io.Reader) (n int64, err error) {
w.WriteHeaderNow()
n, err = io.Copy(w.ResponseWriter, src)
w.size += int(n)
return
}
func (w *responseWriter) Status() int {
return w.status
}

View File

@ -5,8 +5,10 @@
package gin
import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/assert"
@ -132,3 +134,23 @@ func TestResponseWriterFlush(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
}
func TestResponseWriterReadFrom(t *testing.T) {
testWriter := httptest.NewRecorder()
writer := &responseWriter{}
writer.reset(testWriter)
w := ResponseWriter(writer)
n, err := io.Copy(w, strings.NewReader("hola"))
assert.Equal(t, int64(4), n)
assert.Equal(t, 4, w.Size())
assert.Equal(t, http.StatusOK, w.Status())
assert.Equal(t, http.StatusOK, testWriter.Code)
assert.Equal(t, "hola", testWriter.Body.String())
assert.NoError(t, err)
n, err = writer.ReadFrom(strings.NewReader(" adios"))
assert.Equal(t, int64(6), n)
assert.Equal(t, 10, w.Size())
assert.Equal(t, testWriter.Body.String(), "hola adios")
assert.NoError(t, err)
}