@@ -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,37 @@ 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 static supportsClose ( protocol : string ) : boolean {
41
+ return protocol === 'v5.channel.k8s.io' ;
42
+ }
43
+
44
+ public static closeStream ( streamNum : number , streams : StreamInterface ) : void {
45
+ switch ( streamNum ) {
46
+ case WebSocketHandler . StdinStream :
47
+ streams . stdin . pause ( ) ;
48
+ break ;
49
+ case WebSocketHandler . StdoutStream :
50
+ streams . stdout . end ( ) ;
51
+ break ;
52
+ case WebSocketHandler . StderrStream :
53
+ streams . stderr . end ( ) ;
54
+ break ;
55
+ }
56
+ }
26
57
27
58
public static handleStandardStreams (
28
59
streamNum : number ,
@@ -39,6 +70,7 @@ export class WebSocketHandler implements WebSocketInterface {
39
70
stderr . write ( buff ) ;
40
71
} else if ( streamNum === WebSocketHandler . StatusStream ) {
41
72
// stream closing.
73
+ // Hacky, change tests to use the stream interface
42
74
if ( stdout && stdout !== process . stdout ) {
43
75
stdout . end ( ) ;
44
76
}
@@ -69,6 +101,12 @@ export class WebSocketHandler implements WebSocketInterface {
69
101
} ) ;
70
102
71
103
stdin . on ( 'end' , ( ) => {
104
+ if ( WebSocketHandler . supportsClose ( ws . protocol ) ) {
105
+ const buff = Buffer . alloc ( 2 ) ;
106
+ buff . writeUint8 ( this . CloseStream , 0 ) ;
107
+ buff . writeUint8 ( this . StdinStream , 1 ) ;
108
+ ws . send ( buff ) ;
109
+ }
72
110
ws . close ( ) ;
73
111
} ) ;
74
112
// Keep the stream open
@@ -141,7 +179,16 @@ export class WebSocketHandler implements WebSocketInterface {
141
179
// factory is really just for test injection
142
180
public constructor (
143
181
readonly config : KubeConfig ,
144
- readonly socketFactory ?: ( uri : string , opts : WebSocket . ClientOptions ) => WebSocket . WebSocket ,
182
+ readonly socketFactory ?: (
183
+ uri : string ,
184
+ protocols : string [ ] ,
185
+ opts : WebSocket . ClientOptions ,
186
+ ) => WebSocket . WebSocket ,
187
+ readonly streams : StreamInterface = {
188
+ stdin : process . stdin ,
189
+ stdout : process . stdout ,
190
+ stderr : process . stderr ,
191
+ } ,
145
192
) { }
146
193
147
194
/**
@@ -173,7 +220,7 @@ export class WebSocketHandler implements WebSocketInterface {
173
220
174
221
return await new Promise < WebSocket . WebSocket > ( ( resolve , reject ) => {
175
222
const client = this . socketFactory
176
- ? this . socketFactory ( uri , opts )
223
+ ? this . socketFactory ( uri , protocols , opts )
177
224
: new WebSocket ( uri , protocols , opts ) ;
178
225
let resolved = false ;
179
226
@@ -191,11 +238,17 @@ export class WebSocketHandler implements WebSocketInterface {
191
238
client . onmessage = ( { data } : { data : WebSocket . Data } ) => {
192
239
// TODO: support ArrayBuffer and Buffer[] data types?
193
240
if ( typeof data === 'string' ) {
241
+ if ( data . charCodeAt ( 0 ) === WebSocketHandler . CloseStream ) {
242
+ WebSocketHandler . closeStream ( data . charCodeAt ( 1 ) , this . streams ) ;
243
+ }
194
244
if ( textHandler && ! textHandler ( data ) ) {
195
245
client . close ( ) ;
196
246
}
197
247
} else if ( data instanceof Buffer ) {
198
- const streamNum = data . readInt8 ( 0 ) ;
248
+ const streamNum = data . readUint8 ( 0 ) ;
249
+ if ( streamNum === WebSocketHandler . CloseStream ) {
250
+ WebSocketHandler . closeStream ( data . readInt8 ( 1 ) , this . streams ) ;
251
+ }
199
252
if ( binaryHandler && ! binaryHandler ( streamNum , data . subarray ( 1 ) ) ) {
200
253
client . close ( ) ;
201
254
}
0 commit comments