├── .gitignore ├── Example.mxml ├── README.md ├── com ├── clevr │ └── socket │ │ ├── DataDecoder.as │ │ ├── IoDataEvent.as │ │ ├── SocketIo.as │ │ └── WebSocket.as ├── gimite │ └── WebSocketStateEvent.as └── gsolo │ └── encryption │ └── MD5.as └── example.xml /.gitignore: -------------------------------------------------------------------------------- 1 | com/hurlant 2 | *.swf 3 | *.tmproj -------------------------------------------------------------------------------- /Example.mxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | /g, '>'); 34 | } 35 | 36 | private function addMessage(obj:Object):void { 37 | if ('announcement' in obj) { 38 | chat.htmlText += '

' + esc(obj.announcement) + '

'; 39 | } else if ('message' in obj) { 40 | chat.htmlText += '

' + esc(obj.message[0]) + ': ' + esc(obj.message[1]) + '

'; 41 | } 42 | } 43 | 44 | private function onCreationComplete(event:FlexEvent):void { 45 | socket = new SocketIo("127.0.0.1", {port: 8080}); 46 | socket.addEventListener(IoDataEvent.DATA, function(e:IoDataEvent):void { 47 | if ('buffer' in e.messageData){ 48 | message.text = ''; 49 | 50 | for (var i:String in e.messageData.buffer) { 51 | addMessage(e.messageData.buffer[i]); 52 | } 53 | } else addMessage(e.messageData); 54 | }); 55 | socket.connect(); 56 | } 57 | 58 | private function sendMessage():void { 59 | socket.send(message.text); 60 | addMessage({ message: ['you', message.text] }); 61 | message.text = ''; 62 | } 63 | 64 | 65 | ]]> 66 |
67 | 68 | 69 | 70 | 71 | 72 |
73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | socket.io Actionscript client 2 | ============================ 3 | #### By Matt Kane 4 | 5 | This is a client for [socket.io](https://github.com/LearnBoost/Socket.IO/) implemented in Actionscript 3. It allows you to connect to the socket server from your Adobe AIR/Flex application. I've tested it in AIR but it should probably work in regular Flex, subject to crossdomain policies. 6 | 7 | ### Usage 8 | I've tried to follow the same API as the socket.io javascript client. This is a simple example of connecting to a socket. 9 | 10 | socket = new SocketIo("127.0.0.1", {port: 8080}); 11 | socket.addEventListener(IoDataEvent.DATA, function(e:IoDataEvent):void { 12 | trace(e.messageData); 13 | }); 14 | socket.connect(); 15 | socket.send("Hi!"); 16 | 17 | For a full example, look at Example.mxml, which is a Flex/AIR port of the socket.io chat demo. You'll need to be running the chat demo server for this to work. See the main socket.io project for details of this. 18 | 19 | Compile and run it like so: 20 | 21 | amxmlc Example.mxml 22 | adl example.xml 23 | 24 | ### Requirements 25 | Obviously, you'll need the Flex and/or AIR SDK. You also need a couple of libraries. 26 | 27 | + [as3corelib](https://github.com/mikechambers/as3corelib) 28 | + [as3crypto](http://code.google.com/p/as3crypto/) 29 | 30 | Install these either as compiled .swcs in your flex library folder, or as source in this folder. 31 | 32 | ### Bugs and contributions 33 | If you have a patch, fork the Github repo and send me a pull request. Submit bug reports on GitHub, please. 34 | 35 | 36 | ### Credits 37 | 38 | By [Matt Kane](https://github.com/ascorbic) 39 | 40 | ### License 41 | 42 | (The MIT License) 43 | 44 | Copyright (c) 2010 [CLEVR Ltd](http://www.clevr.ltd.uk/) 45 | 46 | Permission is hereby granted, free of charge, to any person obtaining 47 | a copy of this software and associated documentation files (the 48 | 'Software'), to deal in the Software without restriction, including 49 | without limitation the rights to use, copy, modify, merge, publish, 50 | distribute, sublicense, and/or sell copies of the Software, and to 51 | permit persons to whom the Software is furnished to do so, subject to 52 | the following conditions: 53 | 54 | The above copyright notice and this permission notice shall be 55 | included in all copies or substantial portions of the Software. 56 | 57 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 58 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 59 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 60 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 61 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 62 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 63 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 64 | 65 | Includes code from [web-socket-js](https://github.com/gimite/web-socket-js/) Copyright (c) Hiroshi Ichikawa 66 | Also includes code derived from [socket.io](https://github.com/LearnBoost/Socket.IO/) Copyright (c) 2010 LearnBoost -------------------------------------------------------------------------------- /com/clevr/socket/DataDecoder.as: -------------------------------------------------------------------------------- 1 | /** 2 | * Socket.IO Actionscript client 3 | * 4 | * @author Matt Kane 5 | * @license The MIT license. 6 | * @copyright Copyright (c) 2010 CLEVR Ltd 7 | */ 8 | 9 | 10 | package com.clevr.socket { 11 | import flash.events.EventDispatcher; 12 | import flash.events.AsyncErrorEvent; 13 | import flash.events.Event; 14 | 15 | public class DataDecoder extends EventDispatcher { 16 | 17 | private var buffer:String; 18 | private var i:int; 19 | private var lengthString:String = ''; 20 | private var length:Number; 21 | private var data:String; 22 | private var type:String; 23 | 24 | public function DataDecoder() { 25 | reset(); 26 | buffer = ''; 27 | } 28 | 29 | public function add(data:String):void { 30 | buffer += data; 31 | parse(); 32 | } 33 | 34 | public function parse():void { 35 | for (var l:int = buffer.length; i < l; i++){ 36 | var chr:String = buffer.charAt(i); 37 | 38 | if (type === null){ 39 | if (chr == ':') return error('Data type not specified'); 40 | type = '' + chr; 41 | continue; 42 | } 43 | if (isNaN(length) && lengthString === null && chr == ':'){ 44 | lengthString = ''; 45 | continue; 46 | } 47 | if (data === null){ 48 | 49 | if (chr != ':'){ 50 | 51 | lengthString += chr; 52 | } else { 53 | 54 | if (lengthString.length === 0) 55 | return error('Data length not specified'); 56 | length = parseInt(lengthString); 57 | 58 | lengthString = null; 59 | data = ''; 60 | } 61 | continue; 62 | } 63 | if (data.length === length){ 64 | 65 | if (chr == ','){ 66 | dispatchEvent(new IoDataEvent(IoDataEvent.MESSAGE, data, type)); 67 | buffer = buffer.substr(i + 1); 68 | reset(); 69 | return parse(); 70 | } else { 71 | return error('Termination character "," expected'); 72 | } 73 | } else { 74 | 75 | data += chr; 76 | } 77 | } 78 | 79 | } 80 | 81 | 82 | public function reset():void { 83 | type = data = lengthString = null; 84 | length = Number.NaN; 85 | i = 0; 86 | } 87 | 88 | private function error(reason:String):void { 89 | reset(); 90 | dispatchEvent(new AsyncErrorEvent(AsyncErrorEvent.ASYNC_ERROR, false, false, reason)); 91 | } 92 | 93 | public static function encode(messages:Array):String { 94 | 95 | if(typeof messages[0] != 'array') { 96 | messages = [messages]; 97 | } 98 | 99 | var ret:String = ''; 100 | var str:String; 101 | for (var i:int = 0; i < messages.length; i++){ 102 | str = messages[i][1]; 103 | if (str === null) str = ''; 104 | ret += messages[i][0] + ':' + str.length + ':' + str + ','; 105 | } 106 | return ret; 107 | } 108 | 109 | public static function encodeMessage(msg:String, anns:Object):String { 110 | var data:String = ''; 111 | var v:String; 112 | if(!anns) { 113 | anns = {}; 114 | } 115 | for (var k:Object in anns){ 116 | v = anns[k]; 117 | data += k + (v !== null ? ':' + v : '') + "\n"; 118 | } 119 | data += ':' + (msg === null ? '' : msg); 120 | return data; 121 | } 122 | 123 | public static function decodeMessage(msg:String):Array { 124 | /*trace('decoding message: ' + msg);*/ 125 | var anns:Object = {}; 126 | var data:String; 127 | 128 | var chr:String; 129 | var key:String; 130 | var value:String; 131 | var l:int = msg.length; 132 | 133 | for (var i:int = 0; i < l; i++){ 134 | chr = msg.charAt(i); 135 | if (i === 0 && chr === ':'){ 136 | data = msg.substr(1); 137 | break; 138 | } 139 | if (key == null && value == null && chr == ':'){ 140 | data = msg.substr(i + 1); 141 | break; 142 | } 143 | if (chr === "\n"){ 144 | anns[key] = value; 145 | key = value = null; 146 | continue; 147 | } 148 | if (key == null){ 149 | key = chr; 150 | continue; 151 | } 152 | if (value == null && chr == ':'){ 153 | value = ''; 154 | continue; 155 | } 156 | if (value !== null) 157 | value += chr; 158 | else 159 | key += chr; 160 | } 161 | return [data, anns]; 162 | 163 | } 164 | 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /com/clevr/socket/IoDataEvent.as: -------------------------------------------------------------------------------- 1 | /** 2 | * Socket.IO Actionscript client 3 | * 4 | * @author Matt Kane 5 | * @license The MIT license. 6 | * @copyright Copyright (c) 2010 CLEVR Ltd 7 | */ 8 | 9 | package com.clevr.socket { 10 | import flash.events.Event; 11 | 12 | public class IoDataEvent extends Event { 13 | 14 | public static const DATA : String = "IoDataEventData"; 15 | public static const MESSAGE : String = "IoDataEventMessage"; 16 | 17 | public var messageData:Object; 18 | public var messageType:String; 19 | 20 | public function IoDataEvent( type:String, messageData:Object, messageType:String = null, bubbles:Boolean=true, cancelable:Boolean=false){ 21 | super(type, bubbles, cancelable); 22 | this.messageData = messageData; 23 | if (type == IoDataEvent.MESSAGE) { 24 | this.messageType = messageType; 25 | } 26 | } 27 | 28 | override public function clone() : Event { 29 | return new IoDataEvent(type, messageData, messageType, bubbles, cancelable); 30 | } 31 | 32 | 33 | } 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /com/clevr/socket/SocketIo.as: -------------------------------------------------------------------------------- 1 | /** 2 | * Socket.IO Actionscript client 3 | * 4 | * @author Matt Kane 5 | * @license The MIT license. 6 | * @copyright Copyright (c) 2010 CLEVR Ltd 7 | */ 8 | 9 | package com.clevr.socket 10 | { 11 | import com.adobe.serialization.json.JSON; 12 | import flash.events.EventDispatcher; 13 | import flash.events.Event; 14 | import flash.events.AsyncErrorEvent; 15 | 16 | public class SocketIo extends EventDispatcher { 17 | public var connected:Boolean = false; 18 | public var connecting:Boolean = false; 19 | public var host:String; 20 | public var options:Object; 21 | public var socket:WebSocket; 22 | public var sessionid:String; 23 | 24 | protected var decoder:DataDecoder; 25 | private var _queue:Array; 26 | 27 | public function SocketIo(host:String, options:Object) { 28 | super(); 29 | this.options = { 30 | secure: false, 31 | port: 80, 32 | resource: 'socket.io' 33 | } 34 | this.host = host; 35 | _queue = new Array(); 36 | for(var p:String in options){ 37 | this.options[p] = options[p]; 38 | } 39 | decoder = new DataDecoder(); 40 | var self:SocketIo = this; 41 | decoder.addEventListener(IoDataEvent.MESSAGE, function(event:IoDataEvent):void { 42 | self.onMessage(event.messageType, event.messageData); 43 | }); 44 | 45 | decoder.addEventListener(AsyncErrorEvent.ASYNC_ERROR, function(event:AsyncErrorEvent):void { 46 | onError(event.text); 47 | }) 48 | } 49 | 50 | public function connect():void { 51 | if(connecting) { 52 | disconnect(); 53 | } 54 | connecting = true; 55 | socket = new WebSocket(url, ""); 56 | 57 | var self:SocketIo = this; 58 | socket.addEventListener("message", function(e:Event):void { 59 | var data:Array = self.socket.readSocketData(); 60 | for (var i:int = 0; i < data.length; i++) { 61 | self.decoder.add(data[i]); 62 | } 63 | }); 64 | 65 | socket.addEventListener("close", function(e:Event):void { 66 | onDisconnect(); 67 | }); 68 | 69 | socket.addEventListener("error", function(e:Event):void { 70 | onError("Socket error"); 71 | }); 72 | socket.addEventListener("open", function(e:Event):void { 73 | onConnect(); 74 | }); 75 | } 76 | 77 | public function send(message:Object, atts:Object = null):void { 78 | if(!atts) { 79 | atts = {}; 80 | } 81 | if(typeof message == 'object') { 82 | message = JSON.encode(message); 83 | atts['j'] = null; 84 | } 85 | write('1', DataDecoder.encodeMessage(message.toString(), atts)); 86 | } 87 | 88 | public function json(message:Object, atts:Object = null):void { 89 | if(!atts) { 90 | atts = {}; 91 | } 92 | atts['j'] = null; 93 | write('1', DataDecoder.encodeMessage(JSON.encode(message), atts)); 94 | } 95 | 96 | 97 | public function write(type:Object, message:Object = null):void { 98 | if(!connected || !socket) { 99 | enQueue(type, message); 100 | } else { 101 | socket.send(DataDecoder.encode(typeof type == 'array' ? type as Array: [type, message])); 102 | } 103 | } 104 | 105 | public function disconnect():void { 106 | socket.close(); 107 | onDisconnect(); 108 | } 109 | 110 | protected function enQueue(type:Object, message:Object):void { 111 | if(!_queue) { 112 | _queue = new Array(); 113 | } 114 | _queue.push([type, message]); 115 | } 116 | 117 | protected function runQueue():void { 118 | if(_queue.length && connected && socket) { 119 | write(_queue); 120 | _queue = new Array(); 121 | } 122 | 123 | } 124 | 125 | 126 | protected function get url():String{ 127 | return (options.secure ? 'wss' : 'ws') 128 | + '://' + host 129 | + ':' + options.port 130 | + '/' + options.resource 131 | + '/flashsocket' 132 | + (sessionid ? ('/' + sessionid) : ''); 133 | } 134 | 135 | protected function onMessage(type:String, data:Object):void { 136 | var message:String = data.toString(); 137 | switch (type){ 138 | case '0': 139 | disconnect(); 140 | break; 141 | 142 | case '1': 143 | var msg:Array = DataDecoder.decodeMessage(message); 144 | if ('j' in msg[1]){ 145 | msg[0] = JSON.decode(msg[0]); 146 | } 147 | dispatchEvent(new IoDataEvent(IoDataEvent.DATA, msg[0])); 148 | break; 149 | 150 | case '2': 151 | onHeartbeat(message); 152 | break; 153 | 154 | case '3': 155 | sessionid = message; 156 | onConnect(); 157 | break; 158 | } 159 | } 160 | 161 | protected function onHeartbeat(heartbeat:Object):void { 162 | write('2', heartbeat); // echo 163 | } 164 | 165 | protected function onError(message:String):void { 166 | trace("Error: " + message); 167 | connected = false; 168 | connecting = false; 169 | sessionid = null; 170 | _queue = []; 171 | dispatchEvent(new Event("error")); 172 | } 173 | 174 | protected function onConnect():void { 175 | connected = true; 176 | connecting = false; 177 | runQueue(); 178 | dispatchEvent(new Event(Event.CONNECT)); 179 | } 180 | 181 | protected function onDisconnect():void { 182 | var wasConnected:Boolean = connected; 183 | connected = false; 184 | connecting = false; 185 | sessionid = null; 186 | 187 | _queue = []; 188 | if (wasConnected) { 189 | dispatchEvent(new Event(Event.COMPLETE)); 190 | } 191 | } 192 | } 193 | } -------------------------------------------------------------------------------- /com/clevr/socket/WebSocket.as: -------------------------------------------------------------------------------- 1 | /** 2 | * Based on Web Socket implementation in web-socket-js by Hiroshi Ichikawa 3 | * from https://github.com/gimite/web-socket-js 4 | * Modified by Matt Kane to run outside the context of flash-js bridge swf 5 | **/ 6 | 7 | // Copyright: Hiroshi Ichikawa 8 | // License: New BSD License 9 | // Reference: http://dev.w3.org/html5/websockets/ 10 | // Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 11 | 12 | package com.clevr.socket { 13 | 14 | import flash.display.*; 15 | import flash.events.*; 16 | import flash.external.*; 17 | import flash.net.*; 18 | import flash.system.*; 19 | import flash.utils.*; 20 | import mx.core.*; 21 | import mx.controls.*; 22 | import mx.events.*; 23 | import mx.utils.*; 24 | import com.adobe.net.proxies.RFC2817Socket; 25 | import com.hurlant.crypto.tls.TLSSocket; 26 | import com.hurlant.crypto.tls.TLSConfig; 27 | import com.hurlant.crypto.tls.TLSEngine; 28 | import com.hurlant.crypto.tls.TLSSecurityParameters; 29 | import com.gsolo.encryption.MD5; 30 | import com.gimite.WebSocketStateEvent; 31 | 32 | [Event(name="message", type="flash.events.Event")] 33 | [Event(name="open", type="flash.events.Event")] 34 | [Event(name="close", type="flash.events.Event")] 35 | [Event(name="error", type="flash.events.Event")] 36 | [Event(name="stateChange", type="WebSocketStateEvent")] 37 | public class WebSocket extends EventDispatcher { 38 | 39 | private static var CONNECTING:int = 0; 40 | private static var OPEN:int = 1; 41 | private static var CLOSING:int = 2; 42 | private static var CLOSED:int = 3; 43 | 44 | private var rawSocket:Socket; 45 | private var tlsSocket:TLSSocket; 46 | private var tlsConfig:TLSConfig; 47 | private var socket:Socket; 48 | private var url:String; 49 | private var scheme:String; 50 | private var host:String; 51 | private var port:uint; 52 | private var path:String; 53 | private var origin:String; 54 | private var protocol:String; 55 | private var buffer:ByteArray = new ByteArray(); 56 | private var dataQueue:Array; 57 | private var headerState:int = 0; 58 | private var readyState:int = CONNECTING; 59 | private var bufferedAmount:int = 0; 60 | private var headers:String; 61 | private var noiseChars:Array; 62 | private var expectedDigest:String; 63 | 64 | public function WebSocket( 65 | url:String, protocol:String, 66 | proxyHost:String = null, proxyPort:int = 0, 67 | headers:String = null) { 68 | initNoiseChars(); 69 | this.url = url; 70 | var m:Array = url.match(/^(\w+):\/\/([^\/:]+)(:(\d+))?(\/.*)?$/); 71 | if (!m) onFatal("SYNTAX_ERR: invalid url: " + url); 72 | this.scheme = m[1]; 73 | this.host = m[2]; 74 | this.port = parseInt(m[4] || "80"); 75 | this.origin = 'http://' + this.host + ':' + this.port + '/'; 76 | this.path = m[5] || "/"; 77 | this.protocol = protocol; 78 | // if present and not the empty string, headers MUST end with \r\n 79 | // headers should be zero or more complete lines, for example 80 | // "Header1: xxx\r\nHeader2: yyyy\r\n" 81 | this.headers = headers; 82 | 83 | if (proxyHost != null && proxyPort != 0){ 84 | if (scheme == "wss") { 85 | onFatal("wss with proxy is not supported"); 86 | } 87 | var proxySocket:RFC2817Socket = new RFC2817Socket(); 88 | proxySocket.setProxyInfo(proxyHost, proxyPort); 89 | proxySocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData); 90 | rawSocket = socket = proxySocket; 91 | } else { 92 | rawSocket = new Socket(); 93 | if (scheme == "wss") { 94 | tlsConfig= new TLSConfig(TLSEngine.CLIENT, 95 | null, null, null, null, null, 96 | TLSSecurityParameters.PROTOCOL_VERSION); 97 | tlsConfig.trustAllCertificates = true; 98 | tlsConfig.ignoreCommonNameMismatch = true; 99 | tlsSocket = new TLSSocket(); 100 | tlsSocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData); 101 | socket = tlsSocket; 102 | } else { 103 | rawSocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData); 104 | socket = rawSocket; 105 | } 106 | } 107 | rawSocket.addEventListener(Event.CLOSE, onSocketClose); 108 | rawSocket.addEventListener(Event.CONNECT, onSocketConnect); 109 | rawSocket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError); 110 | rawSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSocketSecurityError); 111 | rawSocket.connect(host, port); 112 | } 113 | 114 | public function send(encData:String):int { 115 | var data:String = encData; 116 | if (readyState == OPEN) { 117 | socket.writeByte(0x00); 118 | socket.writeUTFBytes(data); 119 | socket.writeByte(0xff); 120 | socket.flush(); 121 | return -1; 122 | } else if (readyState == CLOSED) { 123 | var bytes:ByteArray = new ByteArray(); 124 | bytes.writeUTFBytes(data); 125 | bufferedAmount += bytes.length; // not sure whether it should include \x00 and \xff 126 | // We use return value to let caller know bufferedAmount because we cannot fire 127 | // stateChange event here which causes weird error: 128 | // > You are trying to call recursively into the Flash Player which is not allowed. 129 | return bufferedAmount; 130 | } else { 131 | onFatal("INVALID_STATE_ERR: invalid state"); 132 | return 0; 133 | } 134 | } 135 | 136 | public function close():void { 137 | dataQueue = []; 138 | try { 139 | if (readyState == OPEN) { 140 | socket.writeByte(0xff); 141 | socket.writeByte(0x00); 142 | socket.flush(); 143 | } 144 | socket.close(); 145 | } catch (ex:Error) { } 146 | readyState = CLOSED; 147 | // We don't fire any events here because it causes weird error: 148 | // > You are trying to call recursively into the Flash Player which is not allowed. 149 | // We do something equivalent in JavaScript WebSocket#close instead. 150 | } 151 | 152 | public function getReadyState():int { 153 | return readyState; 154 | } 155 | 156 | public function getBufferedAmount():int { 157 | return bufferedAmount; 158 | } 159 | 160 | private function onSocketConnect(event:Event):void { 161 | if (scheme == "wss") { 162 | trace("starting SSL/TLS"); 163 | tlsSocket.startTLS(rawSocket, host, tlsConfig); 164 | } 165 | 166 | dataQueue = []; 167 | var hostValue:String = host + (port == 80 ? "" : ":" + port); 168 | var cookie:String = ""; 169 | var key1:String = generateKey(); 170 | var key2:String = generateKey(); 171 | var key3:String = generateKey3(); 172 | expectedDigest = getSecurityDigest(key1, key2, key3); 173 | var opt:String = ""; 174 | if (protocol) opt += "WebSocket-Protocol: " + protocol + "\r\n"; 175 | // if caller passes additional headers they must end with "\r\n" 176 | if (headers) opt += headers; 177 | 178 | var req:String = StringUtil.substitute( 179 | "GET {0} HTTP/1.1\r\n" + 180 | "Upgrade: WebSocket\r\n" + 181 | "Connection: Upgrade\r\n" + 182 | "Host: {1}\r\n" + 183 | "Origin: {2}\r\n" + 184 | "Cookie: {3}\r\n" + 185 | "Sec-WebSocket-Key1: {4}\r\n" + 186 | "Sec-WebSocket-Key2: {5}\r\n" + 187 | "{6}" + 188 | "\r\n", 189 | path, hostValue, origin, cookie, key1, key2, opt); 190 | trace("request header:\n" + req); 191 | socket.writeUTFBytes(req); 192 | trace("sent key3: " + key3); 193 | writeBytes(key3); 194 | socket.flush(); 195 | } 196 | 197 | private function onSocketClose(event:Event):void { 198 | readyState = CLOSED; 199 | notifyStateChange(); 200 | dispatchEvent(new Event("close")); 201 | } 202 | 203 | private function onSocketIoError(event:IOErrorEvent):void { 204 | var message:String; 205 | if (readyState == CONNECTING) { 206 | message = "cannot connect to Web Socket server at " + url + " (IoError)"; 207 | } else { 208 | message = "error communicating with Web Socket server at " + url + " (IoError)"; 209 | } 210 | onError(message); 211 | } 212 | 213 | private function onSocketSecurityError(event:SecurityErrorEvent):void { 214 | var message:String; 215 | if (readyState == CONNECTING) { 216 | message = 217 | "cannot connect to Web Socket server at " + url + " (SecurityError)\n" + 218 | "make sure the server is running and Flash socket policy file is correctly placed"; 219 | } else { 220 | message = "error communicating with Web Socket server at " + url + " (SecurityError)"; 221 | } 222 | onError(message); 223 | } 224 | 225 | private function onFatal(message:String):void { 226 | throw new Error(message); 227 | } 228 | 229 | private function onError(message:String):void { 230 | var state:int = readyState; 231 | if (state == CLOSED) return; 232 | close(); 233 | notifyStateChange(); 234 | dispatchEvent(new Event(state == CONNECTING ? "close" : "error")); 235 | } 236 | 237 | private function onSocketData(event:ProgressEvent):void { 238 | var pos:int = buffer.length; 239 | socket.readBytes(buffer, pos); 240 | for (; pos < buffer.length; ++pos) { 241 | if (headerState < 4) { 242 | // try to find "\r\n\r\n" 243 | if ((headerState == 0 || headerState == 2) && buffer[pos] == 0x0d) { 244 | ++headerState; 245 | } else if ((headerState == 1 || headerState == 3) && buffer[pos] == 0x0a) { 246 | ++headerState; 247 | } else { 248 | headerState = 0; 249 | } 250 | if (headerState == 4) { 251 | var headerStr:String = readUTFBytes(buffer, 0, pos + 1); 252 | trace("response header:\n" + headerStr); 253 | if (!validateHeader(headerStr)) return; 254 | removeBufferBefore(pos + 1); 255 | pos = -1; 256 | } 257 | } else if (headerState == 4) { 258 | if (pos == 15) { 259 | var replyDigest:String = readBytes(buffer, 0, 16); 260 | trace("reply digest: " + replyDigest); 261 | if (replyDigest != expectedDigest) { 262 | onError("digest doesn't match: " + replyDigest + " != " + expectedDigest); 263 | return; 264 | } 265 | headerState = 5; 266 | removeBufferBefore(pos + 1); 267 | pos = -1; 268 | readyState = OPEN; 269 | notifyStateChange(); 270 | dispatchEvent(new Event("open")); 271 | } 272 | } else { 273 | if (buffer[pos] == 0xff && pos > 0) { 274 | if (buffer[0] != 0x00) { 275 | onError("data must start with \\x00"); 276 | return; 277 | } 278 | var data:String = readUTFBytes(buffer, 1, pos - 1); 279 | dataQueue.push(data); 280 | dispatchEvent(new Event("message")); 281 | removeBufferBefore(pos + 1); 282 | pos = -1; 283 | } else if (pos == 1 && buffer[0] == 0xff && buffer[1] == 0x00) { // closing 284 | trace("received closing packet"); 285 | removeBufferBefore(pos + 1); 286 | pos = -1; 287 | close(); 288 | notifyStateChange(); 289 | dispatchEvent(new Event("close")); 290 | } 291 | } 292 | } 293 | } 294 | 295 | public function readSocketData():Array { 296 | var q:Array = dataQueue; 297 | if (dataQueue.length > 0) { 298 | dataQueue = []; 299 | } 300 | return q; 301 | } 302 | 303 | private function validateHeader(headerStr:String):Boolean { 304 | var lines:Array = headerStr.split(/\r\n/); 305 | if (!lines[0].match(/^HTTP\/1.1 101 /)) { 306 | onError("bad response: " + lines[0]); 307 | return false; 308 | } 309 | var header:Object = {}; 310 | var lowerHeader:Object = {}; 311 | for (var i:int = 1; i < lines.length; ++i) { 312 | if (lines[i].length == 0) continue; 313 | var m:Array = lines[i].match(/^(\S+): (.*)$/); 314 | if (!m) { 315 | onError("failed to parse response header line: " + lines[i]); 316 | return false; 317 | } 318 | header[m[1].toLowerCase()] = m[2]; 319 | lowerHeader[m[1].toLowerCase()] = m[2].toLowerCase(); 320 | } 321 | if (lowerHeader["upgrade"] != "websocket") { 322 | onError("invalid Upgrade: " + header["Upgrade"]); 323 | return false; 324 | } 325 | if (lowerHeader["connection"] != "upgrade") { 326 | onError("invalid Connection: " + header["Connection"]); 327 | return false; 328 | } 329 | if (!lowerHeader["sec-websocket-origin"]) { 330 | if (lowerHeader["websocket-origin"]) { 331 | onError( 332 | "The WebSocket server speaks old WebSocket protocol, " + 333 | "which is not supported by web-socket-js. " + 334 | "It requires WebSocket protocol 76 or later. " + 335 | "Try newer version of the server if available."); 336 | } else { 337 | onError("header Sec-WebSocket-Origin is missing"); 338 | } 339 | return false; 340 | } 341 | var resOrigin:String = lowerHeader["sec-websocket-origin"]; 342 | if (resOrigin != origin) { 343 | onError("origin doesn't match: '" + resOrigin + "' != '" + origin + "'"); 344 | return false; 345 | } 346 | if (protocol && header["sec-websocket-protocol"] != protocol) { 347 | onError("protocol doesn't match: '" + 348 | header["websocket-protocol"] + "' != '" + protocol + "'"); 349 | return false; 350 | } 351 | return true; 352 | } 353 | 354 | private function removeBufferBefore(pos:int):void { 355 | if (pos == 0) return; 356 | var nextBuffer:ByteArray = new ByteArray(); 357 | buffer.position = pos; 358 | buffer.readBytes(nextBuffer); 359 | buffer = nextBuffer; 360 | } 361 | 362 | private function notifyStateChange():void { 363 | dispatchEvent(new WebSocketStateEvent("stateChange", readyState, bufferedAmount)); 364 | } 365 | 366 | private function initNoiseChars():void { 367 | noiseChars = new Array(); 368 | for (var i:int = 0x21; i <= 0x2f; ++i) { 369 | noiseChars.push(String.fromCharCode(i)); 370 | } 371 | for (var j:int = 0x3a; j <= 0x7a; ++j) { 372 | noiseChars.push(String.fromCharCode(j)); 373 | } 374 | } 375 | 376 | private function generateKey():String { 377 | var spaces:uint = randomInt(1, 12); 378 | var max:uint = uint.MAX_VALUE / spaces; 379 | var number:uint = randomInt(0, max); 380 | var key:String = (number * spaces).toString(); 381 | var noises:int = randomInt(1, 12); 382 | var pos:int; 383 | for (var i:int = 0; i < noises; ++i) { 384 | var char:String = noiseChars[randomInt(0, noiseChars.length - 1)]; 385 | pos = randomInt(0, key.length); 386 | key = key.substr(0, pos) + char + key.substr(pos); 387 | } 388 | for (var j:int = 0; j < spaces; ++j) { 389 | pos = randomInt(1, key.length - 1); 390 | key = key.substr(0, pos) + " " + key.substr(pos); 391 | } 392 | return key; 393 | } 394 | 395 | private function generateKey3():String { 396 | var key3:String = ""; 397 | for (var i:int = 0; i < 8; ++i) { 398 | key3 += String.fromCharCode(randomInt(0, 255)); 399 | } 400 | return key3; 401 | } 402 | 403 | private function getSecurityDigest(key1:String, key2:String, key3:String):String { 404 | var bytes1:String = keyToBytes(key1); 405 | var bytes2:String = keyToBytes(key2); 406 | return MD5.rstr_md5(bytes1 + bytes2 + key3); 407 | } 408 | 409 | private function keyToBytes(key:String):String { 410 | var keyNum:uint = parseInt(key.replace(/[^\d]/g, "")); 411 | var spaces:uint = 0; 412 | for (var i:int = 0; i < key.length; ++i) { 413 | if (key.charAt(i) == " ") ++spaces; 414 | } 415 | var resultNum:uint = keyNum / spaces; 416 | var bytes:String = ""; 417 | for (var j:int = 3; j >= 0; --j) { 418 | bytes += String.fromCharCode((resultNum >> (j * 8)) & 0xff); 419 | } 420 | return bytes; 421 | } 422 | 423 | // Writes byte sequence to socket. 424 | // bytes is String in special format where bytes[i] is i-th byte, not i-th character. 425 | private function writeBytes(bytes:String):void { 426 | for (var i:int = 0; i < bytes.length; ++i) { 427 | socket.writeByte(bytes.charCodeAt(i)); 428 | } 429 | } 430 | 431 | // Reads specified number of bytes from buffer, and returns it as special format String 432 | // where bytes[i] is i-th byte (not i-th character). 433 | private function readBytes(buffer:ByteArray, start:int, numBytes:int):String { 434 | buffer.position = start; 435 | var bytes:String = ""; 436 | for (var i:int = 0; i < numBytes; ++i) { 437 | // & 0xff is to make \x80-\xff positive number. 438 | bytes += String.fromCharCode(buffer.readByte() & 0xff); 439 | } 440 | return bytes; 441 | } 442 | 443 | private function readUTFBytes(buffer:ByteArray, start:int, numBytes:int):String { 444 | buffer.position = start; 445 | var data:String = ""; 446 | for(var i:int = start; i < start + numBytes; ++i) { 447 | // Workaround of a bug of ByteArray#readUTFBytes() that bytes after "\x00" is discarded. 448 | if (buffer[i] == 0x00) { 449 | data += buffer.readUTFBytes(i - buffer.position) + "\x00"; 450 | buffer.position = i + 1; 451 | } 452 | } 453 | data += buffer.readUTFBytes(start + numBytes - buffer.position); 454 | return data; 455 | } 456 | 457 | private function randomInt(min:uint, max:uint):uint { 458 | return min + Math.floor(Math.random() * (Number(max) - min + 1)); 459 | } 460 | 461 | // for debug 462 | private function dumpBytes(bytes:String):void { 463 | var output:String = ""; 464 | for (var i:int = 0; i < bytes.length; ++i) { 465 | output += bytes.charCodeAt(i).toString() + ", "; 466 | } 467 | trace(output); 468 | } 469 | 470 | } 471 | 472 | } 473 | -------------------------------------------------------------------------------- /com/gimite/WebSocketStateEvent.as: -------------------------------------------------------------------------------- 1 | // Copyright: Hiroshi Ichikawa 2 | // License: New BSD License 3 | // Reference: http://dev.w3.org/html5/websockets/ 4 | // Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-31 5 | 6 | package com.gimite { 7 | 8 | import flash.display.*; 9 | import flash.events.*; 10 | import flash.external.*; 11 | import flash.net.*; 12 | import flash.system.*; 13 | import flash.utils.*; 14 | import mx.core.*; 15 | import mx.controls.*; 16 | import mx.events.*; 17 | import mx.utils.*; 18 | 19 | public class WebSocketStateEvent extends Event { 20 | 21 | public var readyState:int; 22 | public var bufferedAmount:int; 23 | 24 | public function WebSocketStateEvent(type:String, readyState:int, bufferedAmount:int) { 25 | super(type); 26 | this.readyState = readyState; 27 | this.bufferedAmount = bufferedAmount; 28 | } 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /com/gsolo/encryption/MD5.as: -------------------------------------------------------------------------------- 1 | package com.gsolo.encryption { 2 | public class MD5 { 3 | /* 4 | * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message 5 | * Digest Algorithm, as defined in RFC 1321. 6 | * Version 2.2-alpha Copyright (C) Paul Johnston 1999 - 2005 7 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 8 | * Distributed under the BSD License 9 | * See http://pajhome.org.uk/crypt/md5 for more info. 10 | * 11 | * Converted to AS3 By Geoffrey Williams 12 | */ 13 | 14 | /* 15 | * Configurable variables. You may need to tweak these to be compatible with 16 | * the server-side, but the defaults work in most cases. 17 | */ 18 | 19 | public static const HEX_FORMAT_LOWERCASE:uint = 0; 20 | public static const HEX_FORMAT_UPPERCASE:uint = 1; 21 | 22 | public static const BASE64_PAD_CHARACTER_DEFAULT_COMPLIANCE:String = ""; 23 | public static const BASE64_PAD_CHARACTER_RFC_COMPLIANCE:String = "="; 24 | 25 | public static var hexcase:uint = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ 26 | public static var b64pad:String = ""; /* base-64 pad character. "=" for strict RFC compliance */ 27 | 28 | public static function encrypt (string:String):String { 29 | return hex_md5 (string); 30 | } 31 | 32 | /* 33 | * These are the functions you'll usually want to call 34 | * They take string arguments and return either hex or base-64 encoded strings 35 | */ 36 | public static function hex_md5 (string:String):String { 37 | return rstr2hex (rstr_md5 (str2rstr_utf8 (string))); 38 | } 39 | 40 | public static function b64_md5 (string:String):String { 41 | return rstr2b64 (rstr_md5 (str2rstr_utf8 (string))); 42 | } 43 | 44 | public static function any_md5 (string:String, encoding:String):String { 45 | return rstr2any (rstr_md5 (str2rstr_utf8 (string)), encoding); 46 | } 47 | public static function hex_hmac_md5 (key:String, data:String):String { 48 | return rstr2hex (rstr_hmac_md5 (str2rstr_utf8 (key), str2rstr_utf8 (data))); 49 | } 50 | public static function b64_hmac_md5 (key:String, data:String):String { 51 | return rstr2b64 (rstr_hmac_md5 (str2rstr_utf8 (key), str2rstr_utf8 (data))); 52 | } 53 | public static function any_hmac_md5 (key:String, data:String, encoding:String):String { 54 | return rstr2any(rstr_hmac_md5(str2rstr_utf8(key), str2rstr_utf8(data)), encoding); 55 | } 56 | 57 | /* 58 | * Perform a simple self-test to see if the VM is working 59 | */ 60 | public static function md5_vm_test ():Boolean { 61 | return hex_md5 ("abc") == "900150983cd24fb0d6963f7d28e17f72"; 62 | } 63 | 64 | /* 65 | * Calculate the MD5 of a raw string 66 | */ 67 | public static function rstr_md5 (string:String):String { 68 | return binl2rstr (binl_md5 (rstr2binl (string), string.length * 8)); 69 | } 70 | 71 | /* 72 | * Calculate the HMAC-MD5, of a key and some data (raw strings) 73 | */ 74 | public static function rstr_hmac_md5 (key:String, data:String):String { 75 | var bkey:Array = rstr2binl (key); 76 | if (bkey.length > 16) bkey = binl_md5 (bkey, key.length * 8); 77 | 78 | var ipad:Array = new Array(16), opad:Array = new Array(16); 79 | for(var i:Number = 0; i < 16; i++) { 80 | ipad[i] = bkey[i] ^ 0x36363636; 81 | opad[i] = bkey[i] ^ 0x5C5C5C5C; 82 | } 83 | 84 | var hash:Array = binl_md5 (ipad.concat (rstr2binl (data)), 512 + data.length * 8); 85 | return binl2rstr (binl_md5 (opad.concat (hash), 512 + 128)); 86 | } 87 | 88 | /* 89 | * Convert a raw string to a hex string 90 | */ 91 | public static function rstr2hex (input:String):String { 92 | var hex_tab:String = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; 93 | var output:String = ""; 94 | var x:Number; 95 | for(var i:Number = 0; i < input.length; i++) { 96 | x = input.charCodeAt(i); 97 | output += hex_tab.charAt((x >>> 4) & 0x0F) 98 | + hex_tab.charAt( x & 0x0F); 99 | } 100 | return output; 101 | } 102 | 103 | /* 104 | * Convert a raw string to a base-64 string 105 | */ 106 | public static function rstr2b64 (input:String):String { 107 | var tab:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 108 | var output:String = ""; 109 | var len:Number = input.length; 110 | for(var i:Number = 0; i < len; i += 3) { 111 | var triplet:Number = (input.charCodeAt(i) << 16) 112 | | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) 113 | | (i + 2 < len ? input.charCodeAt(i+2) : 0); 114 | for(var j:Number = 0; j < 4; j++) { 115 | if(i * 8 + j * 6 > input.length * 8) output += b64pad; 116 | else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); 117 | } 118 | } 119 | return output; 120 | } 121 | 122 | /* 123 | * Convert a raw string to an arbitrary string encoding 124 | */ 125 | public static function rstr2any(input:String, encoding:String):String { 126 | var divisor:Number = encoding.length; 127 | var remainders:Array = []; 128 | var i:Number, q:Number, x:Number, quotient:Array; 129 | 130 | /* Convert to an array of 16-bit big-endian values, forming the dividend */ 131 | var dividend:Array = new Array(input.length / 2); 132 | for(i = 0; i < dividend.length; i++) { 133 | dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); 134 | } 135 | 136 | /* 137 | * Repeatedly perform a long division. The binary array forms the dividend, 138 | * the length of the encoding is the divisor. Once computed, the quotient 139 | * forms the dividend for the next step. We stop when the dividend is zero. 140 | * All remainders are stored for later use. 141 | */ 142 | while(dividend.length > 0) { 143 | quotient = []; 144 | x = 0; 145 | for(i = 0; i < dividend.length; i++) { 146 | x = (x << 16) + dividend[i]; 147 | q = Math.floor(x / divisor); 148 | x -= q * divisor; 149 | if(quotient.length > 0 || q > 0) 150 | quotient[quotient.length] = q; 151 | } 152 | remainders[remainders.length] = x; 153 | dividend = quotient; 154 | } 155 | 156 | /* Convert the remainders to the output string */ 157 | var output:String = ""; 158 | for(i = remainders.length - 1; i >= 0; i--) 159 | output += encoding.charAt (remainders[i]); 160 | 161 | return output; 162 | } 163 | 164 | /* 165 | * Encode a string as utf-8. 166 | * For efficiency, this assumes the input is valid utf-16. 167 | */ 168 | public static function str2rstr_utf8 (input:String):String { 169 | var output:String = ""; 170 | var i:Number = -1; 171 | var x:Number, y:Number; 172 | 173 | while(++i < input.length) { 174 | /* Decode utf-16 surrogate pairs */ 175 | x = input.charCodeAt(i); 176 | y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; 177 | if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) { 178 | x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); 179 | i++; 180 | } 181 | 182 | /* Encode output as utf-8 */ 183 | if(x <= 0x7F) 184 | output += String.fromCharCode(x); 185 | else if(x <= 0x7FF) 186 | output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), 187 | 0x80 | ( x & 0x3F)); 188 | else if(x <= 0xFFFF) 189 | output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), 190 | 0x80 | ((x >>> 6 ) & 0x3F), 191 | 0x80 | ( x & 0x3F)); 192 | else if(x <= 0x1FFFFF) 193 | output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), 194 | 0x80 | ((x >>> 12) & 0x3F), 195 | 0x80 | ((x >>> 6 ) & 0x3F), 196 | 0x80 | ( x & 0x3F)); 197 | } 198 | return output; 199 | } 200 | 201 | /* 202 | * Encode a string as utf-16 203 | */ 204 | public static function str2rstr_utf16le (input:String):String { 205 | var output:String = ""; 206 | for(var i:Number = 0; i < input.length; i++) 207 | output += String.fromCharCode( input.charCodeAt(i) & 0xFF, 208 | (input.charCodeAt(i) >>> 8) & 0xFF); 209 | return output; 210 | } 211 | 212 | public static function str2rstr_utf16be (input:String):String { 213 | var output:String = ""; 214 | for(var i:Number = 0; i < input.length; i++) 215 | output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, 216 | input.charCodeAt(i) & 0xFF); 217 | return output; 218 | } 219 | 220 | /* 221 | * Convert a raw string to an array of little-endian words 222 | * Characters >255 have their high-byte silently ignored. 223 | */ 224 | public static function rstr2binl (input:String):Array { 225 | var output:Array = new Array(input.length >> 2); 226 | for(var i:Number = 0; i < output.length; i++) 227 | output[i] = 0; 228 | for(i = 0; i < input.length * 8; i += 8) 229 | output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32); 230 | return output; 231 | } 232 | 233 | /* 234 | * Convert an array of little-endian words to a string 235 | */ 236 | public static function binl2rstr (input:Array):String { 237 | var output:String = ""; 238 | for(var i:Number = 0; i < input.length * 32; i += 8) 239 | output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF); 240 | return output; 241 | } 242 | 243 | /* 244 | * Calculate the MD5 of an array of little-endian words, and a bit length. 245 | */ 246 | public static function binl_md5 (x:Array, len:Number):Array { 247 | /* append padding */ 248 | x[len >> 5] |= 0x80 << ((len) % 32); 249 | x[(((len + 64) >>> 9) << 4) + 14] = len; 250 | 251 | var a:Number = 1732584193; 252 | var b:Number = -271733879; 253 | var c:Number = -1732584194; 254 | var d:Number = 271733878; 255 | 256 | for(var i:Number = 0; i < x.length; i += 16) { 257 | var olda:Number = a; 258 | var oldb:Number = b; 259 | var oldc:Number = c; 260 | var oldd:Number = d; 261 | 262 | a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); 263 | d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); 264 | c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); 265 | b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); 266 | a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); 267 | d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); 268 | c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); 269 | b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); 270 | a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); 271 | d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); 272 | c = md5_ff(c, d, a, b, x[i+10], 17, -42063); 273 | b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); 274 | a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); 275 | d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); 276 | c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); 277 | b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); 278 | 279 | a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); 280 | d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); 281 | c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); 282 | b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); 283 | a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); 284 | d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); 285 | c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); 286 | b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); 287 | a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); 288 | d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); 289 | c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); 290 | b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); 291 | a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); 292 | d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); 293 | c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); 294 | b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); 295 | 296 | a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); 297 | d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); 298 | c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); 299 | b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); 300 | a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); 301 | d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); 302 | c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); 303 | b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); 304 | a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); 305 | d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); 306 | c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); 307 | b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); 308 | a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); 309 | d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); 310 | c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); 311 | b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); 312 | 313 | a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); 314 | d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); 315 | c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); 316 | b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); 317 | a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); 318 | d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); 319 | c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); 320 | b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); 321 | a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); 322 | d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); 323 | c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); 324 | b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); 325 | a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); 326 | d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); 327 | c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); 328 | b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); 329 | 330 | a = safe_add(a, olda); 331 | b = safe_add(b, oldb); 332 | c = safe_add(c, oldc); 333 | d = safe_add(d, oldd); 334 | } 335 | return [a, b, c, d]; 336 | } 337 | 338 | /* 339 | * These functions implement the four basic operations the algorithm uses. 340 | */ 341 | public static function md5_cmn (q:Number, a:Number, b:Number, x:Number, s:Number, t:Number):Number { 342 | return safe_add (bit_rol (safe_add (safe_add (a, q), safe_add(x, t)), s), b); 343 | } 344 | public static function md5_ff (a:Number, b:Number, c:Number, d:Number, x:Number, s:Number, t:Number):Number { 345 | return md5_cmn ((b & c) | ((~b) & d), a, b, x, s, t); 346 | } 347 | public static function md5_gg (a:Number, b:Number, c:Number, d:Number, x:Number, s:Number, t:Number):Number { 348 | return md5_cmn ((b & d) | (c & (~d)), a, b, x, s, t); 349 | } 350 | public static function md5_hh (a:Number, b:Number, c:Number, d:Number, x:Number, s:Number, t:Number):Number { 351 | return md5_cmn (b ^ c ^ d, a, b, x, s, t); 352 | } 353 | public static function md5_ii (a:Number, b:Number, c:Number, d:Number, x:Number, s:Number, t:Number):Number { 354 | return md5_cmn (c ^ (b | (~d)), a, b, x, s, t); 355 | } 356 | 357 | /* 358 | * Add integers, wrapping at 2^32. This uses 16-bit operations internally 359 | * to work around bugs in some JS interpreters. 360 | */ 361 | public static function safe_add (x:Number, y:Number):Number { 362 | var lsw:Number = (x & 0xFFFF) + (y & 0xFFFF); 363 | var msw:Number = (x >> 16) + (y >> 16) + (lsw >> 16); 364 | return (msw << 16) | (lsw & 0xFFFF); 365 | } 366 | 367 | /* 368 | * Bitwise rotate a 32-bit number to the left. 369 | */ 370 | public static function bit_rol (num:Number, cnt:Number):Number { 371 | return (num << cnt) | (num >>> (32 - cnt)); 372 | } 373 | 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /example.xml: -------------------------------------------------------------------------------- 1 | 2 | com.clevr.SocketExample 3 | 1 4 | Socket.IO Example 5 | Socket.IO Example 6 | © CLEVR Ltd. 2010 7 | true 8 | 9 | Example.swf 10 | 800 11 | 550 12 | 800 550 13 | standard 14 | 15 | 16 | 17 | --------------------------------------------------------------------------------