123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- package context
- import (
- "bufio"
- "errors"
- "io"
- "net"
- "net/http"
- "sync"
- )
- // ResponseWriter interface is used by the context to serve an HTTP handler to
- // construct an HTTP response.
- //
- // Note: Only this ResponseWriter is an interface in order to be able
- // for developers to change the response writer of the Context via `context.ResetResponseWriter`.
- // The rest of the response writers implementations (ResponseRecorder & CompressResponseWriter)
- // are coupled to the internal ResponseWriter implementation(*responseWriter).
- //
- // A ResponseWriter may not be used after the Handler
- // has returned.
- type ResponseWriter interface {
- http.ResponseWriter
- // Naive returns the simple, underline and original http.ResponseWriter
- // that backends this response writer.
- Naive() http.ResponseWriter
- // SetWriter sets the underline http.ResponseWriter
- // that this responseWriter should write on.
- SetWriter(underline http.ResponseWriter)
- // BeginResponse receives an http.ResponseWriter
- // and initialize or reset the response writer's field's values.
- BeginResponse(http.ResponseWriter)
- // EndResponse is the last function which is called right before the server sent the final response.
- //
- // Here is the place which we can make the last checks or do a cleanup.
- EndResponse()
- // IsHijacked reports whether this response writer's connection is hijacked.
- IsHijacked() bool
- // StatusCode returns the status code header value.
- StatusCode() int
- // Written should returns the total length of bytes that were being written to the client.
- // In addition iris provides some variables to help low-level actions:
- // NoWritten, means that nothing were written yet and the response writer is still live.
- // StatusCodeWritten, means that status code was written but no other bytes are written to the client, response writer may closed.
- // > 0 means that the reply was written and it's the total number of bytes were written.
- Written() int
- // SetWritten sets manually a value for written, it can be
- // NoWritten(-1) or StatusCodeWritten(0), > 0 means body length which is useless here.
- SetWritten(int)
- // SetBeforeFlush registers the unique callback which called exactly before the response is flushed to the client.
- SetBeforeFlush(cb func())
- // GetBeforeFlush returns (not execute) the before flush callback, or nil if not set by SetBeforeFlush.
- GetBeforeFlush() func()
- // FlushResponse should be called only once before EndResponse.
- // it tries to send the status code if not sent already
- // and calls the before flush callback, if any.
- //
- // FlushResponse can be called before EndResponse, but it should
- // be the last call of this response writer.
- FlushResponse()
- // clone returns a clone of this response writer
- // it copies the header, status code, headers and the beforeFlush finally returns a new ResponseRecorder.
- Clone() ResponseWriter
- // CopyTo writes a response writer (temp: status code, headers and body) to another response writer
- CopyTo(ResponseWriter)
- // Flusher indicates if `Flush` is supported by 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.
- Flusher() (http.Flusher, bool)
- // Flush sends any buffered data to the client.
- Flush() // required by compress writer.
- }
- // ResponseWriterBodyReseter can be implemented by
- // response writers that supports response body overriding
- // (e.g. recorder and compressed).
- type ResponseWriterBodyReseter interface {
- // ResetBody should reset the body and reports back if it could reset successfully.
- ResetBody()
- }
- // ResponseWriterDisabler can be implemented
- // by response writers that can be disabled and restored to their previous state
- // (e.g. compressed).
- type ResponseWriterDisabler interface {
- // Disable should disable this type of response writer and fallback to the default one.
- Disable()
- }
- // ResponseWriterReseter can be implemented
- // by response writers that can clear the whole response
- // so a new handler can write into this from the beginning.
- // E.g. recorder, compressed (full) and common writer (status code and headers).
- type ResponseWriterReseter interface {
- // Reset should reset the whole response and reports
- // whether it could reset successfully.
- Reset() bool
- }
- // ResponseWriterWriteTo can be implemented
- // by response writers that needs a special
- // encoding before writing to their buffers.
- // E.g. a custom recorder that wraps a custom compressed one.
- //
- // Not used by the framework itself.
- type ResponseWriterWriteTo interface {
- WriteTo(dest io.Writer, p []byte)
- }
- // +------------------------------------------------------------+
- // | Response Writer Implementation |
- // +------------------------------------------------------------+
- var rpool = sync.Pool{New: func() interface{} { return &responseWriter{} }}
- // AcquireResponseWriter returns a new *ResponseWriter from the pool.
- // Releasing is done automatically when request and response is done.
- func AcquireResponseWriter() ResponseWriter {
- return rpool.Get().(*responseWriter)
- }
- func releaseResponseWriter(w ResponseWriter) {
- rpool.Put(w)
- }
- // 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 | no needed any more as we have a variable to catch total len of written bytes
- written int // the total size of bytes were written
- // yes only one callback, we need simplicity here because on FireStatusCode 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 FireStatusCode (context's behavior)
- beforeFlush func()
- }
- var _ ResponseWriter = (*responseWriter)(nil)
- const (
- defaultStatusCode = http.StatusOK
- // NoWritten !=-1 => when nothing written before
- NoWritten = -1
- // StatusCodeWritten != 0 => when only status code written
- StatusCodeWritten = 0
- )
- // Naive returns the simple, underline and original http.ResponseWriter
- // that backends this response writer.
- func (w *responseWriter) Naive() http.ResponseWriter {
- return w.ResponseWriter
- }
- // BeginResponse receives an http.ResponseWriter
- // and initialize or reset the response writer's field's values.
- func (w *responseWriter) BeginResponse(underline http.ResponseWriter) {
- w.beforeFlush = nil
- w.written = NoWritten
- w.statusCode = defaultStatusCode
- w.SetWriter(underline)
- }
- // SetWriter sets the underline http.ResponseWriter
- // that this responseWriter should write on.
- func (w *responseWriter) SetWriter(underline http.ResponseWriter) {
- w.ResponseWriter = underline
- }
- // EndResponse is the last function which is called right before the server sent the final response.
- //
- // Here is the place which we can make the last checks or do a cleanup.
- func (w *responseWriter) EndResponse() {
- releaseResponseWriter(w)
- }
- // Reset clears headers, sets the status code to 200
- // and clears the cached body.
- //
- // Implements the `ResponseWriterReseter`.
- func (w *responseWriter) Reset() bool {
- if w.written > 0 {
- return false // if already written we can't reset this type of response writer.
- }
- h := w.Header()
- for k := range h {
- h[k] = nil
- }
- w.written = NoWritten
- w.statusCode = defaultStatusCode
- return true
- }
- // SetWritten sets manually a value for written, it can be
- // NoWritten(-1) or StatusCodeWritten(0), > 0 means body length which is useless here.
- func (w *responseWriter) SetWritten(n int) {
- if n >= NoWritten && n <= StatusCodeWritten {
- w.written = n
- }
- }
- // Written should returns the total length of bytes that were being written to the client.
- // In addition iris provides some variables to help low-level actions:
- // NoWritten, means that nothing were written yet and the response writer is still live.
- // StatusCodeWritten, means that status code were written but no other bytes are written to the client, response writer may closed.
- // > 0 means that the reply was written and it's the total number of bytes were written.
- func (w *responseWriter) Written() int {
- return w.written
- }
- // 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.
- func (w *responseWriter) WriteHeader(statusCode int) {
- w.statusCode = statusCode
- }
- func (w *responseWriter) tryWriteHeader() {
- if w.written == NoWritten { // before write, once.
- w.written = StatusCodeWritten
- w.ResponseWriter.WriteHeader(w.statusCode)
- }
- }
- // IsHijacked reports whether this response writer's connection is hijacked.
- func (w *responseWriter) IsHijacked() bool {
- // Note:
- // A zero-byte `ResponseWriter.Write` on a hijacked connection will
- // return `http.ErrHijacked` without any other side effects.
- _, err := w.ResponseWriter.Write(nil)
- return err == http.ErrHijacked
- }
- // Write writes to the client
- // 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.
- func (w *responseWriter) Write(contents []byte) (int, error) {
- w.tryWriteHeader()
- n, err := w.ResponseWriter.Write(contents)
- w.written += n
- return n, err
- }
- // StatusCode returns the status code header value
- func (w *responseWriter) StatusCode() int {
- return w.statusCode
- }
- func (w *responseWriter) GetBeforeFlush() func() {
- return w.beforeFlush
- }
- // 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()
- }
- // Clone returns a clone of this response writer
- // it copies the header, status code, headers and the beforeFlush finally returns a new ResponseRecorder.
- func (w *responseWriter) Clone() ResponseWriter {
- wc := &responseWriter{}
- wc.ResponseWriter = w.ResponseWriter
- wc.statusCode = w.statusCode
- wc.beforeFlush = w.beforeFlush
- wc.written = w.written
- return wc
- }
- // CopyTo writes a response writer (temp: status code, headers and body) to another response writer.
- func (w *responseWriter) CopyTo(to ResponseWriter) {
- // set the status code, failure status code are first class
- if w.statusCode >= 400 {
- to.WriteHeader(w.statusCode)
- }
- // append the headers
- for k, values := range w.Header() {
- for _, v := range values {
- if to.Header().Get(v) == "" {
- to.Header().Add(k, v)
- }
- }
- }
- // the body is not copied, this writer doesn't support recording
- }
- // ErrHijackNotSupported is returned by the Hijack method to
- // indicate that Hijack feature is not available.
- var ErrHijackNotSupported = errors.New("hijack is not supported by this ResponseWriter")
- // 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.
- func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
- if h, isHijacker := w.ResponseWriter.(http.Hijacker); isHijacker {
- w.written = StatusCodeWritten
- return h.Hijack()
- }
- return nil, nil, ErrHijackNotSupported
- }
- // Flusher indicates if `Flush` is supported by 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.
- func (w *responseWriter) Flusher() (http.Flusher, bool) {
- flusher, canFlush := w.ResponseWriter.(http.Flusher)
- return flusher, canFlush
- }
- // Flush sends any buffered data to the client.
- func (w *responseWriter) Flush() {
- if flusher, ok := w.Flusher(); ok {
- // Flow: WriteHeader -> Flush -> Write -> Write -> Write....
- w.tryWriteHeader()
- flusher.Flush()
- }
- }
|