@@ -4,7 +4,13 @@ import stream = require('stream');
44import  {  V1Status  }  from  './api' ; 
55import  {  KubeConfig  }  from  './config' ; 
66
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+ ] ; 
814
915export  type  TextHandler  =  ( text : string )  =>  boolean ; 
1016export  type  BinaryHandler  =  ( stream : number ,  buff : Buffer )  =>  boolean ; 
@@ -17,12 +23,37 @@ export interface WebSocketInterface {
1723    ) : Promise < WebSocket . WebSocket > ; 
1824} 
1925
26+ export  interface  StreamInterface  { 
27+     stdin : stream . Readable ; 
28+     stdout : stream . Writable ; 
29+     stderr : stream . Writable ; 
30+ } 
31+ 
2032export  class  WebSocketHandler  implements  WebSocketInterface  { 
2133    public  static  readonly  StdinStream : number  =  0 ; 
2234    public  static  readonly  StdoutStream : number  =  1 ; 
2335    public  static  readonly  StderrStream : number  =  2 ; 
2436    public  static  readonly  StatusStream : number  =  3 ; 
2537    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+     } 
2657
2758    public  static  handleStandardStreams ( 
2859        streamNum : number , 
@@ -39,6 +70,7 @@ export class WebSocketHandler implements WebSocketInterface {
3970            stderr . write ( buff ) ; 
4071        }  else  if  ( streamNum  ===  WebSocketHandler . StatusStream )  { 
4172            // stream closing. 
73+             // Hacky, change tests to use the stream interface 
4274            if  ( stdout  &&  stdout  !==  process . stdout )  { 
4375                stdout . end ( ) ; 
4476            } 
@@ -69,6 +101,12 @@ export class WebSocketHandler implements WebSocketInterface {
69101        } ) ; 
70102
71103        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+             } 
72110            ws . close ( ) ; 
73111        } ) ; 
74112        // Keep the stream open 
@@ -141,7 +179,16 @@ export class WebSocketHandler implements WebSocketInterface {
141179    // factory is really just for test injection 
142180    public  constructor ( 
143181        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+         } , 
145192    )  { } 
146193
147194    /** 
@@ -173,7 +220,7 @@ export class WebSocketHandler implements WebSocketInterface {
173220
174221        return  await  new  Promise < WebSocket . WebSocket > ( ( resolve ,  reject )  =>  { 
175222            const  client  =  this . socketFactory 
176-                 ? this . socketFactory ( uri ,  opts ) 
223+                 ? this . socketFactory ( uri ,  protocols ,   opts ) 
177224                : new  WebSocket ( uri ,  protocols ,  opts ) ; 
178225            let  resolved  =  false ; 
179226
@@ -191,11 +238,17 @@ export class WebSocketHandler implements WebSocketInterface {
191238            client . onmessage  =  ( {  data } : {  data : WebSocket . Data  } )  =>  { 
192239                // TODO: support ArrayBuffer and Buffer[] data types? 
193240                if  ( typeof  data  ===  'string' )  { 
241+                     if  ( data . charCodeAt ( 0 )  ===  WebSocketHandler . CloseStream )  { 
242+                         WebSocketHandler . closeStream ( data . charCodeAt ( 1 ) ,  this . streams ) ; 
243+                     } 
194244                    if  ( textHandler  &&  ! textHandler ( data ) )  { 
195245                        client . close ( ) ; 
196246                    } 
197247                }  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+                     } 
199252                    if  ( binaryHandler  &&  ! binaryHandler ( streamNum ,  data . subarray ( 1 ) ) )  { 
200253                        client . close ( ) ; 
201254                    } 
0 commit comments