Skip to content

Commit 441fc63

Browse files
authored
fix(NODE-3928): don't throw error in Response constructor (#3199)
1 parent f1c1ca0 commit 441fc63

File tree

2 files changed

+134
-22
lines changed

2 files changed

+134
-22
lines changed

src/cmap/commands.ts

+23-22
Original file line numberDiff line numberDiff line change
@@ -484,15 +484,15 @@ export class Response {
484484
responseTo: number;
485485
opCode: number;
486486
fromCompressed?: boolean;
487-
responseFlags: number;
488-
cursorId: Long;
489-
startingFrom: number;
490-
numberReturned: number;
491-
documents: (Document | Buffer)[];
492-
cursorNotFound: boolean;
493-
queryFailure: boolean;
494-
shardConfigStale: boolean;
495-
awaitCapable: boolean;
487+
responseFlags?: number;
488+
cursorId?: Long;
489+
startingFrom?: number;
490+
numberReturned?: number;
491+
documents: (Document | Buffer)[] = new Array(0);
492+
cursorNotFound?: boolean;
493+
queryFailure?: boolean;
494+
shardConfigStale?: boolean;
495+
awaitCapable?: boolean;
496496
promoteLongs: boolean;
497497
promoteValues: boolean;
498498
promoteBuffers: boolean;
@@ -522,20 +522,7 @@ export class Response {
522522
this.opCode = msgHeader.opCode;
523523
this.fromCompressed = msgHeader.fromCompressed;
524524

525-
// Read the message body
526-
this.responseFlags = msgBody.readInt32LE(0);
527-
this.cursorId = new BSON.Long(msgBody.readInt32LE(4), msgBody.readInt32LE(8));
528-
this.startingFrom = msgBody.readInt32LE(12);
529-
this.numberReturned = msgBody.readInt32LE(16);
530-
531-
// Preallocate document array
532-
this.documents = new Array(this.numberReturned);
533-
534525
// Flag values
535-
this.cursorNotFound = (this.responseFlags & CURSOR_NOT_FOUND) !== 0;
536-
this.queryFailure = (this.responseFlags & QUERY_FAILURE) !== 0;
537-
this.shardConfigStale = (this.responseFlags & SHARD_CONFIG_STALE) !== 0;
538-
this.awaitCapable = (this.responseFlags & AWAIT_CAPABLE) !== 0;
539526
this.promoteLongs = typeof this.opts.promoteLongs === 'boolean' ? this.opts.promoteLongs : true;
540527
this.promoteValues =
541528
typeof this.opts.promoteValues === 'boolean' ? this.opts.promoteValues : true;
@@ -574,6 +561,20 @@ export class Response {
574561
// (See https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#wire-op-reply)
575562
this.index = 20;
576563

564+
// Read the message body
565+
this.responseFlags = this.data.readInt32LE(0);
566+
this.cursorId = new BSON.Long(this.data.readInt32LE(4), this.data.readInt32LE(8));
567+
this.startingFrom = this.data.readInt32LE(12);
568+
this.numberReturned = this.data.readInt32LE(16);
569+
570+
// Preallocate document array
571+
this.documents = new Array(this.numberReturned);
572+
573+
this.cursorNotFound = (this.responseFlags & CURSOR_NOT_FOUND) !== 0;
574+
this.queryFailure = (this.responseFlags & QUERY_FAILURE) !== 0;
575+
this.shardConfigStale = (this.responseFlags & SHARD_CONFIG_STALE) !== 0;
576+
this.awaitCapable = (this.responseFlags & AWAIT_CAPABLE) !== 0;
577+
577578
// Parse Body
578579
for (let i = 0; i < this.numberReturned; i++) {
579580
bsonSize =

test/unit/cmap/commands.test.js

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
const { expect } = require('chai');
2+
const { Response } = require('../../../src/cmap/commands');
3+
4+
describe('commands', function () {
5+
describe('Response', function () {
6+
describe('#parse', function () {
7+
context('when the message body is invalid', function () {
8+
context('when the buffer is empty', function () {
9+
const message = Buffer.from([]);
10+
const header = {
11+
length: 0,
12+
requestId: 0,
13+
responseTo: 0,
14+
opCode: 0
15+
};
16+
const body = Buffer.from([]);
17+
18+
it('throws an exception', function () {
19+
const response = new Response(message, header, body);
20+
expect(() => response.parse()).to.throw(RangeError, /outside buffer bounds/);
21+
});
22+
});
23+
24+
context('when numReturned is invalid', function () {
25+
const message = Buffer.from([]);
26+
const header = {
27+
length: 0,
28+
requestId: 0,
29+
responseTo: 0,
30+
opCode: 0
31+
};
32+
const body = Buffer.alloc(5 * 4);
33+
body.writeInt32LE(-1, 16);
34+
35+
it('throws an exception', function () {
36+
const response = new Response(message, header, body);
37+
expect(() => response.parse()).to.throw(RangeError, /Invalid array length/);
38+
});
39+
});
40+
});
41+
});
42+
43+
describe('#constructor', function () {
44+
context('when the message body is invalid', function () {
45+
const message = Buffer.from([]);
46+
const header = {
47+
length: 0,
48+
requestId: 0,
49+
responseTo: 0,
50+
opCode: 0
51+
};
52+
const body = Buffer.from([]);
53+
54+
it('does not throw an exception', function () {
55+
let error;
56+
try {
57+
new Response(message, header, body);
58+
} catch (err) {
59+
error = err;
60+
}
61+
expect(error).to.be.undefined;
62+
});
63+
64+
it('initializes the documents to an empty array', function () {
65+
const response = new Response(message, header, body);
66+
expect(response.documents).to.be.empty;
67+
});
68+
69+
it('does not set the responseFlags', function () {
70+
const response = new Response(message, header, body);
71+
expect(response.responseFlags).to.be.undefined;
72+
});
73+
74+
it('does not set the cursorNotFound flag', function () {
75+
const response = new Response(message, header, body);
76+
expect(response.cursorNotFound).to.be.undefined;
77+
});
78+
79+
it('does not set the cursorId', function () {
80+
const response = new Response(message, header, body);
81+
expect(response.cursorId).to.be.undefined;
82+
});
83+
84+
it('does not set startingFrom', function () {
85+
const response = new Response(message, header, body);
86+
expect(response.startingFrom).to.be.undefined;
87+
});
88+
89+
it('does not set numberReturned', function () {
90+
const response = new Response(message, header, body);
91+
expect(response.numberReturned).to.be.undefined;
92+
});
93+
94+
it('does not set queryFailure', function () {
95+
const response = new Response(message, header, body);
96+
expect(response.queryFailure).to.be.undefined;
97+
});
98+
99+
it('does not set shardConfigStale', function () {
100+
const response = new Response(message, header, body);
101+
expect(response.shardConfigStale).to.be.undefined;
102+
});
103+
104+
it('does not set awaitCapable', function () {
105+
const response = new Response(message, header, body);
106+
expect(response.awaitCapable).to.be.undefined;
107+
});
108+
});
109+
});
110+
});
111+
});

0 commit comments

Comments
 (0)