Skip to content

Commit dd8f86d

Browse files
committed
refactor(NODE-6056): implement MongoDBResponse class
1 parent e318e7e commit dd8f86d

File tree

14 files changed

+284
-191
lines changed

14 files changed

+284
-191
lines changed

src/cmap/auth/gssapi.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,9 @@ type MechanismProperties = {
2929
async function externalCommand(
3030
connection: Connection,
3131
command: ReturnType<typeof saslStart> | ReturnType<typeof saslContinue>
32-
): Promise<{ payload: string; conversationId: any }> {
33-
return await (connection.command(ns('$external.$cmd'), command, undefined) as Promise<{
34-
payload: string;
35-
conversationId: any;
36-
}>);
32+
): Promise<{ payload: string; conversationId: number }> {
33+
const response = await connection.command(ns('$external.$cmd'), command);
34+
return response as { payload: string; conversationId: number };
3735
}
3836

3937
let krb: typeof Kerberos;

src/cmap/commands.ts

Lines changed: 24 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,6 @@ export interface OpQueryOptions extends CommandOptions {
5353
exhaustAllowed?: boolean;
5454
}
5555

56-
/**************************************************************
57-
* QUERY
58-
**************************************************************/
5956
/** @internal */
6057
export class OpQueryRequest {
6158
ns: string;
@@ -289,7 +286,7 @@ export interface OpResponseOptions extends BSONSerializeOptions {
289286
}
290287

291288
/** @internal */
292-
export class OpQueryResponse {
289+
export class OpReply {
293290
parsed: boolean;
294291
raw: Buffer;
295292
data: Buffer;
@@ -303,7 +300,6 @@ export class OpQueryResponse {
303300
cursorId?: Long;
304301
startingFrom?: number;
305302
numberReturned?: number;
306-
documents: (Document | Buffer)[] = new Array(0);
307303
cursorNotFound?: boolean;
308304
queryFailure?: boolean;
309305
shardConfigStale?: boolean;
@@ -313,7 +309,8 @@ export class OpQueryResponse {
313309
promoteValues: boolean;
314310
promoteBuffers: boolean;
315311
bsonRegExp?: boolean;
316-
index?: number;
312+
index = 0;
313+
sections: Uint8Array[] = [];
317314

318315
/** moreToCome is an OP_MSG only concept */
319316
moreToCome = false;
@@ -356,29 +353,9 @@ export class OpQueryResponse {
356353
return this.parsed;
357354
}
358355

359-
parse(options: OpResponseOptions): void {
356+
parse(): Uint8Array {
360357
// Don't parse again if not needed
361-
if (this.parsed) return;
362-
options = options ?? {};
363-
364-
// Allow the return of raw documents instead of parsing
365-
const raw = options.raw || false;
366-
const documentsReturnedIn = options.documentsReturnedIn || null;
367-
const useBigInt64 = options.useBigInt64 ?? this.opts.useBigInt64;
368-
const promoteLongs = options.promoteLongs ?? this.opts.promoteLongs;
369-
const promoteValues = options.promoteValues ?? this.opts.promoteValues;
370-
const promoteBuffers = options.promoteBuffers ?? this.opts.promoteBuffers;
371-
const bsonRegExp = options.bsonRegExp ?? this.opts.bsonRegExp;
372-
let bsonSize;
373-
374-
// Set up the options
375-
const _options: BSONSerializeOptions = {
376-
useBigInt64,
377-
promoteLongs,
378-
promoteValues,
379-
promoteBuffers,
380-
bsonRegExp
381-
};
358+
if (this.parsed) return this.sections[0];
382359

383360
// Position within OP_REPLY at which documents start
384361
// (See https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol/#wire-op-reply)
@@ -390,8 +367,11 @@ export class OpQueryResponse {
390367
this.startingFrom = this.data.readInt32LE(12);
391368
this.numberReturned = this.data.readInt32LE(16);
392369

393-
// Preallocate document array
394-
this.documents = new Array(this.numberReturned);
370+
if (this.numberReturned < 0 || this.numberReturned > 2 ** 32 - 1) {
371+
throw new RangeError(
372+
`OP_REPLY numberReturned is an invalid array length ${this.numberReturned}`
373+
);
374+
}
395375

396376
this.cursorNotFound = (this.responseFlags & CURSOR_NOT_FOUND) !== 0;
397377
this.queryFailure = (this.responseFlags & QUERY_FAILURE) !== 0;
@@ -400,67 +380,26 @@ export class OpQueryResponse {
400380

401381
// Parse Body
402382
for (let i = 0; i < this.numberReturned; i++) {
403-
bsonSize =
383+
const bsonSize =
404384
this.data[this.index] |
405385
(this.data[this.index + 1] << 8) |
406386
(this.data[this.index + 2] << 16) |
407387
(this.data[this.index + 3] << 24);
408388

409-
// If we have raw results specified slice the return document
410-
if (raw) {
411-
this.documents[i] = this.data.slice(this.index, this.index + bsonSize);
412-
} else {
413-
this.documents[i] = BSON.deserialize(
414-
this.data.slice(this.index, this.index + bsonSize),
415-
_options
416-
);
417-
}
389+
const section = this.data.subarray(this.index, this.index + bsonSize);
390+
this.sections.push(section);
418391

419392
// Adjust the index
420393
this.index = this.index + bsonSize;
421394
}
422395

423-
if (this.documents.length === 1 && documentsReturnedIn != null && raw) {
424-
const fieldsAsRaw: Document = {};
425-
fieldsAsRaw[documentsReturnedIn] = true;
426-
_options.fieldsAsRaw = fieldsAsRaw;
427-
428-
const doc = BSON.deserialize(this.documents[0] as Buffer, _options);
429-
this.documents = [doc];
430-
}
431-
432396
// Set parsed
433397
this.parsed = true;
398+
399+
return this.sections[0];
434400
}
435401
}
436402

437-
// Implementation of OP_MSG spec:
438-
// https://github.com/mongodb/specifications/blob/master/source/message/OP_MSG.rst
439-
//
440-
// struct Section {
441-
// uint8 payloadType;
442-
// union payload {
443-
// document document; // payloadType == 0
444-
// struct sequence { // payloadType == 1
445-
// int32 size;
446-
// cstring identifier;
447-
// document* documents;
448-
// };
449-
// };
450-
// };
451-
452-
// struct OP_MSG {
453-
// struct MsgHeader {
454-
// int32 messageLength;
455-
// int32 requestID;
456-
// int32 responseTo;
457-
// int32 opCode = 2013;
458-
// };
459-
// uint32 flagBits;
460-
// Section+ sections;
461-
// [uint32 checksum;]
462-
// };
463-
464403
// Msg Flags
465404
const OPTS_CHECKSUM_PRESENT = 1;
466405
const OPTS_MORE_TO_COME = 2;
@@ -603,8 +542,8 @@ export class OpMsgResponse {
603542
promoteValues: boolean;
604543
promoteBuffers: boolean;
605544
bsonRegExp: boolean;
606-
documents: (Document | Buffer)[];
607-
index?: number;
545+
index = 0;
546+
sections: Uint8Array[] = [];
608547

609548
constructor(
610549
message: Buffer,
@@ -642,47 +581,26 @@ export class OpMsgResponse {
642581
this.promoteBuffers =
643582
typeof this.opts.promoteBuffers === 'boolean' ? this.opts.promoteBuffers : false;
644583
this.bsonRegExp = typeof this.opts.bsonRegExp === 'boolean' ? this.opts.bsonRegExp : false;
645-
646-
this.documents = [];
647584
}
648585

649586
isParsed(): boolean {
650587
return this.parsed;
651588
}
652589

653-
parse(options: OpResponseOptions): void {
590+
parse(): Uint8Array {
654591
// Don't parse again if not needed
655-
if (this.parsed) return;
656-
options = options ?? {};
592+
if (this.parsed) return this.sections[0];
657593

658594
this.index = 4;
659-
// Allow the return of raw documents instead of parsing
660-
const raw = options.raw || false;
661-
const documentsReturnedIn = options.documentsReturnedIn || null;
662-
const useBigInt64 = options.useBigInt64 ?? this.opts.useBigInt64;
663-
const promoteLongs = options.promoteLongs ?? this.opts.promoteLongs;
664-
const promoteValues = options.promoteValues ?? this.opts.promoteValues;
665-
const promoteBuffers = options.promoteBuffers ?? this.opts.promoteBuffers;
666-
const bsonRegExp = options.bsonRegExp ?? this.opts.bsonRegExp;
667-
const validation = this.parseBsonSerializationOptions(options);
668-
669-
// Set up the options
670-
const bsonOptions: BSONSerializeOptions = {
671-
useBigInt64,
672-
promoteLongs,
673-
promoteValues,
674-
promoteBuffers,
675-
bsonRegExp,
676-
validation
677-
// Due to the strictness of the BSON libraries validation option we need this cast
678-
} as BSONSerializeOptions & { validation: { utf8: { writeErrors: boolean } } };
679595

680596
while (this.index < this.data.length) {
681597
const payloadType = this.data.readUInt8(this.index++);
682598
if (payloadType === 0) {
683599
const bsonSize = this.data.readUInt32LE(this.index);
684-
const bin = this.data.slice(this.index, this.index + bsonSize);
685-
this.documents.push(raw ? bin : BSON.deserialize(bin, bsonOptions));
600+
const bin = this.data.subarray(this.index, this.index + bsonSize);
601+
602+
this.sections.push(bin);
603+
686604
this.index += bsonSize;
687605
} else if (payloadType === 1) {
688606
// It was decided that no driver makes use of payload type 1
@@ -692,25 +610,9 @@ export class OpMsgResponse {
692610
}
693611
}
694612

695-
if (this.documents.length === 1 && documentsReturnedIn != null && raw) {
696-
const fieldsAsRaw: Document = {};
697-
fieldsAsRaw[documentsReturnedIn] = true;
698-
bsonOptions.fieldsAsRaw = fieldsAsRaw;
699-
const doc = BSON.deserialize(this.documents[0] as Buffer, bsonOptions);
700-
this.documents = [doc];
701-
}
702-
703613
this.parsed = true;
704-
}
705-
706-
parseBsonSerializationOptions({ enableUtf8Validation }: BSONSerializeOptions): {
707-
utf8: { writeErrors: false } | false;
708-
} {
709-
if (enableUtf8Validation === false) {
710-
return { utf8: false };
711-
}
712614

713-
return { utf8: { writeErrors: false } };
615+
return this.sections[0];
714616
}
715617
}
716618

0 commit comments

Comments
 (0)