mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-22 17:42:14 +08:00
The private ResponseWriter implementation in the stdlib implements this interface to enable some optimizations around sendfile. The interface is asserted for in io.Copy, probably the most common way to serve files, so without this method we are missing out on those optimizations. As we're only interested in writing the headers and capturing the number of bytes written, we can just call io.Copy on the wrapped http.ResponseWriter and it will do the right thing, calling ReadFrom if it's implemented and doing a regular copy if not.
125 lines
2.6 KiB
Go
125 lines
2.6 KiB
Go
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
// Use of this source code is governed by a MIT style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gin
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
)
|
|
|
|
const (
|
|
noWritten = -1
|
|
defaultStatus = http.StatusOK
|
|
)
|
|
|
|
type responseWriterBase interface {
|
|
http.ResponseWriter
|
|
http.Hijacker
|
|
http.Flusher
|
|
http.CloseNotifier
|
|
io.ReaderFrom
|
|
|
|
// Returns the HTTP response status code of the current request.
|
|
Status() int
|
|
|
|
// Returns the number of bytes already written into the response http body.
|
|
// See Written()
|
|
Size() int
|
|
|
|
// Writes the string into the response body.
|
|
WriteString(string) (int, error)
|
|
|
|
// Returns true if the response body was already written.
|
|
Written() bool
|
|
|
|
// Forces to write the http header (status code + headers).
|
|
WriteHeaderNow()
|
|
}
|
|
|
|
type responseWriter struct {
|
|
http.ResponseWriter
|
|
size int
|
|
status int
|
|
}
|
|
|
|
var _ ResponseWriter = &responseWriter{}
|
|
|
|
func (w *responseWriter) reset(writer http.ResponseWriter) {
|
|
w.ResponseWriter = writer
|
|
w.size = noWritten
|
|
w.status = defaultStatus
|
|
}
|
|
|
|
func (w *responseWriter) WriteHeader(code int) {
|
|
if code > 0 && w.status != code {
|
|
if w.Written() {
|
|
debugPrint("[WARNING] Headers were already written. Wanted to override status code %d with %d", w.status, code)
|
|
}
|
|
w.status = code
|
|
}
|
|
}
|
|
|
|
func (w *responseWriter) WriteHeaderNow() {
|
|
if !w.Written() {
|
|
w.size = 0
|
|
w.ResponseWriter.WriteHeader(w.status)
|
|
}
|
|
}
|
|
|
|
func (w *responseWriter) Write(data []byte) (n int, err error) {
|
|
w.WriteHeaderNow()
|
|
n, err = w.ResponseWriter.Write(data)
|
|
w.size += n
|
|
return
|
|
}
|
|
|
|
func (w *responseWriter) WriteString(s string) (n int, err error) {
|
|
w.WriteHeaderNow()
|
|
n, err = io.WriteString(w.ResponseWriter, s)
|
|
w.size += n
|
|
return
|
|
}
|
|
|
|
func (w *responseWriter) Status() int {
|
|
return w.status
|
|
}
|
|
|
|
func (w *responseWriter) Size() int {
|
|
return w.size
|
|
}
|
|
|
|
func (w *responseWriter) Written() bool {
|
|
return w.size != noWritten
|
|
}
|
|
|
|
// Hijack implements the http.Hijacker interface.
|
|
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|
if w.size < 0 {
|
|
w.size = 0
|
|
}
|
|
return w.ResponseWriter.(http.Hijacker).Hijack()
|
|
}
|
|
|
|
// CloseNotify implements the http.CloseNotify interface.
|
|
func (w *responseWriter) CloseNotify() <-chan bool {
|
|
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
|
}
|
|
|
|
// Flush implements the http.Flush interface.
|
|
func (w *responseWriter) Flush() {
|
|
w.WriteHeaderNow()
|
|
w.ResponseWriter.(http.Flusher).Flush()
|
|
}
|
|
|
|
// ReadFrom implements the io.ReaderFrom interface.
|
|
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
|
|
}
|