1/* 2 Copyright The containerd 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/* 18Copyright 2016 The Kubernetes Authors. 19 20Licensed under the Apache License, Version 2.0 (the "License"); 21you may not use this file except in compliance with the License. 22You may obtain a copy of the License at 23 24 http://www.apache.org/licenses/LICENSE-2.0 25 26Unless required by applicable law or agreed to in writing, software 27distributed under the License is distributed on an "AS IS" BASIS, 28WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29See the License for the specific language governing permissions and 30limitations under the License. 31*/ 32 33package remotecommand 34 35import ( 36 "fmt" 37 "net/http" 38 "time" 39 40 "k8s.io/apimachinery/pkg/util/runtime" 41 "k8s.io/apiserver/pkg/server/httplog" 42 "k8s.io/apiserver/pkg/util/wsstream" 43) 44 45const ( 46 stdinChannel = iota 47 stdoutChannel 48 stderrChannel 49 errorChannel 50 resizeChannel 51 52 preV4BinaryWebsocketProtocol = wsstream.ChannelWebSocketProtocol 53 preV4Base64WebsocketProtocol = wsstream.Base64ChannelWebSocketProtocol 54 v4BinaryWebsocketProtocol = "v4." + wsstream.ChannelWebSocketProtocol 55 v4Base64WebsocketProtocol = "v4." + wsstream.Base64ChannelWebSocketProtocol 56) 57 58// createChannels returns the standard channel types for a shell connection (STDIN 0, STDOUT 1, STDERR 2) 59// along with the approximate duplex value. It also creates the error (3) and resize (4) channels. 60func createChannels(opts *Options) []wsstream.ChannelType { 61 // open the requested channels, and always open the error channel 62 channels := make([]wsstream.ChannelType, 5) 63 channels[stdinChannel] = readChannel(opts.Stdin) 64 channels[stdoutChannel] = writeChannel(opts.Stdout) 65 channels[stderrChannel] = writeChannel(opts.Stderr) 66 channels[errorChannel] = wsstream.WriteChannel 67 channels[resizeChannel] = wsstream.ReadChannel 68 return channels 69} 70 71// readChannel returns wsstream.ReadChannel if real is true, or wsstream.IgnoreChannel. 72func readChannel(real bool) wsstream.ChannelType { 73 if real { 74 return wsstream.ReadChannel 75 } 76 return wsstream.IgnoreChannel 77} 78 79// writeChannel returns wsstream.WriteChannel if real is true, or wsstream.IgnoreChannel. 80func writeChannel(real bool) wsstream.ChannelType { 81 if real { 82 return wsstream.WriteChannel 83 } 84 return wsstream.IgnoreChannel 85} 86 87// createWebSocketStreams returns a context containing the websocket connection and 88// streams needed to perform an exec or an attach. 89func createWebSocketStreams(req *http.Request, w http.ResponseWriter, opts *Options, idleTimeout time.Duration) (*context, bool) { 90 channels := createChannels(opts) 91 conn := wsstream.NewConn(map[string]wsstream.ChannelProtocolConfig{ 92 "": { 93 Binary: true, 94 Channels: channels, 95 }, 96 preV4BinaryWebsocketProtocol: { 97 Binary: true, 98 Channels: channels, 99 }, 100 preV4Base64WebsocketProtocol: { 101 Binary: false, 102 Channels: channels, 103 }, 104 v4BinaryWebsocketProtocol: { 105 Binary: true, 106 Channels: channels, 107 }, 108 v4Base64WebsocketProtocol: { 109 Binary: false, 110 Channels: channels, 111 }, 112 }) 113 conn.SetIdleTimeout(idleTimeout) 114 negotiatedProtocol, streams, err := conn.Open(httplog.Unlogged(req, w), req) 115 if err != nil { 116 runtime.HandleError(fmt.Errorf("unable to upgrade websocket connection: %v", err)) 117 return nil, false 118 } 119 120 // Send an empty message to the lowest writable channel to notify the client the connection is established 121 // TODO: make generic to SPDY and WebSockets and do it outside of this method? 122 switch { 123 case opts.Stdout: 124 streams[stdoutChannel].Write([]byte{}) 125 case opts.Stderr: 126 streams[stderrChannel].Write([]byte{}) 127 default: 128 streams[errorChannel].Write([]byte{}) 129 } 130 131 ctx := &context{ 132 conn: conn, 133 stdinStream: streams[stdinChannel], 134 stdoutStream: streams[stdoutChannel], 135 stderrStream: streams[stderrChannel], 136 tty: opts.TTY, 137 resizeStream: streams[resizeChannel], 138 } 139 140 switch negotiatedProtocol { 141 case v4BinaryWebsocketProtocol, v4Base64WebsocketProtocol: 142 ctx.writeStatus = v4WriteStatusFunc(streams[errorChannel]) 143 default: 144 ctx.writeStatus = v1WriteStatusFunc(streams[errorChannel]) 145 } 146 147 return ctx, true 148} 149