faye 0.6.3 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of faye might be problematic. Click here for more details.
- data/History.txt +8 -0
- data/lib/faye-browser-min.js +1 -1
- data/lib/faye.rb +1 -1
- data/lib/faye/adapters/rack_adapter.rb +3 -3
- data/lib/faye/engines/memory.rb +12 -7
- data/lib/faye/mixins/logging.rb +2 -0
- data/lib/faye/util/web_socket.rb +30 -33
- data/lib/faye/util/web_socket/draft75_parser.rb +52 -0
- data/lib/faye/util/web_socket/draft76_parser.rb +54 -0
- data/lib/faye/util/web_socket/protocol8_parser.rb +184 -0
- data/lib/thin_extensions.rb +7 -68
- data/spec/install.sh +11 -6
- data/spec/testswarm +33 -0
- metadata +7 -6
- data/spec/testswarm.pl +0 -200
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
=== 0.6.4 / 2011-08-18
|
2
|
+
TestSwarm build: http://swarm.jcoglan.com/job/96/
|
3
|
+
|
4
|
+
* Support WebSocket protocol used by Chrome 14 and Firefox 6
|
5
|
+
* Fix handling of multibyte characters in WebSocket messages on Node
|
6
|
+
* Improve message routing in Node memory engine to avoid false duplicates
|
7
|
+
|
8
|
+
|
1
9
|
=== 0.6.3 / 2011-07-10
|
2
10
|
TestSwarm build: http://swarm.jcoglan.com/job/69/
|
3
11
|
|
data/lib/faye-browser-min.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
if(!this.Faye)Faye={};Faye.extend=function(a,b,c){if(!b)return a;for(var d in b){if(!b.hasOwnProperty(d))continue;if(a.hasOwnProperty(d)&&c===false)continue;if(a[d]!==b[d])a[d]=b[d]}return a};Faye.extend(Faye,{VERSION:'0.6.3',BAYEUX_VERSION:'1.0',ID_LENGTH:128,JSONP_CALLBACK:'jsonpcallback',CONNECTION_TYPES:['long-polling','cross-origin-long-polling','callback-polling','websocket','in-process'],MANDATORY_CONNECTION_TYPES:['long-polling','callback-polling','in-process'],ENV:(function(){return this})(),random:function(a){a=a||this.ID_LENGTH;if(a>32){var b=Math.ceil(a/32),c='';while(b--)c+=this.random(32);return c}var d=Math.pow(2,a)-1,f=d.toString(36).length,c=Math.floor(Math.random()*d).toString(36);while(c.length<f)c='0'+c;return c},commonElement:function(a,b){for(var c=0,d=a.length;c<d;c++){if(this.indexOf(b,a[c])!==-1)return a[c]}return null},indexOf:function(a,b){for(var c=0,d=a.length;c<d;c++){if(a[c]===b)return c}return-1},each:function(a,b,c){if(a instanceof Array){for(var d=0,f=a.length;d<f;d++){if(a[d]!==undefined)b.call(c||null,a[d],d)}}else{for(var g in a){if(a.hasOwnProperty(g))b.call(c||null,g,a[g])}}},map:function(a,b,c){var d=[];this.each(a,function(){d.push(b.apply(c||null,arguments))});return d},filter:function(a,b,c){var d=[];this.each(a,function(){if(b.apply(c,arguments))d.push(arguments[0])});return d},size:function(a){var b=0;this.each(a,function(){b+=1});return b},enumEqual:function(c,d){if(d instanceof Array){if(!(c instanceof Array))return false;var f=c.length;if(f!==d.length)return false;while(f--){if(c[f]!==d[f])return false}return true}else{if(!(c instanceof Object))return false;if(this.size(d)!==this.size(c))return false;var g=true;this.each(c,function(a,b){g=g&&(d[a]===b)});return g}},asyncEach:function(a,b,c,d){var f=a.length,g=-1,h=0,i=false;var j=function(){h-=1;g+=1;if(g===f)return c&&c.call(d);b(a[g],m)};var k=function(){if(i)return;i=true;while(h>0)j();i=false};var m=function(){h+=1;k()};m()},toJSON:function(a){if(this.stringify)return this.stringify(a,function(key,value){return(this[key]instanceof Array)?this[key]:value});return JSON.stringify(a)},timestamp:function(){var b=new Date(),c=b.getFullYear(),d=b.getMonth()+1,f=b.getDate(),g=b.getHours(),h=b.getMinutes(),i=b.getSeconds();var j=function(a){return a<10?'0'+a:String(a)};return j(c)+'-'+j(d)+'-'+j(f)+' '+j(g)+':'+j(h)+':'+j(i)}});Faye.Class=function(a,b){if(typeof a!=='function'){b=a;a=Object}var c=function(){if(!this.initialize)return this;return this.initialize.apply(this,arguments)||this};var d=function(){};d.prototype=a.prototype;c.prototype=new d();Faye.extend(c.prototype,b);return c};Faye.Namespace=Faye.Class({initialize:function(){this._c={}},exists:function(a){return this._c.hasOwnProperty(a)},generate:function(){var a=Faye.random();while(this._c.hasOwnProperty(a))a=Faye.random();return this._c[a]=a},release:function(a){delete this._c[a]}});Faye.Error=Faye.Class({initialize:function(a,b,c){this.code=a;this.params=Array.prototype.slice.call(b);this.message=c},toString:function(){return this.code+':'+this.params.join(',')+':'+this.message}});Faye.Error.parse=function(a){a=a||'';if(!Faye.Grammar.ERROR.test(a))return new this(null,[],a);var b=a.split(':'),c=parseInt(b[0]),d=b[1].split(','),a=b[2];return new this(c,d,a)};Faye.Error.versionMismatch=function(){return new this(300,arguments,"Version mismatch").toString()};Faye.Error.conntypeMismatch=function(){return new this(301,arguments,"Connection types not supported").toString()};Faye.Error.extMismatch=function(){return new this(302,arguments,"Extension mismatch").toString()};Faye.Error.badRequest=function(){return new this(400,arguments,"Bad request").toString()};Faye.Error.clientUnknown=function(){return new this(401,arguments,"Unknown client").toString()};Faye.Error.parameterMissing=function(){return new this(402,arguments,"Missing required parameter").toString()};Faye.Error.channelForbidden=function(){return new this(403,arguments,"Forbidden channel").toString()};Faye.Error.channelUnknown=function(){return new this(404,arguments,"Unknown channel").toString()};Faye.Error.channelInvalid=function(){return new this(405,arguments,"Invalid channel").toString()};Faye.Error.extUnknown=function(){return new this(406,arguments,"Unknown extension").toString()};Faye.Error.publishFailed=function(){return new this(407,arguments,"Failed to publish").toString()};Faye.Error.serverError=function(){return new this(500,arguments,"Internal server error").toString()};Faye.Deferrable={callback:function(a,b){if(!a)return;if(this._t==='succeeded')return a.apply(b,this._j);this._k=this._k||[];this._k.push([a,b])},errback:function(a,b){if(!a)return;if(this._t==='failed')return a.apply(b,this._j);this._l=this._l||[];this._l.push([a,b])},setDeferredStatus:function(){var a=Array.prototype.slice.call(arguments),b=a.shift(),c;this._t=b;this._j=a;if(b==='succeeded')c=this._k;else if(b==='failed')c=this._l;if(!c)return;var d;while(d=c.shift())d[0].apply(d[1],this._j)}};Faye.Publisher={countSubscribers:function(a){if(!this._3||!this._3[a])return 0;return this._3[a].length},addSubscriber:function(a,b,c){this._3=this._3||{};var d=this._3[a]=this._3[a]||[];d.push([b,c])},removeSubscriber:function(a,b,c){if(!this._3||!this._3[a])return;if(!b){delete this._3[a];return}var d=this._3[a],f=d.length;while(f--){if(b!==d[f][0])continue;if(c&&d[f][1]!==c)continue;d.splice(f,1)}},removeSubscribers:function(){this._3={}},publishEvent:function(){var b=Array.prototype.slice.call(arguments),c=b.shift();if(!this._3||!this._3[c])return;Faye.each(this._3[c],function(a){a[0].apply(a[1],b)})}};Faye.Timeouts={addTimeout:function(a,b,c,d){this._4=this._4||{};if(this._4.hasOwnProperty(a))return;var f=this;this._4[a]=Faye.ENV.setTimeout(function(){delete f._4[a];c.call(d)},1000*b)},removeTimeout:function(a){this._4=this._4||{};var b=this._4[a];if(!b)return;clearTimeout(b);delete this._4[a]}};Faye.Logging={LOG_LEVELS:{error:3,warn:2,info:1,debug:0},logLevel:'error',log:function(a,b){if(!Faye.logger)return;var c=Faye.Logging.LOG_LEVELS;if(c[Faye.Logging.logLevel]>c[b])return;var a=Array.prototype.slice.apply(a),d=' ['+b.toUpperCase()+'] [Faye',f=this.className,g=a.shift().replace(/\?/g,function(){try{return Faye.toJSON(a.shift())}catch(e){return'[Object]'}});for(var h in Faye){if(f)continue;if(typeof Faye[h]!=='function')continue;if(this instanceof Faye[h])f=h}if(f)d+='.'+f;d+='] ';Faye.logger(Faye.timestamp()+d+g)}};Faye.each(Faye.Logging.LOG_LEVELS,function(a,b){Faye.Logging[a]=function(){this.log(arguments,a)}});Faye.Grammar={LOWALPHA:/^[a-z]$/,UPALPHA:/^[A-Z]$/,ALPHA:/^([a-z]|[A-Z])$/,DIGIT:/^[0-9]$/,ALPHANUM:/^(([a-z]|[A-Z])|[0-9])$/,MARK:/^(\-|\_|\!|\~|\(|\)|\$|\@)$/,STRING:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,TOKEN:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,INTEGER:/^([0-9])+$/,CHANNEL_SEGMENT:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,CHANNEL_SEGMENTS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,CHANNEL_NAME:/^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,WILD_CARD:/^\*{1,2}$/,CHANNEL_PATTERN:/^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,VERSION_ELEMENT:/^(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*$/,VERSION:/^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/,CLIENT_ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ERROR_MESSAGE:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,ERROR_ARGS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*$/,ERROR_CODE:/^[0-9][0-9][0-9]$/,ERROR:/^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/};Faye.Extensible={addExtension:function(a){this._5=this._5||[];this._5.push(a);if(a.added)a.added()},removeExtension:function(a){if(!this._5)return;var b=this._5.length;while(b--){if(this._5[b]!==a)continue;this._5.splice(b,1);if(a.removed)a.removed()}},pipeThroughExtensions:function(c,d,f,g){this.debug('Passing through ? extensions: ?',c,d);if(!this._5)return f.call(g,d);var h=this._5.slice();var i=function(a){if(!a)return f.call(g,a);var b=h.shift();if(!b)return f.call(g,a);if(b[c])b[c](a,i);else i(a)};i(d)}};Faye.extend(Faye.Extensible,Faye.Logging);Faye.Channel=Faye.Class({initialize:function(a){this.id=this.name=a},push:function(a){this.publishEvent('message',a)},isUnused:function(){return this.countSubscribers('message')===0}});Faye.extend(Faye.Channel.prototype,Faye.Publisher);Faye.extend(Faye.Channel,{HANDSHAKE:'/meta/handshake',CONNECT:'/meta/connect',SUBSCRIBE:'/meta/subscribe',UNSUBSCRIBE:'/meta/unsubscribe',DISCONNECT:'/meta/disconnect',META:'meta',SERVICE:'service',expand:function(a){var b=this.parse(a),c=['/**',a];var d=b.slice();d[d.length-1]='*';c.push(this.unparse(d));for(var f=1,g=b.length;f<g;f++){d=b.slice(0,f);d.push('**');c.push(this.unparse(d))}return c},isValid:function(a){return Faye.Grammar.CHANNEL_NAME.test(a)||Faye.Grammar.CHANNEL_PATTERN.test(a)},parse:function(a){if(!this.isValid(a))return null;return a.split('/').slice(1)},unparse:function(a){return'/'+a.join('/')},isMeta:function(a){var b=this.parse(a);return b?(b[0]===this.META):null},isService:function(a){var b=this.parse(a);return b?(b[0]===this.SERVICE):null},isSubscribable:function(a){if(!this.isValid(a))return null;return!this.isMeta(a)&&!this.isService(a)},Set:Faye.Class({initialize:function(){this._2={}},getKeys:function(){var c=[];Faye.each(this._2,function(a,b){c.push(a)});return c},remove:function(a){delete this._2[a]},hasSubscription:function(a){return this._2.hasOwnProperty(a)},subscribe:function(c,d,f){if(!d)return;Faye.each(c,function(a){var b=this._2[a]=this._2[a]||new Faye.Channel(a);b.addSubscriber('message',d,f)},this)},unsubscribe:function(a,b,c){var d=this._2[a];if(!d)return false;d.removeSubscriber('message',b,c);if(d.isUnused()){this.remove(a);return true}else{return false}},distributeMessage:function(c){var d=Faye.Channel.expand(c.channel);Faye.each(d,function(a){var b=this._2[a];if(b)b.publishEvent('message',c.data)},this)}})});Faye.Subscription=Faye.Class({initialize:function(a,b,c,d){this._8=a;this._2=b;this._m=c;this._n=d;this._u=false},cancel:function(){if(this._u)return;this._8.unsubscribe(this._2,this._m,this._n);this._u=true},unsubscribe:function(){this.cancel()}});Faye.extend(Faye.Subscription.prototype,Faye.Deferrable);Faye.Client=Faye.Class({UNCONNECTED:1,CONNECTING:2,CONNECTED:3,DISCONNECTED:4,HANDSHAKE:'handshake',RETRY:'retry',NONE:'none',CONNECTION_TIMEOUT:60.0,DEFAULT_ENDPOINT:'/bayeux',INTERVAL:0.0,initialize:function(b,c){this.info('New client created for ?',b);this.endpoint=b||this.DEFAULT_ENDPOINT;this._v=c||{};Faye.Transport.get(this,Faye.MANDATORY_CONNECTION_TYPES,function(a){this._d=a},this);this._1=this.UNCONNECTED;this._2=new Faye.Channel.Set();this._e=0;this._o={};this._6={reconnect:this.RETRY,interval:1000*(this._v.interval||this.INTERVAL),timeout:1000*(this._v.timeout||this.CONNECTION_TIMEOUT)};if(Faye.Event)Faye.Event.on(Faye.ENV,'beforeunload',this.disconnect,this)},getClientId:function(){return this._0},getState:function(){switch(this._1){case this.UNCONNECTED:return'UNCONNECTED';case this.CONNECTING:return'CONNECTING';case this.CONNECTED:return'CONNECTED';case this.DISCONNECTED:return'DISCONNECTED'}},handshake:function(c,d){if(this._6.reconnect===this.NONE)return;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;var f=this;this.info('Initiating handshake with ?',this.endpoint);this._9({channel:Faye.Channel.HANDSHAKE,version:Faye.BAYEUX_VERSION,supportedConnectionTypes:[this._d.connectionType]},function(b){if(b.successful){this._1=this.CONNECTED;this._0=b.clientId;Faye.Transport.get(this,b.supportedConnectionTypes,function(a){this._d=a},this);this.info('Handshake successful: ?',this._0);this.subscribe(this._2.getKeys(),true);if(c)c.call(d)}else{this.info('Handshake unsuccessful');Faye.ENV.setTimeout(function(){f.handshake(c,d)},this._6.interval);this._1=this.UNCONNECTED}},this)},connect:function(a,b){if(this._6.reconnect===this.NONE)return;if(this._1===this.DISCONNECTED)return;if(this._1===this.UNCONNECTED)return this.handshake(function(){this.connect(a,b)},this);this.callback(a,b);if(this._1!==this.CONNECTED)return;this.info('Calling deferred actions for ?',this._0);this.setDeferredStatus('succeeded');this.setDeferredStatus('deferred');if(this._p)return;this._p=true;this.info('Initiating connection for ?',this._0);this._9({channel:Faye.Channel.CONNECT,clientId:this._0,connectionType:this._d.connectionType},this._w,this)},disconnect:function(){if(this._1!==this.CONNECTED)return;this._1=this.DISCONNECTED;this.info('Disconnecting ?',this._0);this._9({channel:Faye.Channel.DISCONNECT,clientId:this._0});this.info('Clearing channel listeners for ?',this._0);this._2=new Faye.Channel.Set()},subscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.subscribe(channel,d,f)},this);var g=new Faye.Subscription(this,c,d,f);var h=(d===true);if(!h&&this._2.hasSubscription(c)){this._2.subscribe([c],d,f);g.setDeferredStatus('succeeded');return g}this.connect(function(){this.info('Client ? attempting to subscribe to ?',this._0,c);this._9({channel:Faye.Channel.SUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return g.setDeferredStatus('failed',Faye.Error.parse(a.error));var b=[].concat(a.subscription);this.info('Subscription acknowledged for ? to ?',this._0,b);if(!h)this._2.subscribe(b,d,f);g.setDeferredStatus('succeeded')},this)},this);return g},unsubscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.unsubscribe(channel,d,f)},this);var g=this._2.unsubscribe(c,d,f);if(!g)return;this.connect(function(){this.info('Client ? attempting to unsubscribe from ?',this._0,c);this._9({channel:Faye.Channel.UNSUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return;var b=[].concat(a.subscription);this.info('Unsubscription acknowledged for ? from ?',this._0,b)},this)},this)},publish:function(a,b){if(!Faye.Grammar.CHANNEL_NAME.test(a))throw new Error("Cannot publish: '"+a+"' is not a valid channel name");this.connect(function(){this.info('Client ? queueing published message to ?: ?',this._0,a,b);this._9({channel:a,data:b,clientId:this._0})},this)},receiveMessage:function(c){this.pipeThroughExtensions('incoming',c,function(a){if(!a)return;if(a.advice)this._z(a.advice);var b=this._o[a.id];if(b){delete this._o[a.id];b[0].call(b[1],a)}this._A(a)},this)},_9:function(b,c,d){b.id=this._B();if(c)this._o[b.id]=[c,d];this.pipeThroughExtensions('outgoing',b,function(a){if(!a)return;this._d.send(a,this._6.timeout/1000)},this)},_B:function(){this._e+=1;if(this._e>=Math.pow(2,32))this._e=0;return this._e.toString(36)},_z:function(a){Faye.extend(this._6,a);if(this._6.reconnect===this.HANDSHAKE&&this._1!==this.DISCONNECTED){this._1=this.UNCONNECTED;this._0=null;this._w()}},_A:function(a){if(!a.channel||!a.data)return;this.info('Client ? calling listeners for ? with ?',this._0,a.channel,a.data);this._2.distributeMessage(a)},_C:function(){if(!this._p)return;this._p=null;this.info('Closed connection for ?',this._0)},_w:function(){this._C();var a=this;Faye.ENV.setTimeout(function(){a.connect()},this._6.interval)}});Faye.extend(Faye.Client.prototype,Faye.Deferrable);Faye.extend(Faye.Client.prototype,Faye.Logging);Faye.extend(Faye.Client.prototype,Faye.Extensible);Faye.Transport=Faye.extend(Faye.Class({MAX_DELAY:0.0,batching:true,initialize:function(a,b){this.debug('Created new ? transport for ?',this.connectionType,b);this._8=a;this._a=b;this._f=[]},send:function(a,b){this.debug('Client ? sending message to ?: ?',this._8._0,this._a,a);if(!this.batching)return this.request([a],b);this._f.push(a);this._7=b;if(a.channel===Faye.Channel.HANDSHAKE)return this.flush();if(a.channel===Faye.Channel.CONNECT)this._q=a;this.addTimeout('publish',this.MAX_DELAY,this.flush,this)},flush:function(){this.removeTimeout('publish');if(this._f.length>1&&this._q)this._q.advice={timeout:0};this.request(this._f,this._7);this._q=null;this._f=[]},receive:function(a){this.debug('Client ? received from ?: ?',this._8._0,this._a,a);Faye.each(a,this._8.receiveMessage,this._8)},retry:function(a,b){var c=this;return function(){Faye.ENV.setTimeout(function(){c.request(a,2*b)},1000*b)}}}),{get:function(g,h,i,j){var k=g.endpoint;if(h===undefined)h=this.supportedConnectionTypes();Faye.asyncEach(this._r,function(b,c){var d=b[0],f=b[1];if(Faye.indexOf(h,d)<0)return c();f.isUsable(k,function(a){if(a)i.call(j,new f(g,k));else c()})},function(){throw new Error('Could not find a usable connection type for '+k);})},register:function(a,b){this._r.push([a,b]);b.prototype.connectionType=a},_r:[],supportedConnectionTypes:function(){return Faye.map(this._r,function(a){return a[0]})}});Faye.extend(Faye.Transport.prototype,Faye.Logging);Faye.extend(Faye.Transport.prototype,Faye.Timeouts);Faye.Event={_g:[],on:function(a,b,c,d){var f=function(){c.call(d)};if(a.addEventListener)a.addEventListener(b,f,false);else a.attachEvent('on'+b,f);this._g.push({_h:a,_s:b,_m:c,_n:d,_x:f})},detach:function(a,b,c,d){var f=this._g.length,g;while(f--){g=this._g[f];if((a&&a!==g._h)||(b&&b!==g._s)||(c&&c!==g._m)||(d&&d!==g._n))continue;if(g._h.removeEventListener)g._h.removeEventListener(g._s,g._x,false);else g._h.detachEvent('on'+g._s,g._x);this._g.splice(f,1);g=null}}};Faye.Event.on(Faye.ENV,'unload',Faye.Event.detach,Faye.Event);Faye.URI=Faye.extend(Faye.Class({queryString:function(){var c=[],d;Faye.each(this.params,function(a,b){c.push(encodeURIComponent(a)+'='+encodeURIComponent(b))});return c.join('&')},isLocal:function(){var a=Faye.URI.parse(Faye.ENV.location.href);var b=(a.hostname!==this.hostname)||(a.port!==this.port)||(a.protocol!==this.protocol);return!b},toURL:function(){var a=this.queryString();return this.protocol+this.hostname+':'+this.port+this.pathname+(a?'?'+a:'')}}),{parse:function(d,f){if(typeof d!=='string')return d;var g=new this();var h=function(b,c){d=d.replace(c,function(a){if(a)g[b]=a;return''})};h('protocol',/^https?\:\/+/);h('hostname',/^[^\/\:]+/);h('port',/^:[0-9]+/);Faye.extend(g,{protocol:'http://',hostname:Faye.ENV.location.hostname,port:Faye.ENV.location.port},false);if(!g.port)g.port=(g.protocol==='https://')?'443':'80';g.port=g.port.replace(/\D/g,'');var i=d.split('?'),j=i.shift(),k=i.join('?'),m=k?k.split('&'):[],o=m.length,l={};while(o--){i=m[o].split('=');l[decodeURIComponent(i[0]||'')]=decodeURIComponent(i[1]||'')}if(typeof f==='object')Faye.extend(l,f);g.pathname=j;g.params=l;return g}});if(!this.JSON){JSON={}}(function(){function k(a){return a<10?'0'+a:a}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(a){return this.getUTCFullYear()+'-'+k(this.getUTCMonth()+1)+'-'+k(this.getUTCDate())+'T'+k(this.getUTCHours())+':'+k(this.getUTCMinutes())+':'+k(this.getUTCSeconds())+'Z'};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()}}var m=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,o=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,l,p,s={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},n;function r(c){o.lastIndex=0;return o.test(c)?'"'+c.replace(o,function(a){var b=s[a];return typeof b==='string'?b:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+c+'"'}function q(a,b){var c,d,f,g,h=l,i,j=b[a];if(j&&typeof j==='object'&&typeof j.toJSON==='function'){j=j.toJSON(a)}if(typeof n==='function'){j=n.call(b,a,j)}switch(typeof j){case'string':return r(j);case'number':return isFinite(j)?String(j):'null';case'boolean':case'null':return String(j);case'object':if(!j){return'null'}l+=p;i=[];if(Object.prototype.toString.apply(j)==='[object Array]'){g=j.length;for(c=0;c<g;c+=1){i[c]=q(c,j)||'null'}f=i.length===0?'[]':l?'[\n'+l+i.join(',\n'+l)+'\n'+h+']':'['+i.join(',')+']';l=h;return f}if(n&&typeof n==='object'){g=n.length;for(c=0;c<g;c+=1){d=n[c];if(typeof d==='string'){f=q(d,j);if(f){i.push(r(d)+(l?': ':':')+f)}}}}else{for(d in j){if(Object.hasOwnProperty.call(j,d)){f=q(d,j);if(f){i.push(r(d)+(l?': ':':')+f)}}}}f=i.length===0?'{}':l?'{\n'+l+i.join(',\n'+l)+'\n'+h+'}':'{'+i.join(',')+'}';l=h;return f}}Faye.stringify=function(a,b,c){var d;l='';p='';if(typeof c==='number'){for(d=0;d<c;d+=1){p+=' '}}else if(typeof c==='string'){p=c}n=b;if(b&&typeof b!=='function'&&(typeof b!=='object'||typeof b.length!=='number')){throw new Error('JSON.stringify');}return q('',{'':a})};if(typeof JSON.stringify!=='function'){JSON.stringify=Faye.stringify}if(typeof JSON.parse!=='function'){JSON.parse=function(g,h){var i;function j(a,b){var c,d,f=a[b];if(f&&typeof f==='object'){for(c in f){if(Object.hasOwnProperty.call(f,c)){d=j(f,c);if(d!==undefined){f[c]=d}else{delete f[c]}}}}return h.call(a,b,f)}m.lastIndex=0;if(m.test(g)){g=g.replace(m,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(g.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){i=eval('('+g+')');return typeof h==='function'?j({'':i},''):i}throw new SyntaxError('JSON.parse');}}}());Faye.Transport.WebSocket=Faye.extend(Faye.Class(Faye.Transport,{UNCONNECTED:1,CONNECTING:2,CONNECTED:3,batching:false,request:function(b,c){this._7=this._7||c;this._i=this._i||{};Faye.each(b,function(a){this._i[a.id]=a},this);this.withSocket(function(a){a.send(Faye.toJSON(b))})},withSocket:function(a,b){this.callback(a,b);this.connect()},connect:function(){this._1=this._1||this.UNCONNECTED;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;this._b=new WebSocket(Faye.Transport.WebSocket.getSocketUrl(this._a));var d=this;this._b.onopen=function(){delete d._7;d._1=d.CONNECTED;d.setDeferredStatus('succeeded',d._b)};this._b.onmessage=function(b){var c=[].concat(JSON.parse(b.data));Faye.each(c,function(a){delete d._i[a.id]});d.receive(c)};this._b.onclose=function(){var a=(d._1===d.CONNECTED);d.setDeferredStatus('deferred');d._1=d.UNCONNECTED;delete d._b;if(a)return d.resend();Faye.ENV.setTimeout(function(){d.connect()},1000*d._7);d._7=d._7*2}},resend:function(){var c=Faye.map(this._i,function(a,b){return b});this.request(c)}}),{WEBSOCKET_TIMEOUT:1000,getSocketUrl:function(a){return Faye.URI.parse(a).toURL().replace(/^http(s?):/ig,'ws$1:')},isUsable:function(a,b,c){if(!Faye.ENV.WebSocket)return b.call(c,false);var d=false,f=false,g=this.getSocketUrl(a),h=new WebSocket(g);h.onopen=function(){d=true;h.close();b.call(c,true);f=true;h=null};var i=function(){if(!f&&!d)b.call(c,false);f=true};h.onclose=h.onerror=i;Faye.ENV.setTimeout(i,this.WEBSOCKET_TIMEOUT)}});Faye.extend(Faye.Transport.WebSocket.prototype,Faye.Deferrable);Faye.Transport.register('websocket',Faye.Transport.WebSocket);Faye.Transport.XHR=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c){var d=this.retry(b,c),f=Faye.URI.parse(this._a).pathname,g=this,h=Faye.ENV.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();h.open('POST',f,true);h.setRequestHeader('Content-Type','application/json');h.setRequestHeader('X-Requested-With','XMLHttpRequest');h.onreadystatechange=function(){if(h.readyState!==4)return;var a=h.status;try{if((a>=200&&a<300)||a===304||a===1223)g.receive(JSON.parse(h.responseText));else d()}catch(e){d()}finally{Faye.Event.detach(Faye.ENV,'beforeunload',i);h.onreadystatechange=function(){};h=null}};var i=function(){h.abort()};Faye.Event.on(Faye.ENV,'beforeunload',i);h.send(Faye.toJSON(b))}}),{isUsable:function(a,b,c){b.call(c,Faye.URI.parse(a).isLocal())}});Faye.Transport.register('long-polling',Faye.Transport.XHR);Faye.Transport.CORS=Faye.extend(Faye.Class(Faye.Transport,{request:function(a,b){var c=Faye.ENV.XDomainRequest?XDomainRequest:XMLHttpRequest,d=new c(),f=this.retry(a,b),g=this;d.open('POST',this._a,true);d.onload=function(){try{g.receive(JSON.parse(d.responseText))}catch(e){f()}finally{d.onload=d.onerror=null;d=null}};d.onerror=f;d.onprogress=function(){};d.send('message='+encodeURIComponent(Faye.toJSON(a)))}}),{isUsable:function(a,b,c){if(Faye.URI.parse(a).isLocal())return b.call(c,false);if(Faye.ENV.XDomainRequest)return b.call(c,true);if(Faye.ENV.XMLHttpRequest){var d=new Faye.ENV.XMLHttpRequest();return b.call(c,d.withCredentials!==undefined)}return b.call(c,false)}});Faye.Transport.register('cross-origin-long-polling',Faye.Transport.CORS);Faye.Transport.JSONP=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c){var d={message:Faye.toJSON(b)},f=document.getElementsByTagName('head')[0],g=document.createElement('script'),h=Faye.Transport.JSONP.getCallbackName(),i=Faye.URI.parse(this._a,d),j=this;var k=function(){if(!g.parentNode)return false;g.parentNode.removeChild(g);return true};Faye.ENV[h]=function(a){Faye.ENV[h]=undefined;try{delete Faye.ENV[h]}catch(e){}if(!k())return;j.receive(a)};Faye.ENV.setTimeout(function(){if(!Faye.ENV[h])return;k();j.request(b,2*c)},1000*c);i.params.jsonp=h;g.type='text/javascript';g.src=i.toURL();f.appendChild(g)}}),{_y:0,getCallbackName:function(){this._y+=1;return'__jsonp'+this._y+'__'},isUsable:function(a,b,c){b.call(c,true)}});Faye.Transport.register('callback-polling',Faye.Transport.JSONP);
|
1
|
+
if(!this.Faye)Faye={};Faye.extend=function(a,b,c){if(!b)return a;for(var d in b){if(!b.hasOwnProperty(d))continue;if(a.hasOwnProperty(d)&&c===false)continue;if(a[d]!==b[d])a[d]=b[d]}return a};Faye.extend(Faye,{VERSION:'0.6.4',BAYEUX_VERSION:'1.0',ID_LENGTH:128,JSONP_CALLBACK:'jsonpcallback',CONNECTION_TYPES:['long-polling','cross-origin-long-polling','callback-polling','websocket','in-process'],MANDATORY_CONNECTION_TYPES:['long-polling','callback-polling','in-process'],ENV:(function(){return this})(),random:function(a){a=a||this.ID_LENGTH;if(a>32){var b=Math.ceil(a/32),c='';while(b--)c+=this.random(32);return c}var d=Math.pow(2,a)-1,f=d.toString(36).length,c=Math.floor(Math.random()*d).toString(36);while(c.length<f)c='0'+c;return c},commonElement:function(a,b){for(var c=0,d=a.length;c<d;c++){if(this.indexOf(b,a[c])!==-1)return a[c]}return null},indexOf:function(a,b){for(var c=0,d=a.length;c<d;c++){if(a[c]===b)return c}return-1},each:function(a,b,c){if(a instanceof Array){for(var d=0,f=a.length;d<f;d++){if(a[d]!==undefined)b.call(c||null,a[d],d)}}else{for(var g in a){if(a.hasOwnProperty(g))b.call(c||null,g,a[g])}}},map:function(a,b,c){if(a.map)return a.map(b,c);var d=[];this.each(a,function(){d.push(b.apply(c||null,arguments))});return d},filter:function(a,b,c){var d=[];this.each(a,function(){if(b.apply(c,arguments))d.push(arguments[0])});return d},size:function(a){var b=0;this.each(a,function(){b+=1});return b},enumEqual:function(c,d){if(d instanceof Array){if(!(c instanceof Array))return false;var f=c.length;if(f!==d.length)return false;while(f--){if(c[f]!==d[f])return false}return true}else{if(!(c instanceof Object))return false;if(this.size(d)!==this.size(c))return false;var g=true;this.each(c,function(a,b){g=g&&(d[a]===b)});return g}},asyncEach:function(a,b,c,d){var f=a.length,g=-1,h=0,i=false;var j=function(){h-=1;g+=1;if(g===f)return c&&c.call(d);b(a[g],m)};var k=function(){if(i)return;i=true;while(h>0)j();i=false};var m=function(){h+=1;k()};m()},toJSON:function(a){if(this.stringify)return this.stringify(a,function(key,value){return(this[key]instanceof Array)?this[key]:value});return JSON.stringify(a)},timestamp:function(){var b=new Date(),c=b.getFullYear(),d=b.getMonth()+1,f=b.getDate(),g=b.getHours(),h=b.getMinutes(),i=b.getSeconds();var j=function(a){return a<10?'0'+a:String(a)};return j(c)+'-'+j(d)+'-'+j(f)+' '+j(g)+':'+j(h)+':'+j(i)}});Faye.Class=function(a,b){if(typeof a!=='function'){b=a;a=Object}var c=function(){if(!this.initialize)return this;return this.initialize.apply(this,arguments)||this};var d=function(){};d.prototype=a.prototype;c.prototype=new d();Faye.extend(c.prototype,b);return c};Faye.Namespace=Faye.Class({initialize:function(){this._c={}},exists:function(a){return this._c.hasOwnProperty(a)},generate:function(){var a=Faye.random();while(this._c.hasOwnProperty(a))a=Faye.random();return this._c[a]=a},release:function(a){delete this._c[a]}});Faye.Error=Faye.Class({initialize:function(a,b,c){this.code=a;this.params=Array.prototype.slice.call(b);this.message=c},toString:function(){return this.code+':'+this.params.join(',')+':'+this.message}});Faye.Error.parse=function(a){a=a||'';if(!Faye.Grammar.ERROR.test(a))return new this(null,[],a);var b=a.split(':'),c=parseInt(b[0]),d=b[1].split(','),a=b[2];return new this(c,d,a)};Faye.Error.versionMismatch=function(){return new this(300,arguments,"Version mismatch").toString()};Faye.Error.conntypeMismatch=function(){return new this(301,arguments,"Connection types not supported").toString()};Faye.Error.extMismatch=function(){return new this(302,arguments,"Extension mismatch").toString()};Faye.Error.badRequest=function(){return new this(400,arguments,"Bad request").toString()};Faye.Error.clientUnknown=function(){return new this(401,arguments,"Unknown client").toString()};Faye.Error.parameterMissing=function(){return new this(402,arguments,"Missing required parameter").toString()};Faye.Error.channelForbidden=function(){return new this(403,arguments,"Forbidden channel").toString()};Faye.Error.channelUnknown=function(){return new this(404,arguments,"Unknown channel").toString()};Faye.Error.channelInvalid=function(){return new this(405,arguments,"Invalid channel").toString()};Faye.Error.extUnknown=function(){return new this(406,arguments,"Unknown extension").toString()};Faye.Error.publishFailed=function(){return new this(407,arguments,"Failed to publish").toString()};Faye.Error.serverError=function(){return new this(500,arguments,"Internal server error").toString()};Faye.Deferrable={callback:function(a,b){if(!a)return;if(this._t==='succeeded')return a.apply(b,this._j);this._k=this._k||[];this._k.push([a,b])},errback:function(a,b){if(!a)return;if(this._t==='failed')return a.apply(b,this._j);this._l=this._l||[];this._l.push([a,b])},setDeferredStatus:function(){var a=Array.prototype.slice.call(arguments),b=a.shift(),c;this._t=b;this._j=a;if(b==='succeeded')c=this._k;else if(b==='failed')c=this._l;if(!c)return;var d;while(d=c.shift())d[0].apply(d[1],this._j)}};Faye.Publisher={countSubscribers:function(a){if(!this._3||!this._3[a])return 0;return this._3[a].length},addSubscriber:function(a,b,c){this._3=this._3||{};var d=this._3[a]=this._3[a]||[];d.push([b,c])},removeSubscriber:function(a,b,c){if(!this._3||!this._3[a])return;if(!b){delete this._3[a];return}var d=this._3[a],f=d.length;while(f--){if(b!==d[f][0])continue;if(c&&d[f][1]!==c)continue;d.splice(f,1)}},removeSubscribers:function(){this._3={}},publishEvent:function(){var b=Array.prototype.slice.call(arguments),c=b.shift();if(!this._3||!this._3[c])return;Faye.each(this._3[c],function(a){a[0].apply(a[1],b)})}};Faye.Timeouts={addTimeout:function(a,b,c,d){this._4=this._4||{};if(this._4.hasOwnProperty(a))return;var f=this;this._4[a]=Faye.ENV.setTimeout(function(){delete f._4[a];c.call(d)},1000*b)},removeTimeout:function(a){this._4=this._4||{};var b=this._4[a];if(!b)return;clearTimeout(b);delete this._4[a]}};Faye.Logging={LOG_LEVELS:{error:3,warn:2,info:1,debug:0},logLevel:'error',log:function(a,b){if(!Faye.logger)return;var c=Faye.Logging.LOG_LEVELS;if(c[Faye.Logging.logLevel]>c[b])return;var a=Array.prototype.slice.apply(a),d=' ['+b.toUpperCase()+'] [Faye',f=this.className,g=a.shift().replace(/\?/g,function(){try{return Faye.toJSON(a.shift())}catch(e){return'[Object]'}});for(var h in Faye){if(f)continue;if(typeof Faye[h]!=='function')continue;if(this instanceof Faye[h])f=h}if(f)d+='.'+f;d+='] ';Faye.logger(Faye.timestamp()+d+g)}};Faye.each(Faye.Logging.LOG_LEVELS,function(a,b){Faye.Logging[a]=function(){this.log(arguments,a)}});Faye.Grammar={LOWALPHA:/^[a-z]$/,UPALPHA:/^[A-Z]$/,ALPHA:/^([a-z]|[A-Z])$/,DIGIT:/^[0-9]$/,ALPHANUM:/^(([a-z]|[A-Z])|[0-9])$/,MARK:/^(\-|\_|\!|\~|\(|\)|\$|\@)$/,STRING:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,TOKEN:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,INTEGER:/^([0-9])+$/,CHANNEL_SEGMENT:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,CHANNEL_SEGMENTS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,CHANNEL_NAME:/^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,WILD_CARD:/^\*{1,2}$/,CHANNEL_PATTERN:/^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,VERSION_ELEMENT:/^(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*$/,VERSION:/^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/,CLIENT_ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ERROR_MESSAGE:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,ERROR_ARGS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*$/,ERROR_CODE:/^[0-9][0-9][0-9]$/,ERROR:/^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/};Faye.Extensible={addExtension:function(a){this._5=this._5||[];this._5.push(a);if(a.added)a.added()},removeExtension:function(a){if(!this._5)return;var b=this._5.length;while(b--){if(this._5[b]!==a)continue;this._5.splice(b,1);if(a.removed)a.removed()}},pipeThroughExtensions:function(c,d,f,g){this.debug('Passing through ? extensions: ?',c,d);if(!this._5)return f.call(g,d);var h=this._5.slice();var i=function(a){if(!a)return f.call(g,a);var b=h.shift();if(!b)return f.call(g,a);if(b[c])b[c](a,i);else i(a)};i(d)}};Faye.extend(Faye.Extensible,Faye.Logging);Faye.Channel=Faye.Class({initialize:function(a){this.id=this.name=a},push:function(a){this.publishEvent('message',a)},isUnused:function(){return this.countSubscribers('message')===0}});Faye.extend(Faye.Channel.prototype,Faye.Publisher);Faye.extend(Faye.Channel,{HANDSHAKE:'/meta/handshake',CONNECT:'/meta/connect',SUBSCRIBE:'/meta/subscribe',UNSUBSCRIBE:'/meta/unsubscribe',DISCONNECT:'/meta/disconnect',META:'meta',SERVICE:'service',expand:function(a){var b=this.parse(a),c=['/**',a];var d=b.slice();d[d.length-1]='*';c.push(this.unparse(d));for(var f=1,g=b.length;f<g;f++){d=b.slice(0,f);d.push('**');c.push(this.unparse(d))}return c},isValid:function(a){return Faye.Grammar.CHANNEL_NAME.test(a)||Faye.Grammar.CHANNEL_PATTERN.test(a)},parse:function(a){if(!this.isValid(a))return null;return a.split('/').slice(1)},unparse:function(a){return'/'+a.join('/')},isMeta:function(a){var b=this.parse(a);return b?(b[0]===this.META):null},isService:function(a){var b=this.parse(a);return b?(b[0]===this.SERVICE):null},isSubscribable:function(a){if(!this.isValid(a))return null;return!this.isMeta(a)&&!this.isService(a)},Set:Faye.Class({initialize:function(){this._2={}},getKeys:function(){var c=[];Faye.each(this._2,function(a,b){c.push(a)});return c},remove:function(a){delete this._2[a]},hasSubscription:function(a){return this._2.hasOwnProperty(a)},subscribe:function(c,d,f){if(!d)return;Faye.each(c,function(a){var b=this._2[a]=this._2[a]||new Faye.Channel(a);b.addSubscriber('message',d,f)},this)},unsubscribe:function(a,b,c){var d=this._2[a];if(!d)return false;d.removeSubscriber('message',b,c);if(d.isUnused()){this.remove(a);return true}else{return false}},distributeMessage:function(c){var d=Faye.Channel.expand(c.channel);Faye.each(d,function(a){var b=this._2[a];if(b)b.publishEvent('message',c.data)},this)}})});Faye.Subscription=Faye.Class({initialize:function(a,b,c,d){this._8=a;this._2=b;this._m=c;this._n=d;this._u=false},cancel:function(){if(this._u)return;this._8.unsubscribe(this._2,this._m,this._n);this._u=true},unsubscribe:function(){this.cancel()}});Faye.extend(Faye.Subscription.prototype,Faye.Deferrable);Faye.Client=Faye.Class({UNCONNECTED:1,CONNECTING:2,CONNECTED:3,DISCONNECTED:4,HANDSHAKE:'handshake',RETRY:'retry',NONE:'none',CONNECTION_TIMEOUT:60.0,DEFAULT_ENDPOINT:'/bayeux',INTERVAL:0.0,initialize:function(b,c){this.info('New client created for ?',b);this.endpoint=b||this.DEFAULT_ENDPOINT;this._v=c||{};Faye.Transport.get(this,Faye.MANDATORY_CONNECTION_TYPES,function(a){this._d=a},this);this._1=this.UNCONNECTED;this._2=new Faye.Channel.Set();this._e=0;this._o={};this._6={reconnect:this.RETRY,interval:1000*(this._v.interval||this.INTERVAL),timeout:1000*(this._v.timeout||this.CONNECTION_TIMEOUT)};if(Faye.Event)Faye.Event.on(Faye.ENV,'beforeunload',this.disconnect,this)},getClientId:function(){return this._0},getState:function(){switch(this._1){case this.UNCONNECTED:return'UNCONNECTED';case this.CONNECTING:return'CONNECTING';case this.CONNECTED:return'CONNECTED';case this.DISCONNECTED:return'DISCONNECTED'}},handshake:function(c,d){if(this._6.reconnect===this.NONE)return;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;var f=this;this.info('Initiating handshake with ?',this.endpoint);this._9({channel:Faye.Channel.HANDSHAKE,version:Faye.BAYEUX_VERSION,supportedConnectionTypes:[this._d.connectionType]},function(b){if(b.successful){this._1=this.CONNECTED;this._0=b.clientId;Faye.Transport.get(this,b.supportedConnectionTypes,function(a){this._d=a},this);this.info('Handshake successful: ?',this._0);this.subscribe(this._2.getKeys(),true);if(c)c.call(d)}else{this.info('Handshake unsuccessful');Faye.ENV.setTimeout(function(){f.handshake(c,d)},this._6.interval);this._1=this.UNCONNECTED}},this)},connect:function(a,b){if(this._6.reconnect===this.NONE)return;if(this._1===this.DISCONNECTED)return;if(this._1===this.UNCONNECTED)return this.handshake(function(){this.connect(a,b)},this);this.callback(a,b);if(this._1!==this.CONNECTED)return;this.info('Calling deferred actions for ?',this._0);this.setDeferredStatus('succeeded');this.setDeferredStatus('deferred');if(this._p)return;this._p=true;this.info('Initiating connection for ?',this._0);this._9({channel:Faye.Channel.CONNECT,clientId:this._0,connectionType:this._d.connectionType},this._w,this)},disconnect:function(){if(this._1!==this.CONNECTED)return;this._1=this.DISCONNECTED;this.info('Disconnecting ?',this._0);this._9({channel:Faye.Channel.DISCONNECT,clientId:this._0});this.info('Clearing channel listeners for ?',this._0);this._2=new Faye.Channel.Set()},subscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.subscribe(channel,d,f)},this);var g=new Faye.Subscription(this,c,d,f);var h=(d===true);if(!h&&this._2.hasSubscription(c)){this._2.subscribe([c],d,f);g.setDeferredStatus('succeeded');return g}this.connect(function(){this.info('Client ? attempting to subscribe to ?',this._0,c);this._9({channel:Faye.Channel.SUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return g.setDeferredStatus('failed',Faye.Error.parse(a.error));var b=[].concat(a.subscription);this.info('Subscription acknowledged for ? to ?',this._0,b);if(!h)this._2.subscribe(b,d,f);g.setDeferredStatus('succeeded')},this)},this);return g},unsubscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.unsubscribe(channel,d,f)},this);var g=this._2.unsubscribe(c,d,f);if(!g)return;this.connect(function(){this.info('Client ? attempting to unsubscribe from ?',this._0,c);this._9({channel:Faye.Channel.UNSUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return;var b=[].concat(a.subscription);this.info('Unsubscription acknowledged for ? from ?',this._0,b)},this)},this)},publish:function(a,b){if(!Faye.Grammar.CHANNEL_NAME.test(a))throw new Error("Cannot publish: '"+a+"' is not a valid channel name");this.connect(function(){this.info('Client ? queueing published message to ?: ?',this._0,a,b);this._9({channel:a,data:b,clientId:this._0})},this)},receiveMessage:function(c){this.pipeThroughExtensions('incoming',c,function(a){if(!a)return;if(a.advice)this._z(a.advice);var b=this._o[a.id];if(b){delete this._o[a.id];b[0].call(b[1],a)}this._A(a)},this)},_9:function(b,c,d){b.id=this._B();if(c)this._o[b.id]=[c,d];this.pipeThroughExtensions('outgoing',b,function(a){if(!a)return;this._d.send(a,this._6.timeout/1000)},this)},_B:function(){this._e+=1;if(this._e>=Math.pow(2,32))this._e=0;return this._e.toString(36)},_z:function(a){Faye.extend(this._6,a);if(this._6.reconnect===this.HANDSHAKE&&this._1!==this.DISCONNECTED){this._1=this.UNCONNECTED;this._0=null;this._w()}},_A:function(a){if(!a.channel||!a.data)return;this.info('Client ? calling listeners for ? with ?',this._0,a.channel,a.data);this._2.distributeMessage(a)},_C:function(){if(!this._p)return;this._p=null;this.info('Closed connection for ?',this._0)},_w:function(){this._C();var a=this;Faye.ENV.setTimeout(function(){a.connect()},this._6.interval)}});Faye.extend(Faye.Client.prototype,Faye.Deferrable);Faye.extend(Faye.Client.prototype,Faye.Logging);Faye.extend(Faye.Client.prototype,Faye.Extensible);Faye.Transport=Faye.extend(Faye.Class({MAX_DELAY:0.0,batching:true,initialize:function(a,b){this.debug('Created new ? transport for ?',this.connectionType,b);this._8=a;this._a=b;this._f=[]},send:function(a,b){this.debug('Client ? sending message to ?: ?',this._8._0,this._a,a);if(!this.batching)return this.request([a],b);this._f.push(a);this._7=b;if(a.channel===Faye.Channel.HANDSHAKE)return this.flush();if(a.channel===Faye.Channel.CONNECT)this._q=a;this.addTimeout('publish',this.MAX_DELAY,this.flush,this)},flush:function(){this.removeTimeout('publish');if(this._f.length>1&&this._q)this._q.advice={timeout:0};this.request(this._f,this._7);this._q=null;this._f=[]},receive:function(a){this.debug('Client ? received from ?: ?',this._8._0,this._a,a);Faye.each(a,this._8.receiveMessage,this._8)},retry:function(a,b){var c=this;return function(){Faye.ENV.setTimeout(function(){c.request(a,2*b)},1000*b)}}}),{get:function(g,h,i,j){var k=g.endpoint;if(h===undefined)h=this.supportedConnectionTypes();Faye.asyncEach(this._r,function(b,c){var d=b[0],f=b[1];if(Faye.indexOf(h,d)<0)return c();f.isUsable(k,function(a){if(a)i.call(j,new f(g,k));else c()})},function(){throw new Error('Could not find a usable connection type for '+k);})},register:function(a,b){this._r.push([a,b]);b.prototype.connectionType=a},_r:[],supportedConnectionTypes:function(){return Faye.map(this._r,function(a){return a[0]})}});Faye.extend(Faye.Transport.prototype,Faye.Logging);Faye.extend(Faye.Transport.prototype,Faye.Timeouts);Faye.Event={_g:[],on:function(a,b,c,d){var f=function(){c.call(d)};if(a.addEventListener)a.addEventListener(b,f,false);else a.attachEvent('on'+b,f);this._g.push({_h:a,_s:b,_m:c,_n:d,_x:f})},detach:function(a,b,c,d){var f=this._g.length,g;while(f--){g=this._g[f];if((a&&a!==g._h)||(b&&b!==g._s)||(c&&c!==g._m)||(d&&d!==g._n))continue;if(g._h.removeEventListener)g._h.removeEventListener(g._s,g._x,false);else g._h.detachEvent('on'+g._s,g._x);this._g.splice(f,1);g=null}}};Faye.Event.on(Faye.ENV,'unload',Faye.Event.detach,Faye.Event);Faye.URI=Faye.extend(Faye.Class({queryString:function(){var c=[],d;Faye.each(this.params,function(a,b){c.push(encodeURIComponent(a)+'='+encodeURIComponent(b))});return c.join('&')},isLocal:function(){var a=Faye.URI.parse(Faye.ENV.location.href);var b=(a.hostname!==this.hostname)||(a.port!==this.port)||(a.protocol!==this.protocol);return!b},toURL:function(){var a=this.queryString();return this.protocol+this.hostname+':'+this.port+this.pathname+(a?'?'+a:'')}}),{parse:function(d,f){if(typeof d!=='string')return d;var g=new this();var h=function(b,c){d=d.replace(c,function(a){if(a)g[b]=a;return''})};h('protocol',/^https?\:\/+/);h('hostname',/^[^\/\:]+/);h('port',/^:[0-9]+/);Faye.extend(g,{protocol:'http://',hostname:Faye.ENV.location.hostname,port:Faye.ENV.location.port},false);if(!g.port)g.port=(g.protocol==='https://')?'443':'80';g.port=g.port.replace(/\D/g,'');var i=d.split('?'),j=i.shift(),k=i.join('?'),m=k?k.split('&'):[],o=m.length,l={};while(o--){i=m[o].split('=');l[decodeURIComponent(i[0]||'')]=decodeURIComponent(i[1]||'')}if(typeof f==='object')Faye.extend(l,f);g.pathname=j;g.params=l;return g}});if(!this.JSON){JSON={}}(function(){function k(a){return a<10?'0'+a:a}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(a){return this.getUTCFullYear()+'-'+k(this.getUTCMonth()+1)+'-'+k(this.getUTCDate())+'T'+k(this.getUTCHours())+':'+k(this.getUTCMinutes())+':'+k(this.getUTCSeconds())+'Z'};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()}}var m=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,o=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,l,p,s={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},n;function r(c){o.lastIndex=0;return o.test(c)?'"'+c.replace(o,function(a){var b=s[a];return typeof b==='string'?b:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+c+'"'}function q(a,b){var c,d,f,g,h=l,i,j=b[a];if(j&&typeof j==='object'&&typeof j.toJSON==='function'){j=j.toJSON(a)}if(typeof n==='function'){j=n.call(b,a,j)}switch(typeof j){case'string':return r(j);case'number':return isFinite(j)?String(j):'null';case'boolean':case'null':return String(j);case'object':if(!j){return'null'}l+=p;i=[];if(Object.prototype.toString.apply(j)==='[object Array]'){g=j.length;for(c=0;c<g;c+=1){i[c]=q(c,j)||'null'}f=i.length===0?'[]':l?'[\n'+l+i.join(',\n'+l)+'\n'+h+']':'['+i.join(',')+']';l=h;return f}if(n&&typeof n==='object'){g=n.length;for(c=0;c<g;c+=1){d=n[c];if(typeof d==='string'){f=q(d,j);if(f){i.push(r(d)+(l?': ':':')+f)}}}}else{for(d in j){if(Object.hasOwnProperty.call(j,d)){f=q(d,j);if(f){i.push(r(d)+(l?': ':':')+f)}}}}f=i.length===0?'{}':l?'{\n'+l+i.join(',\n'+l)+'\n'+h+'}':'{'+i.join(',')+'}';l=h;return f}}Faye.stringify=function(a,b,c){var d;l='';p='';if(typeof c==='number'){for(d=0;d<c;d+=1){p+=' '}}else if(typeof c==='string'){p=c}n=b;if(b&&typeof b!=='function'&&(typeof b!=='object'||typeof b.length!=='number')){throw new Error('JSON.stringify');}return q('',{'':a})};if(typeof JSON.stringify!=='function'){JSON.stringify=Faye.stringify}if(typeof JSON.parse!=='function'){JSON.parse=function(g,h){var i;function j(a,b){var c,d,f=a[b];if(f&&typeof f==='object'){for(c in f){if(Object.hasOwnProperty.call(f,c)){d=j(f,c);if(d!==undefined){f[c]=d}else{delete f[c]}}}}return h.call(a,b,f)}m.lastIndex=0;if(m.test(g)){g=g.replace(m,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(g.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){i=eval('('+g+')');return typeof h==='function'?j({'':i},''):i}throw new SyntaxError('JSON.parse');}}}());Faye.Transport.WebSocket=Faye.extend(Faye.Class(Faye.Transport,{UNCONNECTED:1,CONNECTING:2,CONNECTED:3,batching:false,request:function(b,c){this._7=this._7||c;this._i=this._i||{};Faye.each(b,function(a){this._i[a.id]=a},this);this.withSocket(function(a){a.send(Faye.toJSON(b))})},withSocket:function(a,b){this.callback(a,b);this.connect()},connect:function(){this._1=this._1||this.UNCONNECTED;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;var d=Faye.ENV.WebSocket||Faye.ENV.MozWebSocket;this._b=new d(Faye.Transport.WebSocket.getSocketUrl(this._a));var f=this;this._b.onopen=function(){delete f._7;f._1=f.CONNECTED;f.setDeferredStatus('succeeded',f._b)};this._b.onmessage=function(b){var c=[].concat(JSON.parse(b.data));Faye.each(c,function(a){delete f._i[a.id]});f.receive(c)};this._b.onclose=function(){var a=(f._1===f.CONNECTED);f.setDeferredStatus('deferred');f._1=f.UNCONNECTED;delete f._b;if(a)return f.resend();Faye.ENV.setTimeout(function(){f.connect()},1000*f._7);f._7=f._7*2}},resend:function(){var c=Faye.map(this._i,function(a,b){return b});this.request(c)}}),{WEBSOCKET_TIMEOUT:1000,getSocketUrl:function(a){return Faye.URI.parse(a).toURL().replace(/^http(s?):/ig,'ws$1:')},isUsable:function(a,b,c){var d=Faye.ENV.WebSocket||Faye.ENV.MozWebSocket;if(!d)return b.call(c,false);var f=false,g=false,h=this.getSocketUrl(a),i=new d(h);i.onopen=function(){f=true;i.close();b.call(c,true);g=true;i=null};var j=function(){if(!g&&!f)b.call(c,false);g=true};i.onclose=i.onerror=j;Faye.ENV.setTimeout(j,this.WEBSOCKET_TIMEOUT)}});Faye.extend(Faye.Transport.WebSocket.prototype,Faye.Deferrable);Faye.Transport.register('websocket',Faye.Transport.WebSocket);Faye.Transport.XHR=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c){var d=this.retry(b,c),f=Faye.URI.parse(this._a).pathname,g=this,h=Faye.ENV.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();h.open('POST',f,true);h.setRequestHeader('Content-Type','application/json');h.setRequestHeader('X-Requested-With','XMLHttpRequest');h.onreadystatechange=function(){if(h.readyState!==4)return;var a=h.status;try{if((a>=200&&a<300)||a===304||a===1223)g.receive(JSON.parse(h.responseText));else d()}catch(e){d()}finally{Faye.Event.detach(Faye.ENV,'beforeunload',i);h.onreadystatechange=function(){};h=null}};var i=function(){h.abort()};Faye.Event.on(Faye.ENV,'beforeunload',i);h.send(Faye.toJSON(b))}}),{isUsable:function(a,b,c){b.call(c,Faye.URI.parse(a).isLocal())}});Faye.Transport.register('long-polling',Faye.Transport.XHR);Faye.Transport.CORS=Faye.extend(Faye.Class(Faye.Transport,{request:function(a,b){var c=Faye.ENV.XDomainRequest?XDomainRequest:XMLHttpRequest,d=new c(),f=this.retry(a,b),g=this;d.open('POST',this._a,true);d.onload=function(){try{g.receive(JSON.parse(d.responseText))}catch(e){f()}finally{d.onload=d.onerror=null;d=null}};d.onerror=f;d.onprogress=function(){};d.send('message='+encodeURIComponent(Faye.toJSON(a)))}}),{isUsable:function(a,b,c){if(Faye.URI.parse(a).isLocal())return b.call(c,false);if(Faye.ENV.XDomainRequest)return b.call(c,true);if(Faye.ENV.XMLHttpRequest){var d=new Faye.ENV.XMLHttpRequest();return b.call(c,d.withCredentials!==undefined)}return b.call(c,false)}});Faye.Transport.register('cross-origin-long-polling',Faye.Transport.CORS);Faye.Transport.JSONP=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c){var d={message:Faye.toJSON(b)},f=document.getElementsByTagName('head')[0],g=document.createElement('script'),h=Faye.Transport.JSONP.getCallbackName(),i=Faye.URI.parse(this._a,d),j=this;var k=function(){if(!g.parentNode)return false;g.parentNode.removeChild(g);return true};Faye.ENV[h]=function(a){Faye.ENV[h]=undefined;try{delete Faye.ENV[h]}catch(e){}if(!k())return;j.receive(a)};Faye.ENV.setTimeout(function(){if(!Faye.ENV[h])return;k();j.request(b,2*c)},1000*c);i.params.jsonp=h;g.type='text/javascript';g.src=i.toURL();f.appendChild(g)}}),{_y:0,getCallbackName:function(){this._y+=1;return'__jsonp'+this._y+'__'},isUsable:function(a,b,c){b.call(c,true)}});Faye.Transport.register('callback-polling',Faye.Transport.JSONP);
|
data/lib/faye.rb
CHANGED
@@ -59,7 +59,7 @@ module Faye
|
|
59
59
|
end
|
60
60
|
|
61
61
|
return handle_options(request) if env['REQUEST_METHOD'] == 'OPTIONS'
|
62
|
-
return handle_upgrade(request) if env['HTTP_UPGRADE']
|
62
|
+
return handle_upgrade(request) if env['HTTP_UPGRADE'] =~ /^WebSocket$/i
|
63
63
|
return [200, TYPE_SCRIPT, File.new(SCRIPT_PATH)] if request.path_info =~ /\.js$/
|
64
64
|
handle_request(request)
|
65
65
|
end
|
@@ -127,9 +127,9 @@ module Faye
|
|
127
127
|
socket.onmessage = lambda do |message|
|
128
128
|
begin
|
129
129
|
message = JSON.parse(message.data)
|
130
|
-
debug
|
130
|
+
debug "Received via WebSocket[#{socket.version}]: ?", message
|
131
131
|
@server.process(message, false) do |replies|
|
132
|
-
debug
|
132
|
+
debug "Sending via WebSocket[#{socket.version}]: ?", replies
|
133
133
|
socket.send(JSON.unparse(replies))
|
134
134
|
end
|
135
135
|
rescue
|
data/lib/faye/engines/memory.rb
CHANGED
@@ -72,15 +72,20 @@ module Faye
|
|
72
72
|
|
73
73
|
def publish(message)
|
74
74
|
debug 'Publishing message ?', message
|
75
|
+
|
75
76
|
channels = Channel.expand(message['channel'])
|
77
|
+
clients = Set.new
|
78
|
+
|
76
79
|
channels.each do |channel|
|
77
|
-
next unless
|
78
|
-
clients.
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
80
|
+
next unless subs = @channels[channel]
|
81
|
+
subs.each(&clients.method(:add))
|
82
|
+
end
|
83
|
+
|
84
|
+
clients.each do |client_id|
|
85
|
+
debug 'Queueing for client ?: ?', client_id, message
|
86
|
+
@messages[client_id] ||= []
|
87
|
+
@messages[client_id] << message
|
88
|
+
empty_queue(client_id)
|
84
89
|
end
|
85
90
|
end
|
86
91
|
|
data/lib/faye/mixins/logging.rb
CHANGED
data/lib/faye/util/web_socket.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
module Faye
|
2
2
|
class WebSocket
|
3
3
|
|
4
|
+
autoload :Draft75Parser, File.expand_path('..', __FILE__) + '/web_socket/draft75_parser'
|
5
|
+
autoload :Draft76Parser, File.expand_path('..', __FILE__) + '/web_socket/draft76_parser'
|
6
|
+
autoload :Protocol8Parser, File.expand_path('..', __FILE__) + '/web_socket/protocol8_parser'
|
7
|
+
|
4
8
|
include Publisher
|
5
9
|
|
6
10
|
CONNECTING = 0
|
@@ -8,9 +12,22 @@ module Faye
|
|
8
12
|
CLOSING = 2
|
9
13
|
CLOSED = 3
|
10
14
|
|
11
|
-
attr_reader :url, :ready_state
|
15
|
+
attr_reader :url, :ready_state
|
12
16
|
attr_accessor :onopen, :onmessage, :onerror, :onclose
|
13
17
|
|
18
|
+
extend Forwardable
|
19
|
+
def_delegators :@parser, :version
|
20
|
+
|
21
|
+
def self.parser(request)
|
22
|
+
if request.env['HTTP_SEC_WEBSOCKET_VERSION']
|
23
|
+
Protocol8Parser
|
24
|
+
elsif request.env['HTTP_SEC_WEBSOCKET_KEY1']
|
25
|
+
Draft76Parser
|
26
|
+
else
|
27
|
+
Draft75Parser
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
14
31
|
def initialize(request)
|
15
32
|
@request = request
|
16
33
|
@callback = @request.env['async.callback']
|
@@ -19,23 +36,25 @@ module Faye
|
|
19
36
|
|
20
37
|
@url = @request.env['websocket.url']
|
21
38
|
@ready_state = OPEN
|
22
|
-
@buffered_amount = 0
|
23
39
|
|
24
40
|
event = Event.new
|
25
41
|
event.init_event('open', false, false)
|
26
42
|
dispatch_event(event)
|
27
43
|
|
28
|
-
@
|
29
|
-
@
|
30
|
-
|
31
|
-
@request.env[Thin::Request::WEBSOCKET_RECEIVE_CALLBACK] = lambda do |data|
|
32
|
-
data.each_char(&method(:handle_char))
|
33
|
-
end
|
44
|
+
@parser = WebSocket.parser(@request).new(self)
|
45
|
+
@request.env[Thin::Request::WEBSOCKET_RECEIVE_CALLBACK] = @parser.method(:parse)
|
34
46
|
end
|
35
47
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
48
|
+
def receive(data)
|
49
|
+
event = Event.new
|
50
|
+
event.init_event('message', false, false)
|
51
|
+
event.data = encode(data)
|
52
|
+
dispatch_event(event)
|
53
|
+
end
|
54
|
+
|
55
|
+
def send(data, type = nil, error_type = nil)
|
56
|
+
frame = @parser.frame(encode(data), type, error_type)
|
57
|
+
@stream.write(frame) if frame
|
39
58
|
end
|
40
59
|
|
41
60
|
def close
|
@@ -58,28 +77,6 @@ module Faye
|
|
58
77
|
callback.call(event) if callback
|
59
78
|
end
|
60
79
|
|
61
|
-
private
|
62
|
-
|
63
|
-
def handle_char(data)
|
64
|
-
case data
|
65
|
-
when "\x00" then
|
66
|
-
@buffering = true
|
67
|
-
|
68
|
-
when "\xFF" then
|
69
|
-
event = Event.new
|
70
|
-
event.init_event('message', false, false)
|
71
|
-
event.data = encode(@buffer.join(''))
|
72
|
-
|
73
|
-
dispatch_event(event)
|
74
|
-
|
75
|
-
@buffer = []
|
76
|
-
@buffering = false
|
77
|
-
|
78
|
-
else
|
79
|
-
@buffer.push(data) if @buffering
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
80
|
def encode(string, encoding = 'UTF-8')
|
84
81
|
return string unless string.respond_to?(:force_encoding)
|
85
82
|
string.force_encoding(encoding)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Faye
|
2
|
+
class WebSocket
|
3
|
+
|
4
|
+
class Draft75Parser
|
5
|
+
def self.handshake(request)
|
6
|
+
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
7
|
+
upgrade << "Upgrade: WebSocket\r\n"
|
8
|
+
upgrade << "Connection: Upgrade\r\n"
|
9
|
+
upgrade << "WebSocket-Origin: #{request.env['HTTP_ORIGIN']}\r\n"
|
10
|
+
upgrade << "WebSocket-Location: #{request.websocket_url}\r\n\r\n"
|
11
|
+
upgrade
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(web_socket)
|
15
|
+
@socket = web_socket
|
16
|
+
@buffer = []
|
17
|
+
@buffering = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def version
|
21
|
+
'draft-75'
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse(data)
|
25
|
+
data.each_char(&method(:handle_char))
|
26
|
+
end
|
27
|
+
|
28
|
+
def frame(data, type = nil, error_type = nil)
|
29
|
+
"\x00#{ data }\xFF"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def handle_char(data)
|
35
|
+
case data
|
36
|
+
when "\x00" then
|
37
|
+
@buffering = true
|
38
|
+
|
39
|
+
when "\xFF" then
|
40
|
+
@socket.receive(@buffer.join(''))
|
41
|
+
@buffer = []
|
42
|
+
@buffering = false
|
43
|
+
|
44
|
+
else
|
45
|
+
@buffer.push(data) if @buffering
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
module Faye
|
4
|
+
class WebSocket
|
5
|
+
|
6
|
+
class Draft76Parser < Draft75Parser
|
7
|
+
class << self
|
8
|
+
def handshake(request)
|
9
|
+
key1 = request.env['HTTP_SEC_WEBSOCKET_KEY1']
|
10
|
+
value1 = number_from_key(key1) / spaces_in_key(key1)
|
11
|
+
|
12
|
+
key2 = request.env['HTTP_SEC_WEBSOCKET_KEY2']
|
13
|
+
value2 = number_from_key(key2) / spaces_in_key(key2)
|
14
|
+
|
15
|
+
hash = Digest::MD5.digest(big_endian(value1) +
|
16
|
+
big_endian(value2) +
|
17
|
+
request.body.read)
|
18
|
+
|
19
|
+
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
20
|
+
upgrade << "Upgrade: WebSocket\r\n"
|
21
|
+
upgrade << "Connection: Upgrade\r\n"
|
22
|
+
upgrade << "Sec-WebSocket-Origin: #{request.env['HTTP_ORIGIN']}\r\n"
|
23
|
+
upgrade << "Sec-WebSocket-Location: #{request.websocket_url}\r\n\r\n"
|
24
|
+
upgrade << hash
|
25
|
+
upgrade
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def number_from_key(key)
|
31
|
+
key.scan(/[0-9]/).join('').to_i(10)
|
32
|
+
end
|
33
|
+
|
34
|
+
def spaces_in_key(key)
|
35
|
+
key.scan(/ /).size
|
36
|
+
end
|
37
|
+
|
38
|
+
def big_endian(number)
|
39
|
+
string = ''
|
40
|
+
[24,16,8,0].each do |offset|
|
41
|
+
string << (number >> offset & 0xFF).chr
|
42
|
+
end
|
43
|
+
string
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def version
|
48
|
+
'draft-76'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
module Faye
|
5
|
+
class WebSocket
|
6
|
+
|
7
|
+
class Protocol8Parser
|
8
|
+
GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
9
|
+
|
10
|
+
FIN = MASK = 0b10000000
|
11
|
+
RSV1 = 0b01000000
|
12
|
+
RSV2 = 0b00100000
|
13
|
+
RSV3 = 0b00010000
|
14
|
+
OPCODE = 0b00001111
|
15
|
+
LENGTH = 0b01111111
|
16
|
+
|
17
|
+
OPCODES = {
|
18
|
+
:continuation => 0,
|
19
|
+
:text => 1,
|
20
|
+
:binary => 2,
|
21
|
+
:close => 8,
|
22
|
+
:ping => 9,
|
23
|
+
:pong => 10
|
24
|
+
}
|
25
|
+
|
26
|
+
ERRORS = {
|
27
|
+
:normal_closure => 1000,
|
28
|
+
:going_away => 1001,
|
29
|
+
:protocol_error => 1002,
|
30
|
+
:unacceptable => 1003,
|
31
|
+
:too_large => 1004,
|
32
|
+
:encoding_error => 1007
|
33
|
+
}
|
34
|
+
|
35
|
+
def self.handshake(request)
|
36
|
+
sec_key = request.env['HTTP_SEC_WEBSOCKET_KEY']
|
37
|
+
return '' unless String === sec_key
|
38
|
+
|
39
|
+
accept = Base64.encode64(Digest::SHA1.digest(sec_key + GUID)).strip
|
40
|
+
|
41
|
+
upgrade = "HTTP/1.1 101 Switching Protocols\r\n"
|
42
|
+
upgrade << "Upgrade: websocket\r\n"
|
43
|
+
upgrade << "Connection: Upgrade\r\n"
|
44
|
+
upgrade << "Sec-WebSocket-Accept: #{accept}\r\n\r\n"
|
45
|
+
upgrade
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(web_socket)
|
49
|
+
reset
|
50
|
+
@socket = web_socket
|
51
|
+
end
|
52
|
+
|
53
|
+
def version
|
54
|
+
'protocol-8'
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse(data)
|
58
|
+
byte0 = getbyte(data, 0)
|
59
|
+
final = (byte0 & FIN) == FIN
|
60
|
+
opcode = (byte0 & OPCODE)
|
61
|
+
|
62
|
+
return close(:protocol_error) unless OPCODES.values.include?(opcode)
|
63
|
+
reset unless opcode == OPCODES[:continuation]
|
64
|
+
|
65
|
+
byte1 = getbyte(data, 1)
|
66
|
+
masked = (byte1 & MASK) == MASK
|
67
|
+
length = (byte1 & LENGTH)
|
68
|
+
offset = 0
|
69
|
+
|
70
|
+
case length
|
71
|
+
when 126 then
|
72
|
+
length = integer(data, 2, 2)
|
73
|
+
offset = 2
|
74
|
+
when 127 then
|
75
|
+
length = integer(data, 2, 8)
|
76
|
+
offset = 8
|
77
|
+
end
|
78
|
+
|
79
|
+
if masked
|
80
|
+
payload_offset = 2 + offset + 4
|
81
|
+
mask_octets = (0..3).map { |i| getbyte(data, 2 + offset + i) }
|
82
|
+
else
|
83
|
+
payload_offset = 2 + offset
|
84
|
+
mask_octets = []
|
85
|
+
end
|
86
|
+
|
87
|
+
return close(:too_large) if getbyte(data, payload_offset + length)
|
88
|
+
|
89
|
+
raw_payload = data[payload_offset...(payload_offset + length)]
|
90
|
+
payload = unmask(raw_payload, mask_octets)
|
91
|
+
|
92
|
+
case opcode
|
93
|
+
when OPCODES[:continuation] then
|
94
|
+
return unless @mode == :text
|
95
|
+
@buffer << payload
|
96
|
+
if final
|
97
|
+
message = @buffer * ''
|
98
|
+
reset
|
99
|
+
@socket.receive(message)
|
100
|
+
end
|
101
|
+
|
102
|
+
when OPCODES[:text] then
|
103
|
+
if final
|
104
|
+
@socket.receive(payload)
|
105
|
+
else
|
106
|
+
@mode = :text
|
107
|
+
@buffer << payload
|
108
|
+
end
|
109
|
+
|
110
|
+
when OPCODES[:binary] then
|
111
|
+
close(:unacceptable)
|
112
|
+
|
113
|
+
when OPCODES[:close] then
|
114
|
+
close(:normal_closure)
|
115
|
+
|
116
|
+
when OPCODES[:ping] then
|
117
|
+
@socket.send(payload, :pong)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def frame(data, type = nil, error_type = nil)
|
122
|
+
return nil if @closed
|
123
|
+
|
124
|
+
if error_type
|
125
|
+
data = [ERRORS[error_type]].pack('n') + data
|
126
|
+
end
|
127
|
+
|
128
|
+
opcode = OPCODES[type || :text]
|
129
|
+
frame = (FIN | opcode).chr
|
130
|
+
length = data.size
|
131
|
+
|
132
|
+
case length
|
133
|
+
when 0..125 then
|
134
|
+
frame << length.chr
|
135
|
+
when 126..65535 then
|
136
|
+
frame << 126.chr
|
137
|
+
frame << [length].pack('n')
|
138
|
+
else
|
139
|
+
frame << 127.chr
|
140
|
+
frame << [length >> 32, length & 0xFFFFFFFF].pack('NN')
|
141
|
+
end
|
142
|
+
|
143
|
+
@socket.encode(frame, 'UTF-8') +
|
144
|
+
@socket.encode(data, 'UTF-8')
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def reset
|
150
|
+
@buffer = []
|
151
|
+
@mode = nil
|
152
|
+
end
|
153
|
+
|
154
|
+
def close(error_type)
|
155
|
+
return if @closed
|
156
|
+
@socket.send('', :close, error_type)
|
157
|
+
@closed = true
|
158
|
+
end
|
159
|
+
|
160
|
+
def getbyte(data, offset)
|
161
|
+
data.respond_to?(:getbyte) ? data.getbyte(offset) : data[offset]
|
162
|
+
end
|
163
|
+
|
164
|
+
def integer(data, offset, length)
|
165
|
+
number = 0
|
166
|
+
(0...length).each do |i|
|
167
|
+
number += getbyte(data, offset + i) << (8 * (length - 1 - i))
|
168
|
+
end
|
169
|
+
number
|
170
|
+
end
|
171
|
+
|
172
|
+
def unmask(payload, mask_octets)
|
173
|
+
return payload unless mask_octets.size > 0
|
174
|
+
unmasked = ''
|
175
|
+
(0...payload.size).each do |i|
|
176
|
+
unmasked << (getbyte(payload, i) ^ mask_octets[i % 4]).chr
|
177
|
+
end
|
178
|
+
unmasked
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
data/lib/thin_extensions.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# Based on code from the Cramp project
|
3
3
|
# http://github.com/lifo/cramp
|
4
4
|
|
5
|
-
# Copyright (c) 2009-
|
5
|
+
# Copyright (c) 2009-2011 Pratik Naik
|
6
6
|
#
|
7
7
|
# Permission is hereby granted, free of charge, to any person obtaining
|
8
8
|
# a copy of this software and associated documentation files (the
|
@@ -23,8 +23,6 @@
|
|
23
23
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
24
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
25
|
|
26
|
-
require 'digest/md5'
|
27
|
-
|
28
26
|
class Thin::Connection
|
29
27
|
def receive_data(data)
|
30
28
|
trace { data }
|
@@ -55,7 +53,9 @@ class Thin::Request
|
|
55
53
|
WEBSOCKET_RECEIVE_CALLBACK = 'websocket.receive_callback'.freeze
|
56
54
|
|
57
55
|
def websocket?
|
58
|
-
@env['HTTP_CONNECTION']
|
56
|
+
@env['HTTP_CONNECTION'] and
|
57
|
+
@env['HTTP_CONNECTION'].split(/\s*,\s*/).include?('Upgrade') and
|
58
|
+
['WebSocket', 'websocket'].include?(@env['HTTP_UPGRADE'])
|
59
59
|
end
|
60
60
|
|
61
61
|
def secure_websocket?
|
@@ -68,73 +68,12 @@ class Thin::Request
|
|
68
68
|
|
69
69
|
def websocket_url
|
70
70
|
scheme = secure_websocket? ? 'wss:' : 'ws:'
|
71
|
-
@env['websocket.url'] = "#{ scheme }//#{ @env['HTTP_HOST'] }#{ @env['
|
71
|
+
@env['websocket.url'] = "#{ scheme }//#{ @env['HTTP_HOST'] }#{ @env['REQUEST_URI'] }"
|
72
72
|
end
|
73
73
|
|
74
74
|
def websocket_upgrade_data
|
75
|
-
|
76
|
-
|
77
|
-
else
|
78
|
-
Protocol75
|
79
|
-
end
|
80
|
-
handler.new(self).handshake
|
81
|
-
end
|
82
|
-
|
83
|
-
class WebSocketHandler
|
84
|
-
def initialize(request)
|
85
|
-
@request = request
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
class Protocol75 < WebSocketHandler
|
90
|
-
def handshake
|
91
|
-
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
92
|
-
upgrade << "Upgrade: WebSocket\r\n"
|
93
|
-
upgrade << "Connection: Upgrade\r\n"
|
94
|
-
upgrade << "WebSocket-Origin: #{@request.env['HTTP_ORIGIN']}\r\n"
|
95
|
-
upgrade << "WebSocket-Location: #{@request.websocket_url}\r\n\r\n"
|
96
|
-
upgrade
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
class Protocol76 < WebSocketHandler
|
101
|
-
def handshake
|
102
|
-
key1 = @request.env['HTTP_SEC_WEBSOCKET_KEY1']
|
103
|
-
value1 = number_from_key(key1) / spaces_in_key(key1)
|
104
|
-
|
105
|
-
key2 = @request.env['HTTP_SEC_WEBSOCKET_KEY2']
|
106
|
-
value2 = number_from_key(key2) / spaces_in_key(key2)
|
107
|
-
|
108
|
-
hash = Digest::MD5.digest(big_endian(value1) +
|
109
|
-
big_endian(value2) +
|
110
|
-
@request.body.read)
|
111
|
-
|
112
|
-
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
113
|
-
upgrade << "Upgrade: WebSocket\r\n"
|
114
|
-
upgrade << "Connection: Upgrade\r\n"
|
115
|
-
upgrade << "Sec-WebSocket-Origin: #{@request.env['HTTP_ORIGIN']}\r\n"
|
116
|
-
upgrade << "Sec-WebSocket-Location: #{@request.websocket_url}\r\n\r\n"
|
117
|
-
upgrade << hash
|
118
|
-
upgrade
|
119
|
-
end
|
120
|
-
|
121
|
-
private
|
122
|
-
|
123
|
-
def number_from_key(key)
|
124
|
-
key.scan(/[0-9]/).join('').to_i(10)
|
125
|
-
end
|
126
|
-
|
127
|
-
def spaces_in_key(key)
|
128
|
-
key.scan(/ /).size
|
129
|
-
end
|
130
|
-
|
131
|
-
def big_endian(number)
|
132
|
-
string = ''
|
133
|
-
[24,16,8,0].each do |offset|
|
134
|
-
string << (number >> offset & 0xFF).chr
|
135
|
-
end
|
136
|
-
string
|
137
|
-
end
|
75
|
+
parser = Faye::WebSocket.parser(self)
|
76
|
+
parser.handshake(self)
|
138
77
|
end
|
139
78
|
end
|
140
79
|
|
data/spec/install.sh
CHANGED
@@ -5,9 +5,9 @@
|
|
5
5
|
# http://uec-images.ubuntu.com/releases/10.04/release/
|
6
6
|
|
7
7
|
FAYE_BRANCH=master
|
8
|
-
NODE_VERSION=0.4.
|
8
|
+
NODE_VERSION=0.4.10
|
9
9
|
PHANTOM_VERSION=1.2
|
10
|
-
REDIS_VERSION=2.2.
|
10
|
+
REDIS_VERSION=2.2.12
|
11
11
|
RUBY_VERSION=1.9.2
|
12
12
|
TSUNG_VERSION=1.3.3
|
13
13
|
|
@@ -17,24 +17,25 @@ sudo apt-get install build-essential g++ git-core curl wget \
|
|
17
17
|
apache2-utils erlang gnuplot \
|
18
18
|
libqt4-dev qt4-qmake xvfb
|
19
19
|
|
20
|
+
# Install RVM and Ruby
|
20
21
|
bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
|
21
22
|
echo "source \"\$HOME/.rvm/scripts/rvm\"" | tee -a ~/.bashrc
|
22
23
|
source ~/.rvm/scripts/rvm
|
23
|
-
rvm install
|
24
|
-
rvm --default use
|
25
|
-
|
24
|
+
rvm install $RUBY_VERSION
|
25
|
+
rvm --default use $RUBY_VERSION
|
26
26
|
echo "install: --no-rdoc --no-ri
|
27
27
|
update: --no-rdoc --no-ri" | tee ~/.gemrc
|
28
28
|
gem install rake bundler
|
29
29
|
|
30
|
+
# Install nvm and Node
|
30
31
|
cd ~
|
31
32
|
git clone git://github.com/creationix/nvm.git ~/.nvm
|
32
33
|
. ~/.nvm/nvm.sh
|
33
34
|
echo ". ~/.nvm/nvm.sh" | tee -a ~/.bashrc
|
34
35
|
nvm install v$NODE_VERSION
|
35
36
|
nvm use v$NODE_VERSION
|
36
|
-
npm install redis
|
37
37
|
|
38
|
+
# Install Redis from source
|
38
39
|
cd /usr/src
|
39
40
|
sudo wget http://redis.googlecode.com/files/redis-$REDIS_VERSION.tar.gz
|
40
41
|
sudo tar zxvf redis-$REDIS_VERSION.tar.gz
|
@@ -43,6 +44,7 @@ sudo make
|
|
43
44
|
sudo ln -s /usr/src/redis-$REDIS_VERSION/src/redis-server /usr/bin/redis-server
|
44
45
|
sudo ln -s /usr/src/redis-$REDIS_VERSION/src/redis-cli /usr/bin/redis-cli
|
45
46
|
|
47
|
+
# Install PhantomJS
|
46
48
|
cd /usr/src
|
47
49
|
sudo git clone git://github.com/ariya/phantomjs.git
|
48
50
|
cd phantomjs
|
@@ -52,6 +54,7 @@ sudo make
|
|
52
54
|
sudo ln -s /usr/src/phantomjs/bin/phantomjs /usr/bin/phantomjs
|
53
55
|
echo "To use phantomjs, run DISPLAY=:1 Xvfb :1 -screen 0 1024x768x16"
|
54
56
|
|
57
|
+
# Install Tsung and required Perl modules
|
55
58
|
cd /usr/src
|
56
59
|
sudo wget http://tsung.erlang-projects.org/dist/tsung-$TSUNG_VERSION.tar.gz
|
57
60
|
sudo tar zxvf tsung-$TSUNG_VERSION.tar.gz
|
@@ -63,11 +66,13 @@ sudo ln -s /usr/lib/tsung/bin/tsung_stats.pl /usr/bin/tsung-stats
|
|
63
66
|
echo "To use tsung-stats you need to 'install Template' from CPAN"
|
64
67
|
sudo perl -MCPAN -eshell
|
65
68
|
|
69
|
+
# Check out and build Faye project
|
66
70
|
cd ~
|
67
71
|
git clone git://github.com/jcoglan/faye.git
|
68
72
|
cd faye
|
69
73
|
git checkout $FAYE_BRANCH
|
70
74
|
git submodule update --init --recursive
|
71
75
|
bundle install
|
76
|
+
npm install redis
|
72
77
|
cd vendor/js.class && jake
|
73
78
|
cd ../.. && jake
|
data/spec/testswarm
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/ruby1.8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'testswarm/client'
|
5
|
+
|
6
|
+
client = TestSwarm::Client.new('http://swarm.jcoglan.com')
|
7
|
+
project = client.project('faye', :auth => '6d26e250d81b32099fccc59db53a3a0e648f0e6d')
|
8
|
+
|
9
|
+
job = TestSwarm::Job.create(
|
10
|
+
:rcs => {
|
11
|
+
:type => "git",
|
12
|
+
:url => "git://github.com/jcoglan/faye.git"
|
13
|
+
},
|
14
|
+
:directory => "/home/jcoglan/www/swarm.jcoglan.com/app/changeset/#{project.name}",
|
15
|
+
:diff => ["javascript", "spec/javascript"],
|
16
|
+
:inject => "spec/browser.html",
|
17
|
+
|
18
|
+
:build => [
|
19
|
+
"cd vendor/js.class && jake",
|
20
|
+
"rm -rf build",
|
21
|
+
"jake"
|
22
|
+
]
|
23
|
+
)
|
24
|
+
|
25
|
+
exit unless job.new?
|
26
|
+
|
27
|
+
path = "#{client.url}/changeset/#{project.name}/#{job.revision}"
|
28
|
+
job.add_suite "Faye tests", "#{path}/spec/browser.html"
|
29
|
+
|
30
|
+
project.submit_job "Faye Commit #{job.revision}", job,
|
31
|
+
:browsers => "all",
|
32
|
+
:max => 5
|
33
|
+
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: faye
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.6.
|
5
|
+
version: 0.6.4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- James Coglan
|
@@ -10,8 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
14
|
-
default_executable:
|
13
|
+
date: 2011-08-17 00:00:00 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: eventmachine
|
@@ -182,7 +181,6 @@ files:
|
|
182
181
|
- spec/browser.html
|
183
182
|
- spec/phantom.js
|
184
183
|
- spec/redis.conf
|
185
|
-
- spec/testswarm.pl
|
186
184
|
- spec/install.sh
|
187
185
|
- spec/thin_proxy.rb
|
188
186
|
- spec/ruby/server/handshake_spec.rb
|
@@ -214,9 +212,13 @@ files:
|
|
214
212
|
- spec/javascript/node_adapter_spec.js
|
215
213
|
- spec/javascript/client_spec.js
|
216
214
|
- spec/javascript/grammar_spec.js
|
215
|
+
- spec/testswarm
|
217
216
|
- spec/spec_helper.rb
|
218
217
|
- lib/faye/error.rb
|
219
218
|
- lib/faye/util/namespace.rb
|
219
|
+
- lib/faye/util/web_socket/draft75_parser.rb
|
220
|
+
- lib/faye/util/web_socket/protocol8_parser.rb
|
221
|
+
- lib/faye/util/web_socket/draft76_parser.rb
|
220
222
|
- lib/faye/util/web_socket.rb
|
221
223
|
- lib/faye/mixins/logging.rb
|
222
224
|
- lib/faye/mixins/publisher.rb
|
@@ -237,7 +239,6 @@ files:
|
|
237
239
|
- lib/faye/engines/base.rb
|
238
240
|
- lib/thin_extensions.rb
|
239
241
|
- lib/faye.rb
|
240
|
-
has_rdoc: true
|
241
242
|
homepage: http://faye.jcoglan.com
|
242
243
|
licenses: []
|
243
244
|
|
@@ -262,7 +263,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
262
263
|
requirements: []
|
263
264
|
|
264
265
|
rubyforge_project:
|
265
|
-
rubygems_version: 1.
|
266
|
+
rubygems_version: 1.8.5
|
266
267
|
signing_key:
|
267
268
|
specification_version: 3
|
268
269
|
summary: Simple pub/sub messaging for the web
|
data/spec/testswarm.pl
DELETED
@@ -1,200 +0,0 @@
|
|
1
|
-
#!/usr/bin/perl
|
2
|
-
|
3
|
-
# CONFIGURE
|
4
|
-
|
5
|
-
# The location of the TestSwarm that you're going to run against.
|
6
|
-
|
7
|
-
my $SWARM = "http://swarm.jcoglan.com";
|
8
|
-
my $SWARM_INJECT = "/js/inject.js";
|
9
|
-
|
10
|
-
# Your TestSwarm username.
|
11
|
-
|
12
|
-
my $USER = "faye";
|
13
|
-
|
14
|
-
# Your authorization token.
|
15
|
-
|
16
|
-
my $AUTH_TOKEN = "6d26e250d81b32099fccc59db53a3a0e648f0e6d";
|
17
|
-
|
18
|
-
# The maximum number of times you want the tests to be run.
|
19
|
-
|
20
|
-
my $MAX_RUNS = 5;
|
21
|
-
|
22
|
-
# The type of revision control system being used.
|
23
|
-
# Currently "svn" or "git" are supported.
|
24
|
-
|
25
|
-
my $RCS_TYPE = "git";
|
26
|
-
|
27
|
-
# The URL from which a copy will be checked out.
|
28
|
-
|
29
|
-
my $RCS_URL = "git://github.com/jcoglan/faye.git";
|
30
|
-
|
31
|
-
# The directory in which the checkouts will occur.
|
32
|
-
|
33
|
-
my $BASE_DIR = "/home/jcoglan/www/swarm.jcoglan.com/app/changeset/$USER";
|
34
|
-
|
35
|
-
# A script tag loading in the TestSwarm injection script will
|
36
|
-
# be added at the bottom of the <head> in the following file.
|
37
|
-
|
38
|
-
my $INJECT_FILE = "spec/browser.html";
|
39
|
-
|
40
|
-
# Any build commands that need to happen.
|
41
|
-
|
42
|
-
my $BUILD = "git submodule init && git submodule update && cd vendor/js.class && jake && cd ../.. && rm -rf build && jake";
|
43
|
-
|
44
|
-
# The name of the job that will be submitted
|
45
|
-
# (pick a descriptive, but short, name to make it easy to search)
|
46
|
-
|
47
|
-
# Note: The string {REV} will be replaced with the current
|
48
|
-
# commit number/hash.
|
49
|
-
|
50
|
-
my $JOB_NAME = "Faye Commit #{REV}";
|
51
|
-
|
52
|
-
# The browsers you wish to run against. Options include:
|
53
|
-
# - "all" all available browsers.
|
54
|
-
# - "popular" the most popular browser (99%+ of all browsers in use)
|
55
|
-
# - "current" the current release of all the major browsers
|
56
|
-
# - "gbs" the browsers currently supported in Yahoo's Graded Browser Support
|
57
|
-
# - "beta" upcoming alpha/beta of popular browsers
|
58
|
-
# - "popularbeta" the most popular browser and their upcoming releases
|
59
|
-
|
60
|
-
my $BROWSERS = "all";
|
61
|
-
|
62
|
-
# All the suites that you wish to run within this job
|
63
|
-
# (can be any number of suites)
|
64
|
-
|
65
|
-
my %SUITES = ();
|
66
|
-
|
67
|
-
# Comment these out if you wish to define a custom set of SUITES above
|
68
|
-
my $SUITE = "$SWARM/changeset/$USER/{REV}";
|
69
|
-
sub BUILD_SUITES {
|
70
|
-
%SUITES = map { /(\w+).html/; $1 => "$SUITE/$_"; } glob($INJECT_FILE);
|
71
|
-
}
|
72
|
-
|
73
|
-
########### NO NEED TO CONFIGURE BELOW HERE ############
|
74
|
-
|
75
|
-
my $DEBUG = 1;
|
76
|
-
my $curdate = time;
|
77
|
-
my $co_dir = "tmp-$curdate";
|
78
|
-
|
79
|
-
print "chdir $BASE_DIR\n" if ( $DEBUG );
|
80
|
-
chdir( $BASE_DIR );
|
81
|
-
|
82
|
-
# Check out a specific revision
|
83
|
-
if ( $RCS_TYPE eq "svn" ) {
|
84
|
-
print "svn co $RCS_URL $co_dir\n" if ( $DEBUG );
|
85
|
-
`svn co $RCS_URL $co_dir`;
|
86
|
-
} elsif ( $RCS_TYPE eq "git" ) {
|
87
|
-
print "git clone $RCS_URL $co_dir\n" if ( $DEBUG );
|
88
|
-
`git clone $RCS_URL $co_dir`;
|
89
|
-
}
|
90
|
-
|
91
|
-
if ( ! -e $co_dir ) {
|
92
|
-
die "Problem checking out source.";
|
93
|
-
}
|
94
|
-
|
95
|
-
print "chdir $co_dir\n" if ( $DEBUG );
|
96
|
-
chdir( $co_dir );
|
97
|
-
|
98
|
-
my $rev;
|
99
|
-
|
100
|
-
# Figure out the revision of the checkout
|
101
|
-
if ( $RCS_TYPE eq "svn" ) {
|
102
|
-
print "svn info | grep Revision\n" if ( $DEBUG );
|
103
|
-
$rev = `svn info | grep Revision`;
|
104
|
-
$rev =~ s/Revision: //;
|
105
|
-
} elsif ( $RCS_TYPE eq "git" ) {
|
106
|
-
print "git log --abbrev-commit | head -1\n" if ( $DEBUG );
|
107
|
-
$rev = `git log --abbrev-commit | head -1`;
|
108
|
-
$rev =~ s/commit.*?(\w+).*$/$1/;
|
109
|
-
}
|
110
|
-
|
111
|
-
$rev =~ s/\s*//g;
|
112
|
-
|
113
|
-
print "Revision: $rev\n" if ( $DEBUG );
|
114
|
-
|
115
|
-
if ( ! $rev ) {
|
116
|
-
remove_tmp();
|
117
|
-
die "Revision information not found.";
|
118
|
-
|
119
|
-
} elsif ( ! -e "../$rev" ) {
|
120
|
-
print "chdir $BASE_DIR\n" if ( $DEBUG );
|
121
|
-
chdir( $BASE_DIR );
|
122
|
-
|
123
|
-
print "rename $co_dir $rev\n" if ( $DEBUG );
|
124
|
-
rename( $co_dir, $rev );
|
125
|
-
|
126
|
-
print "chdir $rev\n" if ( $DEBUG );
|
127
|
-
chdir ( $rev );
|
128
|
-
|
129
|
-
if ( $BUILD ) {
|
130
|
-
print "$BUILD\n" if ( $DEBUG );
|
131
|
-
`$BUILD`;
|
132
|
-
}
|
133
|
-
|
134
|
-
if ( exists &BUILD_SUITES ) {
|
135
|
-
&BUILD_SUITES();
|
136
|
-
}
|
137
|
-
|
138
|
-
foreach my $file ( glob($INJECT_FILE) ) {
|
139
|
-
my $inject_file = `cat $file`;
|
140
|
-
|
141
|
-
# Inject the TestSwarm injection script into the test suite
|
142
|
-
$inject_file =~ s/<\/head>/<script>document.write("<scr" + "ipt src='$SWARM$SWARM_INJECT?" + (new Date).getTime() + "'><\/scr" + "ipt>");<\/script><\/head>/;
|
143
|
-
|
144
|
-
open( F, ">$file" );
|
145
|
-
print F $inject_file;
|
146
|
-
close( F );
|
147
|
-
}
|
148
|
-
|
149
|
-
my %props = (
|
150
|
-
"state" => "addjob",
|
151
|
-
"output" => "dump",
|
152
|
-
"user" => $USER,
|
153
|
-
"max" => $MAX_RUNS,
|
154
|
-
"job_name" => $JOB_NAME,
|
155
|
-
"browsers" => $BROWSERS,
|
156
|
-
"auth" => $AUTH_TOKEN
|
157
|
-
);
|
158
|
-
|
159
|
-
my $query = "";
|
160
|
-
|
161
|
-
foreach my $prop ( keys %props ) {
|
162
|
-
$query .= ($query ? "&" : "") . $prop . "=" . clean($props{$prop});
|
163
|
-
}
|
164
|
-
|
165
|
-
foreach my $suite ( sort keys %SUITES ) {
|
166
|
-
$query .= "&suites[]=" . clean($suite) .
|
167
|
-
"&urls[]=" . clean($SUITES{$suite});
|
168
|
-
}
|
169
|
-
|
170
|
-
print "curl -d \"$query\" $SWARM\n" if ( $DEBUG );
|
171
|
-
|
172
|
-
my $results = `curl -d "$query" $SWARM`;
|
173
|
-
|
174
|
-
print "Results: $results\n" if ( $DEBUG );
|
175
|
-
|
176
|
-
if ( $results ) {
|
177
|
-
open( F, ">$rev/results.txt" );
|
178
|
-
print F "$SWARM$results";
|
179
|
-
close( F );
|
180
|
-
|
181
|
-
} else {
|
182
|
-
die "Job not submitted properly.";
|
183
|
-
}
|
184
|
-
|
185
|
-
# Otherwise, give up and clean up
|
186
|
-
} else {
|
187
|
-
remove_tmp();
|
188
|
-
}
|
189
|
-
|
190
|
-
sub remove_tmp {
|
191
|
-
chdir( $BASE_DIR );
|
192
|
-
`rm -rf $co_dir`;
|
193
|
-
}
|
194
|
-
|
195
|
-
sub clean {
|
196
|
-
my $str = shift;
|
197
|
-
$str =~ s/{REV}/$rev/g;
|
198
|
-
$str =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
|
199
|
-
$str;
|
200
|
-
}
|