//////////////////////////////////////////////////////////////////////////////// // // 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); } }