1
- import { field } from '@coder/logger' ;
1
+ import { field , logger , Logger } from '@coder/logger' ;
2
2
import * as net from 'net' ;
3
3
import { VSBuffer } from 'vs/base/common/buffer' ;
4
4
import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net' ;
5
5
import { NodeSocket , WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net' ;
6
6
import { AuthRequest , ConnectionTypeRequest , HandshakeMessage } from 'vs/platform/remote/common/remoteAgentConnection' ;
7
- import { logger } from 'vs/server/node/logger' ;
8
7
9
8
export interface SocketOptions {
9
+ /** The token is how we identify and connect to existing sessions. */
10
10
readonly reconnectionToken : string ;
11
+ /** Specifies that the client is trying to reconnect. */
11
12
readonly reconnection : boolean ;
13
+ /** If true assume this is not a web socket (always false for code-server). */
12
14
readonly skipWebSocketFrames : boolean ;
15
+ /** Whether to support compression (web socket only). */
13
16
readonly permessageDeflate ?: boolean ;
17
+ /**
18
+ * Seed zlib with these bytes (web socket only). If parts of inflating was
19
+ * done in a different zlib instance we need to pass all those bytes into zlib
20
+ * otherwise the inflate might hit an inflated portion referencing a distance
21
+ * too far back.
22
+ */
14
23
readonly inflateBytes ?: VSBuffer ;
15
- readonly recordInflateBytes ?: boolean ;
16
24
}
17
25
18
26
export class Protocol extends PersistentProtocol {
27
+ private readonly logger : Logger ;
28
+
19
29
public constructor ( socket : net . Socket , public readonly options : SocketOptions ) {
20
30
super (
21
31
options . skipWebSocketFrames
@@ -24,9 +34,12 @@ export class Protocol extends PersistentProtocol {
24
34
new NodeSocket ( socket ) ,
25
35
options . permessageDeflate || false ,
26
36
options . inflateBytes || null ,
27
- options . recordInflateBytes || false ,
37
+ // Always record inflate bytes if using permessage-deflate.
38
+ options . permessageDeflate || false ,
28
39
) ,
29
40
) ;
41
+
42
+ this . logger = logger . named ( 'protocol' , field ( "token" , this . options . reconnectionToken ) ) ;
30
43
}
31
44
32
45
public getUnderlyingSocket ( ) : net . Socket {
@@ -40,24 +53,25 @@ export class Protocol extends PersistentProtocol {
40
53
* Perform a handshake to get a connection request.
41
54
*/
42
55
public handshake ( ) : Promise < ConnectionTypeRequest > {
43
- logger . trace ( 'Protocol handshake' , field ( 'token' , this . options . reconnectionToken ) ) ;
56
+ this . logger . debug ( 'Initiating handshake...' ) ;
44
57
return new Promise ( ( resolve , reject ) => {
45
58
const timeout = setTimeout ( ( ) => {
46
- logger . error ( 'Handshake timed out' , field ( 'token' , this . options . reconnectionToken ) ) ;
47
- reject ( new Error ( 'timed out' ) ) ;
59
+ this . logger . debug ( 'Handshake timed out' ) ;
60
+ reject ( new Error ( 'protocol handshake timed out' ) ) ;
48
61
} , 10000 ) ; // Matches the client timeout.
49
62
50
63
const handler = this . onControlMessage ( ( rawMessage ) => {
51
64
try {
52
65
const raw = rawMessage . toString ( ) ;
53
- logger . trace ( 'Protocol message' , field ( 'token' , this . options . reconnectionToken ) , field ( 'message' , raw ) ) ;
66
+ this . logger . trace ( 'Got message' , field ( 'message' , raw ) ) ;
54
67
const message = JSON . parse ( raw ) ;
55
68
switch ( message . type ) {
56
69
case 'auth' :
57
70
return this . authenticate ( message ) ;
58
71
case 'connectionType' :
59
72
handler . dispose ( ) ;
60
73
clearTimeout ( timeout ) ;
74
+ this . logger . debug ( 'Handshake completed' ) ;
61
75
return resolve ( message ) ;
62
76
default :
63
77
throw new Error ( 'Unrecognized message type' ) ;
@@ -90,10 +104,38 @@ export class Protocol extends PersistentProtocol {
90
104
}
91
105
92
106
/**
93
- * Send a handshake message. In the case of the extension host, it just sends
94
- * back a debug port.
107
+ * Send a handshake message. In the case of the extension host it should just
108
+ * send a debug port.
95
109
*/
96
- public sendMessage ( message : HandshakeMessage | { debugPort ?: number } ) : void {
110
+ public sendMessage ( message : HandshakeMessage | { debugPort ?: number | null } ) : void {
97
111
this . sendControl ( VSBuffer . fromString ( JSON . stringify ( message ) ) ) ;
98
112
}
113
+
114
+ /**
115
+ * Disconnect and dispose everything including the underlying socket.
116
+ */
117
+ public destroy ( reason ?: string ) : void {
118
+ try {
119
+ if ( reason ) {
120
+ this . sendMessage ( { type : 'error' , reason } ) ;
121
+ }
122
+ // If still connected try notifying the client.
123
+ this . sendDisconnect ( ) ;
124
+ } catch ( error ) {
125
+ // I think the write might fail if already disconnected.
126
+ this . logger . warn ( error . message || error ) ;
127
+ }
128
+ this . dispose ( ) ; // This disposes timers and socket event handlers.
129
+ this . getSocket ( ) . dispose ( ) ; // This will destroy() the socket.
130
+ }
131
+
132
+ /**
133
+ * Get inflateBytes in base64 format from the current socket.
134
+ */
135
+ public get inflateBytes ( ) : string | undefined {
136
+ const socket = this . getSocket ( ) ;
137
+ return socket instanceof WebSocketNodeSocket
138
+ ? Buffer . from ( socket . recordedInflateBytes . buffer ) . toString ( 'base64' )
139
+ : undefined ;
140
+ }
99
141
}
0 commit comments