Class: IB::RawMessageParser

Inherits:
Object show all
Defined in:
lib/ib/raw_message.rb

Overview

Convert data passed in from a TCP socket stream, and convert into raw messages. The messages

Constant Summary collapse

HEADER_LNGTH =
4

Instance Method Summary collapse

Constructor Details

#initialize(socket) ⇒ RawMessageParser

Returns a new instance of RawMessageParser.



8
9
10
11
# File 'lib/ib/raw_message.rb', line 8

def initialize(socket)
  @socket = socket
  @data = String.new
end

Instance Method Details

#append_new_dataObject



69
70
71
# File 'lib/ib/raw_message.rb', line 69

def append_new_data
  @data += @socket.recv_from
end

#eachObject



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/ib/raw_message.rb', line 13

def each
  while true
  append_new_data

  next unless length_data?
  next unless enough_data?

  length = next_msg_length
  validate_data_header(length)

  raw = grab_message(length)
  validate_message_footer(raw, length)
  msg = parse_message(raw, length)
  remove_message
  yield msg
  end
end

#enough_data?Boolean

Returns:

  • (Boolean)


51
52
53
54
55
56
# File 'lib/ib/raw_message.rb', line 51

def enough_data?
  actual_lngth = next_msg_length + HEADER_LNGTH
  echo 'too little data' if next_msg_length.nil?
  return false if next_msg_length.nil?
  @data.bytesize >= actual_lngth
end

#grab_message(length) ⇒ Object

extract message and convert to an array split by null characters.



33
34
35
# File 'lib/ib/raw_message.rb', line 33

def grab_message(length)
  @data.byteslice(HEADER_LNGTH, length)
end

#length_data?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/ib/raw_message.rb', line 58

def length_data?
  @data.bytesize > HEADER_LNGTH
end

#next_msg_lengthObject



62
63
64
65
66
67
# File 'lib/ib/raw_message.rb', line 62

def next_msg_length
  #can't check length if first 4 bytes don't exist
  length = @data.byteslice(0..3).unpack1('N')
  return 0 if length.nil?
  length
end

#parse_message(raw, length) ⇒ Object



37
38
39
# File 'lib/ib/raw_message.rb', line 37

def parse_message(raw, length)
  raw.unpack1("A#{length}").split("\0")
end

#remove_messageObject



41
42
43
44
45
46
47
48
49
# File 'lib/ib/raw_message.rb', line 41

def remove_message
  length = next_msg_length
  leftovers = @data.byteslice(length + HEADER_LNGTH..-1)
  @data = if leftovers.nil?
            String.new
          else
            leftovers
          end
end

#validate_data_header(length) ⇒ Object



80
81
82
83
# File 'lib/ib/raw_message.rb', line 80

def validate_data_header(length)
  return true if length <= 5000
  raise 'Message is longer than sane max length'
end


73
74
75
76
77
78
# File 'lib/ib/raw_message.rb', line 73

def validate_message_footer(msg,length)
  last = msg.bytesize
  last_byte = msg.byteslice(last-1,last)
  raise 'Could not validate last byte' if last_byte.nil?
  raise "Message has an invalid last byte. expecting \0, received: #{last_byte}" if last_byte != "\0"
end