From 9561f32a9dda6969be7f727c9bd1bd96980f5e95 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 6 Jun 2023 19:34:57 +0200 Subject: [PATCH] feat(NODE-5008): add zstd and kerberos to peer deps (#3691) --- package-lock.json | 8 ++++ package.json | 8 ++++ src/cmap/auth/gssapi.ts | 20 ++++++++-- src/cmap/wire_protocol/compression.ts | 57 +++++++++++++++++---------- src/deps.ts | 24 +++++++---- test/action/dependency.test.ts | 2 + 6 files changed, 87 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 652a72283e..a9b0341054 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,6 +69,8 @@ }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.201.0", + "@mongodb-js/zstd": "^1.1.0", + "kerberos": "^2.0.1", "mongodb-client-encryption": ">=2.3.0 <3", "snappy": "^7.2.2" }, @@ -76,6 +78,12 @@ "@aws-sdk/credential-providers": { "optional": true }, + "@mongodb-js/zstd": { + "optional": true + }, + "kerberos": { + "optional": true + }, "mongodb-client-encryption": { "optional": true }, diff --git a/package.json b/package.json index ea49b3fc05..bccad6a1a6 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,8 @@ }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.201.0", + "@mongodb-js/zstd": "^1.1.0", + "kerberos": "^2.0.1", "mongodb-client-encryption": ">=2.3.0 <3", "snappy": "^7.2.2" }, @@ -41,6 +43,12 @@ "@aws-sdk/credential-providers": { "optional": true }, + "@mongodb-js/zstd": { + "optional": true + }, + "kerberos": { + "optional": true + }, "snappy": { "optional": true }, diff --git a/src/cmap/auth/gssapi.ts b/src/cmap/auth/gssapi.ts index 21fe261b9a..3f8135a4e4 100644 --- a/src/cmap/auth/gssapi.ts +++ b/src/cmap/auth/gssapi.ts @@ -1,6 +1,6 @@ import * as dns from 'dns'; -import { Kerberos, type KerberosClient } from '../../deps'; +import { getKerberos, type Kerberos, type KerberosClient } from '../../deps'; import { MongoInvalidArgumentError, MongoMissingCredentialsError } from '../../error'; import { ns } from '../../utils'; import type { Connection } from '../connection'; @@ -36,6 +36,8 @@ async function externalCommand( }>; } +let krb: typeof Kerberos; + export class GSSAPI extends AuthProvider { override async auth(authContext: AuthContext): Promise { const { connection, credentials } = authContext; @@ -77,10 +79,11 @@ async function makeKerberosClient(authContext: AuthContext): Promise { return host; } } + +/** + * Load the Kerberos library. + */ +function loadKrb() { + if (!krb) { + krb = getKerberos(); + } +} diff --git a/src/cmap/wire_protocol/compression.ts b/src/cmap/wire_protocol/compression.ts index 5057091b5d..7701b0d403 100644 --- a/src/cmap/wire_protocol/compression.ts +++ b/src/cmap/wire_protocol/compression.ts @@ -2,7 +2,7 @@ import { promisify } from 'util'; import * as zlib from 'zlib'; import { LEGACY_HELLO_COMMAND } from '../../constants'; -import { Snappy, ZStandard } from '../../deps'; +import { getZstdLibrary, Snappy, type ZStandard } from '../../deps'; import { MongoDecompressionError, MongoInvalidArgumentError } from '../../error'; /** @public */ @@ -37,6 +37,8 @@ const ZSTD_COMPRESSION_LEVEL = 3; const zlibInflate = promisify(zlib.inflate.bind(zlib)); const zlibDeflate = promisify(zlib.deflate.bind(zlib)); +let zstd: typeof ZStandard; + // Facilitate compressing a message using an agreed compressor export async function compress( options: { zlibCompressionLevel: number; agreedCompressor: CompressorName }, @@ -44,28 +46,30 @@ export async function compress( ): Promise { const zlibOptions = {} as zlib.ZlibOptions; switch (options.agreedCompressor) { - case 'snappy': + case 'snappy': { if ('kModuleError' in Snappy) { throw Snappy['kModuleError']; } return Snappy.compress(dataToBeCompressed); - - case 'zstd': - if ('kModuleError' in ZStandard) { - throw ZStandard['kModuleError']; + } + case 'zstd': { + loadZstd(); + if ('kModuleError' in zstd) { + throw zstd['kModuleError']; } - return ZStandard.compress(dataToBeCompressed, ZSTD_COMPRESSION_LEVEL); - - case 'zlib': + return zstd.compress(dataToBeCompressed, ZSTD_COMPRESSION_LEVEL); + } + case 'zlib': { if (options.zlibCompressionLevel) { zlibOptions.level = options.zlibCompressionLevel; } return zlibDeflate(dataToBeCompressed, zlibOptions); - - default: + } + default: { throw new MongoInvalidArgumentError( `Unknown compressor ${options.agreedCompressor} failed to compress` ); + } } } @@ -83,22 +87,33 @@ export async function decompress(compressorID: number, compressedData: Buffer): } switch (compressorID) { - case Compressor.snappy: + case Compressor.snappy: { if ('kModuleError' in Snappy) { throw Snappy['kModuleError']; } return Snappy.uncompress(compressedData, { asBuffer: true }); - - case Compressor.zstd: - if ('kModuleError' in ZStandard) { - throw ZStandard['kModuleError']; + } + case Compressor.zstd: { + loadZstd(); + if ('kModuleError' in zstd) { + throw zstd['kModuleError']; } - return ZStandard.decompress(compressedData); - - case Compressor.zlib: + return zstd.decompress(compressedData); + } + case Compressor.zlib: { return zlibInflate(compressedData); - - default: + } + default: { return compressedData; + } + } +} + +/** + * Load ZStandard if it is not already set. + */ +function loadZstd() { + if (!zstd) { + zstd = getZstdLibrary(); } } diff --git a/src/deps.ts b/src/deps.ts index c9d423e81e..008a275796 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -28,10 +28,15 @@ export let Kerberos: typeof import('kerberos') | { kModuleError: MongoMissingDep ) ); -try { - // Ensure you always wrap an optional require in the try block NODE-3199 - Kerberos = require('kerberos'); -} catch {} // eslint-disable-line +export function getKerberos(): typeof Kerberos | { kModuleError: MongoMissingDependencyError } { + try { + // Ensure you always wrap an optional require in the try block NODE-3199 + Kerberos = require('kerberos'); + return Kerberos; + } catch { + return Kerberos; + } +} export interface KerberosClient { step(challenge: string): Promise; @@ -62,9 +67,14 @@ export let ZStandard: ZStandardLib | { kModuleError: MongoMissingDependencyError ) ); -try { - ZStandard = require('@mongodb-js/zstd'); -} catch {} // eslint-disable-line +export function getZstdLibrary(): typeof ZStandard | { kModuleError: MongoMissingDependencyError } { + try { + ZStandard = require('@mongodb-js/zstd'); + return ZStandard; + } catch { + return ZStandard; + } +} type CredentialProvider = { fromNodeProviderChain(this: void): () => Promise; diff --git a/test/action/dependency.test.ts b/test/action/dependency.test.ts index 88f890bc6e..ffc2a7def1 100644 --- a/test/action/dependency.test.ts +++ b/test/action/dependency.test.ts @@ -9,6 +9,8 @@ import { dependencies, peerDependencies, peerDependenciesMeta } from '../../pack const EXPECTED_DEPENDENCIES = ['bson', 'mongodb-connection-string-url', 'socks']; const EXPECTED_PEER_DEPENDENCIES = [ '@aws-sdk/credential-providers', + '@mongodb-js/zstd', + 'kerberos', 'snappy', 'mongodb-client-encryption' ];