| 30 | exports.server = null; |
| 31 | |
| 32 | const closeServer = async () => { |
| 33 | if (exports.server != null) { |
| 34 | logger.info('Closing HTTP server...'); |
| 35 | // Call exports.server.close() to reject new connections but don't await just yet because the |
| 36 | // Promise won't resolve until all preexisting connections are closed. |
| 37 | const p = util.promisify(exports.server.close.bind(exports.server))(); |
| 38 | await hooks.aCallAll('expressCloseServer'); |
| 39 | // Give existing connections some time to close on their own before forcibly terminating. The |
| 40 | // time should be long enough to avoid interrupting most preexisting transmissions but short |
| 41 | // enough to avoid a noticeable outage. |
| 42 | const timeout = setTimeout(async () => { |
| 43 | logger.info(`Forcibly terminating remaining ${sockets.size} HTTP connections...`); |
| 44 | for (const socket of sockets) socket.destroy(new Error('HTTP server is closing')); |
| 45 | }, 5000); |
| 46 | let lastLogged = 0; |
| 47 | while (sockets.size > 0 && !settings.enableAdminUITests) { |
| 48 | if (Date.now() - lastLogged > 1000) { // Rate limit to avoid filling logs. |
| 49 | logger.info(`Waiting for ${sockets.size} HTTP clients to disconnect...`); |
| 50 | lastLogged = Date.now(); |
| 51 | } |
| 52 | await events.once(socketsEvents, 'updated'); |
| 53 | } |
| 54 | await p; |
| 55 | clearTimeout(timeout); |
| 56 | exports.server = null; |
| 57 | startTime.setValue(0); |
| 58 | logger.info('HTTP server closed'); |
| 59 | } |
| 60 | // @ts-ignore |
| 61 | if (sessionStore) sessionStore.shutdown(); |
| 62 | sessionStore = null; |
| 63 | if (secretRotator) secretRotator.stop(); |
| 64 | secretRotator = null; |
| 65 | }; |
| 66 | |
| 67 | exports.createServer = async () => { |
| 68 | console.log('Report bugs at https://github.com/ether/etherpad/issues'); |