@depup/mongodb 7.5.0-depup.8 → 7.7.0-depup.0
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/README.md +5 -6
- package/changes.json +6 -10
- package/lib/bulk/common.js +27 -5
- package/lib/bulk/common.js.map +1 -1
- package/lib/bulk/ordered.js +25 -7
- package/lib/bulk/ordered.js.map +1 -1
- package/lib/bulk/unordered.js +25 -7
- package/lib/bulk/unordered.js.map +1 -1
- package/lib/client-side-encryption/auto_encrypter.js +8 -2
- package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
- package/lib/client-side-encryption/client_encryption.js +12 -4
- package/lib/client-side-encryption/client_encryption.js.map +1 -1
- package/lib/client-side-encryption/kms_options.js +3 -0
- package/lib/client-side-encryption/kms_options.js.map +1 -0
- package/lib/client-side-encryption/state_machine.js +54 -7
- package/lib/client-side-encryption/state_machine.js.map +1 -1
- package/lib/cmap/auth/gssapi.js +2 -1
- package/lib/cmap/auth/gssapi.js.map +1 -1
- package/lib/cmap/auth/mongo_credentials.js +0 -1
- package/lib/cmap/auth/mongo_credentials.js.map +1 -1
- package/lib/cmap/auth/mongodb_aws.js +0 -3
- package/lib/cmap/auth/mongodb_aws.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/callback_workflow.js +7 -8
- package/lib/cmap/auth/mongodb_oidc/callback_workflow.js.map +1 -1
- package/lib/cmap/command_monitoring_events.js +6 -7
- package/lib/cmap/command_monitoring_events.js.map +1 -1
- package/lib/cmap/commands.js +13 -0
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +2 -2
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +3 -1
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/handshake/client_metadata.js +2 -1
- package/lib/cmap/handshake/client_metadata.js.map +1 -1
- package/lib/cmap/wire_protocol/constants.js +2 -2
- package/lib/connection_string.js +4 -0
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/error.js +7 -11
- package/lib/error.js.map +1 -1
- package/lib/gridfs/download.js +1 -1
- package/lib/gridfs/download.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/delete.js +7 -10
- package/lib/operations/delete.js.map +1 -1
- package/lib/operations/execute_operation.js +29 -2
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find_and_modify.js +1 -5
- package/lib/operations/find_and_modify.js.map +1 -1
- package/lib/operations/get_more.js +2 -3
- package/lib/operations/get_more.js.map +1 -1
- package/lib/operations/indexes.js +3 -9
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/insert.js +6 -2
- package/lib/operations/insert.js.map +1 -1
- package/lib/operations/list_collections.js +2 -3
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/list_databases.js +2 -2
- package/lib/operations/list_databases.js.map +1 -1
- package/lib/operations/update.js +6 -2
- package/lib/operations/update.js.map +1 -1
- package/lib/runtime_adapters.js +15 -16
- package/lib/runtime_adapters.js.map +1 -1
- package/lib/sdam/server.js +1 -1
- package/lib/sdam/server.js.map +1 -1
- package/lib/sessions.js +2 -2
- package/lib/sessions.js.map +1 -1
- package/lib/utils.js +1 -3
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +77 -20
- package/package.json +30 -33
- package/src/bulk/common.ts +37 -5
- package/src/bulk/ordered.ts +23 -7
- package/src/bulk/unordered.ts +23 -8
- package/src/client-side-encryption/auto_encrypter.ts +19 -3
- package/src/client-side-encryption/client_encryption.ts +29 -9
- package/src/client-side-encryption/kms_options.ts +93 -0
- package/src/client-side-encryption/state_machine.ts +68 -47
- package/src/cmap/auth/gssapi.ts +3 -4
- package/src/cmap/auth/mongo_credentials.ts +0 -1
- package/src/cmap/auth/mongodb_aws.ts +2 -12
- package/src/cmap/auth/mongodb_oidc/callback_workflow.ts +17 -17
- package/src/cmap/command_monitoring_events.ts +9 -10
- package/src/cmap/commands.ts +17 -0
- package/src/cmap/connect.ts +3 -3
- package/src/cmap/connection.ts +4 -2
- package/src/cmap/handshake/client_metadata.ts +2 -1
- package/src/cmap/wire_protocol/constants.ts +2 -2
- package/src/connection_string.ts +4 -0
- package/src/cursor/abstract_cursor.ts +0 -5
- package/src/error.ts +7 -19
- package/src/gridfs/download.ts +1 -1
- package/src/index.ts +7 -6
- package/src/mongo_client.ts +1 -1
- package/src/operations/command.ts +0 -5
- package/src/operations/delete.ts +17 -15
- package/src/operations/execute_operation.ts +29 -2
- package/src/operations/find_and_modify.ts +3 -15
- package/src/operations/get_more.ts +3 -5
- package/src/operations/indexes.ts +6 -16
- package/src/operations/insert.ts +13 -2
- package/src/operations/list_collections.ts +5 -6
- package/src/operations/list_databases.ts +3 -3
- package/src/operations/update.ts +9 -2
- package/src/operations/validate_collection.ts +1 -1
- package/src/runtime_adapters.ts +19 -18
- package/src/sdam/server.ts +1 -1
- package/src/sessions.ts +2 -5
- package/src/utils.ts +1 -3
- package/tsconfig.json +9 -2
|
@@ -31,6 +31,11 @@ import {
|
|
|
31
31
|
MongoCryptCreateEncryptedCollectionError,
|
|
32
32
|
MongoCryptInvalidArgumentError
|
|
33
33
|
} from './errors';
|
|
34
|
+
import {
|
|
35
|
+
type ClientEncryptionSocketOptions,
|
|
36
|
+
type CSFLEKMSTlsOptions,
|
|
37
|
+
type KMSConnectCallback
|
|
38
|
+
} from './kms_options';
|
|
34
39
|
import {
|
|
35
40
|
type ClientEncryptionDataKeyProvider,
|
|
36
41
|
type CredentialProviders,
|
|
@@ -38,11 +43,7 @@ import {
|
|
|
38
43
|
type KMSProviders,
|
|
39
44
|
refreshKMSCredentials
|
|
40
45
|
} from './providers/index';
|
|
41
|
-
import {
|
|
42
|
-
type ClientEncryptionSocketOptions,
|
|
43
|
-
type CSFLEKMSTlsOptions,
|
|
44
|
-
StateMachine
|
|
45
|
-
} from './state_machine';
|
|
46
|
+
import { StateMachine } from './state_machine';
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
49
|
* @public
|
|
@@ -75,6 +76,8 @@ export class ClientEncryption {
|
|
|
75
76
|
/** @internal */
|
|
76
77
|
_tlsOptions: CSFLEKMSTlsOptions;
|
|
77
78
|
/** @internal */
|
|
79
|
+
_kmsConnectCallback?: KMSConnectCallback;
|
|
80
|
+
/** @internal */
|
|
78
81
|
_kmsProviders: KMSProviders;
|
|
79
82
|
/** @internal */
|
|
80
83
|
_timeoutMS?: number;
|
|
@@ -125,7 +128,13 @@ export class ClientEncryption {
|
|
|
125
128
|
constructor(client: MongoClient, options: ClientEncryptionOptions) {
|
|
126
129
|
this._client = client;
|
|
127
130
|
this._proxyOptions = options.proxyOptions ?? {};
|
|
131
|
+
if (this._proxyOptions.proxyHost && options.kmsConnectCallback) {
|
|
132
|
+
throw new MongoCryptInvalidArgumentError(
|
|
133
|
+
'Cannot set both proxyOptions and kmsConnectCallback'
|
|
134
|
+
);
|
|
135
|
+
}
|
|
128
136
|
this._tlsOptions = options.tlsOptions ?? {};
|
|
137
|
+
this._kmsConnectCallback = options.kmsConnectCallback;
|
|
129
138
|
this._kmsProviders = options.kmsProviders || {};
|
|
130
139
|
const { timeoutMS } = resolveTimeoutOptions(client, options);
|
|
131
140
|
this._timeoutMS = timeoutMS;
|
|
@@ -226,7 +235,8 @@ export class ClientEncryption {
|
|
|
226
235
|
const stateMachine = new StateMachine({
|
|
227
236
|
proxyOptions: this._proxyOptions,
|
|
228
237
|
tlsOptions: this._tlsOptions,
|
|
229
|
-
socketOptions: autoSelectSocketOptions(this._client.s.options)
|
|
238
|
+
socketOptions: autoSelectSocketOptions(this._client.s.options),
|
|
239
|
+
kmsConnectCallback: this._kmsConnectCallback
|
|
230
240
|
});
|
|
231
241
|
|
|
232
242
|
const timeoutContext =
|
|
@@ -295,7 +305,8 @@ export class ClientEncryption {
|
|
|
295
305
|
const stateMachine = new StateMachine({
|
|
296
306
|
proxyOptions: this._proxyOptions,
|
|
297
307
|
tlsOptions: this._tlsOptions,
|
|
298
|
-
socketOptions: autoSelectSocketOptions(this._client.s.options)
|
|
308
|
+
socketOptions: autoSelectSocketOptions(this._client.s.options),
|
|
309
|
+
kmsConnectCallback: this._kmsConnectCallback
|
|
299
310
|
});
|
|
300
311
|
|
|
301
312
|
const timeoutContext = TimeoutContext.create(
|
|
@@ -699,7 +710,8 @@ export class ClientEncryption {
|
|
|
699
710
|
const stateMachine = new StateMachine({
|
|
700
711
|
proxyOptions: this._proxyOptions,
|
|
701
712
|
tlsOptions: this._tlsOptions,
|
|
702
|
-
socketOptions: autoSelectSocketOptions(this._client.s.options)
|
|
713
|
+
socketOptions: autoSelectSocketOptions(this._client.s.options),
|
|
714
|
+
kmsConnectCallback: this._kmsConnectCallback
|
|
703
715
|
});
|
|
704
716
|
|
|
705
717
|
const timeoutContext =
|
|
@@ -797,7 +809,8 @@ export class ClientEncryption {
|
|
|
797
809
|
const stateMachine = new StateMachine({
|
|
798
810
|
proxyOptions: this._proxyOptions,
|
|
799
811
|
tlsOptions: this._tlsOptions,
|
|
800
|
-
socketOptions: autoSelectSocketOptions(this._client.s.options)
|
|
812
|
+
socketOptions: autoSelectSocketOptions(this._client.s.options),
|
|
813
|
+
kmsConnectCallback: this._kmsConnectCallback
|
|
801
814
|
});
|
|
802
815
|
const context = this._mongoCrypt.makeExplicitEncryptionContext(valueBuffer, contextOptions);
|
|
803
816
|
|
|
@@ -961,6 +974,13 @@ export interface ClientEncryptionOptions {
|
|
|
961
974
|
*/
|
|
962
975
|
tlsOptions?: CSFLEKMSTlsOptions;
|
|
963
976
|
|
|
977
|
+
/**
|
|
978
|
+
* A callback that establishes the socket used to connect to a KMS host, e.g. to route KMS
|
|
979
|
+
* requests through an HTTP proxy via the HTTP CONNECT method. Mutually exclusive with `proxyOptions`.
|
|
980
|
+
* See {@link KMSConnectCallback} for a usage example.
|
|
981
|
+
*/
|
|
982
|
+
kmsConnectCallback?: KMSConnectCallback;
|
|
983
|
+
|
|
964
984
|
/**
|
|
965
985
|
* Sets the expiration time for the DEK in the cache in milliseconds. Defaults to 60000. 0 means no timeout.
|
|
966
986
|
*/
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type Duplex } from 'stream';
|
|
2
|
+
|
|
3
|
+
import { type MongoClientOptions } from '../mongo_client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @public
|
|
7
|
+
*
|
|
8
|
+
* TLS options to use when connecting. The spec specifically calls out which insecure
|
|
9
|
+
* tls options are not allowed:
|
|
10
|
+
*
|
|
11
|
+
* - tlsAllowInvalidCertificates
|
|
12
|
+
* - tlsAllowInvalidHostnames
|
|
13
|
+
* - tlsInsecure
|
|
14
|
+
*
|
|
15
|
+
* These options are not included in the type, and are ignored if provided.
|
|
16
|
+
*/
|
|
17
|
+
export type ClientEncryptionTlsOptions = Pick<
|
|
18
|
+
MongoClientOptions,
|
|
19
|
+
'tlsCAFile' | 'tlsCertificateKeyFile' | 'tlsCertificateKeyFilePassword' | 'secureContext'
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
/** @public */
|
|
23
|
+
export type CSFLEKMSTlsOptions = {
|
|
24
|
+
aws?: ClientEncryptionTlsOptions;
|
|
25
|
+
gcp?: ClientEncryptionTlsOptions;
|
|
26
|
+
kmip?: ClientEncryptionTlsOptions;
|
|
27
|
+
local?: ClientEncryptionTlsOptions;
|
|
28
|
+
azure?: ClientEncryptionTlsOptions;
|
|
29
|
+
|
|
30
|
+
[key: string]: ClientEncryptionTlsOptions | undefined;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @public
|
|
35
|
+
*
|
|
36
|
+
* Socket options to use for KMS requests.
|
|
37
|
+
*/
|
|
38
|
+
export type ClientEncryptionSocketOptions = Pick<
|
|
39
|
+
MongoClientOptions,
|
|
40
|
+
'autoSelectFamily' | 'autoSelectFamilyAttemptTimeout'
|
|
41
|
+
>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @public
|
|
45
|
+
*
|
|
46
|
+
* A callback that establishes the connection to a KMS host.
|
|
47
|
+
*
|
|
48
|
+
* When provided on `AutoEncryptionOptions` or `ClientEncryptionOptions`, the driver invokes this
|
|
49
|
+
* callback instead of connecting to the KMS host itself, passing the target `host` and `port`. The
|
|
50
|
+
* callback MUST return a `Duplex` stream connected to the KMS host, either directly or tunneled
|
|
51
|
+
* through a proxy; a `net.Socket` satisfies this, as does any other `Duplex`. The returned stream is
|
|
52
|
+
* passed to Node.js' `tls.connect()` as its `socket`, and the driver performs the KMS host's TLS
|
|
53
|
+
* handshake over it using the KMS provider's configured TLS options. The callback therefore MUST NOT
|
|
54
|
+
* perform the KMS host's TLS handshake itself, though it MAY use TLS for its own transport, e.g. when
|
|
55
|
+
* connecting to an HTTPS proxy. This enables routing KMS requests through an HTTP proxy via the HTTP
|
|
56
|
+
* CONNECT method.
|
|
57
|
+
*
|
|
58
|
+
* When the operation has a client-side operation timeout (CSOT) configured, `timeoutMS` is the
|
|
59
|
+
* remaining time budget in milliseconds; it is `undefined` otherwise. The `signal` aborts when the
|
|
60
|
+
* connection attempt exceeds that budget; the callback should stop connecting and reject when it fires.
|
|
61
|
+
*
|
|
62
|
+
* @example <caption>Route KMS requests through an HTTP proxy using the HTTP CONNECT method</caption>
|
|
63
|
+
* ```ts
|
|
64
|
+
* import * as net from 'net';
|
|
65
|
+
*
|
|
66
|
+
* const kmsConnectCallback: KMSConnectCallback = ({ host, port, signal }) =>
|
|
67
|
+
* new Promise((resolve, reject) => {
|
|
68
|
+
* // Open a plain connection to the proxy, not to the KMS host.
|
|
69
|
+
* const socket = net.connect({ host: 'proxy.example.com', port: 8080, signal });
|
|
70
|
+
* socket.once('error', reject);
|
|
71
|
+
* socket.once('connect', () => {
|
|
72
|
+
* // Ask the proxy to tunnel to the KMS host, then hand the socket back for the driver's TLS.
|
|
73
|
+
* socket.write(`CONNECT ${host}:${port} HTTP/1.1\r\nHost: ${host}:${port}\r\n\r\n`);
|
|
74
|
+
* socket.once('data', chunk => {
|
|
75
|
+
* if (chunk.toString('utf8').startsWith('HTTP/1.1 200')) resolve(socket);
|
|
76
|
+
* else reject(new Error('Proxy refused the CONNECT request'));
|
|
77
|
+
* });
|
|
78
|
+
* });
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* const clientEncryption = new ClientEncryption(keyVaultClient, {
|
|
82
|
+
* keyVaultNamespace,
|
|
83
|
+
* kmsProviders,
|
|
84
|
+
* kmsConnectCallback
|
|
85
|
+
* });
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export type KMSConnectCallback = (options: {
|
|
89
|
+
host: string;
|
|
90
|
+
port: number;
|
|
91
|
+
timeoutMS?: number;
|
|
92
|
+
signal: AbortSignal;
|
|
93
|
+
}) => Promise<Duplex>;
|
|
@@ -15,7 +15,7 @@ import { type ProxyOptions } from '../cmap/connection';
|
|
|
15
15
|
import { CursorTimeoutContext } from '../cursor/abstract_cursor';
|
|
16
16
|
import { getSocks, type SocksLib } from '../deps';
|
|
17
17
|
import { MongoOperationTimeoutError } from '../error';
|
|
18
|
-
import { type MongoClient
|
|
18
|
+
import { type MongoClient } from '../mongo_client';
|
|
19
19
|
import { type Abortable } from '../mongo_types';
|
|
20
20
|
import { type CollectionInfo } from '../operations/list_collections';
|
|
21
21
|
import { Timeout, type TimeoutContext, TimeoutError } from '../timeout';
|
|
@@ -28,6 +28,12 @@ import {
|
|
|
28
28
|
} from '../utils';
|
|
29
29
|
import { autoSelectSocketOptions, type DataKey } from './client_encryption';
|
|
30
30
|
import { MongoCryptError } from './errors';
|
|
31
|
+
import {
|
|
32
|
+
type ClientEncryptionSocketOptions,
|
|
33
|
+
type ClientEncryptionTlsOptions,
|
|
34
|
+
type CSFLEKMSTlsOptions,
|
|
35
|
+
type KMSConnectCallback
|
|
36
|
+
} from './kms_options';
|
|
31
37
|
import { type MongocryptdManager } from './mongocryptd_manager';
|
|
32
38
|
import { type KMSProviders } from './providers';
|
|
33
39
|
|
|
@@ -93,44 +99,6 @@ declare module 'mongodb-client-encryption' {
|
|
|
93
99
|
}
|
|
94
100
|
}
|
|
95
101
|
|
|
96
|
-
/**
|
|
97
|
-
* @public
|
|
98
|
-
*
|
|
99
|
-
* TLS options to use when connecting. The spec specifically calls out which insecure
|
|
100
|
-
* tls options are not allowed:
|
|
101
|
-
*
|
|
102
|
-
* - tlsAllowInvalidCertificates
|
|
103
|
-
* - tlsAllowInvalidHostnames
|
|
104
|
-
* - tlsInsecure
|
|
105
|
-
*
|
|
106
|
-
* These options are not included in the type, and are ignored if provided.
|
|
107
|
-
*/
|
|
108
|
-
export type ClientEncryptionTlsOptions = Pick<
|
|
109
|
-
MongoClientOptions,
|
|
110
|
-
'tlsCAFile' | 'tlsCertificateKeyFile' | 'tlsCertificateKeyFilePassword' | 'secureContext'
|
|
111
|
-
>;
|
|
112
|
-
|
|
113
|
-
/** @public */
|
|
114
|
-
export type CSFLEKMSTlsOptions = {
|
|
115
|
-
aws?: ClientEncryptionTlsOptions;
|
|
116
|
-
gcp?: ClientEncryptionTlsOptions;
|
|
117
|
-
kmip?: ClientEncryptionTlsOptions;
|
|
118
|
-
local?: ClientEncryptionTlsOptions;
|
|
119
|
-
azure?: ClientEncryptionTlsOptions;
|
|
120
|
-
|
|
121
|
-
[key: string]: ClientEncryptionTlsOptions | undefined;
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* @public
|
|
126
|
-
*
|
|
127
|
-
* Socket options to use for KMS requests.
|
|
128
|
-
*/
|
|
129
|
-
export type ClientEncryptionSocketOptions = Pick<
|
|
130
|
-
MongoClientOptions,
|
|
131
|
-
'autoSelectFamily' | 'autoSelectFamilyAttemptTimeout'
|
|
132
|
-
>;
|
|
133
|
-
|
|
134
102
|
/**
|
|
135
103
|
* This is kind of a hack. For `rewrapManyDataKey`, we have tests that
|
|
136
104
|
* guarantee that when there are no matching keys, `rewrapManyDataKey` returns
|
|
@@ -173,6 +141,9 @@ export type StateMachineOptions = {
|
|
|
173
141
|
|
|
174
142
|
/** Socket specific options we support. */
|
|
175
143
|
socketOptions: ClientEncryptionSocketOptions;
|
|
144
|
+
|
|
145
|
+
/** Optional callback that establishes the KMS socket, if set. */
|
|
146
|
+
kmsConnectCallback?: KMSConnectCallback;
|
|
176
147
|
} & Pick<BSONSerializeOptions, 'promoteLongs' | 'promoteValues'>;
|
|
177
148
|
|
|
178
149
|
/**
|
|
@@ -388,9 +359,55 @@ export class StateMachine {
|
|
|
388
359
|
}
|
|
389
360
|
|
|
390
361
|
let abortListener;
|
|
362
|
+
let kmsRequestTimeout: Timeout | undefined;
|
|
391
363
|
|
|
392
364
|
try {
|
|
393
|
-
|
|
365
|
+
options?.signal?.throwIfAborted();
|
|
366
|
+
|
|
367
|
+
if (this.options.kmsConnectCallback) {
|
|
368
|
+
const remainingTimeMS = options?.timeoutContext?.csotEnabled()
|
|
369
|
+
? options.timeoutContext.getRemainingTimeMSOrThrow(
|
|
370
|
+
`KMS request timed out after ${options.timeoutContext.timeoutMS}ms`
|
|
371
|
+
)
|
|
372
|
+
: undefined;
|
|
373
|
+
// A non-finite budget (timeoutMS: 0) means no timeout; otherwise back the callback with one.
|
|
374
|
+
const timeoutMS = Number.isFinite(remainingTimeMS) ? remainingTimeMS : undefined;
|
|
375
|
+
const controller = new AbortController();
|
|
376
|
+
// Forward the operation's own abort signal so aborting the operation also aborts the callback.
|
|
377
|
+
const forwardAbort = addAbortListener(options?.signal, function () {
|
|
378
|
+
controller.abort(this.reason);
|
|
379
|
+
});
|
|
380
|
+
const connectPromise = this.options.kmsConnectCallback({
|
|
381
|
+
host: socketOptions.host,
|
|
382
|
+
port: socketOptions.port,
|
|
383
|
+
timeoutMS,
|
|
384
|
+
signal: controller.signal
|
|
385
|
+
});
|
|
386
|
+
// If the callback resolves a socket after we already aborted, close it so it does not leak.
|
|
387
|
+
connectPromise.then(
|
|
388
|
+
resolvedSocket => {
|
|
389
|
+
if (controller.signal.aborted) resolvedSocket.destroy();
|
|
390
|
+
},
|
|
391
|
+
() => null
|
|
392
|
+
);
|
|
393
|
+
const timeout = timeoutMS ? Timeout.expires(timeoutMS) : undefined;
|
|
394
|
+
try {
|
|
395
|
+
socketOptions.socket = timeout
|
|
396
|
+
? await Promise.race([connectPromise, timeout])
|
|
397
|
+
: await connectPromise;
|
|
398
|
+
} catch (err) {
|
|
399
|
+
if (TimeoutError.is(err)) {
|
|
400
|
+
const timeoutError = new MongoOperationTimeoutError('KMS request timed out');
|
|
401
|
+
// Abort with the timeout error as the reason so the callback sees it on `signal.reason`.
|
|
402
|
+
controller.abort(timeoutError);
|
|
403
|
+
throw timeoutError;
|
|
404
|
+
}
|
|
405
|
+
throw onerror(err);
|
|
406
|
+
} finally {
|
|
407
|
+
timeout?.clear();
|
|
408
|
+
forwardAbort?.[kDispose]();
|
|
409
|
+
}
|
|
410
|
+
} else if (this.options.proxyOptions && this.options.proxyOptions.proxyHost) {
|
|
394
411
|
netSocket = new net.Socket();
|
|
395
412
|
|
|
396
413
|
const {
|
|
@@ -465,20 +482,24 @@ export class StateMachine {
|
|
|
465
482
|
resolve();
|
|
466
483
|
}
|
|
467
484
|
});
|
|
468
|
-
|
|
469
|
-
?
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
485
|
+
const remainingTimeMS = options?.timeoutContext?.csotEnabled()
|
|
486
|
+
? options.timeoutContext.getRemainingTimeMSOrThrow(
|
|
487
|
+
`KMS request timed out after ${options.timeoutContext.timeoutMS}ms`
|
|
488
|
+
)
|
|
489
|
+
: undefined;
|
|
490
|
+
const timeoutMS = Number.isFinite(remainingTimeMS) ? remainingTimeMS : undefined;
|
|
491
|
+
kmsRequestTimeout = timeoutMS ? Timeout.expires(timeoutMS) : undefined;
|
|
492
|
+
await (kmsRequestTimeout
|
|
493
|
+
? Promise.race([willResolveKmsRequest, kmsRequestTimeout])
|
|
473
494
|
: willResolveKmsRequest);
|
|
474
495
|
} catch (error) {
|
|
475
|
-
if (error
|
|
476
|
-
throw new MongoOperationTimeoutError('KMS request timed out');
|
|
496
|
+
if (TimeoutError.is(error)) throw new MongoOperationTimeoutError('KMS request timed out');
|
|
477
497
|
throw error;
|
|
478
498
|
} finally {
|
|
479
499
|
// There's no need for any more activity on this socket at this point.
|
|
480
500
|
destroySockets();
|
|
481
501
|
abortListener?.[kDispose]();
|
|
502
|
+
kmsRequestTimeout?.clear();
|
|
482
503
|
}
|
|
483
504
|
}
|
|
484
505
|
|
package/src/cmap/auth/gssapi.ts
CHANGED
|
@@ -69,10 +69,7 @@ export class GSSAPI extends AuthProvider {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
async function makeKerberosClient({
|
|
72
|
-
options: {
|
|
73
|
-
hostAddress,
|
|
74
|
-
runtime: { os }
|
|
75
|
-
},
|
|
72
|
+
options: { hostAddress, runtime },
|
|
76
73
|
credentials
|
|
77
74
|
}: AuthContext): Promise<KerberosClient> {
|
|
78
75
|
if (!hostAddress || typeof hostAddress.host !== 'string' || !credentials) {
|
|
@@ -81,6 +78,8 @@ async function makeKerberosClient({
|
|
|
81
78
|
);
|
|
82
79
|
}
|
|
83
80
|
|
|
81
|
+
const { os } = await runtime;
|
|
82
|
+
|
|
84
83
|
loadKrb();
|
|
85
84
|
if ('kModuleError' in krb) {
|
|
86
85
|
throw krb['kModuleError'];
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { type Binary, type BSONSerializeOptions, ByteUtils } from '../../bson';
|
|
2
2
|
import * as BSON from '../../bson';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
MongoMissingCredentialsError,
|
|
6
|
-
MongoRuntimeError
|
|
7
|
-
} from '../../error';
|
|
8
|
-
import { maxWireVersion, ns, randomBytes } from '../../utils';
|
|
3
|
+
import { MongoMissingCredentialsError, MongoRuntimeError } from '../../error';
|
|
4
|
+
import { ns, randomBytes } from '../../utils';
|
|
9
5
|
import { type AuthContext, AuthProvider } from './auth_provider';
|
|
10
6
|
import {
|
|
11
7
|
type AWSCredentialProvider,
|
|
@@ -45,12 +41,6 @@ export class MongoDBAWS extends AuthProvider {
|
|
|
45
41
|
throw new MongoMissingCredentialsError('AuthContext must provide credentials.');
|
|
46
42
|
}
|
|
47
43
|
|
|
48
|
-
if (maxWireVersion(connection) < 9) {
|
|
49
|
-
throw new MongoCompatibilityError(
|
|
50
|
-
'MONGODB-AWS authentication requires MongoDB version 4.4 or later'
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
44
|
authContext.credentials = await makeTempCredentials(
|
|
55
45
|
authContext.credentials,
|
|
56
46
|
this.credentialFetcher
|
|
@@ -154,24 +154,24 @@ export abstract class CallbackWorkflow implements Workflow {
|
|
|
154
154
|
* to every 100ms.
|
|
155
155
|
*/
|
|
156
156
|
protected withLock(callback: OIDCCallbackFunction): OIDCCallbackFunction {
|
|
157
|
-
let
|
|
157
|
+
let queue: Promise<void> = Promise.resolve();
|
|
158
158
|
return async (params: OIDCCallbackParams): Promise<OIDCResponse> => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return await
|
|
159
|
+
const execution = queue.then(async () => {
|
|
160
|
+
const difference = Date.now() - this.lastExecutionTime;
|
|
161
|
+
if (difference <= THROTTLE_MS) {
|
|
162
|
+
await setTimeout(THROTTLE_MS - difference, { signal: params.timeoutContext });
|
|
163
|
+
}
|
|
164
|
+
this.lastExecutionTime = Date.now();
|
|
165
|
+
return await callback(params);
|
|
166
|
+
});
|
|
167
|
+
// The queue exists only to order callers, so it must carry neither a
|
|
168
|
+
// result nor an error: a caller waiting on it gets its own callback
|
|
169
|
+
// invocation, and a failed callback does not block the callers behind it.
|
|
170
|
+
queue = execution.then(
|
|
171
|
+
() => undefined,
|
|
172
|
+
() => undefined
|
|
173
|
+
);
|
|
174
|
+
return await execution;
|
|
175
175
|
};
|
|
176
176
|
}
|
|
177
177
|
}
|
|
@@ -32,7 +32,7 @@ export class CommandStartedEvent {
|
|
|
32
32
|
/**
|
|
33
33
|
* Server generated connection id
|
|
34
34
|
* Distinct from the connection id and is returned by the hello or legacy hello response as "connectionId"
|
|
35
|
-
* from the server
|
|
35
|
+
* from the server.
|
|
36
36
|
*/
|
|
37
37
|
serverConnectionId: bigint | null;
|
|
38
38
|
serviceId?: ObjectId;
|
|
@@ -88,7 +88,7 @@ export class CommandSucceededEvent {
|
|
|
88
88
|
connectionId?: string | number;
|
|
89
89
|
/**
|
|
90
90
|
* Server generated connection id
|
|
91
|
-
* Distinct from the connection id and is returned by the hello or legacy hello response as "connectionId" from the server
|
|
91
|
+
* Distinct from the connection id and is returned by the hello or legacy hello response as "connectionId" from the server.
|
|
92
92
|
*/
|
|
93
93
|
serverConnectionId: bigint | null;
|
|
94
94
|
requestId: number;
|
|
@@ -148,7 +148,7 @@ export class CommandFailedEvent {
|
|
|
148
148
|
connectionId?: string | number;
|
|
149
149
|
/**
|
|
150
150
|
* Server generated connection id
|
|
151
|
-
* Distinct from the connection id and is returned by the hello or legacy hello response as "connectionId" from the server
|
|
151
|
+
* Distinct from the connection id and is returned by the hello or legacy hello response as "connectionId" from the server.
|
|
152
152
|
*/
|
|
153
153
|
serverConnectionId: bigint | null;
|
|
154
154
|
requestId: number;
|
|
@@ -249,13 +249,12 @@ const LEGACY_FIND_OPTIONS_MAP = {
|
|
|
249
249
|
function extractCommand(command: WriteProtocolMessageType): Document {
|
|
250
250
|
if (command instanceof OpMsgRequest) {
|
|
251
251
|
const cmd = { ...command.command };
|
|
252
|
-
// For OP_MSG
|
|
253
|
-
// array
|
|
254
|
-
|
|
255
|
-
cmd
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
cmd.nsInfo = cmd.nsInfo.documents;
|
|
252
|
+
// For OP_MSG payload type 1, replace any document-sequence field with its
|
|
253
|
+
// documents array for monitoring (ops, nsInfo, documents, updates, deletes).
|
|
254
|
+
for (const key of Object.keys(cmd)) {
|
|
255
|
+
if (cmd[key] instanceof DocumentSequence) {
|
|
256
|
+
cmd[key] = cmd[key].documents;
|
|
257
|
+
}
|
|
259
258
|
}
|
|
260
259
|
return cmd;
|
|
261
260
|
}
|
package/src/cmap/commands.ts
CHANGED
|
@@ -500,6 +500,23 @@ export class DocumentSequence {
|
|
|
500
500
|
}
|
|
501
501
|
}
|
|
502
502
|
|
|
503
|
+
/**
|
|
504
|
+
* Build a DocumentSequence from documents that have already been serialized,
|
|
505
|
+
* reusing the provided buffers instead of re-serializing.
|
|
506
|
+
* @internal
|
|
507
|
+
*/
|
|
508
|
+
export function makeDocumentSequence(
|
|
509
|
+
field: string,
|
|
510
|
+
documents: Document[],
|
|
511
|
+
serialized: Uint8Array[]
|
|
512
|
+
): DocumentSequence {
|
|
513
|
+
const sequence = new DocumentSequence(field);
|
|
514
|
+
for (let i = 0; i < documents.length; i++) {
|
|
515
|
+
sequence.push(documents[i], serialized[i]);
|
|
516
|
+
}
|
|
517
|
+
return sequence;
|
|
518
|
+
}
|
|
519
|
+
|
|
503
520
|
/** @internal */
|
|
504
521
|
export class OpMsgRequest {
|
|
505
522
|
requestId: number;
|
package/src/cmap/connect.ts
CHANGED
|
@@ -174,7 +174,7 @@ export async function performInitialHandshake(
|
|
|
174
174
|
|
|
175
175
|
if (error instanceof MongoError) {
|
|
176
176
|
error.addErrorLabel(MongoErrorLabel.HandshakeError);
|
|
177
|
-
if (needsRetryableWriteLabel(error
|
|
177
|
+
if (needsRetryableWriteLabel(error)) {
|
|
178
178
|
error.addErrorLabel(MongoErrorLabel.RetryableWriteError);
|
|
179
179
|
}
|
|
180
180
|
}
|
|
@@ -224,7 +224,7 @@ export interface HandshakeDocument extends Document {
|
|
|
224
224
|
compression: string[];
|
|
225
225
|
saslSupportedMechs?: string;
|
|
226
226
|
loadBalanced?: boolean;
|
|
227
|
-
backpressure:
|
|
227
|
+
backpressure: '2';
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
/**
|
|
@@ -242,7 +242,7 @@ export async function prepareHandshakeDocument(
|
|
|
242
242
|
|
|
243
243
|
const handshakeDoc: HandshakeDocument = {
|
|
244
244
|
[serverApi?.version || options.loadBalanced === true ? 'hello' : LEGACY_HELLO_COMMAND]: 1,
|
|
245
|
-
backpressure:
|
|
245
|
+
backpressure: '2',
|
|
246
246
|
helloOk: true,
|
|
247
247
|
client: clientMetadata,
|
|
248
248
|
compression: compressors
|
package/src/cmap/connection.ts
CHANGED
|
@@ -146,7 +146,7 @@ export interface ConnectionOptions
|
|
|
146
146
|
/** @internal */
|
|
147
147
|
mongoLogger?: MongoLogger | undefined;
|
|
148
148
|
/** @internal */
|
|
149
|
-
runtime: Runtime
|
|
149
|
+
runtime: Promise<Runtime>;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/** @public */
|
|
@@ -286,7 +286,9 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
286
286
|
private get supportsOpMsg(): boolean {
|
|
287
287
|
return (
|
|
288
288
|
this.description != null &&
|
|
289
|
-
//
|
|
289
|
+
// OP_MSG was introduced in server 3.6 (wire version 6). This also guards
|
|
290
|
+
// against the pre-handshake case where maxWireVersion() returns 0.
|
|
291
|
+
// TODO(NODE-6672,NODE-6287): remove this helper once OP_QUERY support is dropped
|
|
290
292
|
maxWireVersion(this) >= 6 &&
|
|
291
293
|
!this.description.__nodejs_mock_server__
|
|
292
294
|
);
|
|
@@ -107,8 +107,9 @@ type MakeClientMetadataOptions = Pick<MongoOptions, 'appName' | 'runtime'>;
|
|
|
107
107
|
*/
|
|
108
108
|
export async function makeClientMetadata(
|
|
109
109
|
driverInfoList: DriverInfo[],
|
|
110
|
-
{ appName = '', runtime
|
|
110
|
+
{ appName = '', runtime }: MakeClientMetadataOptions
|
|
111
111
|
): Promise<ClientMetadata> {
|
|
112
|
+
const { os } = await runtime;
|
|
112
113
|
const metadataDocument = new LimitedSizeDocument(512);
|
|
113
114
|
|
|
114
115
|
// Add app name first, it must be sent
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export const MIN_SUPPORTED_SERVER_VERSION = '4.
|
|
1
|
+
export const MIN_SUPPORTED_SERVER_VERSION = '4.4';
|
|
2
2
|
export const MAX_SUPPORTED_SERVER_VERSION = '9.0';
|
|
3
3
|
export const MIN_SUPPORTED_SNAPSHOT_READS_WIRE_VERSION = 13;
|
|
4
4
|
export const MIN_SUPPORTED_SNAPSHOT_READS_SERVER_VERSION = '5.0';
|
|
5
|
-
export const MIN_SUPPORTED_WIRE_VERSION =
|
|
5
|
+
export const MIN_SUPPORTED_WIRE_VERSION = 9;
|
|
6
6
|
export const MAX_SUPPORTED_WIRE_VERSION = 29;
|
|
7
7
|
export const MIN_SUPPORTED_QE_WIRE_VERSION = 21;
|
|
8
8
|
export const MIN_SUPPORTED_QE_SERVER_VERSION = '7.0';
|
package/src/connection_string.ts
CHANGED
|
@@ -544,6 +544,10 @@ export function parseOptions(
|
|
|
544
544
|
);
|
|
545
545
|
|
|
546
546
|
mongoOptions.runtime = resolveRuntimeAdapters(options);
|
|
547
|
+
// Mark the promise as handled so a rejection before any consumer awaits it (possible only in
|
|
548
|
+
// runtimes where the dynamic import of the default os adapter fails) cannot crash the process
|
|
549
|
+
// as an unhandled rejection; consumers awaiting `runtime` still observe the rejection.
|
|
550
|
+
mongoOptions.runtime.then(undefined, squashError);
|
|
547
551
|
|
|
548
552
|
return mongoOptions;
|
|
549
553
|
}
|
|
@@ -126,11 +126,6 @@ export interface AbstractCursorOptions extends BSONSerializeOptions {
|
|
|
126
126
|
maxAwaitTimeMS?: number;
|
|
127
127
|
/**
|
|
128
128
|
* Comment to apply to the operation.
|
|
129
|
-
*
|
|
130
|
-
* In server versions pre-4.4, 'comment' must be string. A server
|
|
131
|
-
* error will be thrown if any other type is provided.
|
|
132
|
-
*
|
|
133
|
-
* In server versions 4.4 and above, 'comment' can be any valid BSON type.
|
|
134
129
|
*/
|
|
135
130
|
comment?: unknown;
|
|
136
131
|
/**
|