Skip to content

Commit

Permalink
added support for cyclic references
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim Whitlock committed Jul 28, 2010
1 parent fbabb29 commit 98bc7c0
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 105 deletions.
15 changes: 14 additions & 1 deletion examples/http-client/NodeAmfConnection.as
Expand Up @@ -35,6 +35,7 @@ package {
addEventListener( AsyncErrorEvent.ASYNC_ERROR, onError );
addEventListener( SecurityErrorEvent.SECURITY_ERROR, onError );
proxyType = 'HTTP';
/*
// call all available AMF methods defined by the gateway
call("test", responder );
// test a complex structure with all data types
Expand All @@ -48,6 +49,12 @@ package {
// other scalars
true, false, null, undefined
);
*/
// test a cyclic reference
var cyclic = { test:'ok' };
cyclic.self = cyclic;
call( "echo", responder, cyclic );

}


Expand Down Expand Up @@ -112,14 +119,20 @@ package {
if( value is Date ){
return trace( prefix + '(Date) '+ (value as Date).toString() );
}
if( value.__amfref != null ){
return trace( prefix + '(Cyclic)' );
}
value.__amfref = true;
if( value is Array || value.length ){
trace( prefix + '(Array length:'+value.length+') ');
}
else {
trace( prefix + '(Object)');
}
for( var s:String in value ){
dump( value[s], ' . '+tab, s );
if( s !== '__amfref' ){
dump( value[s], ' . '+tab, s );
}
}
}

Expand Down
132 changes: 84 additions & 48 deletions node-amf/amf.js
@@ -1,72 +1,108 @@
/**
* Top level entry to AMF library for Node JS
*/
var createPacket = require('./packet').createPacket;

// object prototypes
var AMFTraits = require('./traits').AMFTraits;
var AMFPacket = require('./packet').AMFPacket;
var AMFMessage = require('./message').AMFMessage;
var AMFSerializer = require('./serialize').AMFSerializer;
var AMFDeserializer = require('./deserialize').AMFDeserializer;


/**
* @return AMFPacket
*/
exports.packet = function( src ){
return createPacket( src );
var Packet;
if( src ){
Packet = AMFPacket.deserialize( src );
}
if( ! Packet ){
Packet = new AMFPacket;
}
return Packet;
}



function define( name, value ){
exports[name] = value;
};


define('AMF0',0);
define('AMF3',3);

define('AMF0_NUMBER', 0 );
define('AMF0_BOOLEAN', 1 );
define('AMF0_STRING', 2 );
define('AMF0_OBJECT', 3 );
define('AMF0_MOVIECLIP', 4 );
define('AMF0_NULL', 5 );
define('AMF0_UNDEFINED', 6 );
define('AMF0_REFERENCE', 7 );
define('AMF0_ECMA_ARRAY', 8 );
define('AMF0_OBJECT_END', 9 );
define('AMF0_STRICT_ARRAY', 0x0A );
define('AMF0_DATE', 0x0B );
define('AMF0_LONG_STRING', 0x0C );
define('AMF0_UNSUPPORTED', 0x0D );
define('AMF0_RECORDSET', 0x0E );
define('AMF0_XML_DOC', 0x0F );
define('AMF0_TYPED_OBJECT', 0x10 );
define('AMF0_AMV_PLUS', 0x11 );
/**
* @return AMFMessage
*/
exports.message = function( value, requestURI, responseURI ){
return new AMFMessage( value, requestURI, responseURI );
}

define('AMF3_UNDEFINED', 0 );
define('AMF3_NULL', 1 );
define('AMF3_FALSE', 2 );
define('AMF3_TRUE', 3 );
define('AMF3_INTEGER', 4 );
define('AMF3_DOUBLE', 5 );
define('AMF3_STRING', 6 );
define('AMF3_XML_DOC', 7 );
define('AMF3_DATE', 8 );
define('AMF3_ARRAY', 9 );
define('AMF3_OBJECT', 0x0A );
define('AMF3_XML', 0x0B );
define('AMF3_BYTE_ARRAY', 0x0C );

