sonixlabs-em-websocket 0.3.8 → 0.5.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +69 -0
  3. data/Gemfile +6 -0
  4. data/LICENCE +7 -0
  5. data/README.md +100 -56
  6. data/README.md.BACKUP.14928.md +195 -0
  7. data/README.md.BASE.14928.md +77 -0
  8. data/README.md.LOCAL.14928.md +98 -0
  9. data/README.md.REMOTE.14928.md +142 -0
  10. data/examples/echo.rb +23 -7
  11. data/examples/ping.rb +24 -0
  12. data/examples/test.html +5 -6
  13. data/lib/em-websocket.rb +4 -2
  14. data/lib/em-websocket/close03.rb +3 -0
  15. data/lib/em-websocket/close05.rb +3 -0
  16. data/lib/em-websocket/close06.rb +3 -0
  17. data/lib/em-websocket/close75.rb +2 -1
  18. data/lib/em-websocket/connection.rb +219 -73
  19. data/lib/em-websocket/framing03.rb +6 -11
  20. data/lib/em-websocket/framing05.rb +6 -11
  21. data/lib/em-websocket/framing07.rb +25 -20
  22. data/lib/em-websocket/framing76.rb +6 -15
  23. data/lib/em-websocket/handler.rb +69 -28
  24. data/lib/em-websocket/handler03.rb +0 -1
  25. data/lib/em-websocket/handler05.rb +0 -1
  26. data/lib/em-websocket/handler06.rb +0 -1
  27. data/lib/em-websocket/handler07.rb +0 -1
  28. data/lib/em-websocket/handler08.rb +0 -1
  29. data/lib/em-websocket/handler13.rb +0 -1
  30. data/lib/em-websocket/handler76.rb +2 -0
  31. data/lib/em-websocket/handshake.rb +156 -0
  32. data/lib/em-websocket/handshake04.rb +18 -56
  33. data/lib/em-websocket/handshake75.rb +15 -8
  34. data/lib/em-websocket/handshake76.rb +15 -14
  35. data/lib/em-websocket/masking04.rb +4 -30
  36. data/lib/em-websocket/message_processor_03.rb +13 -4
  37. data/lib/em-websocket/message_processor_06.rb +25 -13
  38. data/lib/em-websocket/version.rb +1 -1
  39. data/lib/em-websocket/websocket.rb +35 -24
  40. data/spec/helper.rb +82 -55
  41. data/spec/integration/common_spec.rb +90 -70
  42. data/spec/integration/draft03_spec.rb +84 -56
  43. data/spec/integration/draft05_spec.rb +14 -12
  44. data/spec/integration/draft06_spec.rb +66 -9
  45. data/spec/integration/draft13_spec.rb +59 -29
  46. data/spec/integration/draft75_spec.rb +46 -40
  47. data/spec/integration/draft76_spec.rb +113 -109
  48. data/spec/integration/gte_03_examples.rb +42 -0
  49. data/spec/integration/shared_examples.rb +174 -0
  50. data/spec/unit/framing_spec.rb +83 -110
  51. data/spec/unit/handshake_spec.rb +216 -0
  52. data/spec/unit/masking_spec.rb +2 -0
  53. metadata +31 -71
  54. data/examples/flash_policy_file_server.rb +0 -21
  55. data/examples/js/FABridge.js +0 -604
  56. data/examples/js/WebSocketMain.swf +0 -0
  57. data/examples/js/swfobject.js +0 -4
  58. data/examples/js/web_socket.js +0 -312
  59. data/lib/em-websocket/handler_factory.rb +0 -107
  60. data/spec/unit/handler_spec.rb +0 -147
