mongodb 6.12.0 → 6.13.0-dev.20250201.sha.35c703e3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/beta.d.ts +176 -108
- package/lib/bulk/common.js +5 -7
- package/lib/bulk/common.js.map +1 -1
- package/lib/change_stream.js +16 -26
- package/lib/change_stream.js.map +1 -1
- package/lib/client-side-encryption/auto_encrypter.js +4 -2
- package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
- package/lib/client-side-encryption/client_encryption.js +4 -4
- package/lib/client-side-encryption/client_encryption.js.map +1 -1
- package/lib/client-side-encryption/state_machine.js +56 -30
- package/lib/client-side-encryption/state_machine.js.map +1 -1
- package/lib/cmap/auth/mongodb_aws.js +1 -1
- package/lib/cmap/auth/mongodb_aws.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc.js +1 -1
- package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
- package/lib/cmap/command_monitoring_events.js +9 -50
- package/lib/cmap/command_monitoring_events.js.map +1 -1
- package/lib/cmap/connection.js +28 -22
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +88 -117
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/wire_protocol/on_data.js +6 -1
- package/lib/cmap/wire_protocol/on_data.js.map +1 -1
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +68 -86
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +47 -18
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +2 -1
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +2 -1
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/cursor/list_collections_cursor.js +2 -1
- package/lib/cursor/list_collections_cursor.js.map +1 -1
- package/lib/db.js +2 -1
- package/lib/db.js.map +1 -1
- package/lib/encrypter.js +5 -9
- package/lib/encrypter.js.map +1 -1
- package/lib/error.js +10 -18
- package/lib/error.js.map +1 -1
- package/lib/index.js +5 -2
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +46 -26
- package/lib/mongo_client.js.map +1 -1
- package/lib/mongo_logger.js +102 -3
- package/lib/mongo_logger.js.map +1 -1
- package/lib/operations/execute_operation.js +9 -5
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/operation.js +4 -5
- package/lib/operations/operation.js.map +1 -1
- package/lib/sdam/monitor.js +25 -31
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +27 -17
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/topology.js +20 -19
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sessions.js +24 -48
- package/lib/sessions.js.map +1 -1
- package/lib/utils.js +64 -44
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +176 -108
- package/package.json +2 -2
- package/src/bulk/common.ts +6 -9
- package/src/change_stream.ts +21 -33
- package/src/client-side-encryption/auto_encrypter.ts +12 -8
- package/src/client-side-encryption/client_encryption.ts +6 -4
- package/src/client-side-encryption/state_machine.ts +80 -36
- package/src/cmap/auth/mongodb_aws.ts +1 -1
- package/src/cmap/auth/mongodb_oidc.ts +1 -1
- package/src/cmap/command_monitoring_events.ts +10 -55
- package/src/cmap/connection.ts +37 -29
- package/src/cmap/connection_pool.ts +121 -145
- package/src/cmap/wire_protocol/on_data.ts +9 -2
- package/src/collection.ts +15 -8
- package/src/connection_string.ts +74 -99
- package/src/cursor/abstract_cursor.ts +71 -23
- package/src/cursor/aggregation_cursor.ts +5 -3
- package/src/cursor/find_cursor.ts +5 -3
- package/src/cursor/list_collections_cursor.ts +5 -3
- package/src/db.ts +11 -7
- package/src/encrypter.ts +6 -11
- package/src/error.ts +11 -23
- package/src/index.ts +3 -3
- package/src/mongo_client.ts +78 -47
- package/src/mongo_logger.ts +158 -11
- package/src/mongo_types.ts +38 -0
- package/src/operations/execute_operation.ts +11 -6
- package/src/operations/list_collections.ts +4 -1
- package/src/operations/operation.ts +8 -9
- package/src/sdam/monitor.ts +30 -38
- package/src/sdam/server.ts +33 -20
- package/src/sdam/topology.ts +29 -26
- package/src/sessions.ts +37 -58
- package/src/utils.ts +79 -43
package/src/change_stream.ts
CHANGED
|
@@ -24,13 +24,6 @@ import type { ServerSessionId } from './sessions';
|
|
|
24
24
|
import { CSOTTimeoutContext, type TimeoutContext } from './timeout';
|
|
25
25
|
import { filterOptions, getTopology, type MongoDBNamespace, squashError } from './utils';
|
|
26
26
|
|
|
27
|
-
/** @internal */
|
|
28
|
-
const kCursorStream = Symbol('cursorStream');
|
|
29
|
-
/** @internal */
|
|
30
|
-
const kClosed = Symbol('closed');
|
|
31
|
-
/** @internal */
|
|
32
|
-
const kMode = Symbol('mode');
|
|
33
|
-
|
|
34
27
|
const CHANGE_STREAM_OPTIONS = [
|
|
35
28
|
'resumeAfter',
|
|
36
29
|
'startAfter',
|
|
@@ -584,14 +577,14 @@ export class ChangeStream<
|
|
|
584
577
|
namespace: MongoDBNamespace;
|
|
585
578
|
type: symbol;
|
|
586
579
|
/** @internal */
|
|
587
|
-
cursor: ChangeStreamCursor<TSchema, TChange>;
|
|
580
|
+
private cursor: ChangeStreamCursor<TSchema, TChange>;
|
|
588
581
|
streamOptions?: CursorStreamOptions;
|
|
589
582
|
/** @internal */
|
|
590
|
-
|
|
583
|
+
private cursorStream?: Readable & AsyncIterable<TChange>;
|
|
591
584
|
/** @internal */
|
|
592
|
-
|
|
585
|
+
private isClosed: boolean;
|
|
593
586
|
/** @internal */
|
|
594
|
-
|
|
587
|
+
private mode: false | 'iterator' | 'emitter';
|
|
595
588
|
|
|
596
589
|
/** @event */
|
|
597
590
|
static readonly RESPONSE = RESPONSE;
|
|
@@ -668,8 +661,8 @@ export class ChangeStream<
|
|
|
668
661
|
// Create contained Change Stream cursor
|
|
669
662
|
this.cursor = this._createChangeStreamCursor(options);
|
|
670
663
|
|
|
671
|
-
this
|
|
672
|
-
this
|
|
664
|
+
this.isClosed = false;
|
|
665
|
+
this.mode = false;
|
|
673
666
|
|
|
674
667
|
// Listen for any `change` listeners being added to ChangeStream
|
|
675
668
|
this.on('newListener', eventName => {
|
|
@@ -680,7 +673,7 @@ export class ChangeStream<
|
|
|
680
673
|
|
|
681
674
|
this.on('removeListener', eventName => {
|
|
682
675
|
if (eventName === 'change' && this.listenerCount('change') === 0 && this.cursor) {
|
|
683
|
-
this
|
|
676
|
+
this.cursorStream?.removeAllListeners('data');
|
|
684
677
|
}
|
|
685
678
|
});
|
|
686
679
|
|
|
@@ -692,11 +685,6 @@ export class ChangeStream<
|
|
|
692
685
|
}
|
|
693
686
|
}
|
|
694
687
|
|
|
695
|
-
/** @internal */
|
|
696
|
-
get cursorStream(): (Readable & AsyncIterable<TChange>) | undefined {
|
|
697
|
-
return this[kCursorStream];
|
|
698
|
-
}
|
|
699
|
-
|
|
700
688
|
/** The cached resume token that is used to resume after the most recently returned change. */
|
|
701
689
|
get resumeToken(): ResumeToken {
|
|
702
690
|
return this.cursor?.resumeToken;
|
|
@@ -826,8 +814,8 @@ export class ChangeStream<
|
|
|
826
814
|
}
|
|
827
815
|
|
|
828
816
|
/** Is the cursor closed */
|
|
829
|
-
get closed(): boolean {
|
|
830
|
-
return this
|
|
817
|
+
public get closed(): boolean {
|
|
818
|
+
return this.isClosed || this.cursor.closed;
|
|
831
819
|
}
|
|
832
820
|
|
|
833
821
|
/**
|
|
@@ -836,7 +824,7 @@ export class ChangeStream<
|
|
|
836
824
|
async close(): Promise<void> {
|
|
837
825
|
this.timeoutContext?.clear();
|
|
838
826
|
this.timeoutContext = undefined;
|
|
839
|
-
this
|
|
827
|
+
this.isClosed = true;
|
|
840
828
|
|
|
841
829
|
const cursor = this.cursor;
|
|
842
830
|
try {
|
|
@@ -865,24 +853,24 @@ export class ChangeStream<
|
|
|
865
853
|
|
|
866
854
|
/** @internal */
|
|
867
855
|
private _setIsEmitter(): void {
|
|
868
|
-
if (this
|
|
856
|
+
if (this.mode === 'iterator') {
|
|
869
857
|
// TODO(NODE-3485): Replace with MongoChangeStreamModeError
|
|
870
858
|
throw new MongoAPIError(
|
|
871
859
|
'ChangeStream cannot be used as an EventEmitter after being used as an iterator'
|
|
872
860
|
);
|
|
873
861
|
}
|
|
874
|
-
this
|
|
862
|
+
this.mode = 'emitter';
|
|
875
863
|
}
|
|
876
864
|
|
|
877
865
|
/** @internal */
|
|
878
866
|
private _setIsIterator(): void {
|
|
879
|
-
if (this
|
|
867
|
+
if (this.mode === 'emitter') {
|
|
880
868
|
// TODO(NODE-3485): Replace with MongoChangeStreamModeError
|
|
881
869
|
throw new MongoAPIError(
|
|
882
870
|
'ChangeStream cannot be used as an iterator after being used as an EventEmitter'
|
|
883
871
|
);
|
|
884
872
|
}
|
|
885
|
-
this
|
|
873
|
+
this.mode = 'iterator';
|
|
886
874
|
}
|
|
887
875
|
|
|
888
876
|
/**
|
|
@@ -947,8 +935,8 @@ export class ChangeStream<
|
|
|
947
935
|
/** @internal */
|
|
948
936
|
private _streamEvents(cursor: ChangeStreamCursor<TSchema, TChange>): void {
|
|
949
937
|
this._setIsEmitter();
|
|
950
|
-
const stream = this
|
|
951
|
-
this
|
|
938
|
+
const stream = this.cursorStream ?? cursor.stream();
|
|
939
|
+
this.cursorStream = stream;
|
|
952
940
|
stream.on('data', change => {
|
|
953
941
|
try {
|
|
954
942
|
const processedChange = this._processChange(change);
|
|
@@ -963,18 +951,18 @@ export class ChangeStream<
|
|
|
963
951
|
|
|
964
952
|
/** @internal */
|
|
965
953
|
private _endStream(): void {
|
|
966
|
-
const cursorStream = this
|
|
954
|
+
const cursorStream = this.cursorStream;
|
|
967
955
|
if (cursorStream) {
|
|
968
956
|
['data', 'close', 'end', 'error'].forEach(event => cursorStream.removeAllListeners(event));
|
|
969
957
|
cursorStream.destroy();
|
|
970
958
|
}
|
|
971
959
|
|
|
972
|
-
this
|
|
960
|
+
this.cursorStream = undefined;
|
|
973
961
|
}
|
|
974
962
|
|
|
975
963
|
/** @internal */
|
|
976
964
|
private _processChange(change: TChange | null): TChange {
|
|
977
|
-
if (this
|
|
965
|
+
if (this.isClosed) {
|
|
978
966
|
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
979
967
|
throw new MongoAPIError(CHANGESTREAM_CLOSED_ERROR);
|
|
980
968
|
}
|
|
@@ -1002,7 +990,7 @@ export class ChangeStream<
|
|
|
1002
990
|
/** @internal */
|
|
1003
991
|
private _processErrorStreamMode(changeStreamError: AnyError, cursorInitialized: boolean) {
|
|
1004
992
|
// If the change stream has been closed explicitly, do not process error.
|
|
1005
|
-
if (this
|
|
993
|
+
if (this.isClosed) return;
|
|
1006
994
|
|
|
1007
995
|
if (
|
|
1008
996
|
cursorInitialized &&
|
|
@@ -1034,7 +1022,7 @@ export class ChangeStream<
|
|
|
1034
1022
|
|
|
1035
1023
|
/** @internal */
|
|
1036
1024
|
private async _processErrorIteratorMode(changeStreamError: AnyError, cursorInitialized: boolean) {
|
|
1037
|
-
if (this
|
|
1025
|
+
if (this.isClosed) {
|
|
1038
1026
|
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
|
|
1039
1027
|
throw new MongoAPIError(CHANGESTREAM_CLOSED_ERROR);
|
|
1040
1028
|
}
|
|
@@ -11,6 +11,7 @@ import { kDecorateResult } from '../constants';
|
|
|
11
11
|
import { getMongoDBClientEncryption } from '../deps';
|
|
12
12
|
import { MongoRuntimeError } from '../error';
|
|
13
13
|
import { MongoClient, type MongoClientOptions } from '../mongo_client';
|
|
14
|
+
import { type Abortable } from '../mongo_types';
|
|
14
15
|
import { MongoDBCollectionNamespace } from '../utils';
|
|
15
16
|
import { autoSelectSocketOptions } from './client_encryption';
|
|
16
17
|
import * as cryptoCallbacks from './crypto_callbacks';
|
|
@@ -372,8 +373,10 @@ export class AutoEncrypter {
|
|
|
372
373
|
async encrypt(
|
|
373
374
|
ns: string,
|
|
374
375
|
cmd: Document,
|
|
375
|
-
options: CommandOptions = {}
|
|
376
|
+
options: CommandOptions & Abortable = {}
|
|
376
377
|
): Promise<Document | Uint8Array> {
|
|
378
|
+
options.signal?.throwIfAborted();
|
|
379
|
+
|
|
377
380
|
if (this._bypassEncryption) {
|
|
378
381
|
// If `bypassAutoEncryption` has been specified, don't encrypt
|
|
379
382
|
return cmd;
|
|
@@ -398,7 +401,7 @@ export class AutoEncrypter {
|
|
|
398
401
|
socketOptions: autoSelectSocketOptions(this._client.s.options)
|
|
399
402
|
});
|
|
400
403
|
|
|
401
|
-
return deserialize(await stateMachine.execute(this, context, options
|
|
404
|
+
return deserialize(await stateMachine.execute(this, context, options), {
|
|
402
405
|
promoteValues: false,
|
|
403
406
|
promoteLongs: false
|
|
404
407
|
});
|
|
@@ -407,7 +410,12 @@ export class AutoEncrypter {
|
|
|
407
410
|
/**
|
|
408
411
|
* Decrypt a command response
|
|
409
412
|
*/
|
|
410
|
-
async decrypt(
|
|
413
|
+
async decrypt(
|
|
414
|
+
response: Uint8Array,
|
|
415
|
+
options: CommandOptions & Abortable = {}
|
|
416
|
+
): Promise<Uint8Array> {
|
|
417
|
+
options.signal?.throwIfAborted();
|
|
418
|
+
|
|
411
419
|
const context = this._mongocrypt.makeDecryptionContext(response);
|
|
412
420
|
|
|
413
421
|
context.id = this._contextCounter++;
|
|
@@ -419,11 +427,7 @@ export class AutoEncrypter {
|
|
|
419
427
|
socketOptions: autoSelectSocketOptions(this._client.s.options)
|
|
420
428
|
});
|
|
421
429
|
|
|
422
|
-
return await stateMachine.execute(
|
|
423
|
-
this,
|
|
424
|
-
context,
|
|
425
|
-
options.timeoutContext?.csotEnabled() ? options.timeoutContext : undefined
|
|
426
|
-
);
|
|
430
|
+
return await stateMachine.execute(this, context, options);
|
|
427
431
|
}
|
|
428
432
|
|
|
429
433
|
/**
|
|
@@ -225,7 +225,7 @@ export class ClientEncryption {
|
|
|
225
225
|
TimeoutContext.create(resolveTimeoutOptions(this._client, { timeoutMS: this._timeoutMS }));
|
|
226
226
|
|
|
227
227
|
const dataKey = deserialize(
|
|
228
|
-
await stateMachine.execute(this, context, timeoutContext)
|
|
228
|
+
await stateMachine.execute(this, context, { timeoutContext })
|
|
229
229
|
) as DataKey;
|
|
230
230
|
|
|
231
231
|
const { db: dbName, collection: collectionName } = MongoDBCollectionNamespace.fromString(
|
|
@@ -293,7 +293,9 @@ export class ClientEncryption {
|
|
|
293
293
|
resolveTimeoutOptions(this._client, { timeoutMS: this._timeoutMS })
|
|
294
294
|
);
|
|
295
295
|
|
|
296
|
-
const { v: dataKeys } = deserialize(
|
|
296
|
+
const { v: dataKeys } = deserialize(
|
|
297
|
+
await stateMachine.execute(this, context, { timeoutContext })
|
|
298
|
+
);
|
|
297
299
|
if (dataKeys.length === 0) {
|
|
298
300
|
return {};
|
|
299
301
|
}
|
|
@@ -696,7 +698,7 @@ export class ClientEncryption {
|
|
|
696
698
|
? TimeoutContext.create(resolveTimeoutOptions(this._client, { timeoutMS: this._timeoutMS }))
|
|
697
699
|
: undefined;
|
|
698
700
|
|
|
699
|
-
const { v } = deserialize(await stateMachine.execute(this, context, timeoutContext));
|
|
701
|
+
const { v } = deserialize(await stateMachine.execute(this, context, { timeoutContext }));
|
|
700
702
|
|
|
701
703
|
return v;
|
|
702
704
|
}
|
|
@@ -780,7 +782,7 @@ export class ClientEncryption {
|
|
|
780
782
|
this._timeoutMS != null
|
|
781
783
|
? TimeoutContext.create(resolveTimeoutOptions(this._client, { timeoutMS: this._timeoutMS }))
|
|
782
784
|
: undefined;
|
|
783
|
-
const { v } = deserialize(await stateMachine.execute(this, context, timeoutContext));
|
|
785
|
+
const { v } = deserialize(await stateMachine.execute(this, context, { timeoutContext }));
|
|
784
786
|
return v;
|
|
785
787
|
}
|
|
786
788
|
}
|
|
@@ -15,8 +15,15 @@ import { CursorTimeoutContext } from '../cursor/abstract_cursor';
|
|
|
15
15
|
import { getSocks, type SocksLib } from '../deps';
|
|
16
16
|
import { MongoOperationTimeoutError } from '../error';
|
|
17
17
|
import { type MongoClient, type MongoClientOptions } from '../mongo_client';
|
|
18
|
+
import { type Abortable } from '../mongo_types';
|
|
18
19
|
import { Timeout, type TimeoutContext, TimeoutError } from '../timeout';
|
|
19
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
addAbortListener,
|
|
22
|
+
BufferPool,
|
|
23
|
+
kDispose,
|
|
24
|
+
MongoDBCollectionNamespace,
|
|
25
|
+
promiseWithResolvers
|
|
26
|
+
} from '../utils';
|
|
20
27
|
import { autoSelectSocketOptions, type DataKey } from './client_encryption';
|
|
21
28
|
import { MongoCryptError } from './errors';
|
|
22
29
|
import { type MongocryptdManager } from './mongocryptd_manager';
|
|
@@ -189,7 +196,7 @@ export class StateMachine {
|
|
|
189
196
|
async execute(
|
|
190
197
|
executor: StateMachineExecutable,
|
|
191
198
|
context: MongoCryptContext,
|
|
192
|
-
timeoutContext?: TimeoutContext
|
|
199
|
+
options: { timeoutContext?: TimeoutContext } & Abortable
|
|
193
200
|
): Promise<Uint8Array> {
|
|
194
201
|
const keyVaultNamespace = executor._keyVaultNamespace;
|
|
195
202
|
const keyVaultClient = executor._keyVaultClient;
|
|
@@ -199,6 +206,7 @@ export class StateMachine {
|
|
|
199
206
|
let result: Uint8Array | null = null;
|
|
200
207
|
|
|
201
208
|
while (context.state !== MONGOCRYPT_CTX_DONE && context.state !== MONGOCRYPT_CTX_ERROR) {
|
|
209
|
+
options.signal?.throwIfAborted();
|
|
202
210
|
debug(`[context#${context.id}] ${stateToString.get(context.state) || context.state}`);
|
|
203
211
|
|
|
204
212
|
switch (context.state) {
|
|
@@ -214,7 +222,7 @@ export class StateMachine {
|
|
|
214
222
|
metaDataClient,
|
|
215
223
|
context.ns,
|
|
216
224
|
filter,
|
|
217
|
-
|
|
225
|
+
options
|
|
218
226
|
);
|
|
219
227
|
if (collInfo) {
|
|
220
228
|
context.addMongoOperationResponse(collInfo);
|
|
@@ -235,9 +243,9 @@ export class StateMachine {
|
|
|
235
243
|
// When we are using the shared library, we don't have a mongocryptd manager.
|
|
236
244
|
const markedCommand: Uint8Array = mongocryptdManager
|
|
237
245
|
? await mongocryptdManager.withRespawn(
|
|
238
|
-
this.markCommand.bind(this, mongocryptdClient, context.ns, command,
|
|
246
|
+
this.markCommand.bind(this, mongocryptdClient, context.ns, command, options)
|
|
239
247
|
)
|
|
240
|
-
: await this.markCommand(mongocryptdClient, context.ns, command,
|
|
248
|
+
: await this.markCommand(mongocryptdClient, context.ns, command, options);
|
|
241
249
|
|
|
242
250
|
context.addMongoOperationResponse(markedCommand);
|
|
243
251
|
context.finishMongoOperation();
|
|
@@ -246,12 +254,7 @@ export class StateMachine {
|
|
|
246
254
|
|
|
247
255
|
case MONGOCRYPT_CTX_NEED_MONGO_KEYS: {
|
|
248
256
|
const filter = context.nextMongoOperation();
|
|
249
|
-
const keys = await this.fetchKeys(
|
|
250
|
-
keyVaultClient,
|
|
251
|
-
keyVaultNamespace,
|
|
252
|
-
filter,
|
|
253
|
-
timeoutContext
|
|
254
|
-
);
|
|
257
|
+
const keys = await this.fetchKeys(keyVaultClient, keyVaultNamespace, filter, options);
|
|
255
258
|
|
|
256
259
|
if (keys.length === 0) {
|
|
257
260
|
// See docs on EMPTY_V
|
|
@@ -273,7 +276,7 @@ export class StateMachine {
|
|
|
273
276
|
}
|
|
274
277
|
|
|
275
278
|
case MONGOCRYPT_CTX_NEED_KMS: {
|
|
276
|
-
await Promise.all(this.requests(context,
|
|
279
|
+
await Promise.all(this.requests(context, options));
|
|
277
280
|
context.finishKMSRequests();
|
|
278
281
|
break;
|
|
279
282
|
}
|
|
@@ -315,11 +318,13 @@ export class StateMachine {
|
|
|
315
318
|
* @param kmsContext - A C++ KMS context returned from the bindings
|
|
316
319
|
* @returns A promise that resolves when the KMS reply has be fully parsed
|
|
317
320
|
*/
|
|
318
|
-
async kmsRequest(
|
|
321
|
+
async kmsRequest(
|
|
322
|
+
request: MongoCryptKMSRequest,
|
|
323
|
+
options?: { timeoutContext?: TimeoutContext } & Abortable
|
|
324
|
+
): Promise<void> {
|
|
319
325
|
const parsedUrl = request.endpoint.split(':');
|
|
320
326
|
const port = parsedUrl[1] != null ? Number.parseInt(parsedUrl[1], 10) : HTTPS_PORT;
|
|
321
|
-
const socketOptions
|
|
322
|
-
const options: tls.ConnectionOptions & {
|
|
327
|
+
const socketOptions: tls.ConnectionOptions & {
|
|
323
328
|
host: string;
|
|
324
329
|
port: number;
|
|
325
330
|
autoSelectFamily?: boolean;
|
|
@@ -328,7 +333,7 @@ export class StateMachine {
|
|
|
328
333
|
host: parsedUrl[0],
|
|
329
334
|
servername: parsedUrl[0],
|
|
330
335
|
port,
|
|
331
|
-
...socketOptions
|
|
336
|
+
...autoSelectSocketOptions(this.options.socketOptions || {})
|
|
332
337
|
};
|
|
333
338
|
const message = request.message;
|
|
334
339
|
const buffer = new BufferPool();
|
|
@@ -363,7 +368,7 @@ export class StateMachine {
|
|
|
363
368
|
throw error;
|
|
364
369
|
}
|
|
365
370
|
try {
|
|
366
|
-
await this.setTlsOptions(providerTlsOptions,
|
|
371
|
+
await this.setTlsOptions(providerTlsOptions, socketOptions);
|
|
367
372
|
} catch (err) {
|
|
368
373
|
throw onerror(err);
|
|
369
374
|
}
|
|
@@ -380,23 +385,25 @@ export class StateMachine {
|
|
|
380
385
|
.once('close', () => rejectOnNetSocketError(onclose()))
|
|
381
386
|
.once('connect', () => resolveOnNetSocketConnect());
|
|
382
387
|
|
|
388
|
+
let abortListener;
|
|
389
|
+
|
|
383
390
|
try {
|
|
384
391
|
if (this.options.proxyOptions && this.options.proxyOptions.proxyHost) {
|
|
385
392
|
const netSocketOptions = {
|
|
393
|
+
...socketOptions,
|
|
386
394
|
host: this.options.proxyOptions.proxyHost,
|
|
387
|
-
port: this.options.proxyOptions.proxyPort || 1080
|
|
388
|
-
...socketOptions
|
|
395
|
+
port: this.options.proxyOptions.proxyPort || 1080
|
|
389
396
|
};
|
|
390
397
|
netSocket.connect(netSocketOptions);
|
|
391
398
|
await willConnect;
|
|
392
399
|
|
|
393
400
|
try {
|
|
394
401
|
socks ??= loadSocks();
|
|
395
|
-
|
|
402
|
+
socketOptions.socket = (
|
|
396
403
|
await socks.SocksClient.createConnection({
|
|
397
404
|
existing_socket: netSocket,
|
|
398
405
|
command: 'connect',
|
|
399
|
-
destination: { host:
|
|
406
|
+
destination: { host: socketOptions.host, port: socketOptions.port },
|
|
400
407
|
proxy: {
|
|
401
408
|
// host and port are ignored because we pass existing_socket
|
|
402
409
|
host: 'iLoveJavaScript',
|
|
@@ -412,7 +419,7 @@ export class StateMachine {
|
|
|
412
419
|
}
|
|
413
420
|
}
|
|
414
421
|
|
|
415
|
-
socket = tls.connect(
|
|
422
|
+
socket = tls.connect(socketOptions, () => {
|
|
416
423
|
socket.write(message);
|
|
417
424
|
});
|
|
418
425
|
|
|
@@ -422,6 +429,11 @@ export class StateMachine {
|
|
|
422
429
|
resolve
|
|
423
430
|
} = promiseWithResolvers<void>();
|
|
424
431
|
|
|
432
|
+
abortListener = addAbortListener(options?.signal, function () {
|
|
433
|
+
destroySockets();
|
|
434
|
+
rejectOnTlsSocketError(this.reason);
|
|
435
|
+
});
|
|
436
|
+
|
|
425
437
|
socket
|
|
426
438
|
.once('error', err => rejectOnTlsSocketError(onerror(err)))
|
|
427
439
|
.once('close', () => rejectOnTlsSocketError(onclose()))
|
|
@@ -436,8 +448,11 @@ export class StateMachine {
|
|
|
436
448
|
resolve();
|
|
437
449
|
}
|
|
438
450
|
});
|
|
439
|
-
await (timeoutContext?.csotEnabled()
|
|
440
|
-
? Promise.all([
|
|
451
|
+
await (options?.timeoutContext?.csotEnabled()
|
|
452
|
+
? Promise.all([
|
|
453
|
+
willResolveKmsRequest,
|
|
454
|
+
Timeout.expires(options.timeoutContext?.remainingTimeMS)
|
|
455
|
+
])
|
|
441
456
|
: willResolveKmsRequest);
|
|
442
457
|
} catch (error) {
|
|
443
458
|
if (error instanceof TimeoutError)
|
|
@@ -446,16 +461,17 @@ export class StateMachine {
|
|
|
446
461
|
} finally {
|
|
447
462
|
// There's no need for any more activity on this socket at this point.
|
|
448
463
|
destroySockets();
|
|
464
|
+
abortListener?.[kDispose]();
|
|
449
465
|
}
|
|
450
466
|
}
|
|
451
467
|
|
|
452
|
-
*requests(context: MongoCryptContext, timeoutContext?: TimeoutContext) {
|
|
468
|
+
*requests(context: MongoCryptContext, options?: { timeoutContext?: TimeoutContext } & Abortable) {
|
|
453
469
|
for (
|
|
454
470
|
let request = context.nextKMSRequest();
|
|
455
471
|
request != null;
|
|
456
472
|
request = context.nextKMSRequest()
|
|
457
473
|
) {
|
|
458
|
-
yield this.kmsRequest(request,
|
|
474
|
+
yield this.kmsRequest(request, options);
|
|
459
475
|
}
|
|
460
476
|
}
|
|
461
477
|
|
|
@@ -516,14 +532,16 @@ export class StateMachine {
|
|
|
516
532
|
client: MongoClient,
|
|
517
533
|
ns: string,
|
|
518
534
|
filter: Document,
|
|
519
|
-
timeoutContext?: TimeoutContext
|
|
535
|
+
options?: { timeoutContext?: TimeoutContext } & Abortable
|
|
520
536
|
): Promise<Uint8Array | null> {
|
|
521
537
|
const { db } = MongoDBCollectionNamespace.fromString(ns);
|
|
522
538
|
|
|
523
539
|
const cursor = client.db(db).listCollections(filter, {
|
|
524
540
|
promoteLongs: false,
|
|
525
541
|
promoteValues: false,
|
|
526
|
-
timeoutContext:
|
|
542
|
+
timeoutContext:
|
|
543
|
+
options?.timeoutContext && new CursorTimeoutContext(options?.timeoutContext, Symbol()),
|
|
544
|
+
signal: options?.signal
|
|
527
545
|
});
|
|
528
546
|
|
|
529
547
|
// There is always exactly zero or one matching documents, so this should always exhaust the cursor
|
|
@@ -547,17 +565,30 @@ export class StateMachine {
|
|
|
547
565
|
client: MongoClient,
|
|
548
566
|
ns: string,
|
|
549
567
|
command: Uint8Array,
|
|
550
|
-
timeoutContext?: TimeoutContext
|
|
568
|
+
options?: { timeoutContext?: TimeoutContext } & Abortable
|
|
551
569
|
): Promise<Uint8Array> {
|
|
552
570
|
const { db } = MongoDBCollectionNamespace.fromString(ns);
|
|
553
571
|
const bsonOptions = { promoteLongs: false, promoteValues: false };
|
|
554
572
|
const rawCommand = deserialize(command, bsonOptions);
|
|
555
573
|
|
|
574
|
+
const commandOptions: {
|
|
575
|
+
timeoutMS?: number;
|
|
576
|
+
signal?: AbortSignal;
|
|
577
|
+
} = {
|
|
578
|
+
timeoutMS: undefined,
|
|
579
|
+
signal: undefined
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
if (options?.timeoutContext?.csotEnabled()) {
|
|
583
|
+
commandOptions.timeoutMS = options.timeoutContext.remainingTimeMS;
|
|
584
|
+
}
|
|
585
|
+
if (options?.signal) {
|
|
586
|
+
commandOptions.signal = options.signal;
|
|
587
|
+
}
|
|
588
|
+
|
|
556
589
|
const response = await client.db(db).command(rawCommand, {
|
|
557
590
|
...bsonOptions,
|
|
558
|
-
...
|
|
559
|
-
? { timeoutMS: timeoutContext?.remainingTimeMS }
|
|
560
|
-
: undefined)
|
|
591
|
+
...commandOptions
|
|
561
592
|
});
|
|
562
593
|
|
|
563
594
|
return serialize(response, this.bsonOptions);
|
|
@@ -575,17 +606,30 @@ export class StateMachine {
|
|
|
575
606
|
client: MongoClient,
|
|
576
607
|
keyVaultNamespace: string,
|
|
577
608
|
filter: Uint8Array,
|
|
578
|
-
timeoutContext?: TimeoutContext
|
|
609
|
+
options?: { timeoutContext?: TimeoutContext } & Abortable
|
|
579
610
|
): Promise<Array<DataKey>> {
|
|
580
611
|
const { db: dbName, collection: collectionName } =
|
|
581
612
|
MongoDBCollectionNamespace.fromString(keyVaultNamespace);
|
|
582
613
|
|
|
614
|
+
const commandOptions: {
|
|
615
|
+
timeoutContext?: CursorTimeoutContext;
|
|
616
|
+
signal?: AbortSignal;
|
|
617
|
+
} = {
|
|
618
|
+
timeoutContext: undefined,
|
|
619
|
+
signal: undefined
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
if (options?.timeoutContext != null) {
|
|
623
|
+
commandOptions.timeoutContext = new CursorTimeoutContext(options.timeoutContext, Symbol());
|
|
624
|
+
}
|
|
625
|
+
if (options?.signal != null) {
|
|
626
|
+
commandOptions.signal = options.signal;
|
|
627
|
+
}
|
|
628
|
+
|
|
583
629
|
return client
|
|
584
630
|
.db(dbName)
|
|
585
631
|
.collection<DataKey>(collectionName, { readConcern: { level: 'majority' } })
|
|
586
|
-
.find(deserialize(filter),
|
|
587
|
-
timeoutContext: timeoutContext && new CursorTimeoutContext(timeoutContext, Symbol())
|
|
588
|
-
})
|
|
632
|
+
.find(deserialize(filter), commandOptions)
|
|
589
633
|
.toArray();
|
|
590
634
|
}
|
|
591
635
|
}
|
|
@@ -143,7 +143,7 @@ export class MongoDBOIDC extends AuthProvider {
|
|
|
143
143
|
*/
|
|
144
144
|
override async auth(authContext: AuthContext): Promise<void> {
|
|
145
145
|
const { connection, reauthenticating, response } = authContext;
|
|
146
|
-
if (response?.speculativeAuthenticate?.done) {
|
|
146
|
+
if (response?.speculativeAuthenticate?.done && !reauthenticating) {
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
const credentials = getCredentials(authContext);
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
LEGACY_HELLO_COMMAND,
|
|
7
7
|
LEGACY_HELLO_COMMAND_CAMEL_CASE
|
|
8
8
|
} from '../constants';
|
|
9
|
-
import { calculateDurationInMs
|
|
9
|
+
import { calculateDurationInMs } from '../utils';
|
|
10
10
|
import {
|
|
11
11
|
DocumentSequence,
|
|
12
12
|
OpMsgRequest,
|
|
@@ -125,7 +125,7 @@ export class CommandSucceededEvent {
|
|
|
125
125
|
this.requestId = command.requestId;
|
|
126
126
|
this.commandName = commandName;
|
|
127
127
|
this.duration = calculateDurationInMs(started);
|
|
128
|
-
this.reply = maybeRedact(commandName, cmd, extractReply(
|
|
128
|
+
this.reply = maybeRedact(commandName, cmd, extractReply(reply));
|
|
129
129
|
this.serverConnectionId = serverConnectionId;
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -214,7 +214,6 @@ const HELLO_COMMANDS = new Set(['hello', LEGACY_HELLO_COMMAND, LEGACY_HELLO_COMM
|
|
|
214
214
|
|
|
215
215
|
// helper methods
|
|
216
216
|
const extractCommandName = (commandDoc: Document) => Object.keys(commandDoc)[0];
|
|
217
|
-
const namespace = (command: OpQueryRequest) => command.ns;
|
|
218
217
|
const collectionName = (command: OpQueryRequest) => command.ns.split('.')[1];
|
|
219
218
|
const maybeRedact = (commandName: string, commandDoc: Document, result: Error | Document) =>
|
|
220
219
|
SENSITIVE_COMMANDS.has(commandName) ||
|
|
@@ -242,19 +241,10 @@ const LEGACY_FIND_OPTIONS_MAP = {
|
|
|
242
241
|
returnFieldSelector: 'projection'
|
|
243
242
|
} as const;
|
|
244
243
|
|
|
245
|
-
const OP_QUERY_KEYS = [
|
|
246
|
-
'tailable',
|
|
247
|
-
'oplogReplay',
|
|
248
|
-
'noCursorTimeout',
|
|
249
|
-
'awaitData',
|
|
250
|
-
'partial',
|
|
251
|
-
'exhaust'
|
|
252
|
-
] as const;
|
|
253
|
-
|
|
254
244
|
/** Extract the actual command from the query, possibly up-converting if it's a legacy format */
|
|
255
245
|
function extractCommand(command: WriteProtocolMessageType): Document {
|
|
256
246
|
if (command instanceof OpMsgRequest) {
|
|
257
|
-
const cmd =
|
|
247
|
+
const cmd = { ...command.command };
|
|
258
248
|
// For OP_MSG with payload type 1 we need to pull the documents
|
|
259
249
|
// array out of the document sequence for monitoring.
|
|
260
250
|
if (cmd.ops instanceof DocumentSequence) {
|
|
@@ -276,7 +266,7 @@ function extractCommand(command: WriteProtocolMessageType): Document {
|
|
|
276
266
|
result = { find: collectionName(command) };
|
|
277
267
|
Object.keys(LEGACY_FIND_QUERY_MAP).forEach(key => {
|
|
278
268
|
if (command.query[key] != null) {
|
|
279
|
-
result[LEGACY_FIND_QUERY_MAP[key]] =
|
|
269
|
+
result[LEGACY_FIND_QUERY_MAP[key]] = { ...command.query[key] };
|
|
280
270
|
}
|
|
281
271
|
});
|
|
282
272
|
}
|
|
@@ -284,64 +274,29 @@ function extractCommand(command: WriteProtocolMessageType): Document {
|
|
|
284
274
|
Object.keys(LEGACY_FIND_OPTIONS_MAP).forEach(key => {
|
|
285
275
|
const legacyKey = key as keyof typeof LEGACY_FIND_OPTIONS_MAP;
|
|
286
276
|
if (command[legacyKey] != null) {
|
|
287
|
-
result[LEGACY_FIND_OPTIONS_MAP[legacyKey]] =
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
OP_QUERY_KEYS.forEach(key => {
|
|
292
|
-
if (command[key]) {
|
|
293
|
-
result[key] = command[key];
|
|
277
|
+
result[LEGACY_FIND_OPTIONS_MAP[legacyKey]] = command[legacyKey];
|
|
294
278
|
}
|
|
295
279
|
});
|
|
296
280
|
|
|
297
|
-
if (command.pre32Limit != null) {
|
|
298
|
-
result.limit = command.pre32Limit;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (command.query.$explain) {
|
|
302
|
-
return { explain: result };
|
|
303
|
-
}
|
|
304
281
|
return result;
|
|
305
282
|
}
|
|
306
283
|
|
|
307
|
-
|
|
308
|
-
const clonedCommand: Record<string, unknown> = {};
|
|
284
|
+
let clonedQuery: Record<string, unknown> = {};
|
|
285
|
+
const clonedCommand: Record<string, unknown> = { ...command };
|
|
309
286
|
if (command.query) {
|
|
310
|
-
|
|
311
|
-
clonedQuery[k] = deepCopy(command.query[k]);
|
|
312
|
-
}
|
|
287
|
+
clonedQuery = { ...command.query };
|
|
313
288
|
clonedCommand.query = clonedQuery;
|
|
314
289
|
}
|
|
315
290
|
|
|
316
|
-
for (const k in command) {
|
|
317
|
-
if (k === 'query') continue;
|
|
318
|
-
clonedCommand[k] = deepCopy((command as unknown as Record<string, unknown>)[k]);
|
|
319
|
-
}
|
|
320
291
|
return command.query ? clonedQuery : clonedCommand;
|
|
321
292
|
}
|
|
322
293
|
|
|
323
|
-
function extractReply(
|
|
294
|
+
function extractReply(reply?: Document) {
|
|
324
295
|
if (!reply) {
|
|
325
296
|
return reply;
|
|
326
297
|
}
|
|
327
298
|
|
|
328
|
-
|
|
329
|
-
return deepCopy(reply.result ? reply.result : reply);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// is this a legacy find command?
|
|
333
|
-
if (command.query && command.query.$query != null) {
|
|
334
|
-
return {
|
|
335
|
-
ok: 1,
|
|
336
|
-
cursor: {
|
|
337
|
-
id: deepCopy(reply.cursorId),
|
|
338
|
-
ns: namespace(command),
|
|
339
|
-
firstBatch: deepCopy(reply.documents)
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return deepCopy(reply.result ? reply.result : reply);
|
|
299
|
+
return reply.result ? reply.result : reply;
|
|
345
300
|
}
|
|
346
301
|
|
|
347
302
|
function extractConnectionDetails(connection: Connection) {
|