Using AMF with flash.net.URLLoader

flash.net.NetConnection is a native Flash Player class that is the workhorse for AMF based communication. Flash and Flex applications make use of NetConnection to send AMF formatted requests over HTTP or HTTPS to servers such as Flash Remoting, ColdFusion, BlazeDS, etc. However, less HTTP features are exposed by NetConnection than those of flash.net.URLLoader.

This can be a pain when your network architecture has one or more firewalls between external Flash Player clients and your application server. If authentication or session management is performed at the firewall, session timeouts can introduce an annoying problem. If a client makes an AMF request after a session has timed out, the session management layer may challenge the client to re-authenticate. This challenge is unlikely to be an AMF formatted response and so NetConnection is unable to process the information. While the client knows the request failed, the exact cause of failure is not known.

So… I hacked together a prototype replacement for NetConnection based on flash.net.URLLoader. This new “AMFConnection” class creates binary flash.net.URLRequests and sends HTTP POST data as an AMF formatted request and processes the AMF formatted response returned to URLLoader. One could subclass AMFConnection and intercept the response data and try to detect that an HTML re-authentication page has been returned to the client.

Other benefits this approach include being able to set custom HTTP request headers (and even receive HTTP response headers for AMF when used in an AIR application), receive progress events for large AMF response data as it is streaming back to the client, being able to fully customize the batching behavior of multiple AMF requests made in quick succession, or even customize the AMF serialization or deserialization behavior as this is no longer exclusively native code.

I tried to follow the existing NetConnection behavior for AMF over HTTP where possible… even if it meant slightly slower code. One complication has to do with AMF packets in that they reset the by-reference serialization tables for each AMF header or AMF message body (as multiple headers and bodies may be batched in a single request, i.e. AMF packet). I had to use flash.net.ByteArray buffers for each header value or message body (in either a request or response) to ensure the reference indexes were reset to 0.

There are a few minor issues I still need to work on, such as only switching to AMF 3 on encountering a complex object (right now my class starts out in AMF 0 as normal, but it always switches to AMF 3 if the objectEncoding has been set to version 3 regardless of the data type), and exposing an API to control batching (right now each request is sent individually).

One annoying thing I ran into is flash.net.Responder’s constructor takes two Functions for the result and status handlers – but these are not exposed after construction in ActionScript. So instead I had to introduce a custom AMFResponder that must be passed to AMFConnection.call().

Connections in BlazeDS are abstracted by Channels. To test my replacement of NetConnection I created a copy of AMFChannel and replaced the usages of NetConnection with my new AMFConnection. I ran some initial tests and Flex RemoteObject worked smoothly.

I’ve uploaded an unsupported prototype amf.swc as a binary distribution for now. To test it out with RemoteObject you add it to your library path and create an instance of the temporary mx.messaging.channels.AMFChannel2 class and use it in a custom ChannelSet.

import mx.messaging.channels.AMFChannel2;import mx.messaging.channels.Channel;import mx.messaging.channels.ChannelSet;...var channelSet:ChannelSet = new ChannelSet();var channel:Channel = new AMFChannel2(null, "http://localhost:8400/team/messagebroker/amf");channelSet.addChannel(channel);ro.channelSet = channelSet;......

Alternatively, you may want to try out the raw flex.net.AMFConnection class – either directly or using a subclass and custom event listeners for the usual URLLoader events (which are simply redispatched by AMFConnection).

package flex.net{import flash.events.*;import flash.net.URLLoader;import flash.utils.ByteArray;public class AMFConnection extends EventDispatcher{public function AMFConnection(url:String=null);public function get client():Object;public function set client(value:Object):void;public function get connected():Boolean;public static function get defaultObjectEncoding():uint;public static function set defaultObjectEncoding(value:uint):void;public function get objectEncoding():uint;public function set objectEncoding(value:uint):void;public function get url():String;[Deprecated("Please use the 'url' property instead.")]public function get uri():String;protected var responseCounter:uint;protected var urlLoader:URLLoader;protected var pendingRequests:Object = {};public function addHeader(name:String, mustUnderstand:Boolean=false, data:*=undefined):void;public function call(command:String, responder:IAMFResponder, ...arguments:Array):void;public function close():void;public function connect(url:String):void;public function removeHeader(name:String):Boolean;protected function completeHandler(event:Event):void;protected function errorHandler(event:ErrorEvent):void;protected function httpStatusHandler(event:HTTPStatusEvent):void;protected function ioErrorHandler(event:IOErrorEvent):void;protected function progressHandler(event:ProgressEvent):void;protected function securityErrorHandler(event:SecurityErrorEvent):void;protected function getResponseURI():String;protected function response(bytes:ByteArray):void;protected function send(data:ByteArray):void;}}

11 Responses to Using AMF with flash.net.URLLoader

  1. Trevor Baker says:

    You should become a blazeds comitter and contribute this! 😉

  2. Greg says:

    I’m very interested in trying this out, but the link to the SWC is broken.

  3. Peter Farland says:

    Thanks, I’ll update the document with the correct link, here it is below:amf.swc

  4. Greg says:

    Is there any chance you would be willing to release the source code? This comes pretty close to what I need, but I need to deviate slightly from the behavior of AMFChannel (which AMFChannel2 follows).

  5. Peter Farland says:

    I added the source code by logging an enhancement request to the BlazeDS bug base.https://bugs.adobe.com/jira/browse/BLZ-209

  6. gordon says:

    hi ,pete .it seems byteArray.writeObject([‘a’,’a’]);Serialize a strict array to a ecma array when objectEncoding is 0;

  7. Peter Farland says:

    I think that’s just an odd behavior of the player, but technically harmless. Note that on deserialization of either AMF 0 types 0x08 or 0x0A, an Array is still created with the correct length and the keys are indeed of type Number. So I think for all intents and purposes the Array is a strict dense Array.

  8. Urs says:

    Hi Pete!Great work, but I have a problem using this stuff with the latest amfphp. I already debugged the whole night and the key thing seems to be that in AMF3-mode the AMFConnectionChannel::resultHandler() expects msg to be a AcknowledgeMessage but it’s null. AMF0-mode works well. The backend works well with the original NetConnection-stuff (AMF0/3), so I don’t think the bug’s in the backend. I could not compare NetConnection to AMFConnection because of the closed-source-nature of NetConnection.Did you try out the combination of amfphp and your AMFConnection-class?

  9. J says:

    Hi,Can you kindly let me know how can an AMF request be sent from .Net.Problem Scenario:I need to loadtest a .aspx page that contains Flash.Problem Description:This is not happening using VSTS as there is no support in VSTS to send an AMF request.So in short, I want to know1. How I can create an AMF request in .Net to send to the Flash Remoting gateway2. How I can consume the response in the Flash object on the client.Regards,J

  10. Pete says:

    There are several implementations of AMF 3 for .NET. Fluorine is one… check out http://osflash.org/fluorine

  11. Pete says:

    Also Urs, I have not tested it with AMFPHP… but if a non-AcknowledgeMessage is coming back from other AMF 3 implementations, you could always modify my source and make the check optional.