From 1c9ea0113d007d57bb8f793ebe7a64a3dcad7bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Thu, 27 Apr 2023 01:44:01 +0200 Subject: caddyhttp: Impl `ResponseWriter.Unwrap()`, prep for Go 1.20's `ResponseController` (#5509) * feat: add support for ResponseWriter.Unwrap() * cherry-pick Francis' code --- modules/caddyhttp/responsewriter.go | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'modules/caddyhttp/responsewriter.go') diff --git a/modules/caddyhttp/responsewriter.go b/modules/caddyhttp/responsewriter.go index 1b28cf0..398bd15 100644 --- a/modules/caddyhttp/responsewriter.go +++ b/modules/caddyhttp/responsewriter.go @@ -72,6 +72,11 @@ func (rww *ResponseWriterWrapper) ReadFrom(r io.Reader) (n int64, err error) { return io.Copy(rww.ResponseWriter, r) } +// Unwrap returns the underlying ResponseWriter. +func (rww *ResponseWriterWrapper) Unwrap() http.ResponseWriter { + return rww.ResponseWriter +} + // HTTPInterfaces mix all the interfaces that middleware ResponseWriters need to support. type HTTPInterfaces interface { http.ResponseWriter -- cgit v1.2.3 From cd486c25d168caf58f4b6fe5d3252df9432901ec Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Wed, 2 Aug 2023 16:03:26 -0400 Subject: caddyhttp: Make use of `http.ResponseController` (#5654) * caddyhttp: Make use of http.ResponseController Also syncs the reverseproxy implementation with stdlib's which now uses ResponseController as well https://github.com/golang/go/commit/2449bbb5e614954ce9e99c8a481ea2ee73d72d61 * Enable full-duplex for HTTP/1.1 * Appease linter * Add warning for builds with Go 1.20, so it's less surprising to users * Improved godoc for EnableFullDuplex, copied text from stdlib * Only wrap in encode if not already wrapped --- modules/caddyhttp/responsewriter.go | 57 +++++++++---------------------------- 1 file changed, 14 insertions(+), 43 deletions(-) (limited to 'modules/caddyhttp/responsewriter.go') diff --git a/modules/caddyhttp/responsewriter.go b/modules/caddyhttp/responsewriter.go index 398bd15..37c2646 100644 --- a/modules/caddyhttp/responsewriter.go +++ b/modules/caddyhttp/responsewriter.go @@ -24,34 +24,14 @@ import ( ) // ResponseWriterWrapper wraps an underlying ResponseWriter and -// promotes its Pusher/Flusher/Hijacker methods as well. To use -// this type, embed a pointer to it within your own struct type -// that implements the http.ResponseWriter interface, then call -// methods on the embedded value. You can make sure your type -// wraps correctly by asserting that it implements the -// HTTPInterfaces interface. +// promotes its Pusher method as well. To use this type, embed +// a pointer to it within your own struct type that implements +// the http.ResponseWriter interface, then call methods on the +// embedded value. type ResponseWriterWrapper struct { http.ResponseWriter } -// Hijack implements http.Hijacker. It simply calls the underlying -// ResponseWriter's Hijack method if there is one, or returns -// ErrNotImplemented otherwise. -func (rww *ResponseWriterWrapper) Hijack() (net.Conn, *bufio.ReadWriter, error) { - if hj, ok := rww.ResponseWriter.(http.Hijacker); ok { - return hj.Hijack() - } - return nil, nil, ErrNotImplemented -} - -// Flush implements http.Flusher. It simply calls the underlying -// ResponseWriter's Flush method if there is one. -func (rww *ResponseWriterWrapper) Flush() { - if f, ok := rww.ResponseWriter.(http.Flusher); ok { - f.Flush() - } -} - // Push implements http.Pusher. It simply calls the underlying // ResponseWriter's Push method if there is one, or returns // ErrNotImplemented otherwise. @@ -62,29 +42,18 @@ func (rww *ResponseWriterWrapper) Push(target string, opts *http.PushOptions) er return ErrNotImplemented } -// ReadFrom implements io.ReaderFrom. It simply calls the underlying -// ResponseWriter's ReadFrom method if there is one, otherwise it defaults -// to io.Copy. +// ReadFrom implements io.ReaderFrom. It simply calls io.Copy, +// which uses io.ReaderFrom if available. func (rww *ResponseWriterWrapper) ReadFrom(r io.Reader) (n int64, err error) { - if rf, ok := rww.ResponseWriter.(io.ReaderFrom); ok { - return rf.ReadFrom(r) - } return io.Copy(rww.ResponseWriter, r) } -// Unwrap returns the underlying ResponseWriter. +// Unwrap returns the underlying ResponseWriter, necessary for +// http.ResponseController to work correctly. func (rww *ResponseWriterWrapper) Unwrap() http.ResponseWriter { return rww.ResponseWriter } -// HTTPInterfaces mix all the interfaces that middleware ResponseWriters need to support. -type HTTPInterfaces interface { - http.ResponseWriter - http.Pusher - http.Flusher - http.Hijacker -} - // ErrNotImplemented is returned when an underlying // ResponseWriter does not implement the required method. var ErrNotImplemented = fmt.Errorf("method not implemented") @@ -262,7 +231,8 @@ func (rr *responseRecorder) WriteResponse() error { } func (rr *responseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) { - conn, brw, err := rr.ResponseWriterWrapper.Hijack() + //nolint:bodyclose + conn, brw, err := http.NewResponseController(rr.ResponseWriterWrapper).Hijack() if err != nil { return nil, nil, err } @@ -294,7 +264,7 @@ func (hc *hijackedConn) ReadFrom(r io.Reader) (int64, error) { // responses instead of writing them to the client. See // docs for NewResponseRecorder for proper usage. type ResponseRecorder interface { - HTTPInterfaces + http.ResponseWriter Status() int Buffer() *bytes.Buffer Buffered() bool @@ -309,12 +279,13 @@ type ShouldBufferFunc func(status int, header http.Header) bool // Interface guards var ( - _ HTTPInterfaces = (*ResponseWriterWrapper)(nil) - _ ResponseRecorder = (*responseRecorder)(nil) + _ http.ResponseWriter = (*ResponseWriterWrapper)(nil) + _ ResponseRecorder = (*responseRecorder)(nil) // Implementing ReaderFrom can be such a significant // optimization that it should probably be required! // see PR #5022 (25%-50% speedup) _ io.ReaderFrom = (*ResponseWriterWrapper)(nil) _ io.ReaderFrom = (*responseRecorder)(nil) + _ io.ReaderFrom = (*hijackedConn)(nil) ) -- cgit v1.2.3