mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-15 21:06:39 +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.
126 lines
2.6 KiB
Go
126 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 = 200
|
|
)
|
|
|
|
type (
|
|
ResponseWriter 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()
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// 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()
|
|
}
|
|
|
|
// Implements the http.CloseNotify interface
|
|
func (w *responseWriter) CloseNotify() <-chan bool {
|
|
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
|
}
|
|
|
|
// Implements the http.Flush interface
|
|
func (w *responseWriter) Flush() {
|
|
w.ResponseWriter.(http.Flusher).Flush()
|
|
}
|
|
|
|
// 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
|
|
}
|