Skip to content

Commit e0b2410

Browse files
author
Emily Giurleo
committed
RUBY-2345 Validate that documents don't exceed maxBsonObjectSize on servers older than 3.6 (#2055)
1 parent 463c02d commit e0b2410

File tree

6 files changed

+77
-29
lines changed

6 files changed

+77
-29
lines changed

lib/mongo/protocol/message.rb

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,19 @@ def maybe_encrypt(server, client)
177177
#
178178
# @param buffer [String] buffer where the message should be inserted
179179
# @return [String] buffer containing the serialized message
180-
def serialize(buffer = BSON::ByteBuffer.new, max_bson_size = nil)
180+
def serialize(buffer = BSON::ByteBuffer.new, max_bson_size = nil, bson_overhead = nil)
181+
max_size =
182+
if max_bson_size && bson_overhead
183+
max_bson_size + bson_overhead
184+
elsif max_bson_size
185+
max_bson_size
186+
else
187+
nil
188+
end
189+
181190
start = buffer.length
182191
serialize_header(buffer)
183-
serialize_fields(buffer, max_bson_size)
192+
serialize_fields(buffer, max_size)
184193
buffer.replace_int32(start, buffer.length - start)
185194
end
186195

lib/mongo/protocol/msg.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def payload
146146
# @return [ BSON::ByteBuffer ] buffer containing the serialized message.
147147
#
148148
# @since 2.5.0
149-
def serialize(buffer = BSON::ByteBuffer.new, max_bson_size = nil)
149+
def serialize(buffer = BSON::ByteBuffer.new, max_bson_size = nil, bson_overhead = nil)
150150
validate_document_size!(max_bson_size)
151151

152152
super

lib/mongo/protocol/query.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,48 @@ def maybe_compress(compressor, zlib_compression_level = nil)
115115
compress_if_possible(selector.keys.first, compressor, zlib_compression_level)
116116
end
117117

118+
# Serializes message into bytes that can be sent on the wire.
119+
#
120+
# @param [ BSON::ByteBuffer ] buffer where the message should be inserted.
121+
# @param [ Integer ] max_bson_size The maximum bson object size.
122+
#
123+
# @return [ BSON::ByteBuffer ] buffer containing the serialized message.
124+
def serialize(buffer = BSON::ByteBuffer.new, max_bson_size = nil, bson_overhead = nil)
125+
validate_document_size!(max_bson_size)
126+
127+
super
128+
end
129+
118130
protected
119131

120132
attr_reader :upconverter
121133

122134
private
123135

136+
# Validate that the documents in this message are all smaller than the
137+
# maxBsonObjectSize. If not, raise an exception.
138+
def validate_document_size!(max_bson_size)
139+
max_bson_size ||= Mongo::Server::ConnectionBase::DEFAULT_MAX_BSON_OBJECT_SIZE
140+
141+
documents = if @selector.key?(:documents)
142+
@selector[:documents]
143+
elsif @selector.key?(:deletes)
144+
@selector[:deletes]
145+
elsif @selector.key?(:updates)
146+
@selector[:updates]
147+
else
148+
[]
149+
end
150+
151+
contains_too_large_document = documents.any? do |doc|
152+
doc.to_bson.length > max_bson_size
153+
end
154+
155+
if contains_too_large_document
156+
raise Error::MaxBSONSize.new('The document exceeds maximum allowed BSON object size after serialization')
157+
end
158+
end
159+
124160
# The operation code required to specify a Query message.
125161
# @return [Fixnum] the operation code.
126162
#

lib/mongo/server/connection_base.rb

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,8 @@ def serialize(message, client, buffer = BSON::ByteBuffer.new)
204204
if message.bulk_write?
205205
# Make the new maximum size equal to the specified reduced size
206206
# limit plus the 16KiB overhead allowance.
207-
max_bson_size = REDUCED_MAX_BSON_SIZE + MAX_BSON_COMMAND_OVERHEAD
207+
max_bson_size = REDUCED_MAX_BSON_SIZE
208208
end
209-
else
210-
max_bson_size += MAX_BSON_COMMAND_OVERHEAD
211209
end
212210

213211
# RUBY-2234: It is necessary to check that the message size does not
@@ -232,7 +230,7 @@ def serialize(message, client, buffer = BSON::ByteBuffer.new)
232230
# TODO: address the fact that this line mutates the buffer.
233231
temp_buffer.put_bytes(buffer.get_bytes(buffer.length))
234232

