Skip to content

Commit 76c144c

Browse files
committed
feat(NODE-5455): use builtin native crypto
1 parent 6decb68 commit 76c144c

File tree

1 file changed

+72
-19
lines changed

1 file changed

+72
-19
lines changed

.github/scripts/libmongocrypt.mjs

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ async function parseArguments() {
2323
libVersion: { short: 'l', type: 'string', default: pkg['mongodb:libmongocrypt'] },
2424
clean: { short: 'c', type: 'boolean', default: false },
2525
build: { short: 'b', type: 'boolean', default: false },
26+
crypto: { type: 'boolean', default: false }, // Use Node.js builtin crypto
27+
fastDownload: { type: 'boolean', default: false }, // Potentially incorrect download, only for the brave and impatient
2628
help: { short: 'h', type: 'boolean', default: false }
2729
};
2830

@@ -39,7 +41,12 @@ async function parseArguments() {
3941
}
4042

4143
return {
42-
libmongocrypt: { url: args.values.gitURL, ref: args.values.libVersion },
44+
libmongocrypt: {
45+
url: args.values.gitURL,
46+
ref: args.values.libVersion,
47+
crypto: args.values.crypto
48+
},
49+
fastDownload: args.values.fastDownload,
4350
clean: args.values.clean,
4451
build: args.values.build
4552
};
@@ -76,7 +83,7 @@ export async function cloneLibMongoCrypt(libmongocryptRoot, { url, ref }) {
7683
}
7784
}
7885

