2017-01-02 20:20:17 +01:00
|
|
|
package iris
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2017-01-04 14:16:53 +01:00
|
|
|
"fmt"
|
2017-01-02 20:20:17 +01:00
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/kataras/go-errors"
|
|
|
|
"github.com/kataras/go-fs"
|
|
|
|
"github.com/klauspost/compress/gzip"
|
|
|
|
)
|
|
|
|
|
|
|
|
type gzipResponseWriter struct {
|
2017-01-04 14:16:53 +01:00
|
|
|
ResponseWriter
|
2017-01-02 20:20:17 +01:00
|
|
|
gzipWriter *gzip.Writer
|
|
|
|
}
|
|
|
|
|
|
|
|
var gzpool = sync.Pool{New: func() interface{} { return &gzipResponseWriter{} }}
|
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
func acquireGzipResponseWriter(underline ResponseWriter) *gzipResponseWriter {
|
2017-01-02 20:20:17 +01:00
|
|
|
w := gzpool.Get().(*gzipResponseWriter)
|
|
|
|
w.ResponseWriter = underline
|
|
|
|
w.gzipWriter = fs.AcquireGzipWriter(w.ResponseWriter)
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
|
|
|
func releaseGzipResponseWriter(w *gzipResponseWriter) {
|
|
|
|
fs.ReleaseGzipWriter(w.gzipWriter)
|
|
|
|
gzpool.Put(w)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write compresses and writes that data to the underline response writer
|
|
|
|
func (w *gzipResponseWriter) Write(contents []byte) (int, error) {
|
|
|
|
return w.gzipWriter.Write(contents)
|
|
|
|
}
|
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
var rpool = sync.Pool{New: func() interface{} { return &responseWriter{statusCode: StatusOK} }}
|
2017-01-02 20:20:17 +01:00
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
func acquireResponseWriter(underline http.ResponseWriter) *responseWriter {
|
|
|
|
w := rpool.Get().(*responseWriter)
|
2017-01-02 20:20:17 +01:00
|
|
|
w.ResponseWriter = underline
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
func releaseResponseWriter(w *responseWriter) {
|
|
|
|
w.statusCodeSent = false
|
2017-01-02 20:20:17 +01:00
|
|
|
w.beforeFlush = nil
|
2017-01-04 14:16:53 +01:00
|
|
|
w.statusCode = StatusOK
|
2017-01-02 20:20:17 +01:00
|
|
|
rpool.Put(w)
|
|
|
|
}
|
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
// ResponseWriter interface is used by the context to serve an HTTP handler to
|
2017-01-02 20:20:17 +01:00
|
|
|
// construct an HTTP response.
|
|
|
|
//
|
|
|
|
// A ResponseWriter may not be used after the Handler.ServeHTTP method
|
|
|
|
// has returned.
|
2017-01-04 14:16:53 +01:00
|
|
|
type ResponseWriter interface {
|
|
|
|
http.ResponseWriter
|
|
|
|
http.Flusher
|
|
|
|
http.Hijacker
|
|
|
|
http.CloseNotifier
|
|
|
|
|
|
|
|
Writef(format string, a ...interface{}) (n int, err error)
|
|
|
|
WriteString(s string) (n int, err error)
|
|
|
|
SetContentType(cType string)
|
|
|
|
ContentType() string
|
|
|
|
StatusCode() int
|
|
|
|
SetBeforeFlush(cb func())
|
|
|
|
flushResponse()
|
|
|
|
clone() ResponseWriter
|
|
|
|
writeTo(ResponseWriter)
|
|
|
|
releaseMe()
|
|
|
|
}
|
|
|
|
|
|
|
|
// responseWriter is the basic response writer,
|
|
|
|
// it writes directly to the underline http.ResponseWriter
|
|
|
|
type responseWriter struct {
|
|
|
|
http.ResponseWriter
|
|
|
|
statusCode int // the saved status code which will be used from the cache service
|
|
|
|
statusCodeSent bool // reply header has been (logically) written
|
2017-01-02 20:20:17 +01:00
|
|
|
// yes only one callback, we need simplicity here because on EmitError the beforeFlush events should NOT be cleared
|
|
|
|
// but the response is cleared.
|
|
|
|
// Sometimes is useful to keep the event,
|
|
|
|
// so we keep one func only and let the user decide when he/she wants to override it with an empty func before the EmitError (context's behavior)
|
|
|
|
beforeFlush func()
|
|
|
|
}
|
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
var _ ResponseWriter = &responseWriter{}
|
2017-01-02 20:20:17 +01:00
|
|
|
|
|
|
|
// StatusCode returns the status code header value
|
2017-01-04 14:16:53 +01:00
|
|
|
func (w *responseWriter) StatusCode() int {
|
2017-01-02 20:20:17 +01:00
|
|
|
return w.statusCode
|
|
|
|
}
|
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
// Writef formats according to a format specifier and writes to the response.
|
2017-01-02 20:20:17 +01:00
|
|
|
//
|
2017-01-04 14:16:53 +01:00
|
|
|
// Returns the number of bytes written and any write error encountered
|
|
|
|
func (w *responseWriter) Writef(format string, a ...interface{}) (n int, err error) {
|
|
|
|
w.tryWriteHeader()
|
|
|
|
return fmt.Fprintf(w.ResponseWriter, format, a...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteString writes a simple string to the response.
|
|
|
|
//
|
|
|
|
// Returns the number of bytes written and any write error encountered
|
|
|
|
func (w *responseWriter) WriteString(s string) (n int, err error) {
|
|
|
|
return w.Write([]byte(s))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write writes to the client
|
2017-01-02 20:20:17 +01:00
|
|
|
// If WriteHeader has not yet been called, Write calls
|
|
|
|
// WriteHeader(http.StatusOK) before writing the data. If the Header
|
|
|
|
// does not contain a Content-Type line, Write adds a Content-Type set
|
|
|
|
// to the result of passing the initial 512 bytes of written data to
|
|
|
|
// DetectContentType.
|
|
|
|
//
|
|
|
|
// Depending on the HTTP protocol version and the client, calling
|
|
|
|
// Write or WriteHeader may prevent future reads on the
|
|
|
|
// Request.Body. For HTTP/1.x requests, handlers should read any
|
|
|
|
// needed request body data before writing the response. Once the
|
|
|
|
// headers have been flushed (due to either an explicit Flusher.Flush
|
|
|
|
// call or writing enough data to trigger a flush), the request body
|
|
|
|
// may be unavailable. For HTTP/2 requests, the Go HTTP server permits
|
|
|
|
// handlers to continue to read the request body while concurrently
|
|
|
|
// writing the response. However, such behavior may not be supported
|
|
|
|
// by all HTTP/2 clients. Handlers should read before writing if
|
|
|
|
// possible to maximize compatibility.
|
2017-01-04 14:16:53 +01:00
|
|
|
func (w *responseWriter) Write(contents []byte) (int, error) {
|
|
|
|
w.tryWriteHeader()
|
|
|
|
return w.ResponseWriter.Write(contents)
|
2017-01-02 20:20:17 +01:00
|
|
|
}
|
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
// prin to write na benei to write header
|
|
|
|
// meta to write den ginete edw
|
|
|
|
// prepei omws kai mono me WriteHeader kai xwris Write na pigenei to status code
|
|
|
|
// ara...wtf prepei na exw function flushStatusCode kai na elenxei an exei dw9ei status code na to kanei write aliws 200
|
2017-01-02 20:20:17 +01:00
|
|
|
|
|
|
|
// WriteHeader sends an HTTP response header with status code.
|
|
|
|
// If WriteHeader is not called explicitly, the first call to Write
|
|
|
|
// will trigger an implicit WriteHeader(http.StatusOK).
|
|
|
|
// Thus explicit calls to WriteHeader are mainly used to
|
|
|
|
// send error codes.
|
2017-01-04 14:16:53 +01:00
|
|
|
func (w *responseWriter) WriteHeader(statusCode int) {
|
2017-01-02 20:20:17 +01:00
|
|
|
w.statusCode = statusCode
|
|
|
|
}
|
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
// SetBeforeFlush registers the unique callback which called exactly before the response is flushed to the client
|
|
|
|
func (w *responseWriter) SetBeforeFlush(cb func()) {
|
|
|
|
w.beforeFlush = cb
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *responseWriter) flushResponse() {
|
|
|
|
if w.beforeFlush != nil {
|
|
|
|
w.beforeFlush()
|
|
|
|
}
|
|
|
|
w.tryWriteHeader()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *responseWriter) tryWriteHeader() {
|
|
|
|
if !w.statusCodeSent { // by write
|
|
|
|
w.statusCodeSent = true
|
|
|
|
w.ResponseWriter.WriteHeader(w.statusCode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-02 20:20:17 +01:00
|
|
|
// ContentType returns the content type, if not setted returns empty string
|
2017-01-04 14:16:53 +01:00
|
|
|
func (w *responseWriter) ContentType() string {
|
|
|
|
return w.ResponseWriter.Header().Get(contentType)
|
2017-01-02 20:20:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetContentType sets the content type header
|
2017-01-04 14:16:53 +01:00
|
|
|
func (w *responseWriter) SetContentType(cType string) {
|
|
|
|
w.ResponseWriter.Header().Set(contentType, cType)
|
2017-01-02 20:20:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Hijack lets the caller take over the connection.
|
|
|
|
// After a call to Hijack(), the HTTP server library
|
|
|
|
// will not do anything else with the connection.
|
|
|
|
//
|
|
|
|
// It becomes the caller's responsibility to manage
|
|
|
|
// and close the connection.
|
|
|
|
//
|
|
|
|
// The returned net.Conn may have read or write deadlines
|
|
|
|
// already set, depending on the configuration of the
|
|
|
|
// Server. It is the caller's responsibility to set
|
|
|
|
// or clear those deadlines as needed.
|
2017-01-04 14:16:53 +01:00
|
|
|
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
2017-01-02 20:20:17 +01:00
|
|
|
if h, isHijacker := w.ResponseWriter.(http.Hijacker); isHijacker {
|
|
|
|
return h.Hijack()
|
|
|
|
}
|
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
return nil, nil, errors.New("Hijack is not supported to this response writer!")
|
2017-01-02 20:20:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Flush sends any buffered data to the client.
|
2017-01-04 14:16:53 +01:00
|
|
|
func (w *responseWriter) Flush() {
|
2017-01-02 20:20:17 +01:00
|
|
|
// The Flusher interface is implemented by ResponseWriters that allow
|
|
|
|
// an HTTP handler to flush buffered data to the client.
|
|
|
|
//
|
|
|
|
// The default HTTP/1.x and HTTP/2 ResponseWriter implementations
|
|
|
|
// support Flusher, but ResponseWriter wrappers may not. Handlers
|
|
|
|
// should always test for this ability at runtime.
|
|
|
|
//
|
|
|
|
// Note that even for ResponseWriters that support Flush,
|
|
|
|
// if the client is connected through an HTTP proxy,
|
|
|
|
// the buffered data may not reach the client until the response
|
|
|
|
// completes.
|
|
|
|
if fl, isFlusher := w.ResponseWriter.(http.Flusher); isFlusher {
|
|
|
|
fl.Flush()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
// CloseNotify returns a channel that receives at most a
|
|
|
|
// single value (true) when the client connection has gone
|
|
|
|
// away.
|
|
|
|
//
|
|
|
|
// CloseNotify may wait to notify until Request.Body has been
|
|
|
|
// fully read.
|
|
|
|
//
|
|
|
|
// After the Handler has returned, there is no guarantee
|
|
|
|
// that the channel receives a value.
|
|
|
|
//
|
|
|
|
// If the protocol is HTTP/1.1 and CloseNotify is called while
|
|
|
|
// processing an idempotent request (such a GET) while
|
|
|
|
// HTTP/1.1 pipelining is in use, the arrival of a subsequent
|
|
|
|
// pipelined request may cause a value to be sent on the
|
|
|
|
// returned channel. In practice HTTP/1.1 pipelining is not
|
|
|
|
// enabled in browsers and not seen often in the wild. If this
|
|
|
|
// is a problem, use HTTP/2 or only use CloseNotify on methods
|
|
|
|
// such as POST.
|
|
|
|
func (w *responseWriter) CloseNotify() <-chan bool {
|
|
|
|
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
|
|
|
}
|
|
|
|
|
2017-01-02 20:20:17 +01:00
|
|
|
// clone returns a clone of this response writer
|
2017-01-04 14:16:53 +01:00
|
|
|
// it copies the header, status code, headers and the beforeFlush finally returns a new ResponseRecorder
|
|
|
|
func (w *responseWriter) clone() ResponseWriter {
|
|
|
|
wc := &responseWriter{}
|
2017-01-02 20:20:17 +01:00
|
|
|
wc.ResponseWriter = w.ResponseWriter
|
|
|
|
wc.statusCode = w.statusCode
|
|
|
|
wc.beforeFlush = w.beforeFlush
|
2017-01-04 14:16:53 +01:00
|
|
|
wc.statusCodeSent = w.statusCodeSent
|
2017-01-02 20:20:17 +01:00
|
|
|
return wc
|
|
|
|
}
|
|
|
|
|
|
|
|
// writeTo writes a response writer (temp: status code, headers and body) to another response writer
|
2017-01-04 14:16:53 +01:00
|
|
|
func (w *responseWriter) writeTo(to ResponseWriter) {
|
2017-01-02 20:20:17 +01:00
|
|
|
// set the status code, failure status code are first class
|
2017-01-04 14:16:53 +01:00
|
|
|
if w.statusCode >= 400 {
|
|
|
|
to.WriteHeader(w.statusCode)
|
2017-01-02 20:20:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// append the headers
|
2017-01-04 14:16:53 +01:00
|
|
|
if w.Header() != nil {
|
|
|
|
for k, values := range w.Header() {
|
2017-01-02 20:20:17 +01:00
|
|
|
for _, v := range values {
|
2017-01-04 14:16:53 +01:00
|
|
|
if to.Header().Get(v) == "" {
|
|
|
|
to.Header().Add(k, v)
|
2017-01-02 20:20:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2017-01-04 14:16:53 +01:00
|
|
|
// the body is not copied, this writer doesn't supports recording
|
|
|
|
}
|
2017-01-02 20:20:17 +01:00
|
|
|
|
2017-01-04 14:16:53 +01:00
|
|
|
func (w *responseWriter) releaseMe() {
|
|
|
|
releaseResponseWriter(w)
|
2017-01-02 20:20:17 +01:00
|
|
|
}
|