June 20, 2008 3:09 PM
HTTPService has lots of functionality that is indispensable and fits in along with other classes of the mx.rpc package. It handles concurrency and can decode responses into object, xml, e4x, array, etc. But when it comes to data in binary format, people tend to use a URLLoader instead of trying to get HTTPService to understand binary data. All you need to do is specify a custom channel which in turn uses your URLLoader and override the processResult function of HTTPService to handle binary data. In this article, we'll extend HTTPService to support GZIP encoded responses (which currently is missing from AIR1) in addition to decoding to the appropriate resultFormat.

First, we need to extend DirectHTTPChannel2 so that it sets URLLoader's dataFormat to binary:

/* Snippet from DirectHTTPBinaryChannel.as */
override protected function internalSend(msgResp:MessageResponder):void
{
    var httpMsgResp:DirectHTTPBinaryMessageResponder = DirectHTTPBinaryMessageResponder(msgResp);
    var urlRequest:URLRequest;
    
    try
    {
        urlRequest = createURLRequest(httpMsgResp.message);
    }
    catch(e: MessageSerializationError)
    {
        httpMsgResp.agent.fault(e.fault, httpMsgResp.message);
        return;
    }
    
    var urlLoader:URLLoader = httpMsgResp.urlLoader;
    /* yes, binary, finally */
    urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
    urlLoader.addEventListener(ErrorEvent.ERROR, httpMsgResp.errorHandler);
    urlLoader.addEventListener(IOErrorEvent.IO_ERROR, httpMsgResp.errorHandler);
    urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, httpMsgResp.securityErrorHandler);
    urlLoader.addEventListener(Event.COMPLETE, httpMsgResp.completeHandler);
    urlLoader.load(urlRequest);
}

We would also have to write DirectHTTPBinaryMessageResponder even though it is the same as mx.messaging.channels.DirectHTTPMessageResponder because DirectHTTPMessageResponder is an internal class.

Now we can extend HTTPService, override the send() method to force the use of our channel and override the processResult() function to decide the fate of our ByteArray result:

/* Snippet from GzipHTTPService.as */
override public function send(parameters:Object = null):AsyncToken
{
    if ( useProxy == false )
    {
        /* force the use of our binary channel */
        if ( binaryChannelSet == null )
        {
            var dcs:ChannelSet = new ChannelSet();
            binaryChannel = new DirectHTTPBinaryChannel("direct_http_binary_channel");
            dcs.addChannel(binaryChannel);            
            channelSet = dcs
            binaryChannelSet = dcs;
        }
        else if ( channelSet != binaryChannelSet )
        {
            channelSet = binaryChannelSet;
        }       			
    }    		
    return super.send(parameters);	
}

override mx_internal function processResult(message:IMessage, 
                                            token:AsyncToken):Boolean
{
    var body:Object = message.body;
    
    if (body == null )
    {
        _result = null;
        return true;
    }
    else if ( body is ByteArray )
    {
        var barr:ByteArray = body as ByteArray;
        /* decode the gzip encoded result */
        var encoder:GZIPEncoder = new GZIPEncoder();				
        message.body = encoder.uncompressToByteArray(barr).toString();
        /* pass it on to HTTPService */
        return super.processResult(message, token);				
    }
    return false;
}

In this particular case, we pass the binary data to Paul Robertson's excellent GZIPEncoder3 and decode it to obtain the actual result and then pass it on to HTTPService's processResult so that the final result object is in the resultFormat you would expect.

The following MXML file illustrates the use of our new HTTPService:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
		layout="vertical" 
		xmlns:service="com.anirudh.rpc.*"
		creationComplete="service.send()">
<mx:Script>
	<![CDATA[
import mx.rpc.events.ResultEvent;
private function handleResult(event:ResultEvent):void
{
    trace(event.result);
}
	]]>
</mx:Script>
<service:GzipHTTPService id="service" url="test.dat" 
			 resultFormat="e4x" 
			 result="handleResult(event)" />
</mx:Application>

If you set a breakpoint in handleResult(), you would be able to confirm that the result is in E4X format even though our source file test.dat is an XML file compressed with GZIP.

The full code is available for download and is licensed under the MPL 1.1.

CategoryFlex Comment(s)


[1] More info on GZIP encoded HTTP responses in AIR.
[2] Wondered where the URLLoader was while looking at code of HTTPService? Its in DirectHTTPChannel.
[3] GZIPEncoder only works in AIR.

Copyright © 2004-2011 Anirudh Sasikumar. All rights reserved.
Last Updated: June 20, 2008 6:09 PM