79-
export async function buildLibMongoCrypt(libmongocryptRoot, nodeDepsRoot) {
86+
export async function buildLibMongoCrypt(libmongocryptRoot, nodeDepsRoot, options) {
8087
console.error('building libmongocrypt...');
8188

8289
const nodeBuildRoot = resolveRoot(nodeDepsRoot, 'tmp', 'libmongocrypt-build');
@@ -87,7 +94,6 @@ export async function buildLibMongoCrypt(libmongocryptRoot, nodeDepsRoot) {
8794
const CMAKE_FLAGS = toFlags({
8895
/**
8996
* We provide crypto hooks from Node.js binding to openssl (so disable system crypto)
90-
* TODO: NODE-5455
9197
*
9298
* One thing that is not obvious from the build instructions for libmongocrypt
9399
* and the Node.js bindings is that the Node.js driver uses libmongocrypt in
@@ -100,7 +106,7 @@ export async function buildLibMongoCrypt(libmongocryptRoot, nodeDepsRoot) {
100106
* have a copy of OpenSSL available directly, but for now it seems to make sense
101107
* to stick with what the Node.js addon does here.
102108
*/
103-
DDISABLE_NATIVE_CRYPTO: '1',
109+
DDISABLE_NATIVE_CRYPTO: options.crypto ? '0' : '1',
104110
/** A consistent name for the output "library" directory */
105111
DCMAKE_INSTALL_LIBDIR: 'lib',
106112
/** No warnings allowed */
@@ -127,15 +133,26 @@ export async function buildLibMongoCrypt(libmongocryptRoot, nodeDepsRoot) {
127133

128134
await run(
129135
'cmake',
130-
[...CMAKE_FLAGS, ...WINDOWS_CMAKE_FLAGS, ...MACOS_CMAKE_FLAGS, libmongocryptRoot],
136+
[
137+
'--parallel',
138+
'4',
139+
...CMAKE_FLAGS,
140+
...WINDOWS_CMAKE_FLAGS,
141+
...MACOS_CMAKE_FLAGS,
142+
libmongocryptRoot
143+
],
131144
{ cwd: nodeBuildRoot }
132145
);
133-
await run('cmake', ['--build', '.', '--target', 'install', '--config', 'RelWithDebInfo'], {
134-
cwd: nodeBuildRoot
135-
});
146+
await run(
147+
'cmake',
148+
['--parallel', '4', '--build', '.', '--target', 'install', '--config', 'RelWithDebInfo'],
149+
{
150+
cwd: nodeBuildRoot
151+
}
152+
);
136153
}
137154

138-
export async function downloadLibMongoCrypt(nodeDepsRoot, { ref }) {
155+
export async function downloadLibMongoCrypt(nodeDepsRoot, { ref, crypto }, fastDownload) {
139156
const downloadURL =
140157
ref === 'latest'
141158
? 'https://mciuploads.s3.amazonaws.com/libmongocrypt/all/master/latest/libmongocrypt-all.tar.gz'
@@ -163,33 +180,67 @@ export async function downloadLibMongoCrypt(nodeDepsRoot, { ref }) {
163180

164181
console.error(`Platform: ${detectedPlatform} Prebuild: ${prebuild}`);
165182

166-
const unzipArgs = ['-xzv', '-C', `_libmongocrypt-${ref}`, `${prebuild}/nocrypto`];
183+
const downloadDestination = crypto ? `${prebuild}` : `${prebuild}/nocrypto`;
184+
const unzipArgs = ['-xzv', '-C', `_libmongocrypt-${ref}`, downloadDestination];
167185
console.error(`+ tar ${unzipArgs.join(' ')}`);
168186
const unzip = child_process.spawn('tar', unzipArgs, {
169-
stdio: ['pipe', 'inherit'],
187+
stdio: ['pipe', 'inherit', 'pipe'],
170188
cwd: resolveRoot('.')
171189
});
172190

173191
const [response] = await events.once(https.get(downloadURL), 'response');
174192

175193
const start = performance.now();
176-
await stream.pipeline(response, unzip.stdin);
194+
195+
let signal;
196+
if (fastDownload) {
197+
/**
198+
* Tar will print out each file it finds inside MEMBER (ex. macos/nocrypto)
199+
* For each file it prints, we give it a deadline of 10seconds to print the next one.
200+
* If nothing prints after 10 seconds we exit early.
201+
* This depends on the tar file being in order and un-tar-able in under 10sec.
202+
*
203+
* download time went from 230s to 80s
204+
*/
205+
const controller = new AbortController();
206+
signal = controller.signal;
207+
let isFirstStdoutDataEv = true;
208+
let timeout;
209+
unzip.stderr.on('data', chunk => {
210+
process.stderr.write(chunk, () => {
211+
if (isFirstStdoutDataEv) {
212+
isFirstStdoutDataEv = false;
213+
timeout = setTimeout(() => controller.abort(), 10_000);
214+
}
215+
timeout?.refresh();
216+
});
217+
});
218+
}
219+
220+
try {
221+
await stream.pipeline(response, unzip.stdin, { signal });
222+
} catch {
223+
await fs.access(path.join(`_libmongocrypt-${ref}`, downloadDestination));
224+
}
177225
const end = performance.now();
178226

179227
console.error(`downloaded libmongocrypt in ${(end - start) / 1000} secs...`);
180228

181229
await fs.rm(nodeDepsRoot, { recursive: true, force: true });
182-
await fs.cp(resolveRoot(destination, prebuild, 'nocrypto'), nodeDepsRoot, { recursive: true });
183-
const currentPath = path.join(nodeDepsRoot, 'lib64');
230+
const source = crypto
231+
? resolveRoot(destination, prebuild)
232+
: resolveRoot(destination, prebuild, 'nocrypto');
233+
await fs.cp(source, nodeDepsRoot, { recursive: true });
234+
const potentialLib64Path = path.join(nodeDepsRoot, 'lib64');
184235
try {
185-
await fs.rename(currentPath, path.join(nodeDepsRoot, 'lib'));
236+
await fs.rename(potentialLib64Path, path.join(nodeDepsRoot, 'lib'));
186237
} catch (error) {
187-
console.error(`error renaming ${currentPath}: ${error.message}`);
238+
await fs.access(path.join(nodeDepsRoot, 'lib')); // Ensure there is a "lib" directory
188239
}
189240
}
190241

191242
async function main() {
192-
const { libmongocrypt, build, clean } = await parseArguments();
243+
const { libmongocrypt, build, clean, fastDownload } = await parseArguments();
193244

194245
const nodeDepsDir = resolveRoot('deps');
195246

@@ -213,11 +264,13 @@ async function main() {
213264
const isBuilt = libmongocryptBuiltVersion.trim() === libmongocrypt.ref;
214265

215266
if (clean || !isBuilt) {
216-
await buildLibMongoCrypt(libmongocryptCloneDir, nodeDepsDir);
267+
await buildLibMongoCrypt(libmongocryptCloneDir, nodeDepsDir, {
268+
crypto: libmongocrypt.crypto
269+
});
217270
}
218271
} else {
219272
// Download
220-
await downloadLibMongoCrypt(nodeDepsDir, libmongocrypt);
273+
await downloadLibMongoCrypt(nodeDepsDir, libmongocrypt, fastDownload);
221274
}
222275

223276
await fs.rm(resolveRoot('build'), { force: true, recursive: true });

0 commit comments

Comments
 (0)