k8s.io/apiserver@v0.31.1/pkg/endpoints/responsewriter/wrapper.go (about) 1 /* 2 Copyright 2021 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package responsewriter 18 19 import ( 20 "bufio" 21 "net" 22 "net/http" 23 ) 24 25 // UserProvidedDecorator represensts a user (client that uses this package) 26 // provided decorator that wraps an inner http.ResponseWriter object. 27 // The user-provided decorator object must return the inner (decorated) 28 // http.ResponseWriter object via the Unwrap function. 29 type UserProvidedDecorator interface { 30 http.ResponseWriter 31 32 // Unwrap returns the inner http.ResponseWriter object associated 33 // with the user-provided decorator. 34 Unwrap() http.ResponseWriter 35 } 36 37 // WrapForHTTP1Or2 accepts a user-provided decorator of an "inner" http.responseWriter 38 // object and potentially wraps the user-provided decorator with a new http.ResponseWriter 39 // object that implements http.CloseNotifier, http.Flusher, and/or http.Hijacker by 40 // delegating to the user-provided decorator (if it implements the relevant method) or 41 // the inner http.ResponseWriter (otherwise), so that the returned http.ResponseWriter 42 // object implements the same subset of those interfaces as the inner http.ResponseWriter. 43 // 44 // This function handles the following three casses. 45 // - The inner ResponseWriter implements `http.CloseNotifier`, `http.Flusher`, 46 // and `http.Hijacker` (an HTTP/1.1 sever provides such a ResponseWriter). 47 // - The inner ResponseWriter implements `http.CloseNotifier` and `http.Flusher` 48 // but not `http.Hijacker` (an HTTP/2 server provides such a ResponseWriter). 49 // - All the other cases collapse to this one, in which the given ResponseWriter is returned. 50 // 51 // There are three applicable terms: 52 // - "outer": this is the ResponseWriter object returned by the WrapForHTTP1Or2 function. 53 // - "user-provided decorator" or "middle": this is the user-provided decorator 54 // that decorates an inner ResponseWriter object. A user-provided decorator 55 // implements the UserProvidedDecorator interface. A user-provided decorator 56 // may or may not implement http.CloseNotifier, http.Flusher or http.Hijacker. 57 // - "inner": the ResponseWriter that the user-provided decorator extends. 58 func WrapForHTTP1Or2(decorator UserProvidedDecorator) http.ResponseWriter { 59 // from go net/http documentation: 60 // The default HTTP/1.x and HTTP/2 ResponseWriter implementations support Flusher 61 // Handlers should always test for this ability at runtime. 62 // 63 // The Hijacker interface is implemented by ResponseWriters that allow an HTTP handler 64 // to take over the connection. 65 // The default ResponseWriter for HTTP/1.x connections supports Hijacker, but HTTP/2 connections 66 // intentionally do not. ResponseWriter wrappers may also not support Hijacker. 67 // Handlers should always test for this ability at runtime 68 // 69 // The CloseNotifier interface is implemented by ResponseWriters which allow detecting 70 // when the underlying connection has gone away. 71 // Deprecated: the CloseNotifier interface predates Go's context package. 72 // New code should use Request.Context instead. 73 inner := decorator.Unwrap() 74 if innerNotifierFlusher, ok := inner.(CloseNotifierFlusher); ok { 75 // for HTTP/2 request, the default ResponseWriter object (http2responseWriter) 76 // implements Flusher and CloseNotifier. 77 outerHTTP2 := outerWithCloseNotifyAndFlush{ 78 UserProvidedDecorator: decorator, 79 InnerCloseNotifierFlusher: innerNotifierFlusher, 80 } 81 82 if innerHijacker, hijackable := inner.(http.Hijacker); hijackable { 83 // for HTTP/1.x request the default implementation of ResponseWriter 84 // also implement CloseNotifier, Flusher and Hijacker 85 return &outerWithCloseNotifyFlushAndHijack{ 86 outerWithCloseNotifyAndFlush: outerHTTP2, 87 InnerHijacker: innerHijacker, 88 } 89 } 90 91 return outerHTTP2 92 } 93 94 // we should never be here for either http/1.x or http2 request 95 return decorator 96 } 97 98 // CloseNotifierFlusher is a combination of http.CloseNotifier and http.Flusher 99 // This applies to both http/1.x and http2 requests. 100 type CloseNotifierFlusher interface { 101 http.CloseNotifier 102 http.Flusher 103 } 104 105 // GetOriginal goes through the chain of wrapped http.ResponseWriter objects 106 // and returns the original http.ResponseWriter object provided to the first 107 // request handler in the filter chain. 108 func GetOriginal(w http.ResponseWriter) http.ResponseWriter { 109 decorator, ok := w.(UserProvidedDecorator) 110 if !ok { 111 return w 112 } 113 114 inner := decorator.Unwrap() 115 if inner == w { 116 // infinite cycle here, we should never be here though. 117 panic("http.ResponseWriter decorator chain has a cycle") 118 } 119 120 return GetOriginal(inner) 121 } 122 123 //nolint:staticcheck // SA1019 124 var _ http.CloseNotifier = outerWithCloseNotifyAndFlush{} 125 var _ http.Flusher = outerWithCloseNotifyAndFlush{} 126 var _ http.ResponseWriter = outerWithCloseNotifyAndFlush{} 127 var _ UserProvidedDecorator = outerWithCloseNotifyAndFlush{} 128 129 // outerWithCloseNotifyAndFlush is the outer object that extends the 130 // user provied decorator with http.CloseNotifier and http.Flusher only. 131 type outerWithCloseNotifyAndFlush struct { 132 // UserProvidedDecorator is the user-provided object, it decorates 133 // an inner ResponseWriter object. 134 UserProvidedDecorator 135 136 // http.CloseNotifier and http.Flusher for the inner object 137 InnerCloseNotifierFlusher CloseNotifierFlusher 138 } 139 140 func (wr outerWithCloseNotifyAndFlush) CloseNotify() <-chan bool { 141 if notifier, ok := wr.UserProvidedDecorator.(http.CloseNotifier); ok { 142 return notifier.CloseNotify() 143 } 144 145 return wr.InnerCloseNotifierFlusher.CloseNotify() 146 } 147 148 func (wr outerWithCloseNotifyAndFlush) Flush() { 149 if flusher, ok := wr.UserProvidedDecorator.(http.Flusher); ok { 150 flusher.Flush() 151 return 152 } 153 154 wr.InnerCloseNotifierFlusher.Flush() 155 } 156 157 //lint:file-ignore SA1019 Keep supporting deprecated http.CloseNotifier 158 var _ http.CloseNotifier = outerWithCloseNotifyFlushAndHijack{} 159 var _ http.Flusher = outerWithCloseNotifyFlushAndHijack{} 160 var _ http.Hijacker = outerWithCloseNotifyFlushAndHijack{} 161 var _ http.ResponseWriter = outerWithCloseNotifyFlushAndHijack{} 162 var _ UserProvidedDecorator = outerWithCloseNotifyFlushAndHijack{} 163 164 // outerWithCloseNotifyFlushAndHijack is the outer object that extends the 165 // user-provided decorator with http.CloseNotifier, http.Flusher and http.Hijacker. 166 // This applies to http/1.x requests only. 167 type outerWithCloseNotifyFlushAndHijack struct { 168 outerWithCloseNotifyAndFlush 169 170 // http.Hijacker for the inner object 171 InnerHijacker http.Hijacker 172 } 173 174 func (wr outerWithCloseNotifyFlushAndHijack) Hijack() (net.Conn, *bufio.ReadWriter, error) { 175 if hijacker, ok := wr.UserProvidedDecorator.(http.Hijacker); ok { 176 return hijacker.Hijack() 177 } 178 179 return wr.InnerHijacker.Hijack() 180 }