/**
* @return AMFHeader
*/
exports.header = function( value, requestURI, responseURI ){
return new AMFMessage( value, requestURI, responseURI );
}


/**
* @return AMFDeserializer
*/
exports.deserializer = function( src ){
return new AMFDeserializer( src );
}


/**
* @return AMFSerializer
*/
exports.serializer = function(){
return new AMFSerializer( 3 );
}


/**
* @return AMFTraits
*/
exports.traits = function(){
return new AMFTraits;
}


/** Simple traits object */
exports.Traits = function(){
this.clss = 'Object'; // object class
this.dyn = false; // whether object is dynamic (i.e. non strict about members)
this.props = []; // class members
}

/**
* pseudo constants
*/
exports.AMF0 = 0;
exports.AMF3 = 3;
// AMF0 markers
exports.AMF0_NUMBER = 0;
exports.AMF0_BOOLEAN = 1;
exports.AMF0_STRING = 2;
exports.AMF0_OBJECT = 3;
exports.AMF0_MOVIECLIP = 4;
exports.AMF0_NULL = 5;
exports.AMF0_UNDEFINED = 6;
exports.AMF0_REFERENCE = 7;
exports.AMF0_ECMA_ARRAY = 8;
exports.AMF0_OBJECT_END = 9;
exports.AMF0_STRICT_ARRAY = 0x0A;
exports.AMF0_DATE = 0x0B;
exports.AMF0_LONG_STRING = 0x0C;
exports.AMF0_UNSUPPORTED = 0x0D;
exports.AMF0_RECORDSET = 0x0E;
exports.AMF0_XML_DOC = 0x0F;
exports.AMF0_TYPED_OBJECT = 0x10;
exports.AMF0_AMV_PLUS = 0x11;
// AMF3 markers
exports.AMF3_UNDEFINED = 0;
exports.AMF3_NULL = 1;
exports.AMF3_FALSE = 2;
exports.AMF3_TRUE = 3;
exports.AMF3_INTEGER = 4;
exports.AMF3_DOUBLE = 5;
exports.AMF3_STRING = 6;
exports.AMF3_XML_DOC = 7;
exports.AMF3_DATE = 8;
exports.AMF3_ARRAY = 9;
exports.AMF3_OBJECT = 0x0A;
exports.AMF3_XML = 0x0B;
exports.AMF3_BYTE_ARRAY = 0x0C;


23 changes: 10 additions & 13 deletions node-amf/deserialize.js
Expand Up @@ -4,19 +4,16 @@


/** dependencies */
var sys = require('sys');
var amf = require('./amf');
var utils = require('./utils');
var utf8 = require('./utf8');
var unpack = require('./unpack').unpack;
var bin = require('./bin');
var createHeader = require('./header').createHeader;
var createMessage = require('./message').createMessage;


exports.createDeserializer = function( src ){
return new AMFDeserializer( src );
}
/** export constructor */
exports.AMFDeserializer = AMFDeserializer;



