class Mongo::Protocol::Message

A base class providing functionality required by all messages in the MongoDB wire protocol. It provides a minimal DSL for defining typed fields to enable serialization and deserialization over the wire.

@example

class WireProtocolMessage < Message

  private

  def op_code
    1234
  end

  FLAGS = [:first_bit, :bit_two]

  # payload
  field :flags, BitVector.new(FLAGS)
  field :namespace, CString
  field :document, Document
  field :documents, Document, true
end

@abstract @api semiprivate

Constants

BATCH_SIZE

The batch size constant.

@since 2.2.0

COLLECTION

The collection constant.

@since 2.2.0

LIMIT

The limit constant.

@since 2.2.0

MAX_MESSAGE_SIZE

Default max message size of 48MB.

@since 2.2.1

ORDERED

The ordered constant.

@since 2.2.0

Q

The q constant.

@since 2.2.0

Attributes

request_id[R]

Returns the request id for the message

@return [Fixnum] The request id for this message

Public Class Methods

deserialize(io, max_message_size = MAX_MESSAGE_SIZE, expected_response_to = nil) click to toggle source

Deserializes messages from an IO stream

@param [ Integer ] max_message_size The max message size. @param [ IO ] io Stream containing a message

@return [ Message ] Instance of a Message class

# File lib/mongo/protocol/message.rb, line 116
def self.deserialize(io, max_message_size = MAX_MESSAGE_SIZE, expected_response_to = nil)
  length, _request_id, response_to, _op_code = deserialize_header(BSON::ByteBuffer.new(io.read(16)))

  # Protection from potential DOS man-in-the-middle attacks. See
  # DRIVERS-276.
  if length > (max_message_size || MAX_MESSAGE_SIZE)
    raise Error::MaxMessageSize.new(max_message_size)
  end

  # Protection against returning the response to a previous request. See
  # RUBY-1117
  if expected_response_to && response_to != expected_response_to
    raise Error::UnexpectedResponse.new(expected_response_to, response_to)
  end

  buffer = BSON::ByteBuffer.new(io.read(length - 16))
  message = allocate

  fields.each do |field|
    if field[:multi]
      deserialize_array(message, buffer, field)
    else
      deserialize_field(message, buffer, field)
    end
  end
  message
end

Private Class Methods

deserialize_array(message, io, field) click to toggle source

Deserializes an array of fields in a message

The number of items in the array must be described by a previously deserialized field specified in the class by the field dsl under the key :multi

@param message [Message] Message to contain the deserialized array. @param io [IO] Stream containing the array to deserialize. @param field [Hash] Hash representing a field. @return [Message] Message with deserialized array.

# File lib/mongo/protocol/message.rb, line 280
def self.deserialize_array(message, io, field)
  elements = []
  count = message.instance_variable_get(field[:multi])
  count.times { elements << field[:type].deserialize(io) }
  message.instance_variable_set(field[:name], elements)
end
deserialize_field(message, io, field) click to toggle source

Deserializes a single field in a message

@param message [Message] Message to contain the deserialized field. @param io [IO] Stream containing the field to deserialize. @param field [Hash] Hash representing a field. @return [Message] Message with deserialized field.

# File lib/mongo/protocol/message.rb, line 293
def self.deserialize_field(message, io, field)
  message.instance_variable_set(
    field[:name],
    field[:type].deserialize(io)
  )
end
deserialize_header(io) click to toggle source

Deserializes the header of the message

@param io [IO] Stream containing the header. @return [Array<Fixnum>] Deserialized header.

# File lib/mongo/protocol/message.rb, line 241
def self.deserialize_header(io)
  Header.deserialize(io)
end
field(name, type, multi = false) click to toggle source

A method for declaring a message field

@param name [String] Name of the field @param type [Module] Type specific serialization strategies @param multi [true, false, Symbol] Specify as true to

serialize the field's value as an array of type +:type+ or as a
symbol describing the field having the number of items in the
array (used upon deserialization)

  Note: In fields where multi is a symbol representing the field
  containing number items in the repetition, the field containing
  that information *must* be deserialized prior to deserializing
  fields that use the number.

@return [NilClass]

