github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/httpstream/spdy/upgrade.go (about)

     1  /*
     2  Copyright 2015 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 spdy
    18  
    19  import (
    20  	"bufio"
    21  	"fmt"
    22  	"io"
    23  	"net"
    24  	"net/http"
    25  	"strings"
    26  	"sync/atomic"
    27  	"time"
    28  
    29  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/httpstream"
    30  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/runtime"
    31  )
    32  
    33  const HeaderSpdy31 = "SPDY/3.1"
    34  
    35  // responseUpgrader knows how to upgrade HTTP responses. It
    36  // implements the httpstream.ResponseUpgrader interface.
    37  type responseUpgrader struct {
    38  	pingPeriod time.Duration
    39  }
    40  
    41  // connWrapper is used to wrap a hijacked connection and its bufio.Reader. All
    42  // calls will be handled directly by the underlying net.Conn with the exception
    43  // of Read and Close calls, which will consider data in the bufio.Reader. This
    44  // ensures that data already inside the used bufio.Reader instance is also
    45  // read.
    46  type connWrapper struct {
    47  	net.Conn
    48  	closed    int32
    49  	bufReader *bufio.Reader
    50  }
    51  
    52  func (w *connWrapper) Read(b []byte) (n int, err error) {
    53  	if atomic.LoadInt32(&w.closed) == 1 {
    54  		return 0, io.EOF
    55  	}
    56  	return w.bufReader.Read(b)
    57  }
    58  
    59  func (w *connWrapper) Close() error {
    60  	err := w.Conn.Close()
    61  	atomic.StoreInt32(&w.closed, 1)
    62  	return err
    63  }
    64  
    65  // NewResponseUpgrader returns a new httpstream.ResponseUpgrader that is
    66  // capable of upgrading HTTP responses using SPDY/3.1 via the
    67  // spdystream package.
    68  func NewResponseUpgrader() httpstream.ResponseUpgrader {
    69  	return NewResponseUpgraderWithPings(0)
    70  }
    71  
    72  // NewResponseUpgraderWithPings returns a new httpstream.ResponseUpgrader that
    73  // is capable of upgrading HTTP responses using SPDY/3.1 via the spdystream
    74  // package.
    75  //
    76  // If pingPeriod is non-zero, for each incoming connection a background
    77  // goroutine will send periodic Ping frames to the server. Use this to keep
    78  // idle connections through certain load balancers alive longer.
    79  func NewResponseUpgraderWithPings(pingPeriod time.Duration) httpstream.ResponseUpgrader {
    80  	return responseUpgrader{pingPeriod: pingPeriod}
    81  }
    82  
    83  // UpgradeResponse upgrades an HTTP response to one that supports multiplexed
    84  // streams. newStreamHandler will be called synchronously whenever the
    85  // other end of the upgraded connection creates a new stream.
    86  func (u responseUpgrader) UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection {
    87  	connectionHeader := strings.ToLower(req.Header.Get(httpstream.HeaderConnection))
    88  	upgradeHeader := strings.ToLower(req.Header.Get(httpstream.HeaderUpgrade))
    89  	if !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) {
    90  		errorMsg := fmt.Sprintf("unable to upgrade: missing upgrade headers in request: %#v", req.Header)
    91  		http.Error(w, errorMsg, http.StatusBadRequest)
    92  		return nil
    93  	}
    94  
    95  	hijacker, ok := w.(http.Hijacker)
    96  	if !ok {
    97  		errorMsg := "unable to upgrade: unable to hijack response"
    98  		http.Error(w, errorMsg, http.StatusInternalServerError)
    99  		return nil
   100  	}
   101  
   102  	w.Header().Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade)
   103  	w.Header().Add(httpstream.HeaderUpgrade, HeaderSpdy31)
   104  	w.WriteHeader(http.StatusSwitchingProtocols)
   105  
   106  	conn, bufrw, err := hijacker.Hijack()
   107  	if err != nil {
   108  		runtime.HandleError(fmt.Errorf("unable to upgrade: error hijacking response: %v", err))
   109  		return nil
   110  	}
   111  
   112  	connWithBuf := &connWrapper{Conn: conn, bufReader: bufrw.Reader}
   113  	spdyConn, err := NewServerConnectionWithPings(connWithBuf, newStreamHandler, u.pingPeriod)
   114  	if err != nil {
   115  		runtime.HandleError(fmt.Errorf("unable to upgrade: error creating SPDY server connection: %v", err))
   116  		return nil
   117  	}
   118  
   119  	return spdyConn
   120  }