PageRenderTime 5404ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/15_authenticating/rails/vendor/plugins/rubyamf/io/amf_serializer.rb

https://github.com/brownman/flexonrails
Ruby | 364 lines | 288 code | 56 blank | 20 comment | 33 complexity | 2973b71a8f22098c152c5c6780a02876 MD5 | raw file
  1. module RubyAMF
  2. module IO
  3. class AMFSerializer
  4. require 'io/read_write'
  5. include RubyAMF::AMF
  6. include RubyAMF::Configuration
  7. include RubyAMF::App
  8. include RubyAMF::IO::BinaryWriter
  9. include RubyAMF::IO::Constants
  10. include RubyAMF::VoHelper
  11. attr_accessor :stream
  12. def initialize(amfobj)
  13. @amfobj = amfobj
  14. @stream = @amfobj.output_stream #grab the output stream for the amfobj
  15. end
  16. def reset_referencables
  17. @amf0_stored_objects = []
  18. @stored_objects = []
  19. @stored_strings = {} # hash is way faster than array
  20. @stored_strings[""] = true # add this in automatically
  21. @floats_cache = {}
  22. @write_amf3_integer_results = {} # cache the integers
  23. @current_strings_index = 0
  24. end
  25. def run
  26. #write the amf version
  27. write_int16_network(0)
  28. @header_count = @amfobj.num_outheaders
  29. write_int16_network(@header_count)
  30. 0.upto(@header_count - 1) do |i|
  31. #get the header obj at index
  32. @header = @amfobj.get_outheader_at(i)
  33. #write the header name
  34. write_utf(@header.name)
  35. #write the version
  36. write_byte(@header.required)
  37. write_word32_network(-1) #the usual four bytes of FF
  38. #write the header data
  39. write(@header.value)
  40. end
  41. #num bodies
  42. @body_count = @amfobj.num_body
  43. write_int16_network(@body_count)
  44. 0.upto(@body_count - 1) do |i|
  45. reset_referencables #reset any stored references in this scope
  46. #get the body obj at index
  47. @body = @amfobj.get_body_at(i)
  48. #write the response uri
  49. write_utf(@body.response_uri)
  50. #write null (usually target, no use for though)
  51. write_utf("null")
  52. write_word32_network(-1) #the usual four bytes of FF
  53. #write the results of the service call
  54. write(@body.results)
  55. end
  56. end
  57. #write Ruby data as AMF to output stream
  58. def write(value)
  59. if RequestStore.amf_encoding == 'amf3'
  60. write_byte(AMF3_TYPE)
  61. write_amf3(value)
  62. elsif value == nil
  63. @stream << "\005" #write_null
  64. elsif (value.is_a?(Numeric))
  65. write_number(value)
  66. elsif (value.is_a?(String))
  67. write_string(value)
  68. elsif (value.is_a?(TrueClass) || value.is_a?(FalseClass))
  69. (value) ? @stream << "\001" : @stream << "\000"
  70. elsif value.is_a?(ActiveRecord::Base) # Aryk: this way, we can bypass the next four checks most of the time
  71. write_object(VoUtil.get_vo_hash_for_outgoing(value))
  72. elsif(value.is_a?(VoHash))
  73. write_object(value)
  74. elsif (value.is_a?(Array))
  75. write_array(value)
  76. elsif (value.is_a?(Hash))
  77. write_hash(value)
  78. elsif (value.is_a?(Date))
  79. write_date(value.strftime("%s").to_i) # Convert a Date into a time object
  80. elsif (value.is_a?(Time))
  81. write_date(value.to_f)
  82. elsif value.class.to_s == 'REXML::Document'
  83. write_xml(value.write.to_s)
  84. elsif (value.class.to_s == 'BeautifulSoup')
  85. write_xml(value.to_s)
  86. else
  87. write_object(VoUtil.get_vo_hash_for_outgoing(value))
  88. end
  89. end
  90. #AMF3
  91. def write_amf3(value)
  92. if value == nil
  93. @stream << "\001" # represents an amf3 null
  94. elsif (value.is_a?(TrueClass) || value.is_a?(FalseClass))
  95. value ? (@stream << "\003") : (@stream << "\002") # represents an amf3 true and false
  96. elsif value.is_a?(Numeric)
  97. if value.is_a?(Integer) # Aryk: This was also incorrect before because you has Bignum check AFTER the Integer check, which means the Bignum's were getting picked up by Integers
  98. if value.is_a?(Bignum)
  99. @stream << "\005" # represents an amf3 complex number
  100. write_double(value)
  101. else
  102. write_amf3_number(value)
  103. end
  104. elsif(value.is_a?(Float))
  105. @stream << "\005" # represents an amf3 complex number
  106. write_double(value)
  107. elsif value.is_a?(BigDecimal) # Aryk: BigDecimal does not relate to Float, so keep it as a seperate check.
  108. # TODO: Aryk: Not quite sure why you do value.to_s.to_f? can't you just do value.to_f?
  109. value = value.to_s('F').to_f #this is turning a string into a Ruby Float, but because there are no further operations on it it is safe
  110. @stream << "\005" # represents an amf3 complex number
  111. write_double(value)
  112. end
  113. elsif(value.is_a?(String))
  114. @stream << "\006" # represents an amf3 string
  115. write_amf3_string(value)
  116. elsif(value.is_a?(Array))
  117. write_amf3_array(value)
  118. elsif(value.is_a?(Hash))
  119. write_amf3_object(value)
  120. elsif (value.is_a?(Time)||value.is_a?(Date))
  121. @stream << "\b" # represents an amf3 date
  122. write_amf3_date(value)
  123. # I know we can combine this with the last condition, but don't ; the Rexml and Beautiful Soup test is expensive, and for large record sets with many AR its better to be able to skip the next step
  124. elsif value.is_a?(ActiveRecord::Base) # Aryk: this way, we can bypass the "['REXML::Document', 'BeautifulSoup'].include?(value.class.to_s) " operation
  125. write_amf3_object(value)
  126. elsif ['REXML::Document', 'BeautifulSoup'].include?(value.class.to_s)
  127. write_byte(AMF3_XML)
  128. write_amf3_xml(value)
  129. elsif value.is_a?(Object)
  130. write_amf3_object(value)
  131. end
  132. end
  133. def write_amf3_integer(int)
  134. @stream << (@write_amf3_integer_results[int] ||= (
  135. int = int & 0x1fffffff
  136. if(int < 0x80)
  137. [int].pack('c')
  138. elsif(int < 0x4000)
  139. [int >> 7 & 0x7f | 0x80].pack('c')+
  140. [int & 0x7f].pack('c')
  141. elsif(int < 0x200000)
  142. [int >> 14 & 0x7f | 0x80].pack('c')+
  143. [int >> 7 & 0x7f | 0x80].pack('c')+
  144. [int & 0x7f].pack('c')
  145. else
  146. [int >> 22 & 0x7f | 0x80].pack('c')+
  147. [int >> 15 & 0x7f | 0x80].pack('c')+
  148. [int >> 8 & 0x7f | 0x80].pack('c')+
  149. [int & 0xff].pack('c')
  150. end
  151. ))
  152. end
  153. def write_amf3_number(number)
  154. if(number >= AMF3_INTEGER_MIN && number <= AMF3_INTEGER_MAX) #check valid range for 29bits
  155. @stream << "\004" # represents an amf3 integer
  156. write_amf3_integer(number)
  157. else #overflow condition otherwise
  158. @stream << "\005" # represents an amf3 complex number
  159. write_double(number)
  160. end
  161. end
  162. def write_amf3_string(string)
  163. if index = @stored_strings[string]
  164. if string == "" # store this initially so it gets caught by the stored_strings check
  165. @stream << "\001" # represents an amf3 empty string
  166. else
  167. reference = index << 1
  168. write_amf3_integer(reference)
  169. end
  170. else
  171. @stored_strings[string] = @current_strings_index
  172. @current_strings_index += 1 # increment the index
  173. reference = string.length
  174. reference = reference << 1
  175. reference = reference | 1
  176. write_amf3_integer(reference)
  177. writen(string)
  178. end
  179. end
  180. def write_amf3_object(value)
  181. hash = value.is_a?(Hash) ? value : VoUtil.get_vo_hash_for_outgoing(value)
  182. not_vo_hash = !hash.is_a?(VoHash) # is this not a vohash - then doesnt have an _explicitType parameter
  183. # Check if this object has already been written (for circular references)
  184. i = @stored_objects.index(value)
  185. if i != nil
  186. @stream << "\n"
  187. reference = i << 1
  188. write_amf3_integer(reference)
  189. else
  190. @stream << "\n\v" # represents an amf3 object and dynamic object
  191. @stored_objects << value # add object here for circular references
  192. not_vo_hash || !hash._explicitType ? (@stream << "\001") : write_amf3_string(hash._explicitType)
  193. hash.each do |attr, attvalue| # Aryk: no need to remove any "_explicitType" or "rmember" key since they werent added as keys
  194. if not_vo_hash # then that means that the attr might not be symbols and it hasn't gone through camelizing if thats needed
  195. attr = attr.to_s.dup # need this just in case its frozen
  196. attr.to_camel! if ClassMappings.translate_case
  197. end
  198. write_amf3_string(attr) # write property name
  199. attvalue.nil? ? (@stream << "\001") : write_amf3(attvalue) # if value is nil, write an amf null, otherwise, write value
  200. end
  201. @stream << "\001" # represents an amf3 empty string to close open object
  202. end
  203. end
  204. def write_amf3_array(array)
  205. i = @stored_objects.index(array)
  206. if i != nil
  207. ClassMappings.use_array_collection ? @stream << "\n" : @stream << "\t"
  208. reference = i << 1
  209. write_amf3_integer(reference)
  210. else
  211. @stored_objects << array
  212. num_objects = array.length * 2 + 1
  213. if ClassMappings.use_array_collection
  214. @stream << "\n\a" # AMF3_OBJECT and AMF3_XML
  215. write_amf3_string("flex.messaging.io.ArrayCollection")
  216. end
  217. @stream << "\t" # represents an amf3 array
  218. write_amf3_integer(num_objects)
  219. @stream << "\001" # represents an amf3 empty string #write empty for string keyed elements here, as it's never allowed from ruby
  220. array.each{|v| write_amf3(v) }
  221. end
  222. end
  223. def write_amf3_date(datetime) # Aryk: Dates will almost never be the same, so turn off the storing_objects
  224. @stored_objects << datetime # we may not do lookups, but dates are still end up in the object table, so we need to add them here to keep the right counts
  225. write_amf3_integer(1)
  226. seconds = if datetime.is_a?(Time)
  227. datetime.utc unless datetime.utc?
  228. datetime.to_f
  229. elsif datetime.is_a?(Date) # this also handles the case for DateTime
  230. datetime.strftime("%s").to_i
  231. # datetime = Time.gm( datetime.year, datetime.month, datetime.day )
  232. # datetime = Time.gm( datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec )
  233. end
  234. write_double( (seconds*1000).to_i ) # used to be total_milliseconds = datetime.to_i * 1000 + ( datetime.usec/1000 )
  235. end
  236. def write_amf3_xml(value)
  237. xml = value.to_s
  238. a = xml.strip
  239. if(a != nil)
  240. b = a.gsub!(/\>(\n|\r|\r\n| |\t)*\</,'><') #clean whitespace
  241. else
  242. b = xml.gsub!(/\>(\n|\r|\r\n| |\t)*\</,'><') #clean whitespace
  243. end
  244. write_amf3_string(b)
  245. end
  246. #AMF0
  247. def write_null
  248. @stream << "\005" #write_byte(5)
  249. end
  250. def write_number(numeric)
  251. @stream << "\000" #write_byte(0)
  252. write_double(numeric)
  253. end
  254. def write_string(string)
  255. @stream << "\002" #write_byte(2)
  256. write_utf(string.to_s)
  257. end
  258. def write_booleanr(bool)
  259. @stream << "\001" #write_byte(1)
  260. (bool) ? @stream << "\001" : @stream << "\000" #write_boolean(bool)
  261. end
  262. def write_date(seconds)
  263. @stream << "\v" #write_byte(11)
  264. write_double(seconds * 1000)
  265. offset = Time.zone_offset(Time.now.zone)
  266. write_int16_network(offset / 60 * -1)
  267. end
  268. def write_array(array)
  269. @stream << "\n" #write_byte(10)
  270. write_word32_network(array.length)
  271. array.each do |el|
  272. write(el)
  273. end
  274. end
  275. def write_hash(hash)
  276. @stream << "\003" #write_byte(3)
  277. hash.each do |key, value|
  278. key.to_s.dup.to_camel! if ClassMappings.translate_case
  279. write_utf(key)
  280. write(value)
  281. end
  282. #write the end object flag 0x00, 0x00, 0x09
  283. write_int16_network(0)
  284. @stream << "\t" #write_byte(9)
  285. end
  286. def write_object(vohash)
  287. if vohash.is_a?(VoHash) && vohash._explicitType
  288. @stream << "\020" #write_byte(16) #custom class
  289. write_utf(vohash._explicitType)
  290. else
  291. @stream << "\003" #write_byte(3)
  292. end
  293. vohash.each do |key,val|
  294. key.to_s.dup.to_camel! if ClassMappings.translate_case
  295. write_utf(key)
  296. write(val)
  297. end
  298. #write the end object flag 0x00, 0x00, 0x09
  299. write_int16_network(0)
  300. @stream << "\t" #write_byte(9)
  301. end
  302. def write_xml(xml_string)
  303. write_byte(AMF_XML)
  304. write_long_utf(xml_string.to_s)
  305. end
  306. end
  307. end
  308. end