235-
message.serialize(temp_buffer, max_bson_size)
233+
message.serialize(temp_buffer, max_bson_size, MAX_BSON_COMMAND_OVERHEAD)
236234
if temp_buffer.length > max_message_size
237235
raise Error::MaxMessageSize.new(max_message_size)
238236
end
@@ -243,7 +241,7 @@ def serialize(message, client, buffer = BSON::ByteBuffer.new)
243241
# layer should be refactored to allow compression on an already-
244242
# serialized message.
245243
final_message = message.maybe_compress(compressor, options[:zlib_compression_level])
246-
final_message.serialize(buffer, max_bson_size)
244+
final_message.serialize(buffer, max_bson_size, MAX_BSON_COMMAND_OVERHEAD)
247245

248246
buffer
249247
end

spec/integration/client_side_encryption/auto_encryption_bulk_writes_spec.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,15 +162,19 @@
162162
context 'when one operation is larger than 16MiB' do
163163
let(:requests) do
164164
[
165-
{ update_one: { filter: { _id: 1 }, update: { ssn: 'a' * (Mongo::Server::ConnectionBase::DEFAULT_MAX_BSON_OBJECT_SIZE - 100) } } },
165+
{ update_one: { filter: { _id: 1 }, update: { ssn: 'a' * (Mongo::Server::ConnectionBase::DEFAULT_MAX_BSON_OBJECT_SIZE) } } },
166166
{ update_one: { filter: { _id: 2 }, update: { ssn: 'a' * size_limit } } },
167167
]
168168
end
169169

170+
before do
171+
expect(requests.first.to_bson.length).to be > Mongo::Server::ConnectionBase::DEFAULT_MAX_BSON_OBJECT_SIZE
172+
end
173+
170174
it 'raises an exception' do
171175
expect do
172176
bulk_write.execute
173-
end.to raise_error(Mongo::Error::MaxBSONSize, /maximum allowed size: 16777216 bytes/)
177+
end.to raise_error(Mongo::Error::MaxBSONSize, /The document exceeds maximum allowed BSON object size after serialization/)
174178
end
175179
end
176180
end

spec/integration/size_limit_spec.rb

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,30 +62,31 @@
6262
authorized_collection.insert_one(document)
6363
end
6464

65-
context 'on server versions >= 3.6' do
66-
min_server_fcv '3.6'
65+
it 'fails on the driver when a document larger than 16MiB is inserted' do
66+
document = { key: 'a' * (max_document_size - 27), _id: 'foo' }
67+
expect(document.to_bson.length).to eq(max_document_size+1)
6768

68-
it 'fails on the driver when a document larger than 16MiB is inserted' do
69-
document = { key: 'a' * (max_document_size - 27), _id: 'foo' }
70-
expect(document.to_bson.length).to eq(max_document_size+1)
71-
72-
lambda do
73-
authorized_collection.insert_one(document)
74-
end.should raise_error(Mongo::Error::MaxBSONSize, /The document exceeds maximum allowed BSON object size after serialization/)
75-
end
69+
lambda do
70+
authorized_collection.insert_one(document)
71+
end.should raise_error(Mongo::Error::MaxBSONSize, /The document exceeds maximum allowed BSON object size after serialization/)
7672
end
7773

78-
context 'on server versions <= 3.4' do
79-
max_server_fcv '3.4'
74+
it 'fails on the driver when an update larger than 16MiB is performed' do
75+
document = { key: 'a' * (max_document_size - 14) }
76+
expect(document.to_bson.length).to eq(max_document_size+1)
77+
78+
lambda do
79+
authorized_collection.update_one({ _id: 'foo' }, document)
80+
end.should raise_error(Mongo::Error::MaxBSONSize, /The document exceeds maximum allowed BSON object size after serialization/)
81+
end
8082

81-
it 'fails on the server when a document larger than 16MiB is inserted' do
82-
document = { key: 'a' * (max_document_size - 27), _id: 'foo' }
83-
expect(document.to_bson.length).to eq(max_document_size+1)
83+
it 'fails on the driver when an delete larger than 16MiB is performed' do
84+
document = { key: 'a' * (max_document_size - 14) }
85+
expect(document.to_bson.length).to eq(max_document_size+1)
8486

85-
lambda do
86-
authorized_collection.insert_one(document)
87-
end.should raise_error(Mongo::Error::OperationFailure, /object to insert too large/)
88-
end
87+
lambda do
88+
authorized_collection.delete_one(document)
89+
end.should raise_error(Mongo::Error::MaxBSONSize, /The document exceeds maximum allowed BSON object size after serialization/)
8990
end
9091

9192
it 'fails in the driver when a document larger than 16MiB+16KiB is inserted' do

0 commit comments

Comments
 (0)