# File lib/mongo/protocol/message.rb, line 260
def self.field(name, type, multi = false)
  fields << {
    :name => "@#{name}".intern,
    :type => type,
    :multi => multi
  }

  attr_reader name
end
fields() click to toggle source

A class method for getting the fields for a message class

@return [Integer] the fields for the message class

# File lib/mongo/protocol/message.rb, line 192
def self.fields
  @fields ||= []
end

Public Instance Methods

==(other) click to toggle source

Tests for equality between two wire protocol messages by comparing class and field values.

@param other [Mongo::Protocol::Message] The wire protocol message. @return [true, false] The equality of the messages.

# File lib/mongo/protocol/message.rb, line 149
def ==(other)
  return false if self.class != other.class
  fields.all? do |field|
    name = field[:name]
    instance_variable_get(name) ==
      other.instance_variable_get(name)
  end
end
Also aliased as: eql?
eql?(other)
Alias for: ==
hash() click to toggle source

Creates a hash from the values of the fields of a message.

@return [ Fixnum ] The hash code for the message.

# File lib/mongo/protocol/message.rb, line 162
def hash
  fields.map { |field| instance_variable_get(field[:name]) }.hash
end
replyable?() click to toggle source

The default for messages is not to require a reply after sending a message to the server.

@example Does the message require a reply?

message.replyable?

@return [ false ] The default is to not require a reply.

@since 2.0.0

# File lib/mongo/protocol/message.rb, line 93
def replyable?
  false
end
serialize(buffer = BSON::ByteBuffer.new, max_bson_size = nil) click to toggle source

Serializes message into bytes that can be sent on the wire

@param buffer [String] buffer where the message should be inserted @return [String] buffer containing the serialized message

# File lib/mongo/protocol/message.rb, line 101
def serialize(buffer = BSON::ByteBuffer.new, max_bson_size = nil)
  start = buffer.length
  serialize_header(buffer)
  serialize_fields(buffer, max_bson_size)
  buffer.replace_int32(start, buffer.length - start)
end
Also aliased as: to_s
set_request_id() click to toggle source

Generates a request id for a message

@return [Fixnum] a request id used for sending a message to the

server. The server will put this id in the response_to field of
a reply.
# File lib/mongo/protocol/message.rb, line 171
def set_request_id
  @@id_lock.synchronize do
    @request_id = @@request_id += 1
  end
end
to_s(buffer = BSON::ByteBuffer.new, max_bson_size = nil)
Alias for: serialize

Private Instance Methods

fields() click to toggle source

A method for getting the fields for a message class

@return [Integer] the fields for the message class

# File lib/mongo/protocol/message.rb, line 185
def fields
  self.class.fields
end
serialize_fields(buffer, max_bson_size = nil) click to toggle source

Serializes message fields into a buffer

@param buffer [String] buffer to receive the field @return [String] buffer with serialized field

# File lib/mongo/protocol/message.rb, line 200
def serialize_fields(buffer, max_bson_size = nil)
  fields.each do |field|
    value = instance_variable_get(field[:name])
    if field[:multi]
      value.each do |item|
        if field[:type].respond_to?(:size_limited?)
          field[:type].serialize(buffer, item, max_bson_size, validating_keys?)
        else
          field[:type].serialize(buffer, item, validating_keys?)
        end
      end
    else
      if field[:type].respond_to?(:size_limited?)
        field[:type].serialize(buffer, value, max_bson_size, validating_keys?)
      else
        field[:type].serialize(buffer, value, validating_keys?)
      end
    end
  end
end
serialize_header(buffer) click to toggle source

Serializes the header of the message consisting of 4 32bit integers

The integers represent a message length placeholder (calculation of the actual length is deferred) the request id, the response to id, and the op code for the message

Currently uses hardcoded 0 for request id and response to as their values are irrelevent to the server

@param buffer [String] Buffer to receive the header @return [String] Serialized header

# File lib/mongo/protocol/message.rb, line 232
def serialize_header(buffer)
  set_request_id unless @request_id
  Header.serialize(buffer, [0, request_id, 0, op_code])
end
validating_keys?() click to toggle source
# File lib/mongo/protocol/message.rb, line 300
def validating_keys?
  @options[:validating_keys] if @options
end