Binary file
@@ -1,4 +0,0 @@
1
- /* SWFObject v2.2 <http://code.google.com/p/swfobject/>
2
- is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
3
- */
4
- var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
@@ -1,312 +0,0 @@
1
- // Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
2
- // Lincense: New BSD Lincense
3
- // Reference: http://dev.w3.org/html5/websockets/
4
- // Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
5
-
6
- (function() {
7
-
8
- if (window.WebSocket) return;
9
-
10
- var console = window.console;
11
- if (!console) console = {log: function(){ }, error: function(){ }};
12
-
13
- function hasFlash() {
14
- if ('navigator' in window && 'plugins' in navigator && navigator.plugins['Shockwave Flash']) {
15
- return !!navigator.plugins['Shockwave Flash'].description;
16
- }
17
- if ('ActiveXObject' in window) {
18
- try {
19
- return !!new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
20
- } catch (e) {}
21
- }
22
- return false;
23
- }
24
-
25
- if (!hasFlash()) {
26
- console.error("Flash Player is not installed.");
27
- return;
28
- }
29
-
30
- WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
31
- var self = this;
32
- self.readyState = WebSocket.CONNECTING;
33
- self.bufferedAmount = 0;
34
- WebSocket.__addTask(function() {
35
- self.__flash =
36
- WebSocket.__flash.create(url, protocol, proxyHost || null, proxyPort || 0, headers || null);
37
-
38
- self.__flash.addEventListener("open", function(fe) {
39
- try {
40
- if (self.onopen) self.onopen();
41
- } catch (e) {
42
- console.error(e.toString());
43
- }
44
- });
45
-
46
- self.__flash.addEventListener("close", function(fe) {
47
- try {
48
- if (self.onclose) self.onclose();
49
- } catch (e) {
50
- console.error(e.toString());
51
- }
52
- });
53
-
54
- self.__flash.addEventListener("message", function(fe) {
55
- var data = decodeURIComponent(fe.getData());
56
- try {
57
- if (self.onmessage) {
58
- var e;
59
- if (window.MessageEvent) {
60
- e = document.createEvent("MessageEvent");
61
- e.initMessageEvent("message", false, false, data, null, null, window);
62
- } else { // IE
63
- e = {data: data};
64
- }
65
- self.onmessage(e);
66
- }
67
- } catch (e) {
68
- console.error(e.toString());
69
- }
70
- });
71
-
72
- self.__flash.addEventListener("stateChange", function(fe) {
73
- try {
74
- self.readyState = fe.getReadyState();
75
- self.bufferedAmount = fe.getBufferedAmount();
76
- } catch (e) {
77
- console.error(e.toString());
78
- }
79
- });
80
-
81
- //console.log("[WebSocket] Flash object is ready");
82
- });
83
- }
84
-
85
- WebSocket.prototype.send = function(data) {
86
- if (!this.__flash || this.readyState == WebSocket.CONNECTING) {
87
- throw "INVALID_STATE_ERR: Web Socket connection has not been established";
88
- }
89
- var result = this.__flash.send(data);
90
- if (result < 0) { // success
91
- return true;
92
- } else {
93
- this.bufferedAmount = result;
94
- return false;
95
- }
96
- };
97
-
98
- WebSocket.prototype.close = function() {
99
- if (!this.__flash) return;
100
- if (this.readyState != WebSocket.OPEN) return;
101
- this.__flash.close();
102
- // Sets/calls them manually here because Flash WebSocketConnection.close cannot fire events
103
- // which causes weird error:
104
- // > You are trying to call recursively into the Flash Player which is not allowed.
105
- this.readyState = WebSocket.CLOSED;
106
- if (this.onclose) this.onclose();
107
- };
108
-
109
- /**
110
- * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
111
- *
112
- * @param {string} type
113
- * @param {function} listener
114
- * @param {boolean} useCapture !NB Not implemented yet
115
- * @return void
116
- */
117
- WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
118
- if (!('__events' in this)) {
119
- this.__events = {};
120
- }
121
- if (!(type in this.__events)) {
122
- this.__events[type] = [];
123
- if ('function' == typeof this['on' + type]) {
124
- this.__events[type].defaultHandler = this['on' + type];
125
- this['on' + type] = WebSocket_FireEvent(this, type);
126
- }
127
- }
128
- this.__events[type].push(listener);
129
- };
130
-
131
- /**
132
- * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
133
- *
134
- * @param {string} type
135
- * @param {function} listener
136
- * @param {boolean} useCapture NB! Not implemented yet
137
- * @return void
138
- */
139
- WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
140
- if (!('__events' in this)) {
141
- this.__events = {};
142
- }
143
- if (!(type in this.__events)) return;
144
- for (var i = this.__events.length; i > -1; --i) {
145
- if (listener === this.__events[type][i]) {
146
- this.__events[type].splice(i, 1);
147
- break;
148
- }
149
- }
150
- };
151
-
152
- /**
153
- * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
154
- *
155
- * @param {WebSocketEvent} event
156
- * @return void
157
- */
158
- WebSocket.prototype.dispatchEvent = function(event) {
159
- if (!('__events' in this)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
160
- if (!(event.type in this.__events)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
161
-
162
- for (var i = 0, l = this.__events[event.type].length; i < l; ++ i) {
163
- this.__events[event.type][i](event);
164
- if (event.cancelBubble) break;
165
- }
166
-
167
- if (false !== event.returnValue &&
168
- 'function' == typeof this.__events[event.type].defaultHandler)
169
- {
170
- this.__events[event.type].defaultHandler(event);
171
- }
172
- };
173
-
174
- /**
175
- *
176
- * @param {object} object
177
- * @param {string} type
178
- */
179
- function WebSocket_FireEvent(object, type) {
180
- return function(data) {
181
- var event = new WebSocketEvent();
182
- event.initEvent(type, true, true);
183
- event.target = event.currentTarget = object;
184
- for (var key in data) {
185
- event[key] = data[key];
186
- }
187
- object.dispatchEvent(event, arguments);
188
- };
189
- }
190
-
191
- /**
192
- * Basic implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface">DOM 2 EventInterface</a>}
193
- *
194
- * @class
195
- * @constructor
196
- */
197
- function WebSocketEvent(){}
198
-
199
- /**
200
- *
201
- * @type boolean
202
- */
203
- WebSocketEvent.prototype.cancelable = true;
204
-
205
- /**
206
- *
207
- * @type boolean
208
- */
209
- WebSocketEvent.prototype.cancelBubble = false;
210
-
211
- /**
212
- *
213
- * @return void
214
- */
215
- WebSocketEvent.prototype.preventDefault = function() {
216
- if (this.cancelable) {
217
- this.returnValue = false;
218
- }
219
- };
220
-
221
- /**
222
- *
223
- * @return void
224
- */
225
- WebSocketEvent.prototype.stopPropagation = function() {
226
- this.cancelBubble = true;
227
- };
228
-
229
- /**
230
- *
231
- * @param {string} eventTypeArg
232
- * @param {boolean} canBubbleArg
233
- * @param {boolean} cancelableArg
234
- * @return void
235
- */
236
- WebSocketEvent.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) {
237
- this.type = eventTypeArg;
238
- this.cancelable = cancelableArg;
239
- this.timeStamp = new Date();
240
- };
241
-
242
-
243
- WebSocket.CONNECTING = 0;
244
- WebSocket.OPEN = 1;
245
- WebSocket.CLOSED = 2;
246
-
247
- WebSocket.__tasks = [];
248
-
249
- WebSocket.__initialize = function() {
250
- if (!WebSocket.__swfLocation) {
251
- //console.error("[WebSocket] set WebSocket.__swfLocation to location of WebSocketMain.swf");
252
- //return;
253
- WebSocket.__swfLocation = "js/WebSocketMain.swf";
254
- }
255
- var container = document.createElement("div");
256
- container.id = "webSocketContainer";
257
- // Puts the Flash out of the window. Note that we cannot use display: none or visibility: hidden
258
- // here because it prevents Flash from loading at least in IE.
259
- container.style.position = "absolute";
260
- container.style.left = "-100px";
261
- container.style.top = "-100px";
262
- var holder = document.createElement("div");
263
- holder.id = "webSocketFlash";
264
- container.appendChild(holder);
265
- document.body.appendChild(container);
266
- swfobject.embedSWF(
267
- WebSocket.__swfLocation, "webSocketFlash", "8", "8", "9.0.0",
268
- null, {bridgeName: "webSocket"}, null, null,
269
- function(e) {
270
- if (!e.success) console.error("[WebSocket] swfobject.embedSWF failed");
271
- }
272
- );
273
- FABridge.addInitializationCallback("webSocket", function() {
274
- try {
275
- //console.log("[WebSocket] FABridge initializad");
276
- WebSocket.__flash = FABridge.webSocket.root();
277
- WebSocket.__flash.setCallerUrl(location.href);
278
- for (var i = 0; i < WebSocket.__tasks.length; ++i) {
279
- WebSocket.__tasks[i]();
280
- }
281
- WebSocket.__tasks = [];
282
- } catch (e) {
283
- console.error("[WebSocket] " + e.toString());
284
- }
285
- });
286
- };
287
-
288
- WebSocket.__addTask = function(task) {
289
- if (WebSocket.__flash) {
290
- task();
291
- } else {
292
- WebSocket.__tasks.push(task);
293
- }
294
- }
295
-
296
- // called from Flash
297
- function webSocketLog(message) {
298
- console.log(decodeURIComponent(message));
299
- }
300
-
301
- // called from Flash
302
- function webSocketError(message) {
303
- console.error(decodeURIComponent(message));
304
- }
305
-
306
- if (window.addEventListener) {
307
- window.addEventListener("load", WebSocket.__initialize, false);
308
- } else {
309
- window.attachEvent("onload", WebSocket.__initialize);
310
- }
311
-
312
- })();
@@ -1,107 +0,0 @@
1
- module EventMachine
2
- module WebSocket
3
- class HandlerFactory
4
- PATH = /^(\w+) (\/[^\s]*) HTTP\/1\.1$/
5
- HEADER = /^([^:]+):\s*(.+)$/
6
-
7
- def self.build(connection, data, secure = false, debug = false)
8
- (header, remains) = data.split("\r\n\r\n", 2)
9
- unless remains
10
- # The whole header has not been received yet.
11
- return nil
12
- end
13
-
14
- request = {}
15
-
16
- lines = header.split("\r\n")
17
-
18
- # extract request path
19
- first_line = lines.shift.match(PATH)
20
- raise HandshakeError, "Invalid HTTP header" unless first_line
21
- request['method'] = first_line[1].strip
22
- request['path'] = first_line[2].strip
23
-
24
- unless request["method"] == "GET"
25
- raise HandshakeError, "Must be GET request"
26
- end
27
-
28
- # extract query string values
29
- request['query'] = Addressable::URI.parse(request['path']).query_values ||= {}
30
- # extract remaining headers
31
- lines.each do |line|
32
- h = HEADER.match(line)
33
- request[h[1].strip.downcase] = h[2].strip if h
34
- end
35
-
36
- build_with_request(connection, request, remains, secure, debug)
37
- end
38
-
39
- def self.build_with_request(connection, request, remains, secure = false, debug = false)
40
- # Determine version heuristically
41
- version = if request['sec-websocket-version']
42
- # Used from drafts 04 onwards
43
- request['sec-websocket-version'].to_i
44
- elsif request['sec-websocket-draft']
45
- # Used in drafts 01 - 03
46
- request['sec-websocket-draft'].to_i
47
- elsif request['sec-websocket-key1']
48
- 76
49
- else
50
- 75
51
- end
52
-
53
- # Additional handling of bytes after the header if required
54
- case version
55
- when 75
56
- if !remains.empty?
57
- raise HandshakeError, "Extra bytes after header"
58
- end
59
- when 76, 1..3
60
- if remains.length < 8
61
- # The whole third-key has not been received yet.
62
- return nil
63
- elsif remains.length > 8
64
- raise HandshakeError, "Extra bytes after third key"
65
- end
66
- request['third-key'] = remains
67
- end
68
-
69
- # Validate that Connection and Upgrade headers
70
- unless request['connection'] && request['connection'] =~ /Upgrade/ && request['upgrade'] && request['upgrade'].downcase == 'websocket'
71
- raise HandshakeError, "Connection and Upgrade headers required"
72
- end
73
-
74
- # transform headers
75
- protocol = (secure ? "wss" : "ws")
76
- request['host'] = Addressable::URI.parse("#{protocol}://"+request['host'])
77
-
78
- case version
79
- when 75
80
- Handler75.new(connection, request, debug)
81
- when 76
82
- Handler76.new(connection, request, debug)
83
- when 1..3
84
- # We'll use handler03 - I believe they're all compatible
85
- Handler03.new(connection, request, debug)
86
- when 5
87
- Handler05.new(connection, request, debug)
88
- when 6
89
- Handler06.new(connection, request, debug)
90
- when 7
91
- Handler07.new(connection, request, debug)
92
- when 8
93
- # drafts 9, 10, 11 and 12 should never change the version
94
- # number as they are all the same as version 08.
95
- Handler08.new(connection, request, debug)
96
- when 13
97
- # drafts 13 to 17 all identify as version 13 as they are
98
- # only minor changes or text changes.
99
- Handler13.new(connection, request, debug)
100
- else
101
- # According to spec should abort the connection
102
- raise WebSocketError, "Protocol version #{version} not supported"
103
- end
104
- end
105
- end
106
- end
107
- end
@@ -1,147 +0,0 @@
1
- require 'helper'
2
-
3
- describe "EventMachine::WebSocket::Handler" do
4
- before :each do
5
- @request = {
6
- :port => 80,
7
- :method => "GET",
8
- :path => "/demo",
9
- :headers => {
10
- 'Host' => 'example.com',
11
- 'Connection' => 'Upgrade',
12
- 'Sec-WebSocket-Key2' => '12998 5 Y3 1 .P00',
13
- 'Sec-WebSocket-Protocol' => 'sample',
14
- 'Upgrade' => 'WebSocket',
15
- 'Sec-WebSocket-Key1' => '4 @1 46546xW%0l 1 5',
16
- 'Origin' => 'http://example.com'
17
- },
18
- :body => '^n:ds[4U'
19
- }
20
- @secure_request = @request.merge(:port => 443)
21
-
22
- @response = {
23
- :headers => {
24
- "Upgrade" => "WebSocket",
25
- "Connection" => "Upgrade",
26
- "Sec-WebSocket-Location" => "ws://example.com/demo",
27
- "Sec-WebSocket-Origin" => "http://example.com",
28
- "Sec-WebSocket-Protocol" => "sample"
29
- },
30
- :body => "8jKS\'y:G*Co,Wxa-"
31
- }
32
- @secure_response = @response.merge(:headers => @response[:headers].merge('Sec-WebSocket-Location' => "wss://example.com/demo"))
33
- end
34
-
35
- it "should handle good request" do
36
- handler(@request).should send_handshake(@response)
37
- end
38
-
39
- it "should handle good request to secure default port if secure mode is enabled" do
40
- handler(@secure_request, true).should send_handshake(@secure_response)
41
- end
42
-
43
- it "should not handle good request to secure default port if secure mode is disabled" do
44
- handler(@secure_request, false).should_not send_handshake(@secure_response)
45
- end
46
-
47
- it "should handle good request on nondefault port" do
48
- @request[:port] = 8081
49
- @request[:headers]['Host'] = 'example.com:8081'
50
- @response[:headers]['Sec-WebSocket-Location'] =
51
- 'ws://example.com:8081/demo'
52
-
53
- handler(@request).should send_handshake(@response)
54
- end
55
-
56
- it "should handle good request to secure nondefault port" do
57
- @secure_request[:port] = 8081
58
- @secure_request[:headers]['Host'] = 'example.com:8081'
59
- @secure_response[:headers]['Sec-WebSocket-Location'] = 'wss://example.com:8081/demo'
60
- handler(@secure_request, true).should send_handshake(@secure_response)
61
- end
62
-
63
- it "should handle good request with no protocol" do
64
- @request[:headers].delete('Sec-WebSocket-Protocol')
65
- @response[:headers].delete("Sec-WebSocket-Protocol")
66
-
67
- handler(@request).should send_handshake(@response)
68
- end
69
-
70
- it "should handle extra headers by simply ignoring them" do
71
- @request[:headers]['EmptyValue'] = ""
72
- @request[:headers]['AKey'] = "AValue"
73
-
74
- handler(@request).should send_handshake(@response)
75
- end
76
-
77
- it "should raise error on HTTP request" do
78
- @request[:headers] = {
79
- 'Host' => 'www.google.com',
80
- 'User-Agent' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 GTB6 GTBA',
81
- 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
82
- 'Accept-Language' => 'en-us,en;q=0.5',
83
- 'Accept-Encoding' => 'gzip,deflate',
84
- 'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
85
- 'Keep-Alive' => '300',
86
- 'Connection' => 'keep-alive',
87
- }
88
-
89
- lambda {
90
- handler(@request).handshake
91
- }.should raise_error(EM::WebSocket::HandshakeError)
92
- end
93
-
94
- it "should raise error on wrong method" do
95
- @request[:method] = 'POST'
96
-
97
- lambda {
98
- handler(@request).handshake
99
- }.should raise_error(EM::WebSocket::HandshakeError)
100
- end
101
-
102
- it "should raise error if upgrade header incorrect" do
103
- @request[:headers]['Upgrade'] = 'NonWebSocket'
104
-
105
- lambda {
106
- handler(@request).handshake
107
- }.should raise_error(EM::WebSocket::HandshakeError)
108
- end
109
-
110
- it "should raise error if Sec-WebSocket-Protocol is empty" do
111
- @request[:headers]['Sec-WebSocket-Protocol'] = ''
112
-
113
- lambda {
114
- handler(@request).handshake
115
- }.should raise_error(EM::WebSocket::HandshakeError)
116
- end
117
-
118
- %w[Sec-WebSocket-Key1 Sec-WebSocket-Key2].each do |header|
119
- it "should raise error if #{header} has zero spaces" do
120
- @request[:headers][header] = 'nospaces'
121
-
122
- lambda {
123
- handler(@request).handshake
124
- }.should raise_error(EM::WebSocket::HandshakeError, 'Websocket Key1 or Key2 does not contain spaces - this is a symptom of a cross-protocol attack')
125
- end
126
- end
127
-
128
- it "should raise error if spaces do not divide numbers in Sec-WebSocket-Key* " do
129
- @request[:headers]['Sec-WebSocket-Key2'] = '12998 5 Y3 1.P00'
130
-
131
- lambda {
132
- handler(@request).handshake
133
- }.should raise_error(EM::WebSocket::HandshakeError, 'Invalid Key "12998 5 Y3 1.P00"')
134
- end
135
-
136
- it "should leave request with incomplete header" do
137
- data = format_request(@request)
138
- # Sends only half of the request
139
- EM::WebSocket::HandlerFactory.build(mock(EM::WebSocket::Connection), data[0...(data.length / 2)]).should == nil
140
- end
141
-
142
- it "should leave request with incomplete third key" do
143
- data = format_request(@request)
144
- # Removes last two bytes of the third key
145
- EM::WebSocket::HandlerFactory.build(mock(EM::WebSocket::Connection), data[0...(data.length - 2)]).should == nil
146
- end
147
- end