class OnesnooperServer::PayloadParser

Central parsing class for decoding and analyzing the content of incoming monitoring messages. Implements one publicly available method ‘parse(payload)`. Internally, the payload is decoded from Base64 and parsed into a hash-like structure.

Constants

EQUALS
HASH_VALUE
KEY
KEY_HASH_VALUE_REGEXP
KEY_QUOTED_VALUE_REGEXP
KEY_RAW_VALUE_REGEXP
QUOTED_VALUE
TRIM_CLEANUP
VALUE

Public Class Methods

parse(payload) click to toggle source

Parses given payload into a hash-like structure. Payload is decoded from Base64 and then analyzed and parsed.

@param payload [String] Base64-encoded payload with ONE monitoring data @return [Hash] hash-like structure with parsed payload

# File lib/onesnooper_server/payload_parser.rb, line 24
def self.parse(payload)
  return {} if payload.blank?
  analyze(decode(payload))
end

Private Class Methods

analyze(payload, subpayload = false) click to toggle source

Analyzes payload content and returns a corresponding hash-like structure.

@param payload [String] plain text payload in ONE format @param subpayload [Boolean] recursive call for parsing complex values @return [Hash] a hash-like structure with analyzed payload content

# File lib/onesnooper_server/payload_parser.rb, line 51
def self.analyze(payload, subpayload = false)
  ::OnesnooperServer::Log.debug "[#{self.name}] Scanning decoded #{subpayload ? 'sub-' : '' }payload #{payload.inspect}"
  return {} if payload.blank?

  scanned_payload = {}
  scannable_payload = StringScanner.new(payload)
  begin
    if scanned = scannable_payload.scan(KEY_HASH_VALUE_REGEXP)
      scanned.strip!
      ::OnesnooperServer::Log.debug "[#{self.name}] Scanned #{scanned.inspect}"
      analyze_simple_pair(scanned, scanned_payload, KEY_HASH_VALUE_REGEXP, true)
    elsif scanned = scannable_payload.scan(KEY_QUOTED_VALUE_REGEXP)
      scanned.strip!
      scanned.gsub! "\n", ''
      ::OnesnooperServer::Log.debug "[#{self.name}] Scanned #{scanned.inspect}"
      analyze_simple_pair(scanned, scanned_payload, KEY_QUOTED_VALUE_REGEXP)
    elsif scanned = scannable_payload.scan(KEY_RAW_VALUE_REGEXP)
      scanned.strip!
      scanned.gsub! "\n", ''
      ::OnesnooperServer::Log.debug "[#{self.name}] Scanned #{scanned.inspect}"
      analyze_simple_pair(scanned, scanned_payload, KEY_RAW_VALUE_REGEXP)
    else
      ::OnesnooperServer::Log.error "[#{self.name}] Failed scanning #{subpayload ? 'sub-' : '' }payload " \
                                    "#{payload.inspect} at #{scannable_payload.pos}"
      break
    end
  end until scannable_payload.eos?

  scanned_payload
end
analyze_complex_value(complex_value) click to toggle source

Parses complex value strings into a hash-like structure.

@param complex_value [String] input string @return [Hash] result

# File lib/onesnooper_server/payload_parser.rb, line 87
def self.analyze_complex_value(complex_value)
  complex_parsed = analyze(complex_value.gsub(',', "\n").strip.gsub("\n\n", "\n"), true)
  unless complex_parsed['POLL'].blank?
    ::OnesnooperServer::Log.debug "[#{self.name}] Found complex POLL values, triggering analysis"
    complex_parsed['POLL'] = analyze(complex_parsed['POLL'].gsub(/\s+/, "\n"), true)
  end

  complex_parsed
end
analyze_simple_pair(key_value, parsed, regexp, suspected_complex = false) click to toggle source

Parses simple key value strings into the given hash-like structure.

@param key_value [String] input string @param parsed [Hash] output hash-like structure @param regexp [Regexp] regular expression for parsing @param suspected_complex [Boolean] suspect complex value @return [Boolean] success or failure

# File lib/onesnooper_server/payload_parser.rb, line 105
def self.analyze_simple_pair(key_value, parsed, regexp, suspected_complex = false)
  matched = key_value.match(regexp)
  if matched
    ::OnesnooperServer::Log.debug "[#{self.name}] Matched #{key_value.inspect} " \
                                  "as #{matched[:key].inspect} and #{matched[:value].inspect}"
    if suspected_complex
      parsed[matched[:key]] ||= []
      parsed[matched[:key]] << analyze_complex_value(matched[:value])
    else
      parsed[matched[:key]] = typecast_if_num(matched[:value])
    end
  else
    ::OnesnooperServer::Log.error "[#{self.name}] Couldn't match " \
                                  "#{key_value.inspect} as key & simple value"
  end

  true
end
decode(payload) click to toggle source

Decodes given Base64-encoded string.

@param payload [String] Base64-encoded string @return [String] decoded string

# File lib/onesnooper_server/payload_parser.rb, line 35
def self.decode(payload)
  ::OnesnooperServer::Log.debug "[#{self.name}] Decoding #{payload.inspect}"
  begin
    Base64.strict_decode64(payload)
  rescue => ex
    ::OnesnooperServer::Log.error "[#{self.name}] Decoding Base64 failed with: #{ex.message}"
    return ''
  end
end
typecast_if_num(potential_num) click to toggle source

Attempts to type-cast values to ‘Integer` or `Float` if this casting makes sense for the given value. Otherwise the original value is returned.

@param potential_num [String] value to type-cast if applicable @return [String, Integer, Float] type-casted value if applicable

# File lib/onesnooper_server/payload_parser.rb, line 130
def self.typecast_if_num(potential_num)
  return potential_num unless potential_num.kind_of? String

  case potential_num
  when potential_num.to_i.to_s
    potential_num.to_i
  when potential_num.to_f.to_s
    potential_num.to_f
  else
    potential_num
  end
end