@@ -19,13 +19,13 @@ import updateCommand from '@oclif/plugin-update/lib/commands/update';
1919import { HttpToolkitServerApi } from './api/api-server' ;
2020import { checkBrowserConfig } from './browsers' ;
2121import { logError } from './error-tracking' ;
22- import { MOCKTTP_ALLOWED_ORIGINS } from './constants' ;
22+ import { IS_PROD_BUILD , MOCKTTP_ALLOWED_ORIGINS } from './constants' ;
2323
2424import { delay } from './util/promise' ;
2525import { isErrorLike } from './util/error' ;
2626import { readFile , checkAccess , writeFile , ensureDirectoryExists } from './util/fs' ;
2727
28- import { registerShutdownHandler } from './shutdown' ;
28+ import { registerShutdownHandler , shutdown } from './shutdown' ;
2929import { getTimeToCertExpiry , parseCert } from './certificates' ;
3030
3131import {
@@ -77,14 +77,24 @@ function checkCertExpiry(certContents: string): void {
7777 }
7878}
7979
80+ let shutdownTimer : NodeJS . Timeout | undefined ;
81+
8082function manageBackgroundServices (
8183 standalone : PluggableAdmin . AdminServer < {
8284 http : MockttpAdminPlugin ,
8385 webrtc : MockRTCAdminPlugin
8486 } > ,
8587 httpsConfig : { certPath : string , certContent : string }
8688) {
89+ let activeSessions = 0 ;
90+
8791 standalone . on ( 'mock-session-started' , async ( { http, webrtc } , sessionId ) => {
92+ activeSessions += 1 ;
93+ if ( shutdownTimer ) {
94+ clearTimeout ( shutdownTimer ) ;
95+ shutdownTimer = undefined ;
96+ }
97+
8898 const httpProxyPort = http . getMockServer ( ) . port ;
8999
90100 console . log ( `Mock session started, http on port ${
@@ -105,6 +115,7 @@ function manageBackgroundServices(
105115 } ) ;
106116
107117 standalone . on ( 'mock-session-stopping' , ( { http } ) => {
118+ activeSessions -= 1 ;
108119 const httpProxyPort = http . getMockServer ( ) . port ;
109120
110121 stopDockerInterceptionServices ( httpProxyPort , ruleParameters )
@@ -113,6 +124,23 @@ function manageBackgroundServices(
113124 } ) ;
114125
115126 clearWebExtensionConfig ( httpProxyPort ) ;
127+
128+ // In some odd cases, the server can end up running even though all UIs & desktop have exited
129+ // completely. This can be problematic, as it leaves the server holding ports that HTTP Toolkit
130+ // needs, and blocks future startups. To avoid this, if no Mock sessions are running at all
131+ // for 10 minutes, the server shuts down automatically. Skipped for dev, where that might be OK.
132+ // This should catch even hard desktop shell crashes, as sessions shut down automatically if the
133+ // client websocket becomes non-responsive.
134+ if ( activeSessions <= 0 && IS_PROD_BUILD ) {
135+ if ( shutdownTimer ) {
136+ clearTimeout ( shutdownTimer ) ;
137+ shutdownTimer = undefined ;
138+ }
139+
140+ shutdownTimer = setTimeout ( ( ) => {
141+ if ( activeSessions === 0 ) shutdown ( '10 minutes inactive' ) ;
142+ } , 1000 * 60 * 10 ) . unref ( ) ;
143+ }
116144 } ) ;
117145}
118146
0 commit comments