package de.dtele.net.protocol {
import com.adobe.serialization.json.JSON;
import com.adobe.serialization.json.JSONParseError;
import com.hurlant.util.Base64;
import de.dtele.data.MimeTypes;
import de.dtele.net.MediaRequest;
import de.dtele.net.MediaRequestError;
import de.dtele.net.MediaResponse;
import de.dtele.net.events.ProtocolHandlerErrorEvent;
import de.dtele.net.events.ProtocolHandlerEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.net.FileReference;
import mx.managers.BrowserManager;
import mx.managers.IBrowserManager;
import mx.utils.URLUtil;
import net.gimite.WebSocket;
import net.gimite.WebSocketEvent;
/**
* Dispatched when the connection to a source has been established
*/
[Event("connected", type="de.dtele.net.events.ProtocolHandlerEvent")]
/**
* Dispatched when the connection to a source has been closed
*/
[Event("disconnected", type="de.dtele.net.events.ProtocolHandlerEvent")]
/**
* Dispatched when a message from the source was received
*/
[Event("response", type="de.dtele.net.events.ProtocolHandlerEvent")]
/**
* Dispatched when an error has occurred
*/
[Event("error", type="de.dtele.net.events.ProtocolHandlerErrorEvent")]
/**
* Implements the WebSocket protocol for MediaRequest
*
* <p>Uses the <em>HTML5 Web Socket implementation powered by Flash</em>
* for implementing WebSocket communication.</p>
*
* @see https://github.com/guille/web-socket-js
*
* @author Mathias Brodala
*/
public class WebSocketProtocolHandler extends EventDispatcher implements IProtocolHandler {
/**
* The internal WebSocket object
*/
protected var socket:IWebSocket;
/**
* The browser manager, required for the document base URL
*/
protected var browserManager:IBrowserManager = BrowserManager.getInstance();
/**
* Connects this handler to a source
*
* @param url The url of a source
*/
public function connect(url:String):void {
var handler:WebSocketProtocolHandler = this;
try {
this.socket = new WebSocket(
new Date().time,
url,
null,
(URLUtil.getProtocol(this.browserManager.base) + "://" + URLUtil.getServerNameWithPort(this.browserManager.base)).toLowerCase(),
null,
0,
"",
null,
new DummyWebSocketLogger()
);
this.socket.addEventListener(WebSocketEvent.OPEN, this.onOpen);
this.socket.addEventListener(WebSocketEvent.MESSAGE, handler.onMessage);
this.socket.addEventListener(WebSocketEvent.ERROR, this.onError);
this.socket.addEventListener(WebSocketEvent.CLOSE, this.onClose);
} catch (e:Error) {
this.dispatchEvent(new ProtocolHandlerErrorEvent(
ProtocolHandlerErrorEvent.ERROR,
new MediaRequestError(MediaRequestError.CONNECTION_FAILED, e.message)
));
return;
}
}
/**
* Disconnects this handler from a source
*/
public function disconnect():void {
if (this.socket) {
this.socket.close();
this.dispatchEvent(new ProtocolHandlerEvent(ProtocolHandlerEvent.DISCONNECTED));
}
}
/**
* Performs a request to a source
*
* @param request The request to submit
*/
public function send(request:MediaRequest):void {
var handler:WebSocketProtocolHandler = this;
var requestCancelled:Boolean = false;
if (!this.socket) {
this.dispatchEvent(new ProtocolHandlerErrorEvent(
ProtocolHandlerErrorEvent.ERROR,
new MediaRequestError(MediaRequestError.CONNECTION_FAILED),
request
));
return;
}
function onResponse(e:WebSocketEvent):void {
handler.onMessage(e, request);
handler.socket.removeEventListener(WebSocketEvent.MESSAGE, onResponse);
e.stopImmediatePropagation();
}
this.socket.addEventListener(WebSocketEvent.MESSAGE, onResponse, false, 1);
var mediaRequest:Object = {
version: request.version,
type: request.type
};
if (request.credentials) {
mediaRequest.credentials = request.credentials;
}
request.addEventListener(Event.CANCEL, function(e:Event):void {
request.files.forEach(function(file:FileReference, ...a):void {
file.cancel();
});
requestCancelled = true;
});
if (request.type == MediaRequest.ADD) {
mediaRequest.resources = request.resources;
if (request.files.length > 0) {
mediaRequest.files = [];
function isLoaded(file:FileReference, ...a):Boolean {
return !!file.data;
}
request.files.forEach(function(file:FileReference, ...a):void {
file.addEventListener(Event.COMPLETE, function(e:Event):void {
var file:FileReference = e.target as FileReference;
var type:String = file.type;
var dotPosition:int = type.indexOf(".");
if (dotPosition > -1) {
type = MimeTypes.getMimeType(type.substr(dotPosition + 1));
}
mediaRequest.files.push({
name: file.name,
type: type,
size: file.size,
data: Base64.encodeByteArray(file.data)
});
if (request.files.every(isLoaded) && !requestCancelled) {
handler.socket.send(JSON.encode({mediaRequest: mediaRequest}));
}
});
file.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void {
handler.dispatchEvent(new ProtocolHandlerErrorEvent(
ProtocolHandlerErrorEvent.ERROR,
new MediaRequestError(MediaRequestError.UNKNOWN, e.text),
request
));
});
file.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent):void {
request.bytesLoaded = e.bytesLoaded;
request.bytesTotal = e.bytesTotal;
});
file.load();
});
} else if (!requestCancelled) {
this.socket.send(JSON.encode({mediaRequest: mediaRequest}));
}
} else if (!requestCancelled) {
this.socket.send(JSON.encode({mediaRequest: mediaRequest}));
}
}
/**
* Notifies about opened WebSocket connections
*
* @param e The WebSocket event
*/
protected function onOpen(e:WebSocketEvent):void {
this.dispatchEvent(new ProtocolHandlerEvent(ProtocolHandlerEvent.CONNECTED));
this.socket.removeEventListener(WebSocketEvent.OPEN, onOpen);
}
/**
* Notifies about closed WebSocket connections
*
* @param e The WebSocket event
*/
protected function onClose(e:WebSocketEvent):void {
this.dispatchEvent(new ProtocolHandlerEvent(ProtocolHandlerEvent.DISCONNECTED));
}
/**
* Handles messages from the WebSocket
*
* @param e The Websocket event
* @param request The optional MediaRequest preceding this message
*/
protected function onMessage(e:WebSocketEvent, request:MediaRequest = null):void {
var data:Object;
try {
trace("[WebSocketProtocolHandler] Got response: " + e.message);
data = JSON.decode(e.message);
} catch (e:JSONParseError) {
this.dispatchEvent(new ProtocolHandlerErrorEvent(
ProtocolHandlerErrorEvent.ERROR,
new MediaRequestError(MediaRequestError.MALFORMED_RESPONSE),
request
));
return;
}
this.dispatchEvent(new ProtocolHandlerEvent(
ProtocolHandlerEvent.RESPONSE,
request,
new MediaResponse(data)
));
}
/**
* Notifies about connection errors
*
* @param e The WebSocket event
*/
protected function onError(e:WebSocketEvent):void {
this.dispatchEvent(new ProtocolHandlerErrorEvent(
ProtocolHandlerErrorEvent.ERROR,
new MediaRequestError(MediaRequestError.CONNECTION_FAILED)
));
}
}
}