module RocketAMF module Values #:nodoc: # Base class for all special AS3 response messages. Maps to # flex.messaging.messages.AbstractMessage. class AbstractMessage EXTERNALIZABLE_FIELDS = [ %w[ body clientId destination headers messageId timestamp timeToLive ], %w[ clientIdBytes messageIdBytes ] ] attr_accessor :clientId attr_accessor :destination attr_accessor :messageId attr_accessor :timestamp attr_accessor :timeToLive attr_accessor :headers attr_accessor :body def clientIdBytes= bytes @clientId = pretty_uuid(bytes) unless bytes.nil? end def messageIdBytes= bytes @messageId = pretty_uuid(bytes) unless bytes.nil? end def read_external des read_external_fields des, EXTERNALIZABLE_FIELDS end private def rand_uuid [8,4,4,4,12].map {|n| rand_hex_3(n)}.join('-').to_s end def rand_hex_3(l) "%0#{l}x" % rand(1 << l*4) end def pretty_uuid bytes "%08x-%04x-%04x-%04x-%08x%04x" % bytes.string.unpack("NnnnNn") end def read_external_fields des, fields # Read flags flags = [] loop do flags << des.source.read(1).unpack('C').first break if flags.last < 128 end # Read fields and any remaining unmapped fields in a byte-set fields.each_with_index do |list, i| break if flags[i].nil? list.each_with_index do |name, j| if flags[i] & 2**j != 0 send("#{name}=", des.read_object) end end # Read remaining flags even though we don't recognize them # Zero out high bit, as it's the has-next-field marker f = (flags[i] & ~128) >> list.length while f > 0 des.read_object if (f & 1) != 0 f >>= 1 end end end end # Maps to flex.messaging.messages.RemotingMessage class RemotingMessage < AbstractMessage # The name of the service to be called including package name attr_accessor :source # The name of the method to be called attr_accessor :operation def initialize @clientId = nil @destination = nil @messageId = rand_uuid @timestamp = Time.new.to_i*100 @timeToLive = 0 @headers = {} @body = nil end end # Maps to flex.messaging.messages.AsyncMessage class AsyncMessage < AbstractMessage EXTERNALIZABLE_FIELDS = [ %w[ correlationId correlationIdBytes] ] attr_accessor :correlationId def correlationIdBytes= bytes @correlationId = pretty_uuid(bytes) unless bytes.nil? end def read_external des super des read_external_fields des, EXTERNALIZABLE_FIELDS end end class AsyncMessageExt < AsyncMessage #:nodoc: end # Maps to flex.messaging.messages.CommandMessage class CommandMessage < AsyncMessage SUBSCRIBE_OPERATION = 0 UNSUSBSCRIBE_OPERATION = 1 POLL_OPERATION = 2 CLIENT_SYNC_OPERATION = 4 CLIENT_PING_OPERATION = 5 CLUSTER_REQUEST_OPERATION = 7 LOGIN_OPERATION = 8 LOGOUT_OPERATION = 9 SESSION_INVALIDATE_OPERATION = 10 MULTI_SUBSCRIBE_OPERATION = 11 DISCONNECT_OPERATION = 12 UNKNOWN_OPERATION = 10000 EXTERNALIZABLE_FIELDS = [ %w[ operation ] ] attr_accessor :operation def initialize @operation = UNKNOWN_OPERATION end def read_external des super des read_external_fields des, EXTERNALIZABLE_FIELDS end end class CommandMessageExt < CommandMessage #:nodoc: end # Maps to flex.messaging.messages.AcknowledgeMessage class AcknowledgeMessage < AsyncMessage EXTERNALIZABLE_FIELDS = [[]] def initialize message=nil @clientId = rand_uuid @destination = nil @messageId = rand_uuid @timestamp = Time.new.to_i*100 @timeToLive = 0 @headers = {} @body = nil if message.is_a?(AbstractMessage) @correlationId = message.messageId end end def read_external des super des read_external_fields des, EXTERNALIZABLE_FIELDS end end class AcknowledgeMessageExt < AcknowledgeMessage #:nodoc: end # Maps to flex.messaging.messages.ErrorMessage in AMF3 mode class ErrorMessage < AcknowledgeMessage # Extended data that will facilitate custom error processing on the client attr_accessor :extendedData # The fault code for the error, which defaults to the class name of the # causing exception attr_accessor :faultCode # Detailed description of what caused the error attr_accessor :faultDetail # A simple description of the error attr_accessor :faultString # Optional "root cause" of the error attr_accessor :rootCause def initialize message=nil, exception=nil super message unless exception.nil? @e = exception @faultCode = @e.class.name @faultDetail = @e.backtrace.join("\n") @faultString = @e.message end end def encode_amf serializer if serializer.version == 0 data = { :faultCode => @faultCode, :faultDetail => @faultDetail, :faultString => @faultString } serializer.write_object(data) else serializer.write_object(self) end end end end end