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  }