websocket.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. Copyright 2016 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 remotecommand
  14. import (
  15. "fmt"
  16. "net/http"
  17. "time"
  18. "k8s.io/kubernetes/pkg/httplog"
  19. "k8s.io/kubernetes/pkg/util/runtime"
  20. "k8s.io/kubernetes/pkg/util/wsstream"
  21. )
  22. const (
  23. stdinChannel = iota
  24. stdoutChannel
  25. stderrChannel
  26. errorChannel
  27. resizeChannel
  28. preV4BinaryWebsocketProtocol = wsstream.ChannelWebSocketProtocol
  29. preV4Base64WebsocketProtocol = wsstream.Base64ChannelWebSocketProtocol
  30. v4BinaryWebsocketProtocol = "v4." + wsstream.ChannelWebSocketProtocol
  31. v4Base64WebsocketProtocol = "v4." + wsstream.Base64ChannelWebSocketProtocol
  32. )
  33. // createChannels returns the standard channel types for a shell connection (STDIN 0, STDOUT 1, STDERR 2)
  34. // along with the approximate duplex value. It also creates the error (3) and resize (4) channels.
  35. func createChannels(opts *options) []wsstream.ChannelType {
  36. // open the requested channels, and always open the error channel
  37. channels := make([]wsstream.ChannelType, 5)
  38. channels[stdinChannel] = readChannel(opts.stdin)
  39. channels[stdoutChannel] = writeChannel(opts.stdout)
  40. channels[stderrChannel] = writeChannel(opts.stderr)
  41. channels[errorChannel] = wsstream.WriteChannel
  42. channels[resizeChannel] = wsstream.ReadChannel
  43. return channels
  44. }
  45. // readChannel returns wsstream.ReadChannel if real is true, or wsstream.IgnoreChannel.
  46. func readChannel(real bool) wsstream.ChannelType {
  47. if real {
  48. return wsstream.ReadChannel
  49. }
  50. return wsstream.IgnoreChannel
  51. }
  52. // writeChannel returns wsstream.WriteChannel if real is true, or wsstream.IgnoreChannel.
  53. func writeChannel(real bool) wsstream.ChannelType {
  54. if real {
  55. return wsstream.WriteChannel
  56. }
  57. return wsstream.IgnoreChannel
  58. }
  59. // createWebSocketStreams returns a context containing the websocket connection and
  60. // streams needed to perform an exec or an attach.
  61. func createWebSocketStreams(req *http.Request, w http.ResponseWriter, opts *options, idleTimeout time.Duration) (*context, bool) {
  62. channels := createChannels(opts)
  63. conn := wsstream.NewConn(map[string]wsstream.ChannelProtocolConfig{
  64. "": {
  65. Binary: true,
  66. Channels: channels,
  67. },
  68. preV4BinaryWebsocketProtocol: {
  69. Binary: true,
  70. Channels: channels,
  71. },
  72. preV4Base64WebsocketProtocol: {
  73. Binary: false,
  74. Channels: channels,
  75. },
  76. v4BinaryWebsocketProtocol: {
  77. Binary: true,
  78. Channels: channels,
  79. },
  80. v4Base64WebsocketProtocol: {
  81. Binary: false,
  82. Channels: channels,
  83. },
  84. })
  85. conn.SetIdleTimeout(idleTimeout)
  86. negotiatedProtocol, streams, err := conn.Open(httplog.Unlogged(w), req)
  87. if err != nil {
  88. runtime.HandleError(fmt.Errorf("Unable to upgrade websocket connection: %v", err))
  89. return nil, false
  90. }
  91. // Send an empty message to the lowest writable channel to notify the client the connection is established
  92. // TODO: make generic to SPDY and WebSockets and do it outside of this method?
  93. switch {
  94. case opts.stdout:
  95. streams[stdoutChannel].Write([]byte{})
  96. case opts.stderr:
  97. streams[stderrChannel].Write([]byte{})
  98. default:
  99. streams[errorChannel].Write([]byte{})
  100. }
  101. ctx := &context{
  102. conn: conn,
  103. stdinStream: streams[stdinChannel],
  104. stdoutStream: streams[stdoutChannel],
  105. stderrStream: streams[stderrChannel],
  106. tty: opts.tty,
  107. resizeStream: streams[resizeChannel],
  108. }
  109. switch negotiatedProtocol {
  110. case v4BinaryWebsocketProtocol, v4Base64WebsocketProtocol:
  111. ctx.writeStatus = v4WriteStatusFunc(streams[errorChannel])
  112. default:
  113. ctx.writeStatus = v1WriteStatusFunc(streams[errorChannel])
  114. }
  115. return ctx, true
  116. }