httpstream.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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. // RemoveStreams can be used to remove a set of streams from the Connection.
  69. RemoveStreams(streams ...Stream)
  70. }
  71. // Stream represents a bidirectional communications channel that is part of an
  72. // upgraded connection.
  73. type Stream interface {
  74. io.ReadWriteCloser
  75. // Reset closes both directions of the stream, indicating that neither client
  76. // or server can use it any more.
  77. Reset() error
  78. // Headers returns the headers used to create the stream.
  79. Headers() http.Header
  80. // Identifier returns the stream's ID.
  81. Identifier() uint32
  82. }
  83. // IsUpgradeRequest returns true if the given request is a connection upgrade request
  84. func IsUpgradeRequest(req *http.Request) bool {
  85. for _, h := range req.Header[http.CanonicalHeaderKey(HeaderConnection)] {
  86. if strings.Contains(strings.ToLower(h), strings.ToLower(HeaderUpgrade)) {
  87. return true
  88. }
  89. }
  90. return false
  91. }
  92. func negotiateProtocol(clientProtocols, serverProtocols []string) string {
  93. for i := range clientProtocols {
  94. for j := range serverProtocols {
  95. if clientProtocols[i] == serverProtocols[j] {
  96. return clientProtocols[i]
  97. }
  98. }
  99. }
  100. return ""
  101. }
  102. func commaSeparatedHeaderValues(header []string) []string {
  103. var parsedClientProtocols []string
  104. for i := range header {
  105. for _, clientProtocol := range strings.Split(header[i], ",") {
  106. if proto := strings.Trim(clientProtocol, " "); len(proto) > 0 {
  107. parsedClientProtocols = append(parsedClientProtocols, proto)
  108. }
  109. }
  110. }
  111. return parsedClientProtocols
  112. }
  113. // Handshake performs a subprotocol negotiation. If the client did request a
  114. // subprotocol, Handshake will select the first common value found in
  115. // serverProtocols. If a match is found, Handshake adds a response header
  116. // indicating the chosen subprotocol. If no match is found, HTTP forbidden is
  117. // returned, along with a response header containing the list of protocols the
  118. // server can accept.
  119. func Handshake(req *http.Request, w http.ResponseWriter, serverProtocols []string) (string, error) {
  120. clientProtocols := commaSeparatedHeaderValues(req.Header[http.CanonicalHeaderKey(HeaderProtocolVersion)])
  121. if len(clientProtocols) == 0 {
  122. return "", fmt.Errorf("unable to upgrade: %s is required", HeaderProtocolVersion)
  123. }
  124. if len(serverProtocols) == 0 {
  125. panic(fmt.Errorf("unable to upgrade: serverProtocols is required"))
  126. }
  127. negotiatedProtocol := negotiateProtocol(clientProtocols, serverProtocols)
  128. if len(negotiatedProtocol) == 0 {
  129. for i := range serverProtocols {
  130. w.Header().Add(HeaderAcceptedProtocolVersions, serverProtocols[i])
  131. }
  132. err := fmt.Errorf("unable to upgrade: unable to negotiate protocol: client supports %v, server accepts %v", clientProtocols, serverProtocols)
  133. http.Error(w, err.Error(), http.StatusForbidden)
  134. return "", err
  135. }
  136. w.Header().Add(HeaderProtocolVersion, negotiatedProtocol)
  137. return negotiatedProtocol, nil
  138. }