rocket-js 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +5 -52
- data/spec/ruby/spec_helper.rb +2 -1
- metadata +7 -129
- data/src/vendor/web-socket-js/FABridge.js +0 -604
- data/src/vendor/web-socket-js/README.txt +0 -109
- data/src/vendor/web-socket-js/WebSocketMain.swf +0 -0
- data/src/vendor/web-socket-js/WebSocketMainInsecure.zip +0 -0
- data/src/vendor/web-socket-js/flash-src/WebSocket.as +0 -473
- data/src/vendor/web-socket-js/flash-src/WebSocketMain.as +0 -88
- data/src/vendor/web-socket-js/flash-src/WebSocketMainInsecure.as +0 -19
- data/src/vendor/web-socket-js/flash-src/WebSocketStateEvent.as +0 -32
- data/src/vendor/web-socket-js/flash-src/bridge/FABridge.as +0 -943
- data/src/vendor/web-socket-js/flash-src/build.sh +0 -10
- data/src/vendor/web-socket-js/flash-src/com/adobe/net/proxies/RFC2817Socket.as +0 -204
- data/src/vendor/web-socket-js/flash-src/com/gsolo/encryption/MD5.as +0 -375
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/Crypto.as +0 -287
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/MozillaRootCertificates.as +0 -3235
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/X509Certificate.as +0 -218
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/X509CertificateCollection.as +0 -57
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/HMAC.as +0 -82
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/IHMAC.as +0 -27
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/IHash.as +0 -21
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MAC.as +0 -137
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MD2.as +0 -124
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MD5.as +0 -204
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA1.as +0 -106
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA224.as +0 -28
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA256.as +0 -115
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHABase.as +0 -71
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/ARC4.as +0 -90
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/IPRNG.as +0 -20
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/Random.as +0 -119
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/TLSPRF.as +0 -142
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/rsa/RSAKey.as +0 -339
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/AESKey.as +0 -2797
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/BlowFishKey.as +0 -375
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CBCMode.as +0 -55
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CFB8Mode.as +0 -61
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CFBMode.as +0 -64
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CTRMode.as +0 -58
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/DESKey.as +0 -365
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ECBMode.as +0 -86
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ICipher.as +0 -21
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IMode.as +0 -15
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IPad.as +0 -32
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IStreamCipher.as +0 -21
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ISymmetricKey.as +0 -35
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IVMode.as +0 -110
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/NullPad.as +0 -34
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/OFBMode.as +0 -52
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/PKCS5.as +0 -44
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/SSLPad.as +0 -44
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/SimpleIVMode.as +0 -60
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/TLSPad.as +0 -42
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/TripleDESKey.as +0 -88
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/XTeaKey.as +0 -94
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/aeskey.pl +0 -29
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/dump.txt +0 -2304
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/AESKeyTest.as +0 -1220
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ARC4Test.as +0 -58
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/BigIntegerTest.as +0 -39
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/BlowFishKeyTest.as +0 -148
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CBCModeTest.as +0 -160
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CFB8ModeTest.as +0 -71
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CFBModeTest.as +0 -98
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CTRModeTest.as +0 -109
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/DESKeyTest.as +0 -112
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ECBModeTest.as +0 -151
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/HMACTest.as +0 -184
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ITestHarness.as +0 -20
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/MD2Test.as +0 -56
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/MD5Test.as +0 -58
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/OFBModeTest.as +0 -101
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/RSAKeyTest.as +0 -92
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA1Test.as +0 -198
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA224Test.as +0 -58
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA256Test.as +0 -60
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TLSPRFTest.as +0 -51
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TestCase.as +0 -42
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TripleDESKeyTest.as +0 -59
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/XTeaKeyTest.as +0 -66
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/BulkCiphers.as +0 -102
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/CipherSuites.as +0 -117
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/IConnectionState.as +0 -14
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/ISecurityParameters.as +0 -29
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/KeyExchanges.as +0 -24
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/MACs.as +0 -38
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLConnectionState.as +0 -171
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLEvent.as +0 -26
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLSecurityParameters.as +0 -340
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSConfig.as +0 -70
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSConnectionState.as +0 -151
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSEngine.as +0 -895
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSError.as +0 -39
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSEvent.as +0 -27
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSecurityParameters.as +0 -197
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSocket.as +0 -370
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSocketEvent.as +0 -26
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSTest.as +0 -180
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/BarrettReduction.as +0 -90
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/BigInteger.as +0 -1543
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/ClassicReduction.as +0 -35
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/IReduction.as +0 -11
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/MontgomeryReduction.as +0 -85
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/NullReduction.as +0 -34
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/bi_internal.as +0 -11
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/ArrayUtil.as +0 -25
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/Base64.as +0 -189
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/Hex.as +0 -66
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/Memory.as +0 -28
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/ByteString.as +0 -43
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/DER.as +0 -210
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/IAsn1Type.as +0 -21
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Integer.as +0 -44
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/OID.as +0 -35
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/ObjectIdentifier.as +0 -112
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/PEM.as +0 -118
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/PrintableString.as +0 -49
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Sequence.as +0 -90
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Set.as +0 -27
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Type.as +0 -94
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/UTCTime.as +0 -60
- data/src/vendor/web-socket-js/sample.html +0 -76
- data/src/vendor/web-socket-js/swfobject.js +0 -4
- data/src/vendor/web-socket-js/web_socket.js +0 -388
@@ -1,109 +0,0 @@
|
|
1
|
-
* How to try
|
2
|
-
|
3
|
-
Assuming you have Web server (e.g. Apache) running at http://example.com/ .
|
4
|
-
|
5
|
-
- Download web_socket.rb from:
|
6
|
-
http://github.com/gimite/web-socket-ruby/tree/master
|
7
|
-
- Run sample Web Socket server (echo server) in example.com with: (#1)
|
8
|
-
$ ruby web-socket-ruby/samples/echo_server.rb example.com 10081
|
9
|
-
- If your server already provides socket policy file at port 843, modify the file to allow access to port 10081. Otherwise you can skip this step. See below for details.
|
10
|
-
- Publish the web-socket-js directory with your Web server (e.g. put it in ~/public_html).
|
11
|
-
- Change ws://localhost:10081 to ws://example.com:10081 in sample.html.
|
12
|
-
- Open sample.html in your browser.
|
13
|
-
- After "onopen" is shown, input something, click [Send] and confirm echo back.
|
14
|
-
|
15
|
-
#1: First argument of echo_server.rb means that it accepts Web Socket connection from HTML pages in example.com.
|
16
|
-
|
17
|
-
|
18
|
-
* Troubleshooting
|
19
|
-
|
20
|
-
If it doesn't work, try these:
|
21
|
-
|
22
|
-
1. Try Chrome and Firefox 3.x.
|
23
|
-
- It doesn't work on Chrome:
|
24
|
-
-- It's likely an issue of your code or the server. Debug your code as usual e.g. using console.log.
|
25
|
-
- It works on Chrome but it doesn't work on Firefox:
|
26
|
-
-- It's likely an issue of web-socket-js specific configuration (e.g. 3 and 4 below).
|
27
|
-
- It works on both Chrome and Firefox, but it doesn't work on your browser:
|
28
|
-
-- Check "Supported environment" section below. Your browser may not be supported by web-socket-js.
|
29
|
-
|
30
|
-
2. Add this line before your code:
|
31
|
-
WEB_SOCKET_DEBUG = true;
|
32
|
-
and use Developer Tools (Chrome/Safari) or Firebug (Firefox) to see if console.log outputs any errors.
|
33
|
-
|
34
|
-
3. Make sure you do NOT open your HTML page as local file e.g. file:///.../sample.html. web-socket-js doesn't work on local file. Open it via Web server e.g. http:///.../sample.html.
|
35
|
-
|
36
|
-
4. If you are NOT using web-socket-ruby as your WebSocket server, you need to place Flash socket policy file on your server. See "Flash socket policy file" section below for details.
|
37
|
-
|
38
|
-
5. Check if sample.html bundled with web-socket-js works.
|
39
|
-
|
40
|
-
6. Make sure the port used for WebSocket (10081 in example above) is not blocked by your server/client's firewall.
|
41
|
-
|
42
|
-
7. Install debugger version of Flash Player available here to see Flash errors:
|
43
|
-
http://www.adobe.com/support/flashplayer/downloads.html
|
44
|
-
|
45
|
-
|
46
|
-
* Supported environments
|
47
|
-
|
48
|
-
It should work on:
|
49
|
-
- Google Chrome 4 or later (just uses native implementation)
|
50
|
-
- Firefox 3.x, Internet Explorer 8 + Flash Player 9 or later
|
51
|
-
|
52
|
-
It may or may not work on other browsers such as Safari, Opera or IE 6. Patch for these browsers are appreciated, but I will not work on fixing issues specific to these browsers by myself.
|
53
|
-
|
54
|
-
|
55
|
-
* Flash socket policy file
|
56
|
-
|
57
|
-
This implementation uses Flash's socket, which means that your server must provide Flash socket policy file to declare the server accepts connections from Flash.
|
58
|
-
|
59
|
-
If you use web-socket-ruby available at
|
60
|
-
http://github.com/gimite/web-socket-ruby/tree/master
|
61
|
-
, you don't need anything special, because web-socket-ruby handles Flash socket policy file request. But if you already provide socket policy file at port 843, you need to modify the file to allow access to Web Socket port, because it precedes what web-socket-ruby provides.
|
62
|
-
|
63
|
-
If you use other Web Socket server implementation, you need to provide socket policy file yourself. See
|
64
|
-
http://www.lightsphere.com/dev/articles/flash_socket_policy.html
|
65
|
-
for details and sample script to run socket policy file server. node.js implementation is available here:
|
66
|
-
http://github.com/LearnBoost/Socket.IO-node/blob/master/lib/socket.io/transports/flashsocket.js
|
67
|
-
|
68
|
-
Actually, it's still better to provide socket policy file at port 843 even if you use web-socket-ruby. Flash always try to connect to port 843 first, so providing the file at port 843 makes startup faster.
|
69
|
-
|
70
|
-
|
71
|
-
* Cookie considerations
|
72
|
-
|
73
|
-
Cookie is sent if Web Socket host is the same as the origin of JavaScript. Otherwise it is not sent, because I don't know way to send right Cookie (which is Cookie of the host of Web Socket, I heard).
|
74
|
-
|
75
|
-
Note that it's technically possible that client sends arbitrary string as Cookie and any other headers (by modifying this library for example) once you place Flash socket policy file in your server. So don't trust Cookie and other headers if you allow connection from untrusted origin.
|
76
|
-
|
77
|
-
|
78
|
-
* Proxy considerations
|
79
|
-
|
80
|
-
The WebSocket spec (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol) specifies instructions for User Agents to support proxied connections by implementing the HTTP CONNECT method.
|
81
|
-
|
82
|
-
The AS3 Socket class doesn't implement this mechanism, which renders it useless for the scenarios where the user trying to open a socket is behind a proxy.
|
83
|
-
|
84
|
-
The class RFC2817Socket (by Christian Cantrell) effectively lets us implement this, as long as the proxy settings are known and provided by the interface that instantiates the WebSocket. As such, if you want to support proxied conncetions, you'll have to supply this information to the WebSocket constructor when Flash is being used. One way to go about it would be to ask the user for proxy settings information if the initial connection fails.
|
85
|
-
|
86
|
-
|
87
|
-
* How to host HTML file and SWF file in different domains
|
88
|
-
|
89
|
-
By default, HTML file and SWF file must be in the same domain. You can follow steps below to allow hosting them in different domain.
|
90
|
-
|
91
|
-
WARNING: If you use the method below, HTML files in ANY domains can send arbitrary TCP data to your WebSocket server, regardless of configuration in Flash socket policy file. Arbitrary TCP data means that they can even fake request headers including Origin and Cookie.
|
92
|
-
|
93
|
-
- Unzip WebSocketMainInsecure.zip to extract WebSocketMainInsecure.swf.
|
94
|
-
- Put WebSocketMainInsecure.swf on your server, instead of WebSocketMain.swf.
|
95
|
-
- In JavaScript, set WEB_SOCKET_SWF_LOCATION to URL of your WebSocketMainInsecure.swf.
|
96
|
-
|
97
|
-
|
98
|
-
* How to build WebSocketMain.swf
|
99
|
-
|
100
|
-
Install Flex 4 SDK:
|
101
|
-
http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4
|
102
|
-
|
103
|
-
$ cd flash-src
|
104
|
-
$ ./build.sh
|
105
|
-
|
106
|
-
|
107
|
-
* License
|
108
|
-
|
109
|
-
New BSD License.
|
Binary file
|
Binary file
|
@@ -1,473 +0,0 @@
|
|
1
|
-
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
2
|
-
// License: New BSD License
|
3
|
-
// Reference: http://dev.w3.org/html5/websockets/
|
4
|
-
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
|
5
|
-
|
6
|
-
package {
|
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
|
-
import com.adobe.net.proxies.RFC2817Socket;
|
19
|
-
import com.hurlant.crypto.tls.TLSSocket;
|
20
|
-
import com.hurlant.crypto.tls.TLSConfig;
|
21
|
-
import com.hurlant.crypto.tls.TLSEngine;
|
22
|
-
import com.hurlant.crypto.tls.TLSSecurityParameters;
|
23
|
-
import com.gsolo.encryption.MD5;
|
24
|
-
|
25
|
-
[Event(name="message", type="flash.events.Event")]
|
26
|
-
[Event(name="open", type="flash.events.Event")]
|
27
|
-
[Event(name="close", type="flash.events.Event")]
|
28
|
-
[Event(name="error", type="flash.events.Event")]
|
29
|
-
[Event(name="stateChange", type="WebSocketStateEvent")]
|
30
|
-
public class WebSocket extends EventDispatcher {
|
31
|
-
|
32
|
-
private static var CONNECTING:int = 0;
|
33
|
-
private static var OPEN:int = 1;
|
34
|
-
private static var CLOSING:int = 2;
|
35
|
-
private static var CLOSED:int = 3;
|
36
|
-
|
37
|
-
private var rawSocket:Socket;
|
38
|
-
private var tlsSocket:TLSSocket;
|
39
|
-
private var tlsConfig:TLSConfig;
|
40
|
-
private var socket:Socket;
|
41
|
-
private var main:WebSocketMain;
|
42
|
-
private var url:String;
|
43
|
-
private var scheme:String;
|
44
|
-
private var host:String;
|
45
|
-
private var port:uint;
|
46
|
-
private var path:String;
|
47
|
-
private var origin:String;
|
48
|
-
private var protocol:String;
|
49
|
-
private var buffer:ByteArray = new ByteArray();
|
50
|
-
private var dataQueue:Array;
|
51
|
-
private var headerState:int = 0;
|
52
|
-
private var readyState:int = CONNECTING;
|
53
|
-
private var bufferedAmount:int = 0;
|
54
|
-
private var headers:String;
|
55
|
-
private var noiseChars:Array;
|
56
|
-
private var expectedDigest:String;
|
57
|
-
|
58
|
-
public function WebSocket(
|
59
|
-
main:WebSocketMain, url:String, protocol:String,
|
60
|
-
proxyHost:String = null, proxyPort:int = 0,
|
61
|
-
headers:String = null) {
|
62
|
-
this.main = main;
|
63
|
-
initNoiseChars();
|
64
|
-
this.url = url;
|
65
|
-
var m:Array = url.match(/^(\w+):\/\/([^\/:]+)(:(\d+))?(\/.*)?$/);
|
66
|
-
if (!m) main.fatal("SYNTAX_ERR: invalid url: " + url);
|
67
|
-
this.scheme = m[1];
|
68
|
-
this.host = m[2];
|
69
|
-
this.port = parseInt(m[4] || "80");
|
70
|
-
this.path = m[5] || "/";
|
71
|
-
this.origin = main.getOrigin();
|
72
|
-
this.protocol = protocol;
|
73
|
-
// if present and not the empty string, headers MUST end with \r\n
|
74
|
-
// headers should be zero or more complete lines, for example
|
75
|
-
// "Header1: xxx\r\nHeader2: yyyy\r\n"
|
76
|
-
this.headers = headers;
|
77
|
-
|
78
|
-
if (proxyHost != null && proxyPort != 0){
|
79
|
-
if (scheme == "wss") {
|
80
|
-
main.fatal("wss with proxy is not supported");
|
81
|
-
}
|
82
|
-
var proxySocket:RFC2817Socket = new RFC2817Socket();
|
83
|
-
proxySocket.setProxyInfo(proxyHost, proxyPort);
|
84
|
-
proxySocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
|
85
|
-
rawSocket = socket = proxySocket;
|
86
|
-
} else {
|
87
|
-
rawSocket = new Socket();
|
88
|
-
if (scheme == "wss") {
|
89
|
-
tlsConfig= new TLSConfig(TLSEngine.CLIENT,
|
90
|
-
null, null, null, null, null,
|
91
|
-
TLSSecurityParameters.PROTOCOL_VERSION);
|
92
|
-
tlsConfig.trustAllCertificates = true;
|
93
|
-
tlsConfig.ignoreCommonNameMismatch = true;
|
94
|
-
tlsSocket = new TLSSocket();
|
95
|
-
tlsSocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
|
96
|
-
socket = tlsSocket;
|
97
|
-
} else {
|
98
|
-
rawSocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
|
99
|
-
socket = rawSocket;
|
100
|
-
}
|
101
|
-
}
|
102
|
-
rawSocket.addEventListener(Event.CLOSE, onSocketClose);
|
103
|
-
rawSocket.addEventListener(Event.CONNECT, onSocketConnect);
|
104
|
-
rawSocket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError);
|
105
|
-
rawSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSocketSecurityError);
|
106
|
-
rawSocket.connect(host, port);
|
107
|
-
}
|
108
|
-
|
109
|
-
public function send(encData:String):int {
|
110
|
-
var data:String = decodeURIComponent(encData);
|
111
|
-
if (readyState == OPEN) {
|
112
|
-
socket.writeByte(0x00);
|
113
|
-
socket.writeUTFBytes(data);
|
114
|
-
socket.writeByte(0xff);
|
115
|
-
socket.flush();
|
116
|
-
main.log("sent: " + data);
|
117
|
-
return -1;
|
118
|
-
} else if (readyState == CLOSED) {
|
119
|
-
var bytes:ByteArray = new ByteArray();
|
120
|
-
bytes.writeUTFBytes(data);
|
121
|
-
bufferedAmount += bytes.length; // not sure whether it should include \x00 and \xff
|
122
|
-
// We use return value to let caller know bufferedAmount because we cannot fire
|
123
|
-
// stateChange event here which causes weird error:
|
124
|
-
// > You are trying to call recursively into the Flash Player which is not allowed.
|
125
|
-
return bufferedAmount;
|
126
|
-
} else {
|
127
|
-
main.fatal("INVALID_STATE_ERR: invalid state");
|
128
|
-
return 0;
|
129
|
-
}
|
130
|
-
}
|
131
|
-
|
132
|
-
public function close():void {
|
133
|
-
main.log("close");
|
134
|
-
dataQueue = [];
|
135
|
-
try {
|
136
|
-
if (readyState == OPEN) {
|
137
|
-
socket.writeByte(0xff);
|
138
|
-
socket.writeByte(0x00);
|
139
|
-
socket.flush();
|
140
|
-
}
|
141
|
-
socket.close();
|
142
|
-
} catch (ex:Error) { }
|
143
|
-
readyState = CLOSED;
|
144
|
-
// We don't fire any events here because it causes weird error:
|
145
|
-
// > You are trying to call recursively into the Flash Player which is not allowed.
|
146
|
-
// We do something equivalent in JavaScript WebSocket#close instead.
|
147
|
-
}
|
148
|
-
|
149
|
-
public function getReadyState():int {
|
150
|
-
return readyState;
|
151
|
-
}
|
152
|
-
|
153
|
-
public function getBufferedAmount():int {
|
154
|
-
return bufferedAmount;
|
155
|
-
}
|
156
|
-
|
157
|
-
private function onSocketConnect(event:Event):void {
|
158
|
-
main.log("connected");
|
159
|
-
|
160
|
-
if (scheme == "wss") {
|
161
|
-
main.log("starting SSL/TLS");
|
162
|
-
tlsSocket.startTLS(rawSocket, host, tlsConfig);
|
163
|
-
}
|
164
|
-
|
165
|
-
dataQueue = [];
|
166
|
-
var hostValue:String = host + (port == 80 ? "" : ":" + port);
|
167
|
-
var cookie:String = "";
|
168
|
-
if (main.getCallerHost() == host) {
|
169
|
-
cookie = ExternalInterface.call("function(){return document.cookie}");
|
170
|
-
}
|
171
|
-
var key1:String = generateKey();
|
172
|
-
var key2:String = generateKey();
|
173
|
-
var key3:String = generateKey3();
|
174
|
-
expectedDigest = getSecurityDigest(key1, key2, key3);
|
175
|
-
var opt:String = "";
|
176
|
-
if (protocol) opt += "WebSocket-Protocol: " + protocol + "\r\n";
|
177
|
-
// if caller passes additional headers they must end with "\r\n"
|
178
|
-
if (headers) opt += headers;
|
179
|
-
|
180
|
-
var req:String = StringUtil.substitute(
|
181
|
-
"GET {0} HTTP/1.1\r\n" +
|
182
|
-
"Upgrade: WebSocket\r\n" +
|
183
|
-
"Connection: Upgrade\r\n" +
|
184
|
-
"Host: {1}\r\n" +
|
185
|
-
"Origin: {2}\r\n" +
|
186
|
-
"Cookie: {3}\r\n" +
|
187
|
-
"Sec-WebSocket-Key1: {4}\r\n" +
|
188
|
-
"Sec-WebSocket-Key2: {5}\r\n" +
|
189
|
-
"{6}" +
|
190
|
-
"\r\n",
|
191
|
-
path, hostValue, origin, cookie, key1, key2, opt);
|
192
|
-
main.log("request header:\n" + req);
|
193
|
-
socket.writeUTFBytes(req);
|
194
|
-
main.log("sent key3: " + key3);
|
195
|
-
writeBytes(key3);
|
196
|
-
socket.flush();
|
197
|
-
}
|
198
|
-
|
199
|
-
private function onSocketClose(event:Event):void {
|
200
|
-
main.log("closed");
|
201
|
-
readyState = CLOSED;
|
202
|
-
notifyStateChange();
|
203
|
-
dispatchEvent(new Event("close"));
|
204
|
-
}
|
205
|
-
|
206
|
-
private function onSocketIoError(event:IOErrorEvent):void {
|
207
|
-
var message:String;
|
208
|
-
if (readyState == CONNECTING) {
|
209
|
-
message = "cannot connect to Web Socket server at " + url + " (IoError)";
|
210
|
-
} else {
|
211
|
-
message = "error communicating with Web Socket server at " + url + " (IoError)";
|
212
|
-
}
|
213
|
-
onError(message);
|
214
|
-
}
|
215
|
-
|
216
|
-
private function onSocketSecurityError(event:SecurityErrorEvent):void {
|
217
|
-
var message:String;
|
218
|
-
if (readyState == CONNECTING) {
|
219
|
-
message =
|
220
|
-
"cannot connect to Web Socket server at " + url + " (SecurityError)\n" +
|
221
|
-
"make sure the server is running and Flash socket policy file is correctly placed";
|
222
|
-
} else {
|
223
|
-
message = "error communicating with Web Socket server at " + url + " (SecurityError)";
|
224
|
-
}
|
225
|
-
onError(message);
|
226
|
-
}
|
227
|
-
|
228
|
-
private function onError(message:String):void {
|
229
|
-
var state:int = readyState;
|
230
|
-
if (state == CLOSED) return;
|
231
|
-
main.error(message);
|
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
|
-
main.log("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
|
-
main.log("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
|
-
main.log("received: " + data);
|
280
|
-
dataQueue.push(encodeURIComponent(data));
|
281
|
-
dispatchEvent(new Event("message"));
|
282
|
-
removeBufferBefore(pos + 1);
|
283
|
-
pos = -1;
|
284
|
-
} else if (pos == 1 && buffer[0] == 0xff && buffer[1] == 0x00) { // closing
|
285
|
-
main.log("received closing packet");
|
286
|
-
removeBufferBefore(pos + 1);
|
287
|
-
pos = -1;
|
288
|
-
close();
|
289
|
-
notifyStateChange();
|
290
|
-
dispatchEvent(new Event("close"));
|
291
|
-
}
|
292
|
-
}
|
293
|
-
}
|
294
|
-
}
|
295
|
-
|
296
|
-
public function readSocketData():Array {
|
297
|
-
var q:Array = dataQueue;
|
298
|
-
if (dataQueue.length > 0) {
|
299
|
-
dataQueue = [];
|
300
|
-
}
|
301
|
-
return q;
|
302
|
-
}
|
303
|
-
|
304
|
-
private function validateHeader(headerStr:String):Boolean {
|
305
|
-
var lines:Array = headerStr.split(/\r\n/);
|
306
|
-
if (!lines[0].match(/^HTTP\/1.1 101 /)) {
|
307
|
-
onError("bad response: " + lines[0]);
|
308
|
-
return false;
|
309
|
-
}
|
310
|
-
var header:Object = {};
|
311
|
-
var lowerHeader:Object = {};
|
312
|
-
for (var i:int = 1; i < lines.length; ++i) {
|
313
|
-
if (lines[i].length == 0) continue;
|
314
|
-
var m:Array = lines[i].match(/^(\S+): (.*)$/);
|
315
|
-
if (!m) {
|
316
|
-
onError("failed to parse response header line: " + lines[i]);
|
317
|
-
return false;
|
318
|
-
}
|
319
|
-
header[m[1].toLowerCase()] = m[2];
|
320
|
-
lowerHeader[m[1].toLowerCase()] = m[2].toLowerCase();
|
321
|
-
}
|
322
|
-
if (lowerHeader["upgrade"] != "websocket") {
|
323
|
-
onError("invalid Upgrade: " + header["Upgrade"]);
|
324
|
-
return false;
|
325
|
-
}
|
326
|
-
if (lowerHeader["connection"] != "upgrade") {
|
327
|
-
onError("invalid Connection: " + header["Connection"]);
|
328
|
-
return false;
|
329
|
-
}
|
330
|
-
if (!lowerHeader["sec-websocket-origin"]) {
|
331
|
-
if (lowerHeader["websocket-origin"]) {
|
332
|
-
onError(
|
333
|
-
"The WebSocket server speaks old WebSocket protocol, " +
|
334
|
-
"which is not supported by web-socket-js. " +
|
335
|
-
"It requires WebSocket protocol 76 or later. " +
|
336
|
-
"Try newer version of the server if available.");
|
337
|
-
} else {
|
338
|
-
onError("header Sec-WebSocket-Origin is missing");
|
339
|
-
}
|
340
|
-
return false;
|
341
|
-
}
|
342
|
-
var resOrigin:String = lowerHeader["sec-websocket-origin"];
|
343
|
-
if (resOrigin != origin) {
|
344
|
-
onError("origin doesn't match: '" + resOrigin + "' != '" + origin + "'");
|
345
|
-
return false;
|
346
|
-
}
|
347
|
-
if (protocol && header["sec-websocket-protocol"] != protocol) {
|
348
|
-
onError("protocol doesn't match: '" +
|
349
|
-
header["websocket-protocol"] + "' != '" + protocol + "'");
|
350
|
-
return false;
|
351
|
-
}
|
352
|
-
return true;
|
353
|
-
}
|
354
|
-
|
355
|
-
private function removeBufferBefore(pos:int):void {
|
356
|
-
if (pos == 0) return;
|
357
|
-
var nextBuffer:ByteArray = new ByteArray();
|
358
|
-
buffer.position = pos;
|
359
|
-
buffer.readBytes(nextBuffer);
|
360
|
-
buffer = nextBuffer;
|
361
|
-
}
|
362
|
-
|
363
|
-
private function notifyStateChange():void {
|
364
|
-
dispatchEvent(new WebSocketStateEvent("stateChange", readyState, bufferedAmount));
|
365
|
-
}
|
366
|
-
|
367
|
-
private function initNoiseChars():void {
|
368
|
-
noiseChars = new Array();
|
369
|
-
for (var i:int = 0x21; i <= 0x2f; ++i) {
|
370
|
-
noiseChars.push(String.fromCharCode(i));
|
371
|
-
}
|
372
|
-
for (var j:int = 0x3a; j <= 0x7a; ++j) {
|
373
|
-
noiseChars.push(String.fromCharCode(j));
|
374
|
-
}
|
375
|
-
}
|
376
|
-
|
377
|
-
private function generateKey():String {
|
378
|
-
var spaces:uint = randomInt(1, 12);
|
379
|
-
var max:uint = uint.MAX_VALUE / spaces;
|
380
|
-
var number:uint = randomInt(0, max);
|
381
|
-
var key:String = (number * spaces).toString();
|
382
|
-
var noises:int = randomInt(1, 12);
|
383
|
-
var pos:int;
|
384
|
-
for (var i:int = 0; i < noises; ++i) {
|
385
|
-
var char:String = noiseChars[randomInt(0, noiseChars.length - 1)];
|
386
|
-
pos = randomInt(0, key.length);
|
387
|
-
key = key.substr(0, pos) + char + key.substr(pos);
|
388
|
-
}
|
389
|
-
for (var j:int = 0; j < spaces; ++j) {
|
390
|
-
pos = randomInt(1, key.length - 1);
|
391
|
-
key = key.substr(0, pos) + " " + key.substr(pos);
|
392
|
-
}
|
393
|
-
return key;
|
394
|
-
}
|
395
|
-
|
396
|
-
private function generateKey3():String {
|
397
|
-
var key3:String = "";
|
398
|
-
for (var i:int = 0; i < 8; ++i) {
|
399
|
-
key3 += String.fromCharCode(randomInt(0, 255));
|
400
|
-
}
|
401
|
-
return key3;
|
402
|
-
}
|
403
|
-
|
404
|
-
private function getSecurityDigest(key1:String, key2:String, key3:String):String {
|
405
|
-
var bytes1:String = keyToBytes(key1);
|
406
|
-
var bytes2:String = keyToBytes(key2);
|
407
|
-
return MD5.rstr_md5(bytes1 + bytes2 + key3);
|
408
|
-
}
|
409
|
-
|
410
|
-
private function keyToBytes(key:String):String {
|
411
|
-
var keyNum:uint = parseInt(key.replace(/[^\d]/g, ""));
|
412
|
-
var spaces:uint = 0;
|
413
|
-
for (var i:int = 0; i < key.length; ++i) {
|
414
|
-
if (key.charAt(i) == " ") ++spaces;
|
415
|
-
}
|
416
|
-
var resultNum:uint = keyNum / spaces;
|
417
|
-
var bytes:String = "";
|
418
|
-
for (var j:int = 3; j >= 0; --j) {
|
419
|
-
bytes += String.fromCharCode((resultNum >> (j * 8)) & 0xff);
|
420
|
-
}
|
421
|
-
return bytes;
|
422
|
-
}
|
423
|
-
|
424
|
-
// Writes byte sequence to socket.
|
425
|
-
// bytes is String in special format where bytes[i] is i-th byte, not i-th character.
|
426
|
-
private function writeBytes(bytes:String):void {
|
427
|
-
for (var i:int = 0; i < bytes.length; ++i) {
|
428
|
-
socket.writeByte(bytes.charCodeAt(i));
|
429
|
-
}
|
430
|
-
}
|
431
|
-
|
432
|
-
// Reads specified number of bytes from buffer, and returns it as special format String
|
433
|
-
// where bytes[i] is i-th byte (not i-th character).
|
434
|
-
private function readBytes(buffer:ByteArray, start:int, numBytes:int):String {
|
435
|
-
buffer.position = start;
|
436
|
-
var bytes:String = "";
|
437
|
-
for (var i:int = 0; i < numBytes; ++i) {
|
438
|
-
// & 0xff is to make \x80-\xff positive number.
|
439
|
-
bytes += String.fromCharCode(buffer.readByte() & 0xff);
|
440
|
-
}
|
441
|
-
return bytes;
|
442
|
-
}
|
443
|
-
|
444
|
-
private function readUTFBytes(buffer:ByteArray, start:int, numBytes:int):String {
|
445
|
-
buffer.position = start;
|
446
|
-
var data:String = "";
|
447
|
-
for(var i:int = start; i < start + numBytes; ++i) {
|
448
|
-
// Workaround of a bug of ByteArray#readUTFBytes() that bytes after "\x00" is discarded.
|
449
|
-
if (buffer[i] == 0x00) {
|
450
|
-
data += buffer.readUTFBytes(i - buffer.position) + "\x00";
|
451
|
-
buffer.position = i + 1;
|
452
|
-
}
|
453
|
-
}
|
454
|
-
data += buffer.readUTFBytes(start + numBytes - buffer.position);
|
455
|
-
return data;
|
456
|
-
}
|
457
|
-
|
458
|
-
private function randomInt(min:uint, max:uint):uint {
|
459
|
-
return min + Math.floor(Math.random() * (Number(max) - min + 1));
|
460
|
-
}
|
461
|
-
|
462
|
-
// for debug
|
463
|
-
private function dumpBytes(bytes:String):void {
|
464
|
-
var output:String = "";
|
465
|
-
for (var i:int = 0; i < bytes.length; ++i) {
|
466
|
-
output += bytes.charCodeAt(i).toString() + ", ";
|
467
|
-
}
|
468
|
-
main.log(output);
|
469
|
-
}
|
470
|
-
|
471
|
-
}
|
472
|
-
|
473
|
-
}
|