@@ -97,6 +97,9 @@ export async function ensureDockerTunnelRunning(proxyPort: number) {
97
97
if ( ! container . State . Running ) {
98
98
await docker . getContainer ( container . Id ) . start ( ) ;
99
99
}
100
+
101
+ // Asynchronously, update the Docker port that's in use for this container.
102
+ portCache [ proxyPort ] = refreshDockerTunnelPortCache ( proxyPort ) ;
100
103
} ) ;
101
104
}
102
105
@@ -153,22 +156,59 @@ export async function updateDockerTunnelledNetworks(
153
156
} ) ;
154
157
}
155
158
156
- export async function getDockerTunnelPort ( proxyPort : number ) : Promise < number > {
157
- const docker = new Docker ( ) ;
159
+ // A map of proxy port (e.g. 8000) to the automatically mapped Docker tunnel port.
160
+ // Refreshed if it's somehow missing, or on every call to ensureDockerTunnelRunning(), e.g.
161
+ // async at every docker-proxy request & every container interception.
162
+ const portCache : { [ proxyPort : string ] : number | Promise < number > | undefined } = { } ;
158
163
159
- const containerName = getDockerTunnelContainerName ( proxyPort ) ;
160
- let container = await docker . getContainer ( containerName )
161
- . inspect ( ) . catch ( ( ) => undefined ) ;
162
- if ( ! container ) {
163
- // Can't get the container - recreate it first, then continue.
164
- await ensureDockerTunnelRunning ( proxyPort ) ;
165
- container = await docker . getContainer ( containerName ) . inspect ( ) ;
164
+ export async function getDockerTunnelPort ( proxyPort : number ) : Promise < number > {
165
+ if ( ! portCache [ proxyPort ] ) {
166
+ // Update the port and wait for the query to complete:
167
+ portCache [ proxyPort ] = refreshDockerTunnelPortCache ( proxyPort ) ;
166
168
}
167
169
168
- const portMappings = container . NetworkSettings . Ports [ '1080/tcp' ] ;
169
- const localPort = _ . find ( portMappings , ( { HostIp } ) => HostIp === '127.0.0.1' ) ;
170
- if ( ! localPort ) throw new Error ( "No port mapped for Docker tunnel" ) ;
171
- return parseInt ( localPort . HostPort , 10 ) ;
170
+ return portCache [ proxyPort ] ! ;
171
+ }
172
+
173
+ export async function refreshDockerTunnelPortCache ( proxyPort : number ) : Promise < number > {
174
+ try {
175
+ if ( _ . isObject ( portCache [ proxyPort ] ) ) {
176
+ // If there's an existing promise refreshing this data, then don't duplicate:
177
+ return portCache [ proxyPort ] !
178
+ }
179
+
180
+ const docker = new Docker ( ) ;
181
+
182
+ const containerName = getDockerTunnelContainerName ( proxyPort ) ;
183
+ let container = await docker . getContainer ( containerName )
184
+ . inspect ( ) . catch ( ( ) => undefined ) ;
185
+ if ( ! container ) {
186
+ // Can't get the container - recreate it (refreshing the port automatically)
187
+ return ensureDockerTunnelRunning ( proxyPort )
188
+ . then ( ( ) => getDockerTunnelPort ( proxyPort ) ) ;
189
+ }
190
+
191
+ const portMappings = container . NetworkSettings . Ports [ '1080/tcp' ] ;
192
+ const localPort = _ . find ( portMappings , ( { HostIp } ) => HostIp === '127.0.0.1' ) ;
193
+
194
+ if ( ! localPort ) {
195
+ // This can happen if the networks of the container are changed manually. In some cases
196
+ // this can result in the mapping being lots. Kill & restart the container.
197
+ return docker . getContainer ( containerName ) . kill ( )
198
+ . then ( ( ) => ensureDockerTunnelRunning ( proxyPort ) )
199
+ . then ( ( ) => getDockerTunnelPort ( proxyPort ) ) ;
200
+ }
201
+
202
+ const port = parseInt ( localPort . HostPort , 10 ) ;
203
+
204
+ portCache [ proxyPort ] = port ;
205
+ return port ;
206
+ } catch ( e ) {
207
+ // If something goes wrong, reset the port cache, to ensure that future checks
208
+ // will query again from scratch:
209
+ portCache [ proxyPort ] = undefined ;
210
+ throw e ;
211
+ }
172
212
}
173
213
174
214
export async function stopDockerTunnel ( proxyPort : number | 'all' ) : Promise < void > {
0 commit comments