| // Copyright 2022 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package http | |
| import ( | |
| "bufio" | |
| "fmt" | |
| "net" | |
| "time" | |
| ) | |
| // 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 { | |
| rw ResponseWriter | |
| } | |
| // NewResponseController creates a [ResponseController] for a request. | |
| // | |
| // The ResponseWriter should be the original value passed to the [Handler.ServeHTTP] method, | |
| // or have an Unwrap method returning the original ResponseWriter. | |
| // | |
| // If the ResponseWriter implements any of the following methods, the ResponseController | |
| // will call them as appropriate: | |
| // | |
| // Flush() | |
| // FlushError() error // alternative Flush returning an error | |
| // Hijack() (net.Conn, *bufio.ReadWriter, error) | |
| // SetReadDeadline(deadline time.Time) error | |
| // SetWriteDeadline(deadline time.Time) error | |
| // EnableFullDuplex() error | |
| // | |
| // If the ResponseWriter does not support a method, ResponseController returns | |
| // an error matching [ErrNotSupported]. | |
| func NewResponseController(rw ResponseWriter) *ResponseController { | |
| return &ResponseController{rw} | |
| } | |
| type rwUnwrapper interface { | |
| Unwrap() ResponseWriter | |
| } | |
| // Flush flushes buffered data to the client. | |
| func (c *ResponseController) Flush() error { | |
| rw := c.rw | |
| for { | |
| switch t := rw.(type) { | |
| case interface{ FlushError() error }: | |
| return t.FlushError() | |
| case Flusher: | |
| t.Flush() | |
| return nil | |
| case rwUnwrapper: | |
| rw = t.Unwrap() | |
| default: | |
| return errNotSupported() | |
| } | |
| } | |
| } | |
| // Hijack lets the caller take over the connection. | |
| // See the [Hijacker] interface for details. | |
| func (c *ResponseController) Hijack() (net.Conn, *bufio.ReadWriter, error) { | |
| rw := c.rw | |
| for { | |
| switch t := rw.(type) { | |
| case Hijacker: | |
| return t.Hijack() | |
| case rwUnwrapper: | |
| rw = t.Unwrap() | |
| default: | |
| return nil, nil, errNotSupported() | |
| } | |
| } | |
| } | |
| // 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. | |
| // | |
| // Setting the read deadline after it has been exceeded will not extend it. | |
| func (c *ResponseController) SetReadDeadline(deadline time.Time) error { | |
| rw := c.rw | |
| for { | |
| switch t := rw.(type) { | |
| case interface{ SetReadDeadline(time.Time) error }: | |
| return t.SetReadDeadline(deadline) | |
| case rwUnwrapper: | |
| rw = t.Unwrap() | |
| default: | |
| return errNotSupported() | |
| } | |
| } | |
| } | |
| // 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. | |
| // | |
| // Setting the write deadline after it has been exceeded will not extend it. | |
| func (c *ResponseController) SetWriteDeadline(deadline time.Time) error { | |
| rw := c.rw | |
| for { | |
| switch t := rw.(type) { | |
| case interface{ SetWriteDeadline(time.Time) error }: | |
| return t.SetWriteDeadline(deadline) | |
| case rwUnwrapper: | |
| rw = t.Unwrap() | |
| default: | |
| return errNotSupported() | |
| } | |
| } | |
| } | |
| // EnableFullDuplex indicates that the request handler will interleave reads from [Request.Body] | |
| // with writes to the [ResponseWriter]. | |
| // | |
| // For HTTP/1 requests, the Go HTTP server by default consumes any unread portion of | |
| // the request body before beginning to write the response, preventing handlers from | |
| // concurrently reading from the request and writing the response. | |
| // Calling EnableFullDuplex disables this behavior and permits handlers to continue to read | |
| // from the request while concurrently writing the response. | |
| // | |
| // For HTTP/2 requests, the Go HTTP server always permits concurrent reads and responses. | |
| func (c *ResponseController) EnableFullDuplex() error { | |
| rw := c.rw | |
| for { | |
| switch t := rw.(type) { | |
| case interface{ EnableFullDuplex() error }: | |
| return t.EnableFullDuplex() | |
| case rwUnwrapper: | |
| rw = t.Unwrap() | |
| default: | |
| return errNotSupported() | |
| } | |
| } | |
| } | |
| // errNotSupported returns an error that Is ErrNotSupported, | |
| // but is not == to it. | |
| func errNotSupported() error { | |
| return fmt.Errorf("%w", ErrNotSupported) | |
| } |