1
1
import EventEmitter from "events" ;
2
2
import { RedisClientOptions } from "." ;
3
3
import RedisCommandsQueue from "./commands-queue" ;
4
- import RedisSocket from "./socket" ;
4
+ import RedisSocket , { RedisSocketOptions , RedisTcpSocketOptions } from "./socket" ;
5
+ import { RedisArgument } from "../.." ;
6
+ import { isIP } from "net" ;
7
+ import { lookup } from "dns/promises" ;
5
8
6
9
export const MAINTENANCE_EVENTS = {
7
10
PAUSE_WRITING : "pause-writing" ,
@@ -18,13 +21,19 @@ const PN = {
18
21
} ;
19
22
20
23
export interface SocketTimeoutUpdate {
21
- inMaintenance : boolean ,
22
- timeout ?: number
24
+ inMaintenance : boolean ;
25
+ timeout ?: number ;
23
26
}
24
27
25
28
export default class EnterpriseMaintenanceManager extends EventEmitter {
26
29
#commandsQueue: RedisCommandsQueue ;
27
30
#options: RedisClientOptions ;
31
+
32
+ static async getHandshakeCommand ( tls : boolean , host : string ) : Promise < Array < RedisArgument > > {
33
+ const movingEndpointType = await determineEndpoint ( tls , host ) ;
34
+ return [ "CLIENT" , "MAINT_NOTIFICATIONS" , "ON" , "moving-endpoint-type" , movingEndpointType ] ;
35
+ }
36
+
28
37
constructor ( commandsQueue : RedisCommandsQueue , options : RedisClientOptions ) {
29
38
super ( ) ;
30
39
this . #commandsQueue = commandsQueue ;
@@ -103,7 +112,7 @@ export default class EnterpriseMaintenanceManager extends EventEmitter {
103
112
104
113
this . emit ( MAINTENANCE_EVENTS . TIMEOUTS_UPDATE , {
105
114
inMaintenance : true ,
106
- timeout : this . #options. gracefulMaintenance ?. relaxedSocketTimeout
115
+ timeout : this . #options. gracefulMaintenance ?. relaxedSocketTimeout ,
107
116
} satisfies SocketTimeoutUpdate ) ;
108
117
} ;
109
118
@@ -113,8 +122,53 @@ export default class EnterpriseMaintenanceManager extends EventEmitter {
113
122
114
123
this . emit ( MAINTENANCE_EVENTS . TIMEOUTS_UPDATE , {
115
124
inMaintenance : false ,
116
- timeout : undefined
125
+ timeout : undefined ,
117
126
} satisfies SocketTimeoutUpdate ) ;
118
127
} ;
128
+ }
119
129
120
- } ;
130
+ type MovingEndpointType =
131
+ | "internal-ip"
132
+ | "internal-fqdn"
133
+ | "external-ip"
134
+ | "external-fqdn"
135
+ | "none" ;
136
+
137
+ function isPrivateIP ( ip : string ) : boolean {
138
+ const version = isIP ( ip ) ;
139
+ if ( version === 4 ) {
140
+ const octets = ip . split ( "." ) . map ( Number ) ;
141
+ return (
142
+ octets [ 0 ] === 10 ||
143
+ ( octets [ 0 ] === 172 && octets [ 1 ] >= 16 && octets [ 1 ] <= 31 ) ||
144
+ ( octets [ 0 ] === 192 && octets [ 1 ] === 168 )
145
+ ) ;
146
+ }
147
+ if ( version === 6 ) {
148
+ return (
149
+ ip . startsWith ( "fc" ) || // Unique local
150
+ ip . startsWith ( "fd" ) || // Unique local
151
+ ip === "::1" || // Loopback
152
+ ip . startsWith ( "fe80" ) // Link-local unicast
153
+ ) ;
154
+ }
155
+ return false ;
156
+ }
157
+
158
+ async function determineEndpoint (
159
+ tlsEnabled : boolean ,
160
+ host : string ,
161
+ ) : Promise < MovingEndpointType > {
162
+
163
+ const ip = isIP ( host )
164
+ ? host
165
+ : ( await lookup ( host , { family : 0 } ) ) . address
166
+
167
+ const isPrivate = isPrivateIP ( ip ) ;
168
+
169
+ if ( tlsEnabled ) {
170
+ return isPrivate ? "internal-fqdn" : "external-fqdn" ;
171
+ } else {
172
+ return isPrivate ? "internal-ip" : "external-ip" ;
173
+ }
174
+ }
0 commit comments