1// Copyright 2022 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package http
6
7import (
8	"bufio"
9	"fmt"
10	"net"
11	"time"
12)
13
14// A ResponseController is used by an HTTP handler to control the response.
15//
16// A ResponseController may not be used after the [Handler.ServeHTTP] method has returned.
17type ResponseController struct {
18	rw ResponseWriter
19}
20
21// NewResponseController creates a [ResponseController] for a request.
22//
23// The ResponseWriter should be the original value passed to the [Handler.ServeHTTP] method,
24// or have an Unwrap method returning the original ResponseWriter.
25//
26// If the ResponseWriter implements any of the following methods, the ResponseController
27// will call them as appropriate:
28//
29//	Flush()
30//	FlushError() error // alternative Flush returning an error
31//	Hijack() (net.Conn, *bufio.ReadWriter, error)
32//	SetReadDeadline(deadline time.Time) error
33//	SetWriteDeadline(deadline time.Time) error
34//	EnableFullDuplex() error
35//
36// If the ResponseWriter does not support a method, ResponseController returns
37// an error matching [ErrNotSupported].
38func NewResponseController(rw ResponseWriter) *ResponseController {
39	return &ResponseController{rw}
40}
41
42type rwUnwrapper interface {
43	Unwrap() ResponseWriter
44}
45
46// Flush flushes buffered data to the client.
47func (c *ResponseController) Flush() error {
48	rw := c.rw
49	for {
50		switch t := rw.(type) {
51		case interface{ FlushError() error }:
52			return t.FlushError()
53		case Flusher:
54			t.Flush()
55			return nil
56		case rwUnwrapper:
57			rw = t.Unwrap()
58		default:
59			return errNotSupported()
60		}
61	}
62}
63
64// Hijack lets the caller take over the connection.
65// See the Hijacker interface for details.
66func (c *ResponseController) Hijack() (net.Conn, *bufio.ReadWriter, error) {
67	rw := c.rw
68	for {
69		switch t := rw.(type) {
70		case Hijacker:
71			return t.Hijack()
72		case rwUnwrapper:
73			rw = t.Unwrap()
74		default:
75			return nil, nil, errNotSupported()
76		}
77	}
78}
79
80// SetReadDeadline sets the deadline for reading the entire request, including the body.
81// Reads from the request body after the deadline has been exceeded will return an error.
82// A zero value means no deadline.
83//
84// Setting the read deadline after it has been exceeded will not extend it.
85func (c *ResponseController) SetReadDeadline(deadline time.Time) error {
86	rw := c.rw
87	for {
88		switch t := rw.(type) {
89		case interface{ SetReadDeadline(time.Time) error }:
90			return t.SetReadDeadline(deadline)
91		case rwUnwrapper:
92			rw = t.Unwrap()
93		default:
94			return errNotSupported()
95		}
96	}
97}
98
99// SetWriteDeadline sets the deadline for writing the response.
100// Writes to the response body after the deadline has been exceeded will not block,
101// but may succeed if the data has been buffered.
102// A zero value means no deadline.
103//
104// Setting the write deadline after it has been exceeded will not extend it.
105func (c *ResponseController) SetWriteDeadline(deadline time.Time) error {
106	rw := c.rw
107	for {
108		switch t := rw.(type) {
109		case interface{ SetWriteDeadline(time.Time) error }:
110			return t.SetWriteDeadline(deadline)
111		case rwUnwrapper:
112			rw = t.Unwrap()
113		default:
114			return errNotSupported()
115		}
116	}
117}
118
119// EnableFullDuplex indicates that the request handler will interleave reads from [Request.Body]
120// with writes to the [ResponseWriter].
121//
122// For HTTP/1 requests, the Go HTTP server by default consumes any unread portion of
123// the request body before beginning to write the response, preventing handlers from
124// concurrently reading from the request and writing the response.
125// Calling EnableFullDuplex disables this behavior and permits handlers to continue to read
126// from the request while concurrently writing the response.
127//
128// For HTTP/2 requests, the Go HTTP server always permits concurrent reads and responses.
129func (c *ResponseController) EnableFullDuplex() error {
130	rw := c.rw
131	for {
132		switch t := rw.(type) {
133		case interface{ EnableFullDuplex() error }:
134			return t.EnableFullDuplex()
135		case rwUnwrapper:
136			rw = t.Unwrap()
137		default:
138			return errNotSupported()
139		}
140	}
141}
142
143// errNotSupported returns an error that Is ErrNotSupported,
144// but is not == to it.
145func errNotSupported() error {
146	return fmt.Errorf("%w", ErrNotSupported)
147}
148