Class: DiameterMessage
- Inherits:
-
Object
- Object
- DiameterMessage
- Defined in:
- lib/diameter/message.rb
Overview
A Diameter message.
Instance Attribute Summary (collapse)
-
- (Object) app_id
readonly
Returns the value of attribute app_id.
-
- (Object) avps
Returns the value of attribute avps.
-
- (Object) command_code
readonly
Returns the value of attribute command_code.
-
- (Object) ete
readonly
Returns the value of attribute ete.
-
- (Object) hbh
readonly
Returns the value of attribute hbh.
-
- (Object) request
readonly
Returns the value of attribute request.
-
- (Object) version
readonly
The Diameter protocol version (currenmtly always 1).
Class Method Summary (collapse)
-
+ (DiameterMessage) from_bytes(bytes)
Parses a byte representation (a 20-byte header plus AVPs) into a DiameterMessage object.
-
+ (Fixnum) length_from_header(header)
Parses the first four bytes of the Diameter header to learn the length.
Instance Method Summary (collapse)
-
- (Array<AVP>) all_avps_by_code(code, vendor = 0)
Returns all AVPs with the given code and vendor.
-
- (Array<AVP>) all_avps_by_name(name)
Returns all AVPs with the given name.
-
- (true, false) answer
Returns true if this message represents a Diameter answer (i.e. has the 'Request' bit in the header cleared).
-
- (AVP?) avp_by_code(code, vendor = 0)
Returns the first AVP with the given code and vendor.
-
- (AVP?) avp_by_name(name)
Returns the first AVP with the given name.
-
- (DiameterMessage) create_answer(origin_host = nil)
Generates an answer to this request, filling in appropriate fields per http://tools.ietf.org/html/rfc6733#section-6.2.
-
- (DiameterMessage) initialize(options = {})
constructor
A new instance of DiameterMessage.
-
- (String) to_s
Represents this message (and all its AVPs) in human-readable string form.
-
- (String) to_wire
Serializes a Diameter message (header plus AVPs) into the series of bytes representing it on the wire.
Constructor Details
- (DiameterMessage) initialize(options = {})
Returns a new instance of DiameterMessage
12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/diameter/message.rb', line 12 def initialize(={}) @version = [:version] || 1 @command_code = [:command_code] @avps = [:avps] || [] @app_id = [:app_id] @hbh = [:hbh] @ete = [:ete] @request = [:request] || false @proxyable = [:proxyable] || false @retransmitted = false @error = false end |
Instance Attribute Details
- (Object) app_id (readonly)
Returns the value of attribute app_id
9 10 11 |
# File 'lib/diameter/message.rb', line 9 def app_id @app_id end |
- (Object) avps
Returns the value of attribute avps
10 11 12 |
# File 'lib/diameter/message.rb', line 10 def avps @avps end |
- (Object) command_code (readonly)
Returns the value of attribute command_code
9 10 11 |
# File 'lib/diameter/message.rb', line 9 def command_code @command_code end |
- (Object) ete (readonly)
Returns the value of attribute ete
9 10 11 |
# File 'lib/diameter/message.rb', line 9 def ete @ete end |
- (Object) hbh (readonly)
Returns the value of attribute hbh
9 10 11 |
# File 'lib/diameter/message.rb', line 9 def hbh @hbh end |
- (Object) request (readonly)
Returns the value of attribute request
9 10 11 |
# File 'lib/diameter/message.rb', line 9 def request @request end |
- (Object) version (readonly)
The Diameter protocol version (currenmtly always 1)
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/diameter/message.rb', line 8 class DiameterMessage attr_reader :version, :command_code, :app_id, :hbh, :ete, :request attr_accessor :avps def initialize(={}) @version = [:version] || 1 @command_code = [:command_code] @avps = [:avps] || [] @app_id = [:app_id] @hbh = [:hbh] @ete = [:ete] @request = [:request] || false @proxyable = [:proxyable] || false @retransmitted = false @error = false end # Returns true if this message represents a Diameter answer (i.e. # has the 'Request' bit in the header cleared). # # Always the opposite of {DiameterMessage#request}. # # @return [true, false] def answer !@request end # Represents this message (and all its AVPs) in human-readable # string form. # # @see AVP::to_s for how the AVPs are represented. # @return [String] def to_s "#{@command_code}: #{@avps.collect{|a| a.to_s }}" end # Serializes a Diameter message (header plus AVPs) into the series # of bytes representing it on the wire. # # @return [String] The byte-encoded form. def to_wire content = "" @avps.each {|a| content += a.to_wire} length_8, length_16 = UInt24.to_u8_and_u16(content.length + 20) code_8, code_16 = UInt24.to_u8_and_u16(@command_code) request_flag = @request ? "1" : "0" proxy_flag = @proxyable? "1" : "0" flags_str = "#{request_flag}#{proxy_flag}000000" header = [@version, length_8, length_16, flags_str, code_8, code_16, @app_id, @hbh, @ete].pack('CCnB8CnNNN') header + content end # Returns the first AVP with the given name. Only covers "top-level" # AVPs - it won't look inside Grouped AVPs. # # @return [AVP] if there is an AVP with that name # @return [nil] if there is not an AVP with that name def avp_by_name(name) code, _type, vendor = AVPNames.get(name) avp_by_code(code, vendor) end # Returns all AVPs with the given name. Only covers "top-level" # AVPs - it won't look inside Grouped AVPs. # # @return [Array<AVP>] def all_avps_by_name(name) code, _type, vendor = AVPNames.get(name) all_avps_by_code(code, vendor) end # Returns the first AVP with the given code and vendor. Only covers "top-level" # AVPs - it won't look inside Grouped AVPs. # # @return [AVP] if there is an AVP with that code/vendor # @return [nil] if there is not an AVP with that code/vendor def avp_by_code(code, vendor=0) avps = all_avps_by_code(code, vendor) if avps.empty? nil else avps[0] end end # Returns all AVPs with the given code and vendor. Only covers "top-level" # AVPs - it won't look inside Grouped AVPs. # # @return [Array<AVP>] def all_avps_by_code(code, vendor=0) avps.select do |a| vendor_match = if a.vendor_specific? a.vendor_id == vendor else vendor == 0 end (a.code == code) and vendor_match end end # Parses the first four bytes of the Diameter header to learn the # length. Callers should use this to work out how many more bytes # they need to read off a TCP connection to pass to self.from_bytes. # # @param header [String] A four-byte Diameter header # @return [Fixnum] The message length field from the header def self.length_from_header(header) _version, length_8, length_16 = header.unpack('CCn') UInt24.from_u8_and_u16(length_8, length_16) end # Parses a byte representation (a 20-byte header plus AVPs) into a # DiameterMessage object. # # @param bytes [String] The on-the-wire byte representation of a # Diameter message. # @return [DiameterMessage] The parsed object form. def self.from_bytes(bytes) header = bytes[0..20] version, _length_8, _length_16, flags_str, code_8, code_16, app_id, hbh, ete = header.unpack('CCnB8CnNNN') command_code = UInt24.from_u8_and_u16(code_8, code_16) request = (flags_str[0] == "1") proxyable = (flags_str[1] == "1") avps = AVPParser::parse_avps_int(bytes[20..-1]) DiameterMessage.new(version: version, command_code: command_code, app_id: app_id, hbh: hbh, ete: ete, request: request, proxyable: proxyable, retransmitted: false, error: false, avps: avps) end # Generates an answer to this request, filling in appropriate # fields per {http://tools.ietf.org/html/rfc6733#section-6.2}. # # @param origin_host [String] The Origin-Host to fill in on the # response. # @return [DiameterMessage] The response created. def create_answer(origin_host=nil) # Is this a request? # Copy the Session-Id and Proxy-Info # Insert Origin-Host (should the stack do this?) # Don't require or insert a Result-Code - we might want # Experimental-Result-Code instead DiameterMessage.new(version: version, command_code: command_code, app_id: app_id, hbh: hbh, ete: ete, request: false, proxyable: @proxyable, retransmitted: false, error: false) end end |
Class Method Details
+ (DiameterMessage) from_bytes(bytes)
Parses a byte representation (a 20-byte header plus AVPs) into a DiameterMessage object.
128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/diameter/message.rb', line 128 def self.from_bytes(bytes) header = bytes[0..20] version, _length_8, _length_16, flags_str, code_8, code_16, app_id, hbh, ete = header.unpack('CCnB8CnNNN') command_code = UInt24.from_u8_and_u16(code_8, code_16) request = (flags_str[0] == "1") proxyable = (flags_str[1] == "1") avps = AVPParser::parse_avps_int(bytes[20..-1]) DiameterMessage.new(version: version, command_code: command_code, app_id: app_id, hbh: hbh, ete: ete, request: request, proxyable: proxyable, retransmitted: false, error: false, avps: avps) end |
+ (Fixnum) length_from_header(header)
Parses the first four bytes of the Diameter header to learn the length. Callers should use this to work out how many more bytes they need to read off a TCP connection to pass to self.from_bytes.
117 118 119 120 |
# File 'lib/diameter/message.rb', line 117 def self.length_from_header(header) _version, length_8, length_16 = header.unpack('CCn') UInt24.from_u8_and_u16(length_8, length_16) end |
Instance Method Details
- (Array<AVP>) all_avps_by_code(code, vendor = 0)
Returns all AVPs with the given code and vendor. Only covers “top-level” AVPs - it won't look inside Grouped AVPs.
99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/diameter/message.rb', line 99 def all_avps_by_code(code, vendor=0) avps.select do |a| vendor_match = if a.vendor_specific? a.vendor_id == vendor else vendor == 0 end (a.code == code) and vendor_match end end |
- (Array<AVP>) all_avps_by_name(name)
Returns all AVPs with the given name. Only covers “top-level” AVPs - it won't look inside Grouped AVPs.
76 77 78 79 |
# File 'lib/diameter/message.rb', line 76 def all_avps_by_name(name) code, _type, vendor = AVPNames.get(name) all_avps_by_code(code, vendor) end |
- (true, false) answer
Returns true if this message represents a Diameter answer (i.e. has the 'Request' bit in the header cleared).
Always the opposite of #request.
32 33 34 |
# File 'lib/diameter/message.rb', line 32 def answer !@request end |
- (AVP?) avp_by_code(code, vendor = 0)
Returns the first AVP with the given code and vendor. Only covers “top-level” AVPs - it won't look inside Grouped AVPs.
86 87 88 89 90 91 92 93 |
# File 'lib/diameter/message.rb', line 86 def avp_by_code(code, vendor=0) avps = all_avps_by_code(code, vendor) if avps.empty? nil else avps[0] end end |
- (AVP?) avp_by_name(name)
Returns the first AVP with the given name. Only covers “top-level” AVPs - it won't look inside Grouped AVPs.
67 68 69 70 |
# File 'lib/diameter/message.rb', line 67 def avp_by_name(name) code, _type, vendor = AVPNames.get(name) avp_by_code(code, vendor) end |
- (DiameterMessage) create_answer(origin_host = nil)
Generates an answer to this request, filling in appropriate fields per http://tools.ietf.org/html/rfc6733#section-6.2.
146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/diameter/message.rb', line 146 def create_answer(origin_host=nil) # Is this a request? # Copy the Session-Id and Proxy-Info # Insert Origin-Host (should the stack do this?) # Don't require or insert a Result-Code - we might want # Experimental-Result-Code instead DiameterMessage.new(version: version, command_code: command_code, app_id: app_id, hbh: hbh, ete: ete, request: false, proxyable: @proxyable, retransmitted: false, error: false) end |
- (String) to_s
Represents this message (and all its AVPs) in human-readable string form.
41 42 43 |
# File 'lib/diameter/message.rb', line 41 def to_s "#{@command_code}: #{@avps.collect{|a| a.to_s }}" end |
- (String) to_wire
Serializes a Diameter message (header plus AVPs) into the series of bytes representing it on the wire.
49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/diameter/message.rb', line 49 def to_wire content = "" @avps.each {|a| content += a.to_wire} length_8, length_16 = UInt24.to_u8_and_u16(content.length + 20) code_8, code_16 = UInt24.to_u8_and_u16(@command_code) request_flag = @request ? "1" : "0" proxy_flag = @proxyable? "1" : "0" flags_str = "#{request_flag}#{proxy_flag}000000" header = [@version, length_8, length_16, flags_str, code_8, code_16, @app_id, @hbh, @ete].pack('CCnB8CnNNN') header + content end |