Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | 135x 135x 135x 3597x 3597x 3597x 3597x 3597x 4x 3597x 98x 3597x 188x 184x 3585x 3473x 3473x 3473x 3473x 3473x 7478x 7478x 6310x 1168x 1168x 1136x 32x 32x | import type { ChildProcess } from 'child_process'; import { MongoNetworkTimeoutError } from '../error'; import { type AutoEncryptionExtraOptions } from './auto_encrypter'; /** * @internal * An internal class that handles spawning a mongocryptd. */ export class MongocryptdManager { static DEFAULT_MONGOCRYPTD_URI = 'mongodb://localhost:27020'; uri: string; bypassSpawn: boolean; spawnPath = ''; spawnArgs: Array<string> = []; _child?: ChildProcess; constructor(extraOptions: AutoEncryptionExtraOptions = {}) { this.uri = typeof extraOptions.mongocryptdURI === 'string' && extraOptions.mongocryptdURI.length > 0 ? extraOptions.mongocryptdURI : MongocryptdManager.DEFAULT_MONGOCRYPTD_URI; this.bypassSpawn = !!extraOptions.mongocryptdBypassSpawn; if (Object.hasOwn(extraOptions, 'mongocryptdSpawnPath') && extraOptions.mongocryptdSpawnPath) { this.spawnPath = extraOptions.mongocryptdSpawnPath; } if ( Object.hasOwn(extraOptions, 'mongocryptdSpawnArgs') && Array.isArray(extraOptions.mongocryptdSpawnArgs) ) { this.spawnArgs = this.spawnArgs.concat(extraOptions.mongocryptdSpawnArgs); } if ( this.spawnArgs .filter(arg => typeof arg === 'string') .every(arg => arg.indexOf('--idleShutdownTimeoutSecs') < 0) ) { this.spawnArgs.push('--idleShutdownTimeoutSecs', '60'); } } /** * Will check to see if a mongocryptd is up. If it is not up, it will attempt * to spawn a mongocryptd in a detached process, and then wait for it to be up. */ async spawn(): Promise<void> { const cmdName = this.spawnPath || 'mongocryptd'; // eslint-disable-next-line @typescript-eslint/no-require-imports const { spawn } = require('child_process') as typeof import('child_process'); // Spawned with stdio: ignore and detached: true // to ensure child can outlive parent. this._child = spawn(cmdName, this.spawnArgs, { stdio: 'ignore', detached: true }); this._child.on('error', () => { // From the FLE spec: // "The stdout and stderr of the spawned process MUST not be exposed in the driver // (e.g. redirect to /dev/null). Users can pass the argument --logpath to // extraOptions.mongocryptdSpawnArgs if they need to inspect mongocryptd logs. // If spawning is necessary, the driver MUST spawn mongocryptd whenever server // selection on the MongoClient to mongocryptd fails. If the MongoClient fails to // connect after spawning, the server selection error is propagated to the user." // The AutoEncrypter and MongoCryptdManager should work together to spawn // mongocryptd whenever necessary. Additionally, the `mongocryptd` intentionally // shuts down after 60s and gets respawned when necessary. We rely on server // selection timeouts when connecting to the `mongocryptd` to inform users that something // has been configured incorrectly. For those reasons, we suppress stderr from // the `mongocryptd` process and immediately unref the process. }); // unref child to remove handle from event loop this._child.unref(); } /** * @returns the result of `fn` or rejects with an error. */ async withRespawn<T>(fn: () => Promise<T>): ReturnType<typeof fn> { try { const result = await fn(); return result; } catch (err) { // If we are not bypassing spawning, then we should retry once on a MongoTimeoutError (server selection error) const shouldSpawn = err instanceof MongoNetworkTimeoutError && !this.bypassSpawn; if (!shouldSpawn) { throw err; } } await this.spawn(); const result = await fn(); return result; } } |