Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 4C5E8200D08 for ; Thu, 7 Sep 2017 00:34:57 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 4AE9F1609E3; Wed, 6 Sep 2017 22:34:57 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 681E51616A9 for ; Thu, 7 Sep 2017 00:34:55 +0200 (CEST) Received: (qmail 71814 invoked by uid 500); 6 Sep 2017 22:34:53 -0000 Mailing-List: contact commits-help@flex.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@flex.apache.org Delivered-To: mailing list commits@flex.apache.org Received: (qmail 70266 invoked by uid 99); 6 Sep 2017 22:34:52 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 06 Sep 2017 22:34:52 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 2824FF32A6; Wed, 6 Sep 2017 22:34:52 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: carlosrovira@apache.org To: commits@flex.apache.org Date: Wed, 06 Sep 2017 22:35:14 -0000 Message-Id: <339745880e3e481a9d2d864237e73412@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [24/29] git commit: [flex-asjs] [refs/heads/develop] - backport amf.js and get it to use Responder instead of promises, and our Reflection APIs archived-at: Wed, 06 Sep 2017 22:34:57 -0000 backport amf.js and get it to use Responder instead of promises, and our Reflection APIs Project: http://git-wip-us.apache.org/repos/asf/flex-asjs/repo Commit: http://git-wip-us.apache.org/repos/asf/flex-asjs/commit/679268fe Tree: http://git-wip-us.apache.org/repos/asf/flex-asjs/tree/679268fe Diff: http://git-wip-us.apache.org/repos/asf/flex-asjs/diff/679268fe Branch: refs/heads/develop Commit: 679268fe4fb7837703ce540a578b2f457606598d Parents: a2afa56 Author: Alex Harui Authored: Fri Sep 1 11:17:35 2017 -0700 Committer: Carlos Rovira Committed: Thu Sep 7 00:24:03 2017 +0200 ---------------------------------------------------------------------- LICENSE | 5 + .../main/flex/org/apache/flex/net/Responder.as | 65 + .../flex/net/remoting/amf/AMFBinaryData.as | 1162 ++++++++++++++++++ .../flex/net/remoting/amf/AMFNetConnection.as | 394 ++++++ 4 files changed, 1626 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/679268fe/LICENSE ---------------------------------------------------------------------- diff --git a/LICENSE b/LICENSE index 2c07af7..d182092 100644 --- a/LICENSE +++ b/LICENSE @@ -243,6 +243,11 @@ The map coordinates in examples/native/USStatesMap/src/MapCoords.as were placed into the Public Domain by its author. See: https://en.wikipedia.org/wiki/File:USA_CIA_Map.svg#file +Most of the .as files in frameworks/projects/Network/src/main/flex/ in the +package org.apache.flex.net.remoting.amf are derived from +https://github.com/emilkm/amfjs/blob/master/amf.js which is available under +Apache License 2.0. + Most of the .as files in frameworks/projects/GoogleMaps/src/main/flex/google are derived from the google_maps_api_v3_11.js externs file in the Google Closure Library which are available under Apache License 2.0. http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/679268fe/frameworks/projects/Network/src/main/flex/org/apache/flex/net/Responder.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/Network/src/main/flex/org/apache/flex/net/Responder.as b/frameworks/projects/Network/src/main/flex/org/apache/flex/net/Responder.as new file mode 100755 index 0000000..ba0c639 --- /dev/null +++ b/frameworks/projects/Network/src/main/flex/org/apache/flex/net/Responder.as @@ -0,0 +1,65 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// +package org.apache.flex.net +{ + COMPILE::SWF + { + import flash.net.Responder; + } + + [Event(name="result", type="org.apache.flex.net.events.ResultEvent")] + [Event(name="fault", type="org.apache.flex.net.events.FaultEvent")] + public class Responder + { + /** + * + */ + public function Responder(onSuccess:Function, onFailure:Function) + { + this.onSuccess = onSuccess; + this.onFailure = onFailure; + } + + /** + * @private + */ + public var onSuccess:Function; + + /** + * @private + */ + public var onFailure:Function; + + /** + * @private + */ + COMPILE::SWF + public function getActualResponder():flash.net.Responder + { + return new flash.net.Responder(onSuccess, onFailure); + } + + COMPILE::JS + public function getActualResponder():Responder + { + return this; + } + + } +} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/679268fe/frameworks/projects/Network/src/main/flex/org/apache/flex/net/remoting/amf/AMFBinaryData.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/Network/src/main/flex/org/apache/flex/net/remoting/amf/AMFBinaryData.as b/frameworks/projects/Network/src/main/flex/org/apache/flex/net/remoting/amf/AMFBinaryData.as new file mode 100644 index 0000000..cc8fcbf --- /dev/null +++ b/frameworks/projects/Network/src/main/flex/org/apache/flex/net/remoting/amf/AMFBinaryData.as @@ -0,0 +1,1162 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +/*** + * AMF JavaScript library by Emil Malinov https://github.com/emilkm/amfjs + */ + +package org.apache.flex.net.remoting.amf +{ + + import org.apache.flex.reflection.getAliasByClass; + import org.apache.flex.reflection.getClassByAlias; + +/** + * A version of BinaryData specific to AMF. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ +public class AMFBinaryData +{ + //-------------------------------------------------------------------------- + // + // Class Constants + // + //-------------------------------------------------------------------------- + + public static const CLASS_ALIAS:String = "_explicitType"; + public static const EXTERNALIZED_FIELD:String = "_externalizedData"; + + public static const RESULT_METHOD:String = "/onResult"; + public static const STATUS_METHOD:String = "/onStatus"; + + public static const EMPTY_STRING:String = ""; + public static const NULL_STRING:String = "null"; + + public static const AMF0_OBJECT_ENCODING:int = 0; + + public static const AMF0_NUMBER:int = 0; + public static const AMF0_BOOLEAN:int = 1; + public static const AMF0_STRING:int = 2; + public static const AMF0_OBJECT:int = 3; + public static const AMF0_MOVIECLIP:int = 4; + public static const AMF0_NULL:int = 5; + public static const AMF0_UNDEFINED:int = 6; + public static const AMF0_REFERENCE:int = 7; + public static const AMF0_MIXEDARRAY:int = 8; //ECMAArray + public static const AMF0_OBJECTEND:int = 9; + public static const AMF0_ARRAY:int = 10; //StrictArray + public static const AMF0_DATE:int = 11; + public static const AMF0_LONGSTRING:int = 12; + public static const AMF0_UNSUPPORTED:int = 13; + public static const AMF0_RECORDSET:int = 14; + public static const AMF0_XMLDOCUMENT:int = 15; + public static const AMF0_TYPEDOBJECT:int = 16; + public static const AMF0_AMF3:int = 17; + + public static const AMF3_OBJECT_ENCODING:int = 3; + + public static const AMF3_UNDEFINED:int = 0; + public static const AMF3_NULL:int = 1; + public static const AMF3_BOOLEAN_FALSE:int = 2; + public static const AMF3_BOOLEAN_TRUE:int = 3; + public static const AMF3_INTEGER:int = 4; + public static const AMF3_DOUBLE:int = 5; + public static const AMF3_STRING:int = 6; + public static const AMF3_XMLDOCUMENT:int = 7; + public static const AMF3_DATE:int = 8; + public static const AMF3_ARRAY:int = 9; + public static const AMF3_OBJECT:int = 10; + public static const AMF3_XML:int = 11; + public static const AMF3_BYTEARRAY:int = 12; + public static const AMF3_VECTOR_INT:int = 13; + public static const AMF3_VECTOR_UINT:int = 14; + public static const AMF3_VECTOR_DOUBLE:int = 15; + public static const AMF3_VECTOR_OBJECT:int = 16; + public static const AMF3_DICTIONARY:int = 17; + + public static const UNKNOWN_CONTENT_LENGTH:int = 1; + + public static const UINT29_MASK:int = 536870911; + public static const INT28_MAX_VALUE:int = 268435455; + public static const INT28_MIN_VALUE:int = -268435456; + public static const UINT_MAX_VALUE:uint = 4294967295; + public static const UINT_MIN_VALUE:uint = 0; + + public static const MAX_STORED_OBJECTS:int = 1024; + + public static const POW_2_20:int = Math.pow(2, 20); + public static const POW_2_52:int = Math.pow(2, 52); + public static const POW_2_52N:int = Math.pow(2, -52); + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructs an uninitialized AMFBinaryData. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function AMFBinaryData(data:Array = null) + { + // TODO: (aharui) try to share code with BinaryData. + // BinaryData has different methods and also + // has ENDIAN code which AMF doesn't seem to need. + super(); + if (data) + this.data = data; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + public var data:Array = []; + + private var objects:Array = []; + + private var traits:Object = {}; + + private var strings:Object = {}; + + private var stringCount:int = 0; + private var traitCount:int = 0; + private var objectCount:int = 0; + + public var pos:int = 0; + + public function write(v:uint):void + { + data.push(v); + }; + + public function writeShort(v:uint):void + { + write((v >>> 8) & 255); + write((v >>> 0) & 255); + }; + + public function writeUTF(v:String, asAmf:Boolean = false):int + { + var bytearr:Array = []; + var strlen:int = v.length; + var utflen:int = 0; + + for (var i:int = 0; i < strlen; i++) + { + var c1:int = v.charCodeAt(i); + //var enc = null; + + if (c1 < 128) + { + utflen++; + bytearr.push(c1); + //end++; + } + else if (c1 > 127 && c1 < 2048) + { + utflen += 2; + bytearr.push(192 | (c1 >> 6)); + if (asAmf) + bytearr.push(128 | ((c1 >> 0) & 63)); + else + bytearr.push(128 | (c1 & 63)); + } + else if ((c1 & 0xF800) !== 0xD800) + { + utflen += 3; + bytearr.push(224 | (c1 >> 12)); + bytearr.push(128 | ((c1 >> 6) & 63)); + if (asAmf) + bytearr.push(128 | ((c1 >> 0) & 63)); + else + bytearr.push(128 | (c1 & 63)); + } + else + { + utflen += 4; + if ((c1 & 0xFC00) !== 0xD800) + { + throw new RangeError('Unmatched trail surrogate at ' + i); + } + var c2:int = v.charCodeAt(++i); + if ((c2 & 0xFC00) !== 0xDC00) + { + throw new RangeError('Unmatched lead surrogate at ' + (i - 1)); + } + c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000; + bytearr.push(240 | (c1 >> 18)); + bytearr.push(128 | ((c1 >> 12) & 63)); + bytearr.push((c1 >> 6) & 63); + if (asAmf) + bytearr.push(128 | ((c1 >> 0) & 63)); + else + bytearr.push(128 | (c1 & 63)); + } + } + + if (asAmf) + writeUInt29((utflen << 1) | 1); + else + { + bytearr.unshift(utflen & 255); + bytearr.unshift((utflen >>> 8) & 255); + } + + writeAll(bytearr); + return asAmf ? utflen : utflen + 2; + }; + + public function writeUInt29(v:uint):void + { + if (v < 128) + { + this.write(v); + } + else if (v < 16384) + { + this.write(((v >> 7) & 127) | 128); + this.write(v & 127); + } + else if (v < 2097152) + { + this.write(((v >> 14) & 127) | 128); + this.write(((v >> 7) & 127) | 128); + this.write(v & 127); + } + else if (v < 0x40000000) + { + this.write(((v >> 22) & 127) | 128); + this.write(((v >> 15) & 127) | 128); + this.write(((v >> 8) & 127) | 128); + this.write(v & 255); + } + else + { + throw "Integer out of range: " + v; + } + }; + + public function writeAll(bytes:Array):void + { + for (var i:int = 0; i < bytes.length; i++) { + this.write(bytes[i]); + } + }; + + public function writeBoolean(v:Boolean):void + { + this.write(v ? 1 : 0); + }; + + public function writeInt(v:int):void + { + this.write((v >>> 24) & 255); + this.write((v >>> 16) & 255); + this.write((v >>> 8) & 255); + this.write((v >>> 0) & 255); + }; + + public function writeUInt32(v):void + { + v < 0 && (v = -(v ^ UINT_MAX_VALUE) - 1); + v &= UINT_MAX_VALUE; + this.write((v >>> 24) & 255); + this.write((v >>> 16) & 255); + this.write((v >>> 8) & 255); + this.write((v & 255)); + }; + + private function getDouble(v:Number):Array + { + var r:Array = [0,0]; + if (v != v) + { + r[0] = -524288; + return r; + } + var d:int = v < 0 || v === 0 && 1 / v < 0 ? -2147483648 : 0; + v = Math.abs(v); + if (v === Number.POSITIVE_INFINITY) + { + r[0] = d | 2146435072; + return r; + } + for (var e:int = 0; v >= 2 && e <= 1023;) + { + e++; + v /= 2; + } + for (; v < 1 && e >= -1022;) + { + e--; + v *= 2; + } + e += 1023; + if (e == 2047) + { + r[0] = d | 2146435072; + return r; + } + var i:Number; + if (e == 0) + (i = v * Math.pow(2, 23) / 2, v = Math.round(v * POW_2_52 / 2)) + else + (i = v * POW_2_20 - POW_2_20, v = Math.round(v * POW_2_52 - POW_2_52)); + r[0] = d | e << 20 & 2147418112 | i & 1048575; + r[1] = v; + return r; + }; + + public function writeDouble(v:Number):void + { + var parts:Array = getDouble(v); + this.writeUInt32(parts[0]); + this.writeUInt32(parts[1]); + }; + + public function getResult():String + { + return data.join(""); + }; + + public function reset():void + { + this.objects = []; + this.objectCount = 0; + this.traits = {}; + this.traitCount = 0; + this.strings = {}; + this.stringCount = 0; + }; + + private function writeStringWithoutType(v:String):void + { + if (v.length == 0) + this.writeUInt29(1); + else + { + if (!this.stringByReference(v)) + { + this.writeUTF(v, true); + } + } + }; + + private function stringByReference(v):uint + { + var ref:uint = this.strings[v]; + if (ref) + this.writeUInt29(ref << 1); + else + this.strings[v] = this.stringCount++; + return ref; + }; + + public function objectByReference(v):uint + { + var ref:uint = 0; + var found:Boolean = false; + for (; ref < this.objects.length; ref++) + { + if (this.objects[ref] === v) + { + found = true; + break; + } + } + if (found) + this.writeUInt29(ref << 1); + else + { + this.objects.push(v); + this.objectCount++; + } + + return found ? ref : null; + }; + + private function traitsByReference(v:Array, alias:String):uint + { + var s:String = alias + "|"; + for (var i:int = 0; i < v.length; i++) { + s += v[i] + "|"; + } + var ref:uint = this.traits[s]; + if (ref) + this.writeUInt29((ref << 2) | 1); + else + this.traits[s] = this.traitCount++; + + return ref; + }; + + private function writeAmfInt(v:int):void + { + if (v >= INT28_MIN_VALUE && v <= INT28_MAX_VALUE) { + v = v & UINT29_MASK; + this.write(AMF3_INTEGER); + this.writeUInt29(v); + } + else + { + this.write(AMF3_DOUBLE); + this.writeDouble(v); + } + }; + + private function writeDate(v:Date):void + { + this.write(AMF3_DATE); + if (!this.objectByReference(v)) + { + this.writeUInt29(1); + this.writeDouble(v.getTime()); + } + }; + + /** + * @flexjsignorecoercion Class + * @flexjsignorecoercion String + * @flexjsignorecoercion Number + */ + public function writeObject(v:Object):void + { + if (v == null) + { + this.write(AMF3_NULL); + return; + } + if (v is String) + { + this.write(AMF3_STRING); + this.writeStringWithoutType(v as String); + } + else if (v is Number) + { + var n:Number = v as Number; + if (n === +n && n === (n | 0)) + { + this.writeAmfInt(n); + } + else + { + this.write(AMF3_DOUBLE); + this.writeDouble(n); + } + } + else if (v is Boolean) + this.write((v + ? AMF3_BOOLEAN_TRUE + : AMF3_BOOLEAN_FALSE)); + else if (v is Date) + this.writeDate(v as Date); + else + { + if (v is Array) + { + if (v.toString().indexOf("[Vector") == 0) + this.writeVector(v); + else + this.writeArray(v as Array); + } + else if (getAliasByClass(v.constructor as Class)) + this.writeCustomObject(v); + else + this.writeMap(v); + } + }; + + private function writeCustomObject(v:Object):void + { + this.write(AMF3_OBJECT); + if (!this.objectByReference(v)) + { + var traits:Array = this.writeTraits(v); + for (var i:int = 0; i < traits.length; i++) + { + var prop:String = traits[i]; + this.writeObject(v[prop]); + } + } + }; + + /** + * @flexjsignorecoercion Class + */ + private function writeTraits(v:Object):Array + { + var traits:Array = []; + var count:int = 0; + var externalizable:Boolean = false; // some day: v is IExternalizable; + var dynamic:Boolean = false; // some day v.getClassInfo().isDynamic; + + var classInfo:Object = v.FLEXJS_CLASS_INFO; + var reflectionInfo:Object = v.FLEXJS_REFLECTION_INFO(); + var c:Object = v; + var t:String; + var traitsInfo:Object; + var vars:Array = []; + var accs:Array = []; + while (classInfo) + { + var className:String = classInfo.names[0].name; + traitsInfo = reflectionInfo.variables(); + for (t in traitsInfo) + { + vars.push(t); + count++; + } + traitsInfo = reflectionInfo.accessors(); + for (t in traitsInfo) + { + accs.push(t); + count++; + } + if (!c.constructor.superClass_) + break; + classInfo = c.constructor.superClass_.FLEXJS_CLASS_INFO; + reflectionInfo = c.constructor.superClass_.FLEXJS_REFLECTION_INFO(); + c = c.constructor.superClass_; + } + traits = traits.concat(vars); + traits = traits.concat(accs); + if (!this.traitsByReference(traits, getAliasByClass(v.constructor as Class))) + { + this.writeUInt29(3 | (externalizable ? 4 : 0) | (dynamic ? 8 : 0) | (count << 4)); + this.writeStringWithoutType(getAliasByClass(v.constructor as Class)); + if (count > 0) + { + for (var prop:String in traits) + { + this.writeStringWithoutType(traits[prop]); + } + } + } + return traits; + }; + + /* Write map as array + amf.Writer.prototype.writeMap = function(v) { + this.write(amf.const.AMF3_ARRAY); + if (!this.objectByReference(map)) { + this.writeUInt29((0 << 1) | 1); + for (var key in v) { + if (key) { + this.writeStringWithoutType(key); + } else { + this.writeStringWithoutType(amf.const.EMPTY_STRING); + } + this.writeObject(v[key]); + } + this.writeStringWithoutType(amf.const.EMPTY_STRING); + } + };*/ + + private function writeMap(v:Object):void + { + this.write(AMF3_OBJECT); + if (!this.objectByReference(v)) + { + this.writeUInt29(11); + this.traitCount++; //bogus traits entry here + this.writeStringWithoutType(EMPTY_STRING); //class name + for (var key:String in v) + { + if (key) + this.writeStringWithoutType(key); + else + this.writeStringWithoutType(EMPTY_STRING); + this.writeObject(v[key]); + } + this.writeStringWithoutType(EMPTY_STRING); //empty string end of dynamic members + } + }; + + private function writeArray(v:Array):void + { + this.write(AMF3_ARRAY); + var len:int = v.length; + if (!this.objectByReference(v)) + { + this.writeUInt29((len << 1) | 1); + this.writeUInt29(1); //empty string implying no named keys + if (len > 0) + { + for (var i:int = 0; i < len; i++) + { + this.writeObject(v[i]); + } + } + } + }; + + private function writeVector(v:Object):void + { + this.write(v.type); + var i:int; + var len:int = v.length; + if (!this.objectByReference(v)) + { + this.writeUInt29((len << 1) | 1); + this.writeBoolean(v.fixed); + } + if (v.type == AMF3_VECTOR_OBJECT) + { + var className:String = ""; + if (len > 0) { + // TODO: how much of the PHP logic can we do here + className = v[0].constructor.name; + } + this.writeStringWithoutType(className); + for (i = 0; i < len; i++) + { + this.writeObject(v[i]); + } + } + else if (v.type == AMF3_VECTOR_INT) + { + for (i = 0; i < len; i++) + { + this.writeInt(v[i]); + } + } + else if (v.type == AMF3_VECTOR_UINT) + { + for (i = 0; i < len; i++) + { + this.writeUInt32(v[i]); + } + } + else if (v.type == AMF3_VECTOR_DOUBLE) + { + for (i = 0; i < len; i++) + { + this.writeDouble(v[i]); + } + } + }; + + public function read():uint + { + //if (this.pos + 1 > this.datalen) { //this.data.length store in this.datalen + // throw "Cannot read past the end of the data."; + //} + return this.data[this.pos++]; + }; + + public function readUIntN(n:int):uint + { + var value:uint = this.read(); + for (var i:int = 1; i < n; i++) { + value = (value << 8) | this.read(); + } + return value; + }; + + public function readUnsignedShort():uint + { + var c1:uint = this.read(); + var c2:uint = this.read(); + return ((c1 << 8) + (c2 << 0)); + }; + + public function readInt():int + { + var c1:int = this.read(); + var c2:int = this.read(); + var c3:int = this.read() + var c4:int = this.read(); + return ((c1 << 24) + (c2 << 16) + (c3 << 8) + (c4 << 0)); + }; + + public function readUInt32():uint + { + var c1:uint = this.read(); + var c2:uint = this.read(); + var c3:uint = this.read(); + var c4:uint = this.read(); + return (c1 * 0x1000000) + ((c2 << 16) | (c3 << 8) | c4); + }; + + public function readUInt29():int + { + var b:uint = this.read() & 255; + if (b < 128) { + return b; + } + var value:uint = (b & 127) << 7; + b = this.read() & 255; + if (b < 128) + return (value | b); + value = (value | (b & 127)) << 7; + b = this.read() & 255; + if (b < 128) + return (value | b); + value = (value | (b & 127)) << 8; + b = this.read() & 255; + return (value | b); + }; + + public function readFully(buff:Array, start:int, length:int):void + { + for (var i:int = start; i < length; i++) + { + buff[i] = this.read(); + } + }; + + public function readUTF(length:int = 0):String + { + var utflen:int = length ? length : this.readUnsignedShort(); + var len:int = this.pos + utflen; + var chararr:Array = []; + var c1:int = 0; + var seqlen:int = 0; + + while (this.pos < len) + { + c1 = this.read() & 0xFF; + seqlen = 0; + + if (c1 <= 0xBF) + { + c1 = c1 & 0x7F; + seqlen = 1; + } + else if (c1 <= 0xDF) + { + c1 = c1 & 0x1F; + seqlen = 2; + } + else if (c1 <= 0xEF) + { + c1 = c1 & 0x0F; + seqlen = 3; + } + else + { + c1 = c1 & 0x07; + seqlen = 4; + } + + for (var i:int = 1; i < seqlen; ++i) + { + c1 = c1 << 0x06 | this.read() & 0x3F; + } + + if (seqlen === 4) + { + c1 -= 0x10000; + chararr.push(String.fromCharCode(0xD800 | c1 >> 10 & 0x3FF)); + chararr.push(String.fromCharCode(0xDC00 | c1 & 0x3FF)); + } + else + { + chararr.push(String.fromCharCode(c1)); + } + } + + return chararr.join(""); + }; + + public function readObject():Object + { + var type:uint = this.read(); + return this.readObjectValue(type); + }; + + public function readString():String + { + var ref:uint = this.readUInt29(); + if ((ref & 1) == 0) + { + return this.getString(ref >> 1); + } + else + { + var len:int = (ref >> 1); + if (len == 0) + { + return EMPTY_STRING; + } + var str:String = this.readUTF(len); + this.rememberString(str); + return str; + } + return null; + }; + + private function rememberString(v:String):void + { + this.strings[stringCount++] = v; + }; + + private function getString(v:int):String + { + return this.strings[v]; + }; + + private function getObject(v:int):Object + { + return this.objects[v]; + }; + + private function getTraits(v:int):Traits + { + return this.traits[v] as Traits; + }; + + private function rememberTraits(v:Traits):void + { + this.traits[traitCount++] = v; + }; + + private function rememberObject(v:Object):void + { + this.objects.push(v); + }; + + private function readTraits(ref:int):Traits + { + if ((ref & 3) == 1) + return this.getTraits(ref >> 2); + else + { + var ti:Traits = new Traits(); + ti.externalizable = ((ref & 4) == 4); + ti.dynamic = ((ref & 8) == 8); + ti.count = (ref >> 4); + var className:String = this.readString(); + if (className != null && className != "") { + ti.alias = className; + } + for (var i:int = 0; i < ti.count; i++) { + ti.props.push(this.readString()); + } + this.rememberTraits(ti); + return ti; + } + }; + + private function readScriptObject():Object + { + var ref:int = this.readUInt29(); + if ((ref & 1) == 0) + return this.getObject(ref >> 1); + else + { + var traits:Traits = this.readTraits(ref); + var obj:Object; + if (traits.alias) { + var c:Class = getClassByAlias(traits.alias); + if (c) + obj = new c(); + else + { + obj = {}; + obj[CLASS_ALIAS] = traits.alias; + } + } + else + { + obj = {}; + } + this.rememberObject(obj); + if (traits.externalizable) + { + if (obj[CLASS_ALIAS] == "flex.messaging.io.ArrayCollection" + || obj[CLASS_ALIAS] == "flex.messaging.io.ObjectProxy") + return this.readObject(); + else + obj[EXTERNALIZED_FIELD] = this.readObject(); + } + else + { + for (var i:int in traits.props) + obj[traits.props[i]] = this.readObject(); + if (traits.dynamic) + { + for (; ;) + { + var name:String = this.readString(); + if (name == null || name.length == 0) + { + break; + } + obj[name] = this.readObject(); + } + } + } + return obj; + } + }; + + /** + * @flexjsignorecoercion Array + */ + public function readArray():Array + { + var ref:uint = this.readUInt29(); + if ((ref & 1) == 0) + return this.getObject(ref >> 1) as Array; + var len:int = (ref >> 1); + var map:Object = null; + var i:int = 0; + while (true) + { + var name:String = this.readString(); + if (!name) + break; + if (!map) + { + map = {}; + this.rememberObject(map); + } + map[name] = this.readObject(); + } + if (!map) + { + var array:Array = new Array(len); + this.rememberObject(array); + for (i = 0; i < len; i++) + array[i] = this.readObject(); + return array; + } + else + { + var amap:Array = []; + for (i = 0; i < len; i++) + amap[i] = this.readObject(); + return amap; + } + }; + + public function readDouble():Number + { + var c1:uint = this.read() & 255; + var c2:uint = this.read() & 255; + if (c1 === 255) { + if (c2 === 248) + return Number.NaN; + if (c2 === 240) + return Number.NEGATIVE_INFINITY; + } + else if (c1 === 127 && c2 === 240) + return Number.POSITIVE_INFINITY; + var c3:uint = this.read() & 255; + var c4:uint = this.read() & 255; + var c5:uint = this.read() & 255; + var c6:uint = this.read() & 255; + var c7:uint = this.read() & 255; + var c8:uint = this.read() & 255; + if (!c1 && !c2 && !c3 && !c4) + return 0; + var d:uint = (c1 << 4 & 2047 | c2 >> 4) - 1023; + var e:String = ((c2 & 15) << 16 | c3 << 8 | c4).toString(2); + for (var i:uint = e.length; i < 20; i++) + e = "0" + e; + var f:String = ((c5 & 127) << 24 | c6 << 16 | c7 << 8 | c8).toString(2); + for (var j:uint = f.length; j < 31; j++) + f = "0" + f; + var g:int = parseInt(e + (c5 >> 7 ? "1" : "0") + f, 2); + if (g == 0 && d == -1023) + return 0; + return (1 - (c1 >> 7 << 1)) * (1 + POW_2_52N * g) * Math.pow(2, d); + }; + + /** + * @flexjsignorecoercion Array + */ + public function readDate():Date + { + var ref:uint = this.readUInt29(); + if ((ref & 1) == 0) + return this.getObject(ref >> 1) as Date; + var time:Number = this.readDouble(); + var date:Date = new Date(time); + this.rememberObject(date); + return date; + }; + + public function readMap():Object + { + var ref:uint = this.readUInt29(); + if ((ref & 1) == 0) + return this.getObject(ref >> 1); + var length:int = (ref >> 1); + var map:Object = null; + if (length > 0) + { + map = {}; + this.rememberObject(map); + var name:String = this.readObject() as String; + while (name != null) + { + map[name] = this.readObject(); + name = this.readObject() as String; + } + } + return map; + }; + + public function readByteArray():Array + { + var ref:uint = this.readUInt29(); + if ((ref & 1) == 0) + return this.getObject(ref >> 1) as Array; + else + { + var len:int = (ref >> 1); + var ba:Array = []; + this.readFully(ba, 0, len); + this.rememberObject(ba); + return ba; + } + }; + + private function readAmf3Vector(type:int):Object + { + var ref:uint = this.readUInt29(); + if ((ref & 1) == 0) + return this.getObject(ref >> 1); + var len:int = (ref >> 1); + var vector:Array = toVector(type, [], this.readBoolean()); + var i:int; + if (type === AMF3_VECTOR_OBJECT) + { + this.readString(); //className + for (i = 0; i < len; i++) + vector.push(this.readObject()); + } + else if (type === AMF3_VECTOR_INT) + { + for (i = 0; i < len; i++) + vector.push(this.readInt()); + } + else if (type === AMF3_VECTOR_UINT) + { + for (i = 0; i < len; i++) + vector.push(this.readUInt32()); + } + else if (type === AMF3_VECTOR_DOUBLE) + { + for (i = 0; i < len; i++) + vector.push(this.readDouble()); + } + this.rememberObject(vector); + return vector; + }; + + private function readObjectValue(type:uint):Object + { + var value:Object = null; + var u:uint; + + switch (type) + { + case AMF3_STRING: + value = this.readString(); + break; + case AMF3_OBJECT: + try + { + value = this.readScriptObject(); + } + catch (e) + { + throw "Failed to deserialize: " + e; + } + break; + case AMF3_ARRAY: + value = this.readArray(); + //value = this.readMap(); + break; + case AMF3_BOOLEAN_FALSE: + value = false; + break; + case AMF3_BOOLEAN_TRUE: + value = true; + break; + case AMF3_INTEGER: + u = this.readUInt29(); + // Symmetric with writing an integer to fix sign bits for + // negative values... + value = (u << 3) >> 3; + break; + case AMF3_DOUBLE: + value = this.readDouble(); + break; + case AMF3_UNDEFINED: + case AMF3_NULL: + break; + case AMF3_DATE: + value = this.readDate(); + break; + case AMF3_BYTEARRAY: + value = this.readByteArray(); + break; + case AMF3_VECTOR_INT: + case AMF3_VECTOR_UINT: + case AMF3_VECTOR_DOUBLE: + case AMF3_VECTOR_OBJECT: + value = this.readAmf3Vector(type); + break; + case AMF0_AMF3: + value = this.readObject(); + break; + default: + throw "Unsupported AMF type: " + type; + } + return value; + }; + + public function readBoolean():Boolean + { + return this.read() === 1; + }; + + private function toVector(type:uint, array:Array, fixed:Boolean):Array + { + // TODO (aharui) handle vectors + return array; + } + +} + +} + +class Traits +{ + public var alias:String; + public var externalizable:Boolean; + public var dynamic:Boolean; + public var count:int = 0; + public var props:Array = []; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/679268fe/frameworks/projects/Network/src/main/flex/org/apache/flex/net/remoting/amf/AMFNetConnection.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/Network/src/main/flex/org/apache/flex/net/remoting/amf/AMFNetConnection.as b/frameworks/projects/Network/src/main/flex/org/apache/flex/net/remoting/amf/AMFNetConnection.as new file mode 100644 index 0000000..6ca4d10 --- /dev/null +++ b/frameworks/projects/Network/src/main/flex/org/apache/flex/net/remoting/amf/AMFNetConnection.as @@ -0,0 +1,394 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +/*** + * AMF JavaScript library by Emil Malinov https://github.com/emilkm/amfjs + */ + +package org.apache.flex.net.remoting.amf +{ + +COMPILE::SWF +{ +import flash.events.AsyncErrorEvent; +import flash.events.IOErrorEvent; +import flash.events.NetStatusEvent; +import flash.events.SecurityErrorEvent; +import flash.net.NetConnection; +import flash.net.ObjectEncoding; +} + +import org.apache.flex.net.Responder; +import org.apache.flex.net.remoting.messages.ActionMessage; +import org.apache.flex.net.remoting.messages.ErrorMessage; +import org.apache.flex.net.remoting.messages.MessageBody; +import org.apache.flex.net.remoting.messages.MessageHeader; + +/** + * Send data via AMF to a server. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ +public class AMFNetConnection +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function AMFNetConnection() + { + super(); + COMPILE::SWF + { + nc = new NetConnection(); + nc.objectEncoding = ObjectEncoding.AMF3; + nc.client = this; + } + } + + private var url:String; + + COMPILE::SWF + private var nc:NetConnection; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Connect to a server. Pass in an http URL as the commmand for + * connection to AMF server. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function connect(command:String, ... params):void + { + // send a ping to the URL in the command param + url = command; + COMPILE::SWF + { + var args:Array = params.slice(); + args.unshift(command); + nc.addEventListener(NetStatusEvent.NET_STATUS, statusHandler); + nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); + nc.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); + nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler); + nc.connect.apply(nc, args); + } + } + + COMPILE::SWF + private function statusHandler(event:NetStatusEvent):void + { + trace("statusHandler", event.info.code); + } + + COMPILE::SWF + private function securityErrorHandler(event:SecurityErrorEvent):void + { + trace("securityErrorHandler", event); + } + + COMPILE::SWF + private function ioErrorHandler(event:IOErrorEvent):void + { + trace("ioErrorHandler", event); + } + + COMPILE::SWF + private function asyncErrorHandler(event:AsyncErrorEvent):void + { + trace("asyncErrorHandler", event); + } + + + /** + * @private + * Special handler for legacy AMF packet level header "AppendToGatewayUrl". + * When we receive this header we assume the server detected that a session was + * created but it believed the client could not accept its session cookie, so we + * need to decorate the channel endpoint with the session id. + * + * We do not modify the underlying endpoint property, however, as this session + * is transient and should not apply if the channel is disconnected and re-connected + * at some point in the future. + */ + COMPILE::SWF + public function AppendToGatewayUrl(value:String):void + { + if (value != null && value != "") + { + nc.removeEventListener(NetStatusEvent.NET_STATUS, statusHandler); + trace("disconnecting because AppendToGatewayUrl called"); + nc.close(); + trace("disconnecting returned from close()"); + // WSRP support - append any extra stuff on the wsrp-url, not the actual url. + + // Do we have a wsrp-url? + var i:int = url.indexOf("wsrp-url="); + if (i != -1) + { + // Extract the wsrp-url in to a string which will get the + // extra info appended to it + var temp:String = url.substr(i + 9, url.length); + var j:int = temp.indexOf("&"); + if (j != -1) + { + temp = temp.substr(0, j); + } + + // Replace the wsrp-url with a version that has the extra stuff + url = url.replace(temp, temp + value); + } + else + { + // If we didn't find a wsrp-url, just append the info + url += value; + } + nc.addEventListener(NetStatusEvent.NET_STATUS, statusHandler); + trace("reconnecting with " + url); + nc.connect(url); + trace("reconnecting returned from connect()"); + } + } + + COMPILE::JS + private var xhr:XMLHttpRequest; + + private var responder:Responder; + + COMPILE::JS + private var args:Array; + + /** + * Call a server function. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function call(command:String, responder:Responder, ... params):void + { + COMPILE::SWF + { + var args:Array = params.slice(); + args.unshift(responder.getActualResponder()); + args.unshift(command); + nc.call.apply(nc, args); + } + COMPILE::JS + { + this.responder = responder; + this.args = params; + + if (!xhr) + { + xhr = new XMLHttpRequest(); + xhr.onreadystatechange = onReadyStateChange; + } + + xhr.open("POST", url, true); + } + } + + private var sequence:int = 0; + + /** + * @flexjsignorecoercion Array + */ + COMPILE::JS + private function onReadyStateChange():void + { + var readyState:int = xhr.readyState; + if (readyState === 1) + { + xhr.setRequestHeader("Content-Type", "application/x-amf; charset=UTF-8"); + xhr.responseType = "arraybuffer"; + var actionMessage:ActionMessage = new ActionMessage(); + var messageBody:MessageBody = new MessageBody(); + sequence++; + messageBody.responseURI = "/" + sequence.toString(); + messageBody.data = args; + actionMessage.bodies = [ messageBody ]; + var binaryData:AMFBinaryData = new AMFBinaryData(); + writeMessage(binaryData, actionMessage); + xhr.send(new Uint8Array(binaryData.data)); + } + else if (readyState === 4) + { + try + { + if (xhr.status >= 200 && xhr.status <= 300 + && xhr.responseType == "arraybuffer" + && xhr.getResponseHeader("Content-type").indexOf("application/x-amf") > -1) + { + var message:ActionMessage; + var body:MessageBody; + var deserializer:AMFBinaryData = new AMFBinaryData(new Uint8Array(xhr.response) as Array); + try + { + message = readMessage(deserializer) as ActionMessage; + } + catch (e) + { + responder.onFailure({code:-1001, message:"Failed decoding the response.", detail:null, data:null}); + return; + } + for (var i:int in message.bodies) + { + body = message.bodies[i]; + if (!(body.data is ErrorMessage)) + responder.onSuccess(body.data); + else + responder.onFailure(body.data); + } + } + else if (xhr.status == 0 || xhr.responseType == "text") + { + responder.onFailure({code:-1004, message:"Invalid response type.", detail:"Invalid XMLHttpRequest response status or type.", data:null}); + } + else + { + responder.onFailure({code:-1005, message:"Invalid response.", detail:"", data:null}); + } + } + catch (e) + { + responder.onFailure({code:-1006, message:"Unknown error.", detail:e.message, data:null}); + } + + } + } + + COMPILE::JS + private function writeMessage(writer:AMFBinaryData, message:ActionMessage):void + { + try { + writer.writeShort(message.version); + writer.writeShort(message.headers.length); + for (var header:MessageHeader in message.headers) { + this.writeHeader(writer, message.headers[header]); + } + writer.writeShort(message.bodies.length); + for (var body:MessageBody in message.bodies) { + this.writeBody(writer, message.bodies[body]); + } + } catch (error) { + console.log(error); + } + } + + COMPILE::JS + private function writeHeader(writer:AMFBinaryData, header:MessageHeader):void + { + writer.writeUTF(header.name); + writer.writeBoolean(header.mustUnderstand); + writer.writeInt(AMFBinaryData.UNKNOWN_CONTENT_LENGTH); + writer.reset(); + //writer.writeObject(header.data); + writer.write(AMFBinaryData.AMF0_BOOLEAN); + writer.writeBoolean(true); + } + + COMPILE::JS + private function writeBody(writer:AMFBinaryData, body:MessageBody):void + { + if (body.targetURI == null) { + writer.writeUTF(AMFBinaryData.NULL_STRING); + } else { + writer.writeUTF(body.targetURI); + } + if (body.responseURI == null) { + writer.writeUTF(AMFBinaryData.NULL_STRING); + } else { + writer.writeUTF(body.responseURI); + } + writer.writeInt(AMFBinaryData.UNKNOWN_CONTENT_LENGTH); + writer.reset(); + writer.write(AMFBinaryData.AMF0_AMF3); + writer.writeObject(body.data); + + } + + COMPILE::JS + private function readMessage(reader:AMFBinaryData):ActionMessage + { + var message:ActionMessage = new ActionMessage(); + message.version = reader.readUnsignedShort(); + var headerCount:uint = reader.readUnsignedShort(); + for (var i:uint = 0; i < headerCount; i++) { + message.headers.push(this.readHeader(reader)); + } + var bodyCount:uint = reader.readUnsignedShort(); + for (i = 0; i < bodyCount; i++) { + message.bodies.push(this.readBody(reader)); + } + return message; + } + + COMPILE::JS + private function readHeader(reader:AMFBinaryData):MessageHeader + { + var header:MessageHeader = new MessageHeader(); + header.name = reader.readUTF(); + header.mustUnderstand = reader.readBoolean(); + reader.pos += 4; //length + reader.reset(); + var type:uint = reader.read(); + if (type != 2) { //amf0 string + throw "Only string header data supported."; + } + header.data = reader.readUTF(); + return header; + } + + COMPILE::JS + private function readBody(reader:AMFBinaryData):MessageBody + { + var body:MessageBody = new MessageBody(); + body.targetURI = reader.readUTF(); + body.responseURI = reader.readUTF(); + reader.pos += 4; //length + reader.reset(); + body.data = reader.readObject(); + return body; + } +} + +}