Description
This proposal seeks to address #16100 (no way of manipulating timeouts in Handler), and is inspired by #16100 (comment).
HTTP handler timeouts are specified on a per-Server basis: ReadTimeout
, WriteTimeout
. It would be very useful to permit these timeouts (and possibly other options) to be overridden on a per-handler basis. For example, a handler which serves a long-running streaming response may want to extend the WriteTimeout
after each write, and will likely want a different WriteTimeout
than a handler serving a short response.
A problem is that we have no good place at the moment to add functions that adjust these timeouts. We might add methods to the ResponseWriter
implementation and access them via type assertions (as is done with the existing Flush
and Hijack
methods), but this proliferation of undiscoverable magic methods scales poorly and does not interact well with middleware which wraps the ResponseWriter
type.
Proposal: Add a new concrete ResponseController
type which provides additional per-request controls. (A ResponseWriter
writes the response, a ResponseController
provides additional controls.) This type will supersede the existing Flusher
and Hijacker
APIs.
// A ResponseController is used by an HTTP handler to control the response.
//
// A ResponseController may not be used after the Handler.ServeHTTP method has returned.
type ResponseController struct{}
// NewResponseController creates a ResponseController for a request.
//
// The Request must be the original value passed to the Handler.ServeHTTP method.
// The ResponseWriter must be the original value passed to the Handler.ServeHTTP method,
// or have an Unwrap() method returning the original ResponseWriter.
func NewResponseController(rw ResponseWriter, req *Request) *ResponseController
// Flush flushes buffered data to the client.
func (rc *ResponseController) Flush() error
// Hijack lets the caller take over the connection.
// See the Hijacker interface for details.
func (rc *ResponseController) Hijack() (net.Conn, *bufio.ReadWriter, error)
We additionally add the ability to set the read and write deadline on a per-request basis, via ResponseController
. These functions take a deadline rather than a timeout, for consistency with net.Conn
.
// SetReadDeadline sets the deadline for reading the entire request, including the body.
// Reads from the request body after the deadline has been exceeded will return an error.
// A zero value means no deadline.
//
// The read deadline may not be extended after it has been exceeded.
func (rc *ResponseController) SetReadDeadline(deadline time.Time) error
// SetWriteDeadline sets the deadline for writing the response.
// Writes to the response body after the deadline has been exceeded will not block,
// but may succeed if the data has been buffered.
// A zero value means no deadline.
//
// The write deadline may not be extended after it has been exceeded.
func (rc *ResponseController) SetWriteDeadline(deadline time.Time) error
The Handler returned by http.TimeoutHandler
currently receives a ResponseWriter
which does not implement the Flush
or Hijack
methods. This will not change under this proposal: The *ResponseController
for a TimeoutHandler
will return a not-implemented error from Flush
, Hijack
, SetWriteDeadline
, and SetReadDeadline
.