@@ -4,7 +4,13 @@ import stream = require('stream');
4
4
import { V1Status } from './api' ;
5
5
import { KubeConfig } from './config' ;
6
6
7
- const protocols = [ 'v4.channel.k8s.io' , 'v3.channel.k8s.io' , 'v2.channel.k8s.io' , 'channel.k8s.io' ] ;
7
+ const protocols = [
8
+ 'v5.channel.k8s.io' ,
9
+ 'v4.channel.k8s.io' ,
10
+ 'v3.channel.k8s.io' ,
11
+ 'v2.channel.k8s.io' ,
12
+ 'channel.k8s.io' ,
13
+ ] ;
8
14
9
15
export type TextHandler = ( text : string ) => boolean ;
10
16
export type BinaryHandler = ( stream : number , buff : Buffer ) => boolean ;
@@ -17,12 +23,41 @@ export interface WebSocketInterface {
17
23
) : Promise < WebSocket . WebSocket > ;
18
24
}
19
25
26
+ export interface StreamInterface {
27
+ stdin : stream . Readable ;
28
+ stdout : stream . Writable ;
29
+ stderr : stream . Writable ;
30
+ }
31
+
20
32
export class WebSocketHandler implements WebSocketInterface {
21
33
public static readonly StdinStream : number = 0 ;
22
34
public static readonly StdoutStream : number = 1 ;
23
35
public static readonly StderrStream : number = 2 ;
24
36
public static readonly StatusStream : number = 3 ;
25
37
public static readonly ResizeStream : number = 4 ;
38
+ public static readonly CloseStream : number = 255 ;
39
+
40
+ public negotiatedProtocol : string | null = null ;
41
+
42
+ public static supportsClose ( protocol : string ) : boolean {
43
+ return protocol === 'v5.channel.k8s.io' ;
44
+ }
45
+
46
+ public static closeStream ( streamNum : number , streams : StreamInterface ) : void {
47
+ console . log ( 'Closing stream: ' + streamNum ) ;
48
+ switch ( streamNum ) {
49
+ case WebSocketHandler . StdinStream :
50
+ streams . stdin . pause ( ) ;
51
+ break ;
52
+ case WebSocketHandler . StdoutStream :
53
+ console . log ( 'closing stdout' ) ;
54
+ streams . stdout . end ( ) ;
55
+ break ;
56
+ case WebSocketHandler . StderrStream :
57
+ streams . stderr . end ( ) ;
58
+ break ;
59
+ }
60
+ }
26
61
27
62
public static handleStandardStreams (
28
63
streamNum : number ,
@@ -39,6 +74,7 @@ export class WebSocketHandler implements WebSocketInterface {
39
74
stderr . write ( buff ) ;
40
75
} else if ( streamNum === WebSocketHandler . StatusStream ) {
41
76
// stream closing.
77
+ // Hacky, change tests to use the stream interface
42
78
if ( stdout && stdout !== process . stdout ) {
43
79
stdout . end ( ) ;
44
80
}
@@ -69,6 +105,12 @@ export class WebSocketHandler implements WebSocketInterface {
69
105
} ) ;
70
106
71
107
stdin . on ( 'end' , ( ) => {
108
+ if ( WebSocketHandler . supportsClose ( ws . protocol ) ) {
109
+ const buff = Buffer . alloc ( 2 ) ;
110
+ buff . writeUint8 ( this . CloseStream , 0 ) ;
111
+ buff . writeUint8 ( this . StdinStream , 1 ) ;
112
+ ws . send ( buff ) ;
113
+ }
72
114
ws . close ( ) ;
73
115
} ) ;
74
116
// Keep the stream open
@@ -141,7 +183,16 @@ export class WebSocketHandler implements WebSocketInterface {
141
183
// factory is really just for test injection
142
184
public constructor (
143
185
readonly config : KubeConfig ,
144
- readonly socketFactory ?: ( uri : string , opts : WebSocket . ClientOptions ) => WebSocket . WebSocket ,
186
+ readonly socketFactory ?: (
187
+ uri : string ,
188
+ protocols : string [ ] ,
189
+ opts : WebSocket . ClientOptions ,
190
+ ) => WebSocket . WebSocket ,
191
+ readonly streams : StreamInterface = {
192
+ stdin : process . stdin ,
193
+ stdout : process . stdout ,
194
+ stderr : process . stderr ,
195
+ } ,
145
196
) { }
146
197
147
198
/**
@@ -173,7 +224,7 @@ export class WebSocketHandler implements WebSocketInterface {
173
224
174
225
return await new Promise < WebSocket . WebSocket > ( ( resolve , reject ) => {
175
226
const client = this . socketFactory
176
- ? this . socketFactory ( uri , opts )
227
+ ? this . socketFactory ( uri , protocols , opts )
177
228
: new WebSocket ( uri , protocols , opts ) ;
178
229
let resolved = false ;
179
230
@@ -191,11 +242,18 @@ export class WebSocketHandler implements WebSocketInterface {
191
242
client . onmessage = ( { data } : { data : WebSocket . Data } ) => {
192
243
// TODO: support ArrayBuffer and Buffer[] data types?
193
244
if ( typeof data === 'string' ) {
245
+ if ( data . charCodeAt ( 0 ) === WebSocketHandler . CloseStream ) {
246
+ WebSocketHandler . closeStream ( data . charCodeAt ( 1 ) , this . streams ) ;
247
+ }
194
248
if ( textHandler && ! textHandler ( data ) ) {
195
249
client . close ( ) ;
196
250
}
197
251
} else if ( data instanceof Buffer ) {
198
- const streamNum = data . readInt8 ( 0 ) ;
252
+ const streamNum = data . readUint8 ( 0 ) ;
253
+ if ( streamNum === WebSocketHandler . CloseStream ) {
254
+ console . log ( 'Closing stream!' ) ;
255
+ WebSocketHandler . closeStream ( data . readInt8 ( 1 ) , this . streams ) ;
256
+ }
199
257
if ( binaryHandler && ! binaryHandler ( streamNum , data . subarray ( 1 ) ) ) {
200
258
client . close ( ) ;
201
259
}
0 commit comments