////////////////////////////////////////////////////////////////////////////////
//
// 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 mx.messaging.channels
{
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.events.TimerEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import mx.core.mx_internal;
import mx.messaging.FlexClient;
import mx.messaging.MessageAgent;
import mx.messaging.MessageResponder;
import mx.messaging.channels.amfx.AMFXDecoder;
import mx.messaging.channels.amfx.AMFXEncoder;
import mx.messaging.channels.amfx.AMFXHeader;
import mx.messaging.channels.amfx.AMFXResult;
import mx.messaging.config.ConfigMap;
import mx.messaging.config.LoaderConfig;
import mx.messaging.config.ServerConfig;
import mx.messaging.errors.MessageSerializationError;
import mx.messaging.events.ChannelFaultEvent;
import mx.messaging.messages.AbstractMessage;
import mx.messaging.messages.AcknowledgeMessage;
import mx.messaging.messages.AsyncMessage;
import mx.messaging.messages.CommandMessage;
import mx.messaging.messages.ErrorMessage;
import mx.messaging.messages.HTTPRequestMessage;
import mx.messaging.messages.IMessage;
import mx.messaging.messages.MessagePerformanceInfo;
import mx.messaging.messages.MessagePerformanceUtils;
import mx.netmon.NetworkMonitor;
import mx.utils.ObjectUtil;
import mx.utils.StringUtil;
use namespace mx_internal;
/**
* The HTTPChannel class provides the HTTP support for messaging.
* You can configure this Channel to poll the server at an interval
* to approximate server push.
* You can also use this Channel with polling disabled to send RPC messages
* to remote destinations to invoke their methods.
*
*
* The HTTPChannel relies on network services native to Flash Player and AIR,
* and exposed to ActionScript by the URLLoader class.
* This channel uses URLLoader exclusively, and creates a new URLLoader
* per request.
*
*
*
* Channels are created within the framework using the
* ServerConfig.getChannel()
method. Channels can be constructed
* directly and assigned to a ChannelSet if desired.
*
*
*
* Channels represent a physical connection to a remote endpoint.
* Channels are shared across destinations by default.
* This means that a client targetting different destinations may use
* the same Channel to communicate with these destinations.
*
*
*
* When used in polling mode, this Channel polls the server for new messages
* based on the polling-interval-seconds
property in the configuration file,
* and this can be changed by setting the pollingInterval
property.
* The default value is 3 seconds.
* To enable polling, the channel must be connected and the polling-enabled
* property in the configuration file must be set to true
, or the
* pollingEnabled
property of the Channel must be set to true
.
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public class HTTPChannel extends PollingChannel
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @param id The id of this Channel.
* @param uri The uri for this Channel.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function HTTPChannel(id:String = null, uri:String = null)
{
super(id, uri);
_encoder = new AMFXEncoder();
_appendToURL = "";
_messageQueue = [];
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var _appendToURL:String;
/**
* @private
* The loader used to ping the server in internalConnect. We need to hang onto a reference
* in order to time out a connect attempt.
*/
private var _connectLoader:ChannelRequestLoader;
/**
* @private
*/
private var _encoder:AMFXEncoder;
/**
* @private
* Records the request that needs to be completed before other
* requests can be sent.
*/
private var _pendingRequest:ChannelRequestLoader = null;
/**
* @private
* This queue contains the messages from send requests that
* occurred while an authentication attempt is underway.
*/
private var _messageQueue:Array;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// polling
//----------------------------------
/**
* Reports whether the channel is actively polling.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function get polling():Boolean
{
return pollOutstanding;
}
//----------------------------------
// piggybackingEnabled
//----------------------------------
/**
* Indicates whether this channel will piggyback poll requests along
* with regular outbound messages when an outstanding poll is not in
* progress. This allows the server to piggyback data for the client
* along with its response to client's message.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function get piggybackingEnabled():Boolean
{
return internalPiggybackingEnabled;
}
/**
* @private
*/
public function set piggybackingEnabled(value:Boolean):void
{
internalPiggybackingEnabled = value;
}
//----------------------------------
// pollingEnabled
//----------------------------------
/**
* Indicates whether this channel is enabled to poll.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function get pollingEnabled():Boolean
{
return internalPollingEnabled;
}
/**
* @private
*/
public function set pollingEnabled(value:Boolean):void
{
internalPollingEnabled = value;
}
//----------------------------------
// pollingInterval
//----------------------------------
/**
* Provides access to the polling interval for this Channel.
* The value is in milliseconds.
* This value determines how often this Channel requests messages from
* the server, to approximate server push.
*
* @throws ArgumentError If the pollingInterval is assigned a value of 0 or
* less.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function get pollingInterval():Number
{
return internalPollingInterval;
}
/**
* @private
*/
public function set pollingInterval(value:Number):void
{
internalPollingInterval = value;
}
//----------------------------------
// protocol
//----------------------------------
/**
* Returns the protocol for this channel (http).
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
override public function get protocol():String
{
return "http";
}
//--------------------------------------------------------------------------
//
// Internal Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// appendToURL
//----------------------------------
/**
* @private
*/
mx_internal function get appendToURL():String
{
return _appendToURL;
}
/**
* @private
*/
mx_internal function set appendToURL(value:String):void
{
if (value && endpoint)
{
_appendToURL = value;
}
}
//--------------------------------------------------------------------------
//
// Overridden Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Processes polling related configuration settings.
*/
override public function applySettings(settings:XML):void
{
super.applySettings(settings);
applyPollingSettings(settings);
}
//--------------------------------------------------------------------------
//
// Overridden Protected Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function connectTimeoutHandler(event:TimerEvent):void
{
_connectLoader.close();
super.connectTimeoutHandler(event);
}
/**
* @private
*/
override protected function getDefaultMessageResponder(agent:MessageAgent, msg:IMessage):MessageResponder
{
return new HTTPMessageResponder(agent, msg, this);
}
/**
* @private
* Attempts to connect to the remote destination with the current endpoint
* specified for this channel.
* This will determine if a connection can be established.
*/
override protected function internalConnect():void
{
// Ping the server to make sure that it is reachable.
var msg:CommandMessage = new CommandMessage();
if (credentials != null)
{
msg.operation = CommandMessage.LOGIN_OPERATION;
msg.body = credentials;
}
else
{
msg.operation = CommandMessage.CLIENT_PING_OPERATION;
}
// Report the messaging version for this Channel.
// msg.headers[CommandMessage.MESSAGING_VERSION] = messagingVersion;
// Indicate if requesting the dynamic configuration from the server.
if (ServerConfig.needsConfig(this))
msg.headers[CommandMessage.NEEDS_CONFIG_HEADER] = true;
// Add the FlexClient id header.
setFlexClientIdOnMessage(msg);
var urlRequest:URLRequest = createURLRequest(msg);
_connectLoader = new ChannelRequestLoader();
_connectLoader.setErrorCallbacks(pingErrorHandler);
_connectLoader.completeCallback = pingCompleteHandler;
_connectLoader.load(urlRequest);
}
/**
* @private
* Disconnects from the remote destination.
*/
override protected function internalDisconnect(rejected:Boolean = false):void
{
// Attempt to notify the server of the disconnect.
if (!rejected && !shouldBeConnected)
{
var msg:CommandMessage = new CommandMessage();
msg.operation = CommandMessage.DISCONNECT_OPERATION;
internalSend(new MessageResponder(null, msg, null));
}
// Shutdown locally.
setConnected(false);
super.internalDisconnect(rejected);
disconnectSuccess(rejected); // make sure to notify everyone that we have disconnected.
}
/**
* @private
*/
override protected function internalSend(msgResp:MessageResponder):void
{
if (_pendingRequest != null)
{
_messageQueue.push(msgResp);
}
else
{
// Set the global FlexClient Id.
setFlexClientIdOnMessage(msgResp.message);
try
{
// If MPI is enabled initialize MPI object and stamp it with client send time
if (mpiEnabled)
{
var mpii:MessagePerformanceInfo = new MessagePerformanceInfo();
if (recordMessageTimes)
mpii.sendTime = new Date().getTime();
msgResp.message.headers[MessagePerformanceUtils.MPI_HEADER_IN] = mpii;
}
// Finally, if "Small Messages" are enabled, send this form instead of
// the normal message where possible.
/*
if (useSmallMessages && msgResp.message is ISmallMessage)
{
var smallMessage:IMessage = ISmallMessage(msgResp.message).getSmallMessage();
if (smallMessage != null)
msgResp.message = smallMessage;
}
*/
var urlLoader:ChannelRequestLoader;
var urlRequest:URLRequest = createURLRequest(msgResp.message);
if (msgResp is HTTPMessageResponder)
{
var httpMsgResp:HTTPMessageResponder =
HTTPMessageResponder(msgResp);
urlLoader = httpMsgResp.urlLoader;
urlLoader.completeCallback = httpMsgResp.completeHandler;
urlLoader.errorCallback = httpMsgResp.errorHandler;
urlLoader.ioErrorCallback = httpMsgResp.ioErrorHandler;
urlLoader.securityErrorCallback =
httpMsgResp.securityErrorHandler;
}
else
{
var responderWrapper:HTTPWrapperResponder =
new HTTPWrapperResponder(msgResp);
urlLoader = new ChannelRequestLoader();
urlLoader.completeCallback =
responderWrapper.completeHandler;
urlLoader.setErrorCallbacks(responderWrapper.errorHandler);
}
urlLoader.requestProcessedCallback = requestProcessedHandler;
// Do not consider poll requests as pending requests to allow
// clients to send messages while waiting for poll response.
if (!(msgResp.message is CommandMessage && CommandMessage(msgResp.message).operation == CommandMessage.POLL_OPERATION))
_pendingRequest = urlLoader;
urlLoader.load(urlRequest);
}
catch(e:MessageSerializationError)
{
msgResp.agent.fault(e.fault, msgResp.message);
}
}
}
//--------------------------------------------------------------------------
//
// Internal Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Utility function to handle a connection related ErrorMessage.
*
* @param msg The ErrorMessage returned during a connect attempt.
*/
mx_internal function connectionError(msg:ErrorMessage):void
{
var faultEvent:ChannelFaultEvent = ChannelFaultEvent.createEvent(this, false,
"Channel.Connect.Failed", "error", msg.faultDetail + " url: '" + endpoint +
(_appendToURL != null ? _appendToURL : "") + "'");
faultEvent.rootCause = msg;
connectFailed(faultEvent);
}
/**
* @private
* This method will serialize the specified message into a new instance of
* a URLRequest and return it.
*
* @param message Message to serialize
* @return URLRequest
*/
mx_internal function createURLRequest(message:IMessage):URLRequest
{
var result:URLRequest = new URLRequest();
if (_appendToURL)
result.url = endpoint + _appendToURL;
else
result.url = endpoint;
// Propagate our requestTimeout for those platforms
// supporting the idleTimeout property on URLRequest.
if ("idleTimeout" in result && requestTimeout > 0)
result["idleTimeout"] = requestTimeout * 1000;
monitorRpcMessage(message, result);
result.contentType = HTTPRequestMessage.CONTENT_TYPE_XML;
var packet:XML = _encoder.encode(message, null);
result.data = packet.toString();
result.method = "POST";
return result;
}
/**
* change the result url to redirect request to Network Monitor
*/
private function monitorRpcMessage(message:IMessage, result:URLRequest):void
{
if (NetworkMonitor.isMonitoring())
{
var redirectedUrl:String = NetworkMonitor.adjustNetConnectionURL(LoaderConfig.url, result.url);
if(redirectedUrl != null){
result.url = redirectedUrl;
}
}
}
//--------------------------------------------------------------------------
//
// Protected Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
protected function internalPingComplete(msg:AsyncMessage):void
{
if (msg != null)
{
ServerConfig.updateServerConfigData(msg.body as ConfigMap, endpoint);
// Set the server assigned FlexClient Id.
if (FlexClient.getInstance().id == null && msg.headers[AbstractMessage.FLEX_CLIENT_ID_HEADER] != null)
FlexClient.getInstance().id = msg.headers[AbstractMessage.FLEX_CLIENT_ID_HEADER];
}
// Process the features advertised by the server endpoint.
/*
if (msg.headers[CommandMessage.MESSAGING_VERSION] != null)
{
var serverVersion:Number = msg.headers[CommandMessage.MESSAGING_VERSION] as Number;
handleServerMessagingVersion(serverVersion);
}
*/
connectSuccess();
if (credentials != null && !(msg is ErrorMessage))
setAuthenticated(true);
}
//--------------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Special handler for AMFX 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.
*/
private function AppendToGatewayUrl(value:String):void
{
if (value != null)
appendToURL = value;
}
private function decodePacket(event:Event):AMFXResult
{
var raw:String = String(URLLoader(event.target).data);
var xmlData:XML = new XML(raw);
var _decoder:AMFXDecoder = new AMFXDecoder();
var packet:AMFXResult = _decoder.decode(xmlData);
return packet;
}
/**
* @private
* Attempts to replicate the packet-level header functionality that AMFChannel
* uses for response headers such as AppendToGatewayUrl for session id tracking.
*/
private function processHeaders(packet:AMFXResult):void
{
if (packet.headers != null)
{
try
{
for (var i:uint = 0; i < packet.headers.length; i++)
{
var header:AMFXHeader = packet.headers[i];
if (header != null && header.name == APPEND_TO_URL_HEADER)
{
AppendToGatewayUrl(String(header.content));
}
}
}
catch(e:Error)
{
}
}
}
/**
* @private
* This method indicates that we successfully connected to the endpoint.
* Called as a result of the ping operation performed in the
* internalConnect() method.
*/
private function pingCompleteHandler(event:Event):void
{
var packet:AMFXResult = decodePacket(event);
processHeaders(packet);
var msg:AsyncMessage = packet.result as AsyncMessage;
if (msg != null && (msg is ErrorMessage) &&
ErrorMessage(msg).faultCode == "Client.Authentication")
{
internalPingComplete(msg);
var faultEvent:ChannelFaultEvent = ChannelFaultEvent.createEvent(this, false, "Channel.Authentication.Error", "warn");
faultEvent.rootCause = ErrorMessage(msg);
dispatchEvent(faultEvent);
}
else
{
internalPingComplete(msg);
}
}
/**
* @private
* This method dispatches the appropriate error to any message agents, and
* is called as a result of the ping operation performed in the
* internalConnect() method.
*/
private function pingErrorHandler(event:Event):void
{
_log.debug("'{0}' fault handler called. {1}", id, event.toString());
var faultEvent:ChannelFaultEvent = ChannelFaultEvent.createEvent(this, false,
"Channel.Ping.Failed",
"error",
" url: '" + endpoint +
(_appendToURL == null ? "" : _appendToURL + "'") + "'");
faultEvent.rootCause = event;
connectFailed(faultEvent);
}
/**
* @private
* Chains sends for pending messages.
*/
private function requestProcessedHandler
(loader:ChannelRequestLoader, event:Event):void
{
if (_pendingRequest == loader)
{
_pendingRequest = null;
}
// TODO: we should do these in a batch for more efficiency and
// better session maintenance
while ((_messageQueue.length > 0) && (_pendingRequest == null))
{
internalSend(MessageResponder(_messageQueue.shift()));
}
}
//--------------------------------------------------------------------------
//
// Static Constants
//
//--------------------------------------------------------------------------
/**
* @private
*/
private static const APPEND_TO_URL_HEADER:String = "AppendToGatewayUrl";
}
}
//------------------------------------------------------------------------------
//
// Private Classes
//
//------------------------------------------------------------------------------
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import mx.core.mx_internal;
import mx.messaging.MessageAgent;
import mx.messaging.MessageResponder;
import mx.messaging.channels.HTTPChannel;
import mx.messaging.channels.amfx.AMFXDecoder;
import mx.messaging.channels.amfx.AMFXHeader;
import mx.messaging.channels.amfx.AMFXResult;
import mx.messaging.messages.AcknowledgeMessage;
import mx.messaging.messages.AsyncMessage;
import mx.messaging.messages.CommandMessage;
import mx.messaging.messages.ErrorMessage;
import mx.messaging.messages.HTTPRequestMessage;
import mx.messaging.messages.IMessage;
import mx.resources.IResourceManager;
import mx.resources.ResourceManager;
import mx.utils.StringUtil;
use namespace mx_internal;
[ResourceBundle("messaging")]
/**
* @private
* This responder wraps another MessageResponder with HTTP functionality.
*/
class HTTPWrapperResponder
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* @private
* Constructs a HTTPWrappedResponder.
*
* @param wrappedResponder The responder to wrap.
*/
public function HTTPWrapperResponder(wrappedResponder:MessageResponder)
{
super();
_wrappedResponder = wrappedResponder;
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var _wrappedResponder:MessageResponder;
/**
* @private
*/
private var resourceManager:IResourceManager =
ResourceManager.getInstance();
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Handles a result returned from the remote destination.
*
* @param event The completion event from the associated URLLoader.
*/
public function completeHandler(event:Event):void
{
var raw:String = String(URLLoader(event.target).data);
var xmlData:XML = new XML(raw);
var _decoder:AMFXDecoder = new AMFXDecoder();
var packet:AMFXResult = _decoder.decode(xmlData);
if (packet.result is ErrorMessage)
{
_wrappedResponder.status(ErrorMessage(packet.result));
}
else if (packet.result is AsyncMessage)
{
_wrappedResponder.result(AsyncMessage(packet.result));
}
}
/**
* @private
* Handles an error for an outbound request.
*
* @param event The error event from the associated URLLoader.
*/
public function errorHandler(event:Event):void
{
var msg:ErrorMessage = new ErrorMessage();
msg.correlationId = _wrappedResponder.message.messageId;
msg.faultCode = "Server.Error.Request";
msg.faultString = resourceManager.getString(
"messaging", "httpRequestError");
var details:String = event.toString();
if (_wrappedResponder.message is HTTPRequestMessage)
{
details += ". URL: ";
details += HTTPRequestMessage(_wrappedResponder.message).url;
}
msg.faultDetail = resourceManager.getString(
"messaging", "httpRequestError.details", [ details ]);
msg.rootCause = event;
_wrappedResponder.status(msg);
}
}
[ResourceBundle("messaging")]
/**
* @private
* This is an adapter for url loader that is used by the HTTPChannel.
*/
class HTTPMessageResponder extends MessageResponder
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* @private
* Constructs an HTTPMessageResponder.
*
* @param agent The associated MessageAgent.
*
* @param msg The message to send.
*
* @param channel The Channel to send the message over.
*/
public function HTTPMessageResponder
(agent:MessageAgent, msg:IMessage, channel:HTTPChannel)
{
super(agent, msg, channel);
decoder = new AMFXDecoder();
urlLoader = new ChannelRequestLoader();
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var decoder:AMFXDecoder;
/**
* @private
*/
private var resourceManager:IResourceManager =
ResourceManager.getInstance();
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
/**
* The loader associated with this responder.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public var urlLoader:ChannelRequestLoader;
//--------------------------------------------------------------------------
//
// Overridden Protected Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function resultHandler(response:IMessage):void
{
var errorMsg:ErrorMessage;
if (response is AsyncMessage)
{
if (response is ErrorMessage)
{
agent.fault(ErrorMessage(response), message);
}
else if (AsyncMessage(response).correlationId == message.messageId)
{
agent.acknowledge(AcknowledgeMessage(response), message);
}
else
{
errorMsg = new ErrorMessage();
errorMsg.faultCode = "Server.Acknowledge.Failed";
errorMsg.faultString = resourceManager.getString(
"messaging", "ackFailed");
errorMsg.faultDetail = resourceManager.getString(
"messaging", "ackFailed.details",
[ message.messageId, AsyncMessage(response).correlationId ]);
agent.fault(errorMsg, message);
}
}
else if (response != null)
{
errorMsg = new ErrorMessage();
errorMsg.faultCode = "Server.Acknowledge.Failed";
errorMsg.faultString = resourceManager.getString(
"messaging", "noAckMessage");
errorMsg.faultDetail = resourceManager.getString(
"messaging", "noAckMessage.details",
[ mx.utils.ObjectUtil.toString(response) ]);
agent.fault(errorMsg, message);
}
}
/**
* @private
* Handle a request timeout by closing our associated URLLoader and
* faulting the message to the agent.
*/
override protected function requestTimedOut():void
{
urlLoader.close();
status(null);
// send the ack
var ack:AcknowledgeMessage = new AcknowledgeMessage();
ack.correlationId = message.messageId;
ack.headers[AcknowledgeMessage.ERROR_HINT_HEADER] = true; // hint there was an error
agent.acknowledge(ack, message);
// send the fault
agent.fault(createRequestTimeoutErrorMessage(), message);
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
final public function completeHandler(event:Event):void
{
result(null);
var raw:String = String(URLLoader(event.target).data);
var xmlData:XML = new XML(raw);
var packet:AMFXResult = decoder.decode(xmlData);
if (packet.result is IMessage)
{
resultHandler(IMessage(packet.result));
}
}
/**
* @private
*/
public function errorHandler(event:Event):void
{
status(null);
// send the ack
var ack:AcknowledgeMessage = new AcknowledgeMessage();
ack.correlationId = message.messageId;
ack.headers[AcknowledgeMessage.ERROR_HINT_HEADER] = true; // hint there was an error
agent.acknowledge(ack, message);
// send fault
var msg:ErrorMessage = new ErrorMessage();
msg.correlationId = message.messageId;
msg.faultCode = "Server.Error.Request";
msg.faultString = resourceManager.getString(
"messaging", "httpRequestError");
var details:String = event.toString();
if (message is HTTPRequestMessage)
{
details += ". URL: ";
details += HTTPRequestMessage(message).url;
}
msg.faultDetail = resourceManager.getString(
"messaging", "httpRequestError.details", [ details ]);
msg.rootCause = event;
agent.fault(msg, message);
}
/**
* @private
*/
public function ioErrorHandler(event:Event):void
{
status(null);
// send the ack
var ack:AcknowledgeMessage = new AcknowledgeMessage();
ack.correlationId = message.messageId;
ack.headers[AcknowledgeMessage.ERROR_HINT_HEADER] = true; // hint there was an error
agent.acknowledge(ack, message);
// send fault
var msg:ErrorMessage = new ErrorMessage();
msg.correlationId = message.messageId;
msg.faultCode = "Server.Error.Request";
msg.faultString = resourceManager.getString(
"messaging", "httpRequestError");
var details:String = event.toString();
if (message is HTTPRequestMessage)
{
details += ". URL: ";
details += HTTPRequestMessage(message).url;
}
msg.faultDetail = resourceManager.getString(
"messaging", "httpRequestError.details", [ details ]);
msg.rootCause = event;
(channel as HTTPChannel).connectionError(msg);
// already disconnected, now let the agent know the the message faulted
// this is similar to the disconnect() and fault() in the NetConnectionChannel statusHandler
agent.fault(msg, message);
}
/**
* @private
*/
public function securityErrorHandler(event:Event):void
{
status(null);
// send the ack
var ack:AcknowledgeMessage = new AcknowledgeMessage();
ack.correlationId = message.messageId;
ack.headers[AcknowledgeMessage.ERROR_HINT_HEADER] = true; // hint there was an error
agent.acknowledge(ack, message);
// send fault
var msg:ErrorMessage = new ErrorMessage();
msg.correlationId = message.messageId;
msg.faultCode = "Channel.Security.Error";
msg.faultString = resourceManager.getString(
"messaging", "securityError");
msg.faultDetail = resourceManager.getString(
"messaging", "securityError.details", [ message.destination ]);
msg.rootCause = event;
agent.fault(msg, message);
}
}
/**
* @private
* Wraps an URLLoader and manages dispatching its events to the proper handlers.
*/
class ChannelRequestLoader
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* @private
* Constructs a ChannelRequestLoader.
*/
public function ChannelRequestLoader()
{
super();
_urlLoader = new URLLoader();
_urlLoader.addEventListener(ErrorEvent.ERROR, errorHandler);
_urlLoader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
_urlLoader.addEventListener
(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
_urlLoader.addEventListener(Event.COMPLETE, completeHandler);
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
* The wrapped URLLoader.
*/
private var _urlLoader:URLLoader;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
/**
* @private
*/
public var errorCallback:Function;
/**
* @private
*/
public var ioErrorCallback:Function;
/**
* @private
*/
public var securityErrorCallback:Function;
/**
* @private
*/
public var completeCallback:Function;
/**
* @private
*/
public var requestProcessedCallback:Function;
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
public function load(request:URLRequest):void
{
_urlLoader.load(request);
}
/**
* @private
*/
public function close():void
{
_urlLoader.removeEventListener(ErrorEvent.ERROR, errorHandler);
_urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
_urlLoader.removeEventListener
(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
_urlLoader.removeEventListener(Event.COMPLETE, completeHandler);
_urlLoader.close();
}
/**
* @private
*/
public function setErrorCallbacks(callback:Function):void
{
errorCallback = callback;
ioErrorCallback = callback;
securityErrorCallback = callback;
}
//--------------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function callRequestProcessedCallback(event:Event):void
{
if (requestProcessedCallback != null)
requestProcessedCallback(this, event);
}
/**
* @private
*/
private function callEventCallback(callback:Function, event:Event):void
{
if (callback != null)
callback(event);
}
/**
* @private
*/
private function errorHandler(event:Event):void
{
callRequestProcessedCallback(event);
callEventCallback(requestProcessedCallback, event);
}
/**
* @private
*/
private function ioErrorHandler(event:Event):void
{
callRequestProcessedCallback(event);
callEventCallback(ioErrorCallback, event);
}
/**
* @private
*/
private function securityErrorHandler(event:Event):void
{
callRequestProcessedCallback(event);
callEventCallback(securityErrorCallback, event);
}
/**
* @private
*/
private function completeHandler(event:Event):void
{
callRequestProcessedCallback(event);
callEventCallback(completeCallback, event);
}
}