httpstream.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package httpstream
  14. import (
  15. "fmt"
  16. "io"
  17. "net/http"
  18. "strings"
  19. "time"
  20. )
  21. const (
  22. HeaderConnection = "Connection"
  23. HeaderUpgrade = "Upgrade"
  24. HeaderProtocolVersion = "X-Stream-Protocol-Version"
  25. HeaderAcceptedProtocolVersions = "X-Accepted-Stream-Protocol-Versions"
  26. )
  27. // NewStreamHandler defines a function that is called when a new Stream is
  28. // received. If no error is returned, the Stream is accepted; otherwise,
  29. // the stream is rejected. After the reply frame has been sent, replySent is closed.
  30. type NewStreamHandler func(stream Stream, replySent <-chan struct{}) error
  31. // NoOpNewStreamHandler is a stream handler that accepts a new stream and
  32. // performs no other logic.
  33. func NoOpNewStreamHandler(stream Stream, replySent <-chan struct{}) error { return nil }
  34. // Dialer knows how to open a streaming connection to a server.
  35. type Dialer interface {
  36. // Dial opens a streaming connection to a server using one of the protocols
  37. // specified (in order of most preferred to least preferred).
  38. Dial(protocols ...string) (Connection, string, error)
  39. }
  40. // UpgradeRoundTripper is a type of http.RoundTripper that is able to upgrade
  41. // HTTP requests to support multiplexed bidirectional streams. After RoundTrip()
  42. // is invoked, if the upgrade is successful, clients may retrieve the upgraded
  43. // connection by calling UpgradeRoundTripper.Connection().
  44. type UpgradeRoundTripper interface {
  45. http.RoundTripper
  46. // NewConnection validates the response and creates a new Connection.
  47. NewConnection(resp *http.Response) (Connection, error)
  48. }
  49. // ResponseUpgrader knows how to upgrade HTTP requests and responses to
  50. // add streaming support to them.
  51. type ResponseUpgrader interface {
  52. // UpgradeResponse upgrades an HTTP response to one that supports multiplexed
  53. // streams. newStreamHandler will be called asynchronously whenever the
  54. // other end of the upgraded connection creates a new stream.
  55. UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler NewStreamHandler) Connection
  56. }
  57. // Connection represents an upgraded HTTP connection.
  58. type Connection interface {
  59. // CreateStream creates a new Stream with the supplied headers.
  60. CreateStream(headers http.Header) (Stream, error)
  61. // Close resets all streams and closes the connection.
  62. Close() error
  63. // CloseChan returns a channel that is closed when the underlying connection is closed.
  64. CloseChan() <-chan bool
  65. // SetIdleTimeout sets the amount of time the connection may remain idle before
  66. // it is automatically closed.
  67. SetIdleTimeout(timeout time.Duration)
  68. }
  69. // Stream represents a bidirectional communications channel that is part of an
  70. // upgraded connection.
  71. type Stream interface {
  72. io.ReadWriteCloser
  73. // Reset closes both directions of the stream, indicating that neither client
  74. // or server can use it any more.
  75. Reset() error
  76. // Headers returns the headers used to create the stream.
  77. Headers() http.Header
  78. // Identifier returns the stream's ID.
  79. Identifier() uint32
  80. }
  81. // IsUpgradeRequest returns true if the given request is a connection upgrade request
  82. func IsUpgradeRequest(req *http.Request) bool {
  83. for _, h := range req.Header[http.CanonicalHeaderKey(HeaderConnection)] {
  84. if strings.Contains(strings.ToLower(h), strings.ToLower(HeaderUpgrade)) {
  85. return true
  86. }
  87. }
  88. return false
  89. }
  90. func negotiateProtocol(clientProtocols, serverProtocols []string) string {
  91. for i := range clientProtocols {
  92. for j := range serverProtocols {
  93. if clientProtocols[i] == serverProtocols[j] {
  94. return clientProtocols[i]
  95. }
  96. }
  97. }
  98. return ""
  99. }
  100. // Handshake performs a subprotocol negotiation. If the client did request a
  101. // subprotocol, Handshake will select the first common value found in
  102. // serverProtocols. If a match is found, Handshake adds a response header
  103. // indicating the chosen subprotocol. If no match is found, HTTP forbidden is
  104. // returned, along with a response header containing the list of protocols the
  105. // server can accept.
  106. func Handshake(req *http.Request, w http.ResponseWriter, serverProtocols []string) (string, error) {
  107. clientProtocols := req.Header[http.CanonicalHeaderKey(HeaderProtocolVersion)]
  108. if len(clientProtocols) == 0 {
  109. // Kube 1.0 clients didn't support subprotocol negotiation.
  110. // TODO require clientProtocols once Kube 1.0 is no longer supported
  111. return "", nil
  112. }
  113. if len(serverProtocols) == 0 {
  114. // Kube 1.0 servers didn't support subprotocol negotiation. This is mainly for testing.
  115. // TODO require serverProtocols once Kube 1.0 is no longer supported
  116. return "", nil
  117. }
  118. negotiatedProtocol := negotiateProtocol(clientProtocols, serverProtocols)
  119. if len(negotiatedProtocol) == 0 {
  120. w.WriteHeader(http.StatusForbidden)
  121. for i := range serverProtocols {
  122. w.Header().Add(HeaderAcceptedProtocolVersions, serverProtocols[i])
  123. }
  124. fmt.Fprintf(w, "unable to upgrade: unable to negotiate protocol: client supports %v, server accepts %v", clientProtocols, serverProtocols)
  125. return "", fmt.Errorf("unable to upgrade: unable to negotiate protocol: client supports %v, server supports %v", clientProtocols, serverProtocols)
  126. }
  127. w.Header().Add(HeaderProtocolVersion, negotiatedProtocol)
  128. return negotiatedProtocol, nil
  129. }