class QuartzTorrent::ExtendedMetaInfo

An Extended Metainfo message. Used to request metadata pieces, or provide responses to those requests.

Attributes

data[RW]

This field is only set if the msgType is :piece. It contains the data for the piece.

dict[RW]
msgType[RW]

Message type as a symbol. One of :request, :data, or :reject

piece[RW]
totalSize[RW]

This field is only set if the msgType is :piece

Public Class Methods

new() click to toggle source
Calls superclass method QuartzTorrent::Extended::new
# File lib/quartz_torrent/peermsg.rb, line 407
def initialize
  super()
  @extendedMessageId = 0
  @msgType = nil
  @piece = nil
  @totalSize = nil
  @data = nil
  @dict = {}
end

Public Instance Methods

serializeTo(io) click to toggle source
Calls superclass method QuartzTorrent::Extended#serializeTo
# File lib/quartz_torrent/peermsg.rb, line 470
def serializeTo(io)
  super(io)
  updateDictFromProps
  io.write @dict.bencode
  raise "Extended metainfo piece messages must have piece data. This one's data was nil" if ! @data && dict['msg_type'] == 1
  io.write @data if dict['msg_type'] == 1
end
unserialize(payload) click to toggle source
Calls superclass method QuartzTorrent::Extended#unserialize
# File lib/quartz_torrent/peermsg.rb, line 426
def unserialize(payload)
  # Unserialize extended message id
  super(payload)

  # Rest of the message is a bencoded dictionary.
  # Piece messages of this class are encoded in an interesting way: the bencoded dictionary
  # is concatenated with the arbitrary binary data of the piece at the end. To decode this
  # we need to know the position of when we've finished reading the dictionary. We do this by
  # using a Parser object from the bencode library which maintains a stream object which happens
  # to know the current offset of the parsing.

  payload = payload[1,payload.length]
  parser = BEncode::Parser.new payload

  begin
    @dict = parser.parse!
  rescue
    e = RuntimeError.new("Error bdecoding payload '#{payload}' (payload length = #{payload.length})")
    e.set_backtrace($!.backtrace)
    raise e
  end

  @msgType = @dict['msg_type']
  raise "Extended Metainfo message contained no 'msg_type' key." if ! @msgType
  if @msgType == 0
    @msgType = :request
  elsif @msgType == 1
    @msgType = :piece
  elsif @msgType == 2
    @msgType = :reject
  else
    raise "Unknown message type '#{@msgType}' in Extended Metainfo message"
  end

  @piece = @dict['piece']
  raise "Extended Metainfo message contained no 'piece' key." if ! @piece
  
  @totalSize = @dict['total_size'] if @msgType == :piece

  # If this is a piece message, read the data after the dictionary.
  @data = parser.stream.read if @msgType == :piece
  
end

Private Instance Methods

extendedMsgPayloadLength() click to toggle source
# File lib/quartz_torrent/peermsg.rb, line 479
def extendedMsgPayloadLength
  updateDictFromProps
  len = @dict.bencode.length
  len += @data.length if dict['msg_type'] == 1
  len
end
msgTypeSymToVal(sym) click to toggle source
# File lib/quartz_torrent/peermsg.rb, line 492
def msgTypeSymToVal(sym)
  if sym == :request
    0
  elsif sym == :piece
    1
  elsif sym == :reject
    2
  else
    raise "Unknown msg type #{sym}"
  end
end
updateDictFromProps() click to toggle source
# File lib/quartz_torrent/peermsg.rb, line 486
def updateDictFromProps
  @dict['msg_type'] = msgTypeSymToVal(@msgType) if ! @dict.has_key?('msg_type') && @msgType
  @dict['piece'] = @piece if ! @dict.has_key?('piece') 
  @dict['total_size'] = @data.length if @data
end