// ----------------------------------
Expand Down Expand Up @@ -297,7 +294,8 @@ AMFDeserializer.prototype.readObject = function(){
if( n & 1 ){
// check if trait data follows
if( n & 2 ){
Traits = new amf.Traits;
Traits = amf.traits();
this.refTra.push( Traits );
// check if traits externalizable follows (U29O-traits-ext)
if( n & 4 ){
Traits.clss = this.readUTF8( amf.AMF3 );
Expand All @@ -316,8 +314,6 @@ AMFDeserializer.prototype.readObject = function(){
Traits.props.push( prop );
}
}
// index traits object
this.refTra.push( Traits );
}
// else trait reference (U29O-traits-ref)
else {
Expand All @@ -330,6 +326,7 @@ AMFDeserializer.prototype.readObject = function(){
// Have traits - Construct instance
// @todo support class mapping somehow?
var prop, Obj = {};
this.refObj.push( Obj );
for( var i = 0; i < Traits.props; i++ ){
prop = Traits.props[i];
Obj[prop] = this.readValue( amf.AMF3 );
Expand All @@ -340,8 +337,6 @@ AMFDeserializer.prototype.readObject = function(){
Obj[prop] = this.readValue( amf.AMF3 );
}
}
// index this instance
this.refObj.push( Obj );
}
// else object reference ( U29O-ref )
else {
Expand Down Expand Up @@ -408,8 +403,9 @@ AMFDeserializer.prototype.readByteArray = function(){
* @return AMFHeader
*/
AMFDeserializer.prototype.readHeader = function(){
this.resetRefs();
var name = this.readUTF8( amf.AMF0 );
var Header = createHeader( name, '' );
var Header = amf.header( name, '' );
Header.mustunderstand = Boolean( this.readU8() );
var len = this.readU32(); // we won't actually use the length
// @todo lazy creation of header by storing known header byte length
Expand All @@ -423,7 +419,8 @@ AMFDeserializer.prototype.readHeader = function(){
* @return AMFMessage
*/
AMFDeserializer.prototype.readMessage = function(){
var Msg = new createMessage('','','');
this.resetRefs();
var Msg = amf.message('','','');
// request URI - AMF0 UTF-8
Msg.requestURI = this.readUTF8( amf.AMF0 );
// response URI - AMF0 UTF-8
Expand Down
6 changes: 3 additions & 3 deletions node-amf/header.js
Expand Up @@ -3,9 +3,9 @@
var amf = require('./amf');


exports.createHeader = function( name, value ){
return new AMFHeader( name, value );
}
/** export constructor */
exports.AMFHeader = AMFHeader;



// ----------------------------------
Expand Down
9 changes: 4 additions & 5 deletions node-amf/http-server.js
Expand Up @@ -8,7 +8,6 @@ var http = require('http');
// Require node-amf module libraries
var amf = require('./amf');
var utils = require('./utils');
var createMessage = require('./message').createMessage;


/**
Expand Down Expand Up @@ -74,12 +73,12 @@ exports.start = function( listenPort, listenHost, methods ){
}
// call function and create response message with return value
retval = func.apply( null, args );
responseMessage = createMessage( retval, requestMessage.responseURI+'/onResult', '' );
responseMessage = amf.message( retval, requestMessage.responseURI+'/onResult', '' );
}
// errors respond with an onStatus method request to the client - no responseURI required
catch( Er ){
//sys.puts('Error: ' + Er.message);
responseMessage = createMessage( Er.message, requestMessage.responseURI+'/onStatus', '' );
sys.puts('Error: ' + Er.message);
responseMessage = amf.message( Er.message, requestMessage.responseURI+'/onStatus', '' );
}
responsePacket.messages.push( responseMessage );
}
Expand All @@ -94,7 +93,7 @@ exports.start = function( listenPort, listenHost, methods ){
res.write( bin, "binary" );
}
catch( e ){
//sys.puts( 'Error: ' + e.message );
sys.puts( 'Error: ' + e.message );
res.writeHead( 500, {'Content-Type': 'text/plain'} );
res.write( 'Error serializing AMF packet:\n' + e.message );
}
Expand Down
8 changes: 4 additions & 4 deletions node-amf/message.js
Expand Up @@ -4,15 +4,15 @@ var amf = require('./amf');



exports.createMessage = function( value, requestURI, responseURI ){
return new AMFMessage( value, requestURI, responseURI );
}
/** export constructor */
exports.AMFMessage = AMFMessage;



// ----------------------------------


function AMFMessage( value, requestURI, responseURI ){
function AMFMessage ( value, requestURI, responseURI ){
this.value = value;
this.requestURI = requestURI || '';
this.responseURI = responseURI || '';
Expand Down

0 comments on commit 98bc7c0

Please sign in to comment.