faye-authentication 0.4.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.drone.yml +10 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +0 -3
- data/README.md +47 -26
- data/VERSION +1 -0
- data/app/assets/javascripts/faye-authentication.js +18 -11
- data/faye-authentication.gemspec +3 -2
- data/lib/faye/authentication/client_extension.rb +1 -1
- data/lib/faye/authentication/server_extension.rb +3 -2
- data/lib/faye/authentication/version.rb +1 -1
- data/lib/faye/authentication.rb +20 -8
- data/spec/javascripts/faye-authentication_spec.js +151 -48
- data/spec/javascripts/faye-extension_spec.js +1 -1
- data/spec/javascripts/support/jasmine.yml +1 -1
- data/spec/lib/faye/authentication/server_extension_spec.rb +14 -24
- data/spec/lib/faye/authentication_spec.rb +91 -1
- data/spec/utils/javascripts/faye.js +388 -290
- metadata +27 -15
- data/spec/utils/javascripts/core.js +0 -712
- data/spec/utils/javascripts/sha1.js +0 -136
@@ -31,7 +31,7 @@ describe('Faye extension', function() {
|
|
31
31
|
function stubSignature(context, callback) {
|
32
32
|
var self = context;
|
33
33
|
self.client.handshake(function() {
|
34
|
-
var jwtsign = new jwt.WebToken('{"clientId": "' + self.client.
|
34
|
+
var jwtsign = new jwt.WebToken('{"clientId": "' + self.client._dispatcher.clientId + '", "channel": "/foobar", "exp": 2803694528}', '{"alg": "HS256"}');
|
35
35
|
var signature = jwtsign.serialize("macaroni");
|
36
36
|
|
37
37
|
jasmine.Ajax.stubRequest('/faye/auth').andReturn({
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'faye/authentication
|
2
|
+
require 'faye/authentication'
|
3
3
|
|
4
4
|
describe Faye::Authentication::ServerExtension do
|
5
5
|
|
6
6
|
let(:secret) { 'macaroni' }
|
7
7
|
let(:extension) { Faye::Authentication::ServerExtension.new(secret) }
|
8
|
+
let(:channel) { '/channel' }
|
8
9
|
|
9
10
|
describe '#incoming' do
|
10
11
|
shared_examples 'signature_has_error' do
|
@@ -22,31 +23,20 @@ describe Faye::Authentication::ServerExtension do
|
|
22
23
|
end
|
23
24
|
|
24
25
|
shared_examples 'authentication_actions' do
|
25
|
-
context 'not
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
it_should_behave_like 'signature_has_no_error'
|
30
|
-
end
|
26
|
+
context 'does not require signature' do
|
27
|
+
before { expect(Faye::Authentication).to receive(:authentication_required?).with(message, {}).and_return(false) }
|
28
|
+
it_should_behave_like 'signature_has_no_error'
|
29
|
+
end
|
31
30
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
context 'requires signature' do
|
32
|
+
before { expect(Faye::Authentication).to receive(:authentication_required?).with(message, {}).and_return(true) }
|
33
|
+
context 'with signature' do
|
34
|
+
before { message['signature'] = Faye::Authentication.sign(message.merge({'channel' => channel}), secret) }
|
35
|
+
it_should_behave_like 'signature_has_no_error'
|
36
36
|
end
|
37
37
|
|
38
|
-
context '
|
39
|
-
|
40
|
-
let(:channel) { '/whatever' }
|
41
|
-
it_should_behave_like 'signature_has_error'
|
42
|
-
end
|
43
|
-
|
44
|
-
context 'signed' do
|
45
|
-
let(:channel) { '/foo/bar' }
|
46
|
-
before { message['signature'] = Faye::Authentication.sign(message.merge({'channel' => channel}), secret) }
|
47
|
-
it_should_behave_like 'signature_has_no_error'
|
48
|
-
end
|
49
|
-
|
38
|
+
context 'without signature' do
|
39
|
+
it_should_behave_like 'signature_has_error'
|
50
40
|
end
|
51
41
|
end
|
52
42
|
end
|
@@ -70,7 +60,7 @@ describe Faye::Authentication::ServerExtension do
|
|
70
60
|
['/meta/handshake', '/meta/connect', '/meta/unsubscribe', '/meta/disconnect'].each do |channel|
|
71
61
|
it "does not check the signature for #{channel}" do
|
72
62
|
message = {'channel' => channel, 'clientId' => '42', 'text' => 'whatever', 'signature' => 'hello'}
|
73
|
-
expect(Faye::Authentication).to_not receive(:
|
63
|
+
expect(Faye::Authentication).to_not receive(:validate)
|
74
64
|
extension.incoming(message, ->(_) {});
|
75
65
|
end
|
76
66
|
end
|
@@ -8,7 +8,7 @@ describe Faye::Authentication do
|
|
8
8
|
let(:message) { {'channel' => channel, 'clientId' => clientId, 'text' => 'whatever'} }
|
9
9
|
let(:secret) { 'helloworld' }
|
10
10
|
let(:signature) { Faye::Authentication.sign(message, secret) }
|
11
|
-
|
11
|
+
|
12
12
|
describe '#sign' do
|
13
13
|
it 'returns with a default expiry'
|
14
14
|
end
|
@@ -55,4 +55,94 @@ describe Faye::Authentication do
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
describe '#authentication_required?' do
|
59
|
+
|
60
|
+
before(:each) { Faye.logger = nil }
|
61
|
+
|
62
|
+
shared_examples 'subscribe_and_publish' do
|
63
|
+
it 'returns true if no options are passed' do
|
64
|
+
expect(Faye::Authentication.authentication_required?(message)).to be(true)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'returns true if empty options are passed' do
|
68
|
+
expect(Faye::Authentication.authentication_required?(message, {})).to be(true)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'returns true if not a lamda / proc' do
|
72
|
+
expect(Faye::Authentication.authentication_required?(message, {whitelist: 42})).to be(true)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'calls the lambda with the channel or subscription' do
|
76
|
+
block = double
|
77
|
+
expect(block).to receive(:call).with(message['subscription'] || message['channel'])
|
78
|
+
Faye::Authentication.authentication_required?(message, {whitelist: block})
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns true if lambda raises' do
|
82
|
+
expect(Faye::Authentication.authentication_required?(message, {whitelist: lambda { |message| raise "oops" }})).to be(true)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'logs the error if lambda raises' do
|
86
|
+
Faye.logger = double()
|
87
|
+
expect(Faye.logger).to receive(:error).with("[Module] Error caught when evaluating whitelist lambda : oops")
|
88
|
+
Faye::Authentication.authentication_required?(message, {whitelist: lambda { |message| raise "oops" }})
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'returns true if lambda returns false' do
|
92
|
+
expect(Faye::Authentication.authentication_required?(message, {whitelist: lambda { |message| false }})).to be(true)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'returns false if lambda returns true' do
|
96
|
+
expect(Faye::Authentication.authentication_required?(message, {whitelist: lambda { |message| true }})).to be(false)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
shared_examples 'meta_except_subscribe' do
|
101
|
+
it 'returns false if no options are passed' do
|
102
|
+
expect(Faye::Authentication.authentication_required?(message)).to be(false)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'returns false if empty options are passed' do
|
106
|
+
expect(Faye::Authentication.authentication_required?(message, {})).to be(false)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'returns false even if lambda returns false' do
|
110
|
+
expect(Faye::Authentication.authentication_required?(message, {whitelist: lambda { |message| false }})).to be(false)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'does not call lambda / proc' do
|
114
|
+
not_called = double()
|
115
|
+
expect(not_called).to_not receive(:call)
|
116
|
+
(Faye::Authentication.authentication_required?(message, {whitelist: not_called}))
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'publish' do
|
122
|
+
let(:message) { {'channel' => '/foobar'} }
|
123
|
+
it_behaves_like 'subscribe_and_publish'
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'subscribe' do
|
127
|
+
let(:message) { {'channel' => '/meta/subscribe', 'subscription' => '/foobar'} }
|
128
|
+
it_behaves_like 'subscribe_and_publish'
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'handshake' do
|
132
|
+
let(:message) { {'channel' => '/meta/handshake'} }
|
133
|
+
it_behaves_like 'meta_except_subscribe'
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'connect' do
|
137
|
+
let(:message) { {'channel' => '/meta/connect'} }
|
138
|
+
it_behaves_like 'meta_except_subscribe'
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'unsubscribe' do
|
142
|
+
let(:message) { {'channel' => '/meta/unsubscribe', 'subscription' => '/foobar'} }
|
143
|
+
it_behaves_like 'meta_except_subscribe'
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
58
148
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
'use strict';
|
3
3
|
|
4
4
|
var Faye = {
|
5
|
-
VERSION: '1.0.
|
5
|
+
VERSION: '1.0.3',
|
6
6
|
|
7
7
|
BAYEUX_VERSION: '1.0',
|
8
8
|
ID_LENGTH: 160,
|
@@ -26,7 +26,10 @@ var Faye = {
|
|
26
26
|
|
27
27
|
random: function(bitlength) {
|
28
28
|
bitlength = bitlength || this.ID_LENGTH;
|
29
|
-
|
29
|
+
var maxLength = Math.ceil(bitlength * Math.log(2) / Math.log(36));
|
30
|
+
var string = csprng(bitlength, 36);
|
31
|
+
while (string.length < maxLength) string = '0' + string;
|
32
|
+
return string;
|
30
33
|
},
|
31
34
|
|
32
35
|
clientIdFromMessages: function(messages) {
|
@@ -744,7 +747,7 @@ Faye.Deferrable = {
|
|
744
747
|
setDeferredStatus: function(status, value) {
|
745
748
|
if (this._timer) Faye.ENV.clearTimeout(this._timer);
|
746
749
|
|
747
|
-
|
750
|
+
this.then();
|
748
751
|
|
749
752
|
if (status === 'succeeded')
|
750
753
|
this._fulfill(value);
|
@@ -801,7 +804,7 @@ Faye.Timeouts = {
|
|
801
804
|
this._timeouts = this._timeouts || {};
|
802
805
|
var timeout = this._timeouts[name];
|
803
806
|
if (!timeout) return;
|
804
|
-
clearTimeout(timeout);
|
807
|
+
Faye.ENV.clearTimeout(timeout);
|
805
808
|
delete this._timeouts[name];
|
806
809
|
},
|
807
810
|
|
@@ -823,13 +826,13 @@ Faye.Logging = {
|
|
823
826
|
writeLog: function(messageArgs, level) {
|
824
827
|
if (!Faye.logger) return;
|
825
828
|
|
826
|
-
var
|
827
|
-
banner
|
828
|
-
klass
|
829
|
+
var args = Array.prototype.slice.apply(messageArgs),
|
830
|
+
banner = '[Faye',
|
831
|
+
klass = this.className,
|
829
832
|
|
830
|
-
message =
|
833
|
+
message = args.shift().replace(/\?/g, function() {
|
831
834
|
try {
|
832
|
-
return Faye.toJSON(
|
835
|
+
return Faye.toJSON(args.shift());
|
833
836
|
} catch (e) {
|
834
837
|
return '[Object]';
|
835
838
|
}
|
@@ -852,11 +855,11 @@ Faye.Logging = {
|
|
852
855
|
|
853
856
|
(function() {
|
854
857
|
for (var key in Faye.Logging.LOG_LEVELS)
|
855
|
-
(function(level
|
858
|
+
(function(level) {
|
856
859
|
Faye.Logging[level] = function() {
|
857
860
|
this.writeLog(arguments, level);
|
858
861
|
};
|
859
|
-
})(key
|
862
|
+
})(key);
|
860
863
|
})();
|
861
864
|
|
862
865
|
Faye.Grammar = {
|
@@ -999,12 +1002,11 @@ Faye.extend(Faye.Channel, {
|
|
999
1002
|
},
|
1000
1003
|
|
1001
1004
|
subscribe: function(names, callback, context) {
|
1002
|
-
if (!callback) return;
|
1003
1005
|
var name;
|
1004
1006
|
for (var i = 0, n = names.length; i < n; i++) {
|
1005
1007
|
name = names[i];
|
1006
1008
|
var channel = this._channels[name] = this._channels[name] || new Faye.Channel(name);
|
1007
|
-
channel.bind('message', callback, context);
|
1009
|
+
if (callback) channel.bind('message', callback, context);
|
1008
1010
|
}
|
1009
1011
|
},
|
1010
1012
|
|
@@ -1032,17 +1034,6 @@ Faye.extend(Faye.Channel, {
|
|
1032
1034
|
})
|
1033
1035
|
});
|
1034
1036
|
|
1035
|
-
Faye.Envelope = Faye.Class({
|
1036
|
-
initialize: function(message, timeout) {
|
1037
|
-
this.id = message.id;
|
1038
|
-
this.message = message;
|
1039
|
-
|
1040
|
-
if (timeout !== undefined) this.timeout(timeout / 1000, false);
|
1041
|
-
}
|
1042
|
-
});
|
1043
|
-
|
1044
|
-
Faye.extend(Faye.Envelope.prototype, Faye.Deferrable);
|
1045
|
-
|
1046
1037
|
Faye.Publication = Faye.Class(Faye.Deferrable);
|
1047
1038
|
|
1048
1039
|
Faye.Subscription = Faye.Class({
|
@@ -1068,65 +1059,55 @@ Faye.Subscription = Faye.Class({
|
|
1068
1059
|
Faye.extend(Faye.Subscription.prototype, Faye.Deferrable);
|
1069
1060
|
|
1070
1061
|
Faye.Client = Faye.Class({
|
1071
|
-
UNCONNECTED:
|
1072
|
-
CONNECTING:
|
1073
|
-
CONNECTED:
|
1074
|
-
DISCONNECTED:
|
1062
|
+
UNCONNECTED: 1,
|
1063
|
+
CONNECTING: 2,
|
1064
|
+
CONNECTED: 3,
|
1065
|
+
DISCONNECTED: 4,
|
1075
1066
|
|
1076
|
-
HANDSHAKE:
|
1077
|
-
RETRY:
|
1078
|
-
NONE:
|
1067
|
+
HANDSHAKE: 'handshake',
|
1068
|
+
RETRY: 'retry',
|
1069
|
+
NONE: 'none',
|
1079
1070
|
|
1080
|
-
CONNECTION_TIMEOUT:
|
1081
|
-
DEFAULT_RETRY: 5,
|
1082
|
-
MAX_REQUEST_SIZE: 2048,
|
1071
|
+
CONNECTION_TIMEOUT: 60,
|
1083
1072
|
|
1084
|
-
DEFAULT_ENDPOINT:
|
1085
|
-
INTERVAL:
|
1073
|
+
DEFAULT_ENDPOINT: '/bayeux',
|
1074
|
+
INTERVAL: 0,
|
1086
1075
|
|
1087
1076
|
initialize: function(endpoint, options) {
|
1088
1077
|
this.info('New client created for ?', endpoint);
|
1078
|
+
options = options || {};
|
1089
1079
|
|
1090
|
-
this.
|
1091
|
-
this.
|
1092
|
-
this.
|
1093
|
-
this.transports = {};
|
1094
|
-
this.cookies = Faye.CookieJar && new Faye.CookieJar();
|
1095
|
-
this.headers = {};
|
1096
|
-
this.ca = this._options.ca;
|
1097
|
-
this._disabled = [];
|
1098
|
-
this._retry = this._options.retry || this.DEFAULT_RETRY;
|
1099
|
-
|
1100
|
-
for (var key in this.endpoints)
|
1101
|
-
this.endpoints[key] = Faye.URI.parse(this.endpoints[key]);
|
1102
|
-
|
1103
|
-
this.maxRequestSize = this.MAX_REQUEST_SIZE;
|
1080
|
+
this._endpoint = endpoint || this.DEFAULT_ENDPOINT;
|
1081
|
+
this._channels = new Faye.Channel.Set();
|
1082
|
+
this._dispatcher = new Faye.Dispatcher(this, this._endpoint, options);
|
1104
1083
|
|
1105
|
-
this._state = this.UNCONNECTED;
|
1106
|
-
this._channels = new Faye.Channel.Set();
|
1107
1084
|
this._messageId = 0;
|
1085
|
+
this._state = this.UNCONNECTED;
|
1108
1086
|
|
1109
1087
|
this._responseCallbacks = {};
|
1110
1088
|
|
1111
1089
|
this._advice = {
|
1112
1090
|
reconnect: this.RETRY,
|
1113
|
-
interval: 1000 * (
|
1114
|
-
timeout: 1000 * (
|
1091
|
+
interval: 1000 * (options.interval || this.INTERVAL),
|
1092
|
+
timeout: 1000 * (options.timeout || this.CONNECTION_TIMEOUT)
|
1115
1093
|
};
|
1094
|
+
this._dispatcher.timeout = this._advice.timeout / 1000;
|
1095
|
+
|
1096
|
+
this._dispatcher.bind('message', this._receiveMessage, this);
|
1116
1097
|
|
1117
1098
|
if (Faye.Event && Faye.ENV.onbeforeunload !== undefined)
|
1118
1099
|
Faye.Event.on(Faye.ENV, 'beforeunload', function() {
|
1119
|
-
if (Faye.indexOf(this._disabled, 'autodisconnect') < 0)
|
1100
|
+
if (Faye.indexOf(this._dispatcher._disabled, 'autodisconnect') < 0)
|
1120
1101
|
this.disconnect();
|
1121
1102
|
}, this);
|
1122
1103
|
},
|
1123
1104
|
|
1124
1105
|
disable: function(feature) {
|
1125
|
-
this.
|
1106
|
+
return this._dispatcher.disable(feature);
|
1126
1107
|
},
|
1127
1108
|
|
1128
1109
|
setHeader: function(name, value) {
|
1129
|
-
this.
|
1110
|
+
return this._dispatcher.setHeader(name, value);
|
1130
1111
|
},
|
1131
1112
|
|
1132
1113
|
// Request
|
@@ -1155,30 +1136,30 @@ Faye.Client = Faye.Class({
|
|
1155
1136
|
this._state = this.CONNECTING;
|
1156
1137
|
var self = this;
|
1157
1138
|
|
1158
|
-
this.info('Initiating handshake with ?', Faye.URI.stringify(this.
|
1159
|
-
this.
|
1139
|
+
this.info('Initiating handshake with ?', Faye.URI.stringify(this._endpoint));
|
1140
|
+
this._dispatcher.selectTransport(Faye.MANDATORY_CONNECTION_TYPES);
|
1160
1141
|
|
1161
|
-
this.
|
1142
|
+
this._sendMessage({
|
1162
1143
|
channel: Faye.Channel.HANDSHAKE,
|
1163
1144
|
version: Faye.BAYEUX_VERSION,
|
1164
|
-
supportedConnectionTypes: [this.
|
1145
|
+
supportedConnectionTypes: [this._dispatcher.connectionType]
|
1165
1146
|
|
1166
|
-
}, function(response) {
|
1147
|
+
}, {}, function(response) {
|
1167
1148
|
|
1168
1149
|
if (response.successful) {
|
1169
|
-
this._state
|
1170
|
-
this.
|
1150
|
+
this._state = this.CONNECTED;
|
1151
|
+
this._dispatcher.clientId = response.clientId;
|
1171
1152
|
|
1172
|
-
this.
|
1153
|
+
this._dispatcher.selectTransport(response.supportedConnectionTypes);
|
1173
1154
|
|
1174
|
-
this.info('Handshake successful: ?', this.
|
1155
|
+
this.info('Handshake successful: ?', this._dispatcher.clientId);
|
1175
1156
|
|
1176
1157
|
this.subscribe(this._channels.getKeys(), true);
|
1177
1158
|
if (callback) Faye.Promise.defer(function() { callback.call(context) });
|
1178
1159
|
|
1179
1160
|
} else {
|
1180
1161
|
this.info('Handshake unsuccessful');
|
1181
|
-
Faye.ENV.setTimeout(function() { self.handshake(callback, context) }, this.
|
1162
|
+
Faye.ENV.setTimeout(function() { self.handshake(callback, context) }, this._dispatcher.retry * 1000);
|
1182
1163
|
this._state = this.UNCONNECTED;
|
1183
1164
|
}
|
1184
1165
|
}, this);
|
@@ -1203,21 +1184,21 @@ Faye.Client = Faye.Class({
|
|
1203
1184
|
this.callback(callback, context);
|
1204
1185
|
if (this._state !== this.CONNECTED) return;
|
1205
1186
|
|
1206
|
-
this.info('Calling deferred actions for ?', this.
|
1187
|
+
this.info('Calling deferred actions for ?', this._dispatcher.clientId);
|
1207
1188
|
this.setDeferredStatus('succeeded');
|
1208
1189
|
this.setDeferredStatus('unknown');
|
1209
1190
|
|
1210
1191
|
if (this._connectRequest) return;
|
1211
1192
|
this._connectRequest = true;
|
1212
1193
|
|
1213
|
-
this.info('Initiating connection for ?', this.
|
1194
|
+
this.info('Initiating connection for ?', this._dispatcher.clientId);
|
1214
1195
|
|
1215
|
-
this.
|
1196
|
+
this._sendMessage({
|
1216
1197
|
channel: Faye.Channel.CONNECT,
|
1217
|
-
clientId: this.
|
1218
|
-
connectionType: this.
|
1198
|
+
clientId: this._dispatcher.clientId,
|
1199
|
+
connectionType: this._dispatcher.connectionType
|
1219
1200
|
|
1220
|
-
}, this._cycleConnection, this);
|
1201
|
+
}, {}, this._cycleConnection, this);
|
1221
1202
|
},
|
1222
1203
|
|
1223
1204
|
// Request Response
|
@@ -1231,19 +1212,17 @@ Faye.Client = Faye.Class({
|
|
1231
1212
|
if (this._state !== this.CONNECTED) return;
|
1232
1213
|
this._state = this.DISCONNECTED;
|
1233
1214
|
|
1234
|
-
this.info('Disconnecting ?', this.
|
1215
|
+
this.info('Disconnecting ?', this._dispatcher.clientId);
|
1235
1216
|
|
1236
|
-
this.
|
1217
|
+
this._sendMessage({
|
1237
1218
|
channel: Faye.Channel.DISCONNECT,
|
1238
|
-
clientId: this.
|
1219
|
+
clientId: this._dispatcher.clientId
|
1239
1220
|
|
1240
|
-
}, function(response) {
|
1241
|
-
if (
|
1242
|
-
this._transport.close();
|
1243
|
-
delete this._transport;
|
1221
|
+
}, {}, function(response) {
|
1222
|
+
if (response.successful) this._dispatcher.close();
|
1244
1223
|
}, this);
|
1245
1224
|
|
1246
|
-
this.info('Clearing channel listeners for ?', this.
|
1225
|
+
this.info('Clearing channel listeners for ?', this._dispatcher.clientId);
|
1247
1226
|
this._channels = new Faye.Channel.Set();
|
1248
1227
|
},
|
1249
1228
|
|
@@ -1274,22 +1253,22 @@ Faye.Client = Faye.Class({
|
|
1274
1253
|
}
|
1275
1254
|
|
1276
1255
|
this.connect(function() {
|
1277
|
-
this.info('Client ? attempting to subscribe to ?', this.
|
1256
|
+
this.info('Client ? attempting to subscribe to ?', this._dispatcher.clientId, channel);
|
1278
1257
|
if (!force) this._channels.subscribe([channel], callback, context);
|
1279
1258
|
|
1280
|
-
this.
|
1259
|
+
this._sendMessage({
|
1281
1260
|
channel: Faye.Channel.SUBSCRIBE,
|
1282
|
-
clientId: this.
|
1261
|
+
clientId: this._dispatcher.clientId,
|
1283
1262
|
subscription: channel
|
1284
1263
|
|
1285
|
-
}, function(response) {
|
1264
|
+
}, {}, function(response) {
|
1286
1265
|
if (!response.successful) {
|
1287
1266
|
subscription.setDeferredStatus('failed', Faye.Error.parse(response.error));
|
1288
1267
|
return this._channels.unsubscribe(channel, callback, context);
|
1289
1268
|
}
|
1290
1269
|
|
1291
1270
|
var channels = [].concat(response.subscription);
|
1292
|
-
this.info('Subscription acknowledged for ? to ?', this.
|
1271
|
+
this.info('Subscription acknowledged for ? to ?', this._dispatcher.clientId, channels);
|
1293
1272
|
subscription.setDeferredStatus('succeeded');
|
1294
1273
|
}, this);
|
1295
1274
|
}, this);
|
@@ -1317,18 +1296,18 @@ Faye.Client = Faye.Class({
|
|
1317
1296
|
if (!dead) return;
|
1318
1297
|
|
1319
1298
|
this.connect(function() {
|
1320
|
-
this.info('Client ? attempting to unsubscribe from ?', this.
|
1299
|
+
this.info('Client ? attempting to unsubscribe from ?', this._dispatcher.clientId, channel);
|
1321
1300
|
|
1322
|
-
this.
|
1301
|
+
this._sendMessage({
|
1323
1302
|
channel: Faye.Channel.UNSUBSCRIBE,
|
1324
|
-
clientId: this.
|
1303
|
+
clientId: this._dispatcher.clientId,
|
1325
1304
|
subscription: channel
|
1326
1305
|
|
1327
|
-
}, function(response) {
|
1306
|
+
}, {}, function(response) {
|
1328
1307
|
if (!response.successful) return;
|
1329
1308
|
|
1330
1309
|
var channels = [].concat(response.subscription);
|
1331
|
-
this.info('Unsubscription acknowledged for ? from ?', this.
|
1310
|
+
this.info('Unsubscription acknowledged for ? from ?', this._dispatcher.clientId, channels);
|
1332
1311
|
}, this);
|
1333
1312
|
}, this);
|
1334
1313
|
},
|
@@ -1339,18 +1318,18 @@ Faye.Client = Faye.Class({
|
|
1339
1318
|
// MAY include: * clientId MAY include: * id
|
1340
1319
|
// * id * error
|
1341
1320
|
// * ext * ext
|
1342
|
-
publish: function(channel, data) {
|
1321
|
+
publish: function(channel, data, options) {
|
1343
1322
|
var publication = new Faye.Publication();
|
1344
1323
|
|
1345
1324
|
this.connect(function() {
|
1346
|
-
this.info('Client ? queueing published message to ?: ?', this.
|
1325
|
+
this.info('Client ? queueing published message to ?: ?', this._dispatcher.clientId, channel, data);
|
1347
1326
|
|
1348
|
-
this.
|
1327
|
+
this._sendMessage({
|
1349
1328
|
channel: channel,
|
1350
1329
|
data: data,
|
1351
|
-
clientId: this.
|
1330
|
+
clientId: this._dispatcher.clientId
|
1352
1331
|
|
1353
|
-
}, function(response) {
|
1332
|
+
}, options, function(response) {
|
1354
1333
|
if (response.successful)
|
1355
1334
|
publication.setDeferredStatus('succeeded');
|
1356
1335
|
else
|
@@ -1361,8 +1340,28 @@ Faye.Client = Faye.Class({
|
|
1361
1340
|
return publication;
|
1362
1341
|
},
|
1363
1342
|
|
1364
|
-
|
1365
|
-
|
1343
|
+
_sendMessage: function(message, options, callback, context) {
|
1344
|
+
message.id = this._generateMessageId();
|
1345
|
+
|
1346
|
+
var timeout = this._advice.timeout
|
1347
|
+
? 1.2 * this._advice.timeout / 1000
|
1348
|
+
: 1.2 * this._dispatcher.retry;
|
1349
|
+
|
1350
|
+
this.pipeThroughExtensions('outgoing', message, null, function(message) {
|
1351
|
+
if (!message) return;
|
1352
|
+
if (callback) this._responseCallbacks[message.id] = [callback, context];
|
1353
|
+
this._dispatcher.sendMessage(message, timeout, options || {});
|
1354
|
+
}, this);
|
1355
|
+
},
|
1356
|
+
|
1357
|
+
_generateMessageId: function() {
|
1358
|
+
this._messageId += 1;
|
1359
|
+
if (this._messageId >= Math.pow(2,32)) this._messageId = 0;
|
1360
|
+
return this._messageId.toString(36);
|
1361
|
+
},
|
1362
|
+
|
1363
|
+
_receiveMessage: function(message) {
|
1364
|
+
var id = message.id, callback;
|
1366
1365
|
|
1367
1366
|
if (message.successful !== undefined) {
|
1368
1367
|
callback = this._responseCallbacks[id];
|
@@ -1371,39 +1370,90 @@ Faye.Client = Faye.Class({
|
|
1371
1370
|
|
1372
1371
|
this.pipeThroughExtensions('incoming', message, null, function(message) {
|
1373
1372
|
if (!message) return;
|
1374
|
-
|
1375
1373
|
if (message.advice) this._handleAdvice(message.advice);
|
1376
1374
|
this._deliverMessage(message);
|
1377
|
-
|
1378
1375
|
if (callback) callback[0].call(callback[1], message);
|
1379
1376
|
}, this);
|
1380
|
-
|
1381
|
-
if (this._transportUp === true) return;
|
1382
|
-
this._transportUp = true;
|
1383
|
-
this.trigger('transport:up');
|
1384
1377
|
},
|
1385
1378
|
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
id, message, timeout;
|
1379
|
+
_handleAdvice: function(advice) {
|
1380
|
+
Faye.extend(this._advice, advice);
|
1381
|
+
this._dispatcher.timeout = this._advice.timeout / 1000;
|
1390
1382
|
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1383
|
+
if (this._advice.reconnect === this.HANDSHAKE && this._state !== this.DISCONNECTED) {
|
1384
|
+
this._state = this.UNCONNECTED;
|
1385
|
+
this._dispatcher.clientId = null;
|
1386
|
+
this._cycleConnection();
|
1387
|
+
}
|
1388
|
+
},
|
1394
1389
|
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1390
|
+
_deliverMessage: function(message) {
|
1391
|
+
if (!message.channel || message.data === undefined) return;
|
1392
|
+
this.info('Client ? calling listeners for ? with ?', this._dispatcher.clientId, message.channel, message.data);
|
1393
|
+
this._channels.distributeMessage(message);
|
1394
|
+
},
|
1395
|
+
|
1396
|
+
_cycleConnection: function() {
|
1397
|
+
if (this._connectRequest) {
|
1398
|
+
this._connectRequest = null;
|
1399
|
+
this.info('Closed connection for ?', this._dispatcher.clientId);
|
1399
1400
|
}
|
1401
|
+
var self = this;
|
1402
|
+
Faye.ENV.setTimeout(function() { self.connect() }, this._advice.interval);
|
1403
|
+
}
|
1404
|
+
});
|
1405
|
+
|
1406
|
+
Faye.extend(Faye.Client.prototype, Faye.Deferrable);
|
1407
|
+
Faye.extend(Faye.Client.prototype, Faye.Publisher);
|
1408
|
+
Faye.extend(Faye.Client.prototype, Faye.Logging);
|
1409
|
+
Faye.extend(Faye.Client.prototype, Faye.Extensible);
|
1410
|
+
|
1411
|
+
Faye.Dispatcher = Faye.Class({
|
1412
|
+
MAX_REQUEST_SIZE: 2048,
|
1413
|
+
DEFAULT_RETRY: 5,
|
1414
|
+
|
1415
|
+
UP: 1,
|
1416
|
+
DOWN: 2,
|
1417
|
+
|
1418
|
+
initialize: function(client, endpoint, options) {
|
1419
|
+
this._client = client;
|
1420
|
+
this.endpoint = Faye.URI.parse(endpoint);
|
1421
|
+
this._alternates = options.endpoints || {};
|
1422
|
+
|
1423
|
+
this.ca = options.ca;
|
1424
|
+
this.cookies = Faye.Cookies && new Faye.Cookies.CookieJar();
|
1425
|
+
this._disabled = [];
|
1426
|
+
this._envelopes = {};
|
1427
|
+
this.headers = {};
|
1428
|
+
this.retry = options.retry || this.DEFAULT_RETRY;
|
1429
|
+
this._state = 0;
|
1430
|
+
this.transports = {};
|
1431
|
+
|
1432
|
+
for (var type in this._alternates)
|
1433
|
+
this._alternates[type] = Faye.URI.parse(this._alternates[type]);
|
1434
|
+
|
1435
|
+
this.maxRequestSize = this.MAX_REQUEST_SIZE;
|
1436
|
+
},
|
1400
1437
|
|
1401
|
-
|
1402
|
-
this.
|
1403
|
-
this.trigger('transport:down');
|
1438
|
+
endpointFor: function(connectionType) {
|
1439
|
+
return this._alternates[connectionType] || this.endpoint;
|
1404
1440
|
},
|
1405
1441
|
|
1406
|
-
|
1442
|
+
disable: function(feature) {
|
1443
|
+
this._disabled.push(feature);
|
1444
|
+
},
|
1445
|
+
|
1446
|
+
setHeader: function(name, value) {
|
1447
|
+
this.headers[name] = value;
|
1448
|
+
},
|
1449
|
+
|
1450
|
+
close: function() {
|
1451
|
+
var transport = this._transport;
|
1452
|
+
delete this._transport;
|
1453
|
+
if (transport) transport.close();
|
1454
|
+
},
|
1455
|
+
|
1456
|
+
selectTransport: function(transportTypes) {
|
1407
1457
|
Faye.Transport.get(this, transportTypes, this._disabled, function(transport) {
|
1408
1458
|
this.debug('Selected ? transport for ?', transport.connectionType, Faye.URI.stringify(transport.endpoint));
|
1409
1459
|
|
@@ -1411,186 +1461,220 @@ Faye.Client = Faye.Class({
|
|
1411
1461
|
if (this._transport) this._transport.close();
|
1412
1462
|
|
1413
1463
|
this._transport = transport;
|
1464
|
+
this.connectionType = transport.connectionType;
|
1414
1465
|
}, this);
|
1415
1466
|
},
|
1416
1467
|
|
1417
|
-
|
1468
|
+
sendMessage: function(message, timeout, options) {
|
1418
1469
|
if (!this._transport) return;
|
1419
|
-
|
1470
|
+
options = options || {};
|
1420
1471
|
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
}, this);
|
1426
|
-
},
|
1472
|
+
var self = this,
|
1473
|
+
id = message.id,
|
1474
|
+
attempts = options.attempts,
|
1475
|
+
deadline = options.deadline && new Date().getTime() + (options.deadline * 1000),
|
1427
1476
|
|
1428
|
-
|
1429
|
-
|
1477
|
+
envelope = this._envelopes[id] = this._envelopes[id] ||
|
1478
|
+
{message: message, timeout: timeout, attempts: attempts, deadline: deadline};
|
1430
1479
|
|
1431
|
-
|
1432
|
-
envelope = new Faye.Envelope(message, timeout);
|
1480
|
+
if (envelope.request || envelope.timer) return;
|
1433
1481
|
|
1434
|
-
envelope.
|
1435
|
-
this.
|
1436
|
-
|
1482
|
+
if (this._attemptsExhausted(envelope) || this._deadlinePassed(envelope)) {
|
1483
|
+
delete this._envelopes[id];
|
1484
|
+
return;
|
1485
|
+
}
|
1486
|
+
|
1487
|
+
envelope.timer = Faye.ENV.setTimeout(function() {
|
1488
|
+
self.handleError(message);
|
1489
|
+
}, timeout * 1000);
|
1437
1490
|
|
1438
|
-
this._transport.
|
1491
|
+
envelope.request = this._transport.sendMessage(message);
|
1439
1492
|
},
|
1440
1493
|
|
1441
|
-
|
1442
|
-
this.
|
1443
|
-
|
1444
|
-
|
1494
|
+
handleResponse: function(reply) {
|
1495
|
+
var envelope = this._envelopes[reply.id];
|
1496
|
+
|
1497
|
+
if (reply.successful !== undefined && envelope) {
|
1498
|
+
delete this._envelopes[reply.id];
|
1499
|
+
Faye.ENV.clearTimeout(envelope.timer);
|
1500
|
+
}
|
1501
|
+
|
1502
|
+
this.trigger('message', reply);
|
1503
|
+
|
1504
|
+
if (this._state === this.UP) return;
|
1505
|
+
this._state = this.UP;
|
1506
|
+
this._client.trigger('transport:up');
|
1445
1507
|
},
|
1446
1508
|
|
1447
|
-
|
1448
|
-
|
1509
|
+
handleError: function(message, immediate) {
|
1510
|
+
var envelope = this._envelopes[message.id],
|
1511
|
+
request = envelope && envelope.request,
|
1512
|
+
self = this;
|
1449
1513
|
|
1450
|
-
if (
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1514
|
+
if (!request) return;
|
1515
|
+
|
1516
|
+
request.then(function(req) {
|
1517
|
+
if (req && req.abort) req.abort();
|
1518
|
+
});
|
1519
|
+
|
1520
|
+
Faye.ENV.clearTimeout(envelope.timer);
|
1521
|
+
envelope.request = envelope.timer = null;
|
1522
|
+
|
1523
|
+
if (immediate) {
|
1524
|
+
this.sendMessage(envelope.message, envelope.timeout);
|
1525
|
+
} else {
|
1526
|
+
envelope.timer = Faye.ENV.setTimeout(function() {
|
1527
|
+
envelope.timer = null;
|
1528
|
+
self.sendMessage(envelope.message, envelope.timeout);
|
1529
|
+
}, this.retry * 1000);
|
1454
1530
|
}
|
1531
|
+
|
1532
|
+
if (this._state === this.DOWN) return;
|
1533
|
+
this._state = this.DOWN;
|
1534
|
+
this._client.trigger('transport:down');
|
1455
1535
|
},
|
1456
1536
|
|
1457
|
-
|
1458
|
-
if (
|
1459
|
-
|
1460
|
-
|
1537
|
+
_attemptsExhausted: function(envelope) {
|
1538
|
+
if (envelope.attempts === undefined) return false;
|
1539
|
+
envelope.attempts -= 1;
|
1540
|
+
if (envelope.attempts >= 0) return false;
|
1541
|
+
return true;
|
1461
1542
|
},
|
1462
1543
|
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
var self = this;
|
1469
|
-
Faye.ENV.setTimeout(function() { self.connect() }, this._advice.interval);
|
1544
|
+
_deadlinePassed: function(envelope) {
|
1545
|
+
var deadline = envelope.deadline;
|
1546
|
+
if (deadline === undefined) return false;
|
1547
|
+
if (new Date().getTime() <= deadline) return false;
|
1548
|
+
return true;
|
1470
1549
|
}
|
1471
1550
|
});
|
1472
1551
|
|
1473
|
-
Faye.extend(Faye.
|
1474
|
-
Faye.extend(Faye.
|
1475
|
-
Faye.extend(Faye.Client.prototype, Faye.Logging);
|
1476
|
-
Faye.extend(Faye.Client.prototype, Faye.Extensible);
|
1552
|
+
Faye.extend(Faye.Dispatcher.prototype, Faye.Publisher);
|
1553
|
+
Faye.extend(Faye.Dispatcher.prototype, Faye.Logging);
|
1477
1554
|
|
1478
1555
|
Faye.Transport = Faye.extend(Faye.Class({
|
1479
1556
|
MAX_DELAY: 0,
|
1480
1557
|
batching: true,
|
1481
1558
|
|
1482
|
-
initialize: function(
|
1483
|
-
this.
|
1484
|
-
this.endpoint
|
1485
|
-
this._outbox
|
1559
|
+
initialize: function(dispatcher, endpoint) {
|
1560
|
+
this._dispatcher = dispatcher;
|
1561
|
+
this.endpoint = endpoint;
|
1562
|
+
this._outbox = [];
|
1486
1563
|
},
|
1487
1564
|
|
1488
1565
|
close: function() {},
|
1489
1566
|
|
1490
|
-
encode: function(
|
1567
|
+
encode: function(messages) {
|
1491
1568
|
return '';
|
1492
1569
|
},
|
1493
1570
|
|
1494
|
-
|
1495
|
-
var message = envelope.message;
|
1496
|
-
|
1571
|
+
sendMessage: function(message) {
|
1497
1572
|
this.debug('Client ? sending message to ?: ?',
|
1498
|
-
this.
|
1573
|
+
this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), message);
|
1499
1574
|
|
1500
|
-
if (!this.batching) return this.request([
|
1575
|
+
if (!this.batching) return Faye.Promise.fulfilled(this.request([message]));
|
1501
1576
|
|
1502
|
-
this._outbox.push(
|
1577
|
+
this._outbox.push(message);
|
1578
|
+
this._flushLargeBatch();
|
1579
|
+
this._promise = this._promise || new Faye.Promise();
|
1503
1580
|
|
1504
|
-
if (message.channel === Faye.Channel.HANDSHAKE)
|
1505
|
-
|
1581
|
+
if (message.channel === Faye.Channel.HANDSHAKE) {
|
1582
|
+
this.addTimeout('publish', 0.01, this._flush, this);
|
1583
|
+
return this._promise;
|
1584
|
+
}
|
1506
1585
|
|
1507
1586
|
if (message.channel === Faye.Channel.CONNECT)
|
1508
1587
|
this._connectMessage = message;
|
1509
1588
|
|
1510
|
-
this.
|
1511
|
-
|
1589
|
+
this.addTimeout('publish', this.MAX_DELAY, this._flush, this);
|
1590
|
+
return this._promise;
|
1512
1591
|
},
|
1513
1592
|
|
1514
|
-
|
1593
|
+
_flush: function() {
|
1515
1594
|
this.removeTimeout('publish');
|
1516
1595
|
|
1517
1596
|
if (this._outbox.length > 1 && this._connectMessage)
|
1518
1597
|
this._connectMessage.advice = {timeout: 0};
|
1519
1598
|
|
1520
|
-
this.request(this._outbox);
|
1599
|
+
Faye.Promise.fulfill(this._promise, this.request(this._outbox));
|
1600
|
+
delete this._promise;
|
1521
1601
|
|
1522
1602
|
this._connectMessage = null;
|
1523
1603
|
this._outbox = [];
|
1524
1604
|
},
|
1525
1605
|
|
1526
|
-
|
1606
|
+
_flushLargeBatch: function() {
|
1527
1607
|
var string = this.encode(this._outbox);
|
1528
|
-
if (string.length < this.
|
1608
|
+
if (string.length < this._dispatcher.maxRequestSize) return;
|
1529
1609
|
var last = this._outbox.pop();
|
1530
|
-
this.
|
1610
|
+
this._flush();
|
1531
1611
|
if (last) this._outbox.push(last);
|
1532
1612
|
},
|
1533
1613
|
|
1534
|
-
|
1535
|
-
|
1536
|
-
while (n--) envelopes[n].setDeferredStatus('succeeded');
|
1537
|
-
|
1538
|
-
responses = [].concat(responses);
|
1614
|
+
_receive: function(replies) {
|
1615
|
+
replies = [].concat(replies);
|
1539
1616
|
|
1540
|
-
this.debug('Client ? received from ?: ?',
|
1541
|
-
this.
|
1617
|
+
this.debug('Client ? received from ? via ?: ?',
|
1618
|
+
this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), this.connectionType, replies);
|
1542
1619
|
|
1543
|
-
for (var i = 0, n =
|
1544
|
-
this.
|
1620
|
+
for (var i = 0, n = replies.length; i < n; i++)
|
1621
|
+
this._dispatcher.handleResponse(replies[i]);
|
1545
1622
|
},
|
1546
1623
|
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1624
|
+
_handleError: function(messages, immediate) {
|
1625
|
+
messages = [].concat(messages);
|
1626
|
+
|
1627
|
+
this.debug('Client ? failed to send to ? via ?: ?',
|
1628
|
+
this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), this.connectionType, messages);
|
1629
|
+
|
1630
|
+
for (var i = 0, n = messages.length; i < n; i++)
|
1631
|
+
this._dispatcher.handleError(messages[i]);
|
1550
1632
|
},
|
1551
1633
|
|
1552
1634
|
_getCookies: function() {
|
1553
|
-
var cookies = this.
|
1635
|
+
var cookies = this._dispatcher.cookies,
|
1636
|
+
url = Faye.URI.stringify(this.endpoint);
|
1637
|
+
|
1554
1638
|
if (!cookies) return '';
|
1555
1639
|
|
1556
|
-
return cookies.
|
1557
|
-
|
1558
|
-
|
1559
|
-
secure: this.endpoint.protocol === 'https:'
|
1560
|
-
}).toValueString();
|
1640
|
+
return Faye.map(cookies.getCookiesSync(url), function(cookie) {
|
1641
|
+
return cookie.cookieString();
|
1642
|
+
}).join('; ');
|
1561
1643
|
},
|
1562
1644
|
|
1563
1645
|
_storeCookies: function(setCookie) {
|
1564
|
-
|
1646
|
+
var cookies = this._dispatcher.cookies,
|
1647
|
+
url = Faye.URI.stringify(this.endpoint),
|
1648
|
+
cookie;
|
1649
|
+
|
1650
|
+
if (!setCookie || !cookies) return;
|
1565
1651
|
setCookie = [].concat(setCookie);
|
1566
|
-
var cookie;
|
1567
1652
|
|
1568
1653
|
for (var i = 0, n = setCookie.length; i < n; i++) {
|
1569
|
-
cookie =
|
1570
|
-
cookie
|
1571
|
-
cookie.domain = cookie.domain || this.endpoint.hostname;
|
1654
|
+
cookie = Faye.Cookies.Cookie.parse(setCookie[i]);
|
1655
|
+
cookies.setCookieSync(cookie, url);
|
1572
1656
|
}
|
1573
1657
|
}
|
1574
1658
|
|
1575
1659
|
}), {
|
1576
|
-
get: function(
|
1577
|
-
var endpoint =
|
1660
|
+
get: function(dispatcher, allowed, disabled, callback, context) {
|
1661
|
+
var endpoint = dispatcher.endpoint;
|
1578
1662
|
|
1579
1663
|
Faye.asyncEach(this._transports, function(pair, resume) {
|
1580
1664
|
var connType = pair[0], klass = pair[1],
|
1581
|
-
connEndpoint =
|
1665
|
+
connEndpoint = dispatcher.endpointFor(connType);
|
1582
1666
|
|
1583
1667
|
if (Faye.indexOf(disabled, connType) >= 0)
|
1584
1668
|
return resume();
|
1585
1669
|
|
1586
1670
|
if (Faye.indexOf(allowed, connType) < 0) {
|
1587
|
-
klass.isUsable(
|
1671
|
+
klass.isUsable(dispatcher, connEndpoint, function() {});
|
1588
1672
|
return resume();
|
1589
1673
|
}
|
1590
1674
|
|
1591
|
-
klass.isUsable(
|
1675
|
+
klass.isUsable(dispatcher, connEndpoint, function(isUsable) {
|
1592
1676
|
if (!isUsable) return resume();
|
1593
|
-
var transport = klass.hasOwnProperty('create') ? klass.create(
|
1677
|
+
var transport = klass.hasOwnProperty('create') ? klass.create(dispatcher, connEndpoint) : new klass(dispatcher, connEndpoint);
|
1594
1678
|
callback.call(context, transport);
|
1595
1679
|
});
|
1596
1680
|
}, function() {
|
@@ -2154,16 +2238,23 @@ Faye.Transport.WebSocket = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2154
2238
|
this.connect();
|
2155
2239
|
},
|
2156
2240
|
|
2157
|
-
request: function(
|
2241
|
+
request: function(messages) {
|
2158
2242
|
this._pending = this._pending || new Faye.Set();
|
2159
|
-
for (var i = 0, n =
|
2243
|
+
for (var i = 0, n = messages.length; i < n; i++) this._pending.add(messages[i]);
|
2244
|
+
|
2245
|
+
var promise = new Faye.Promise();
|
2160
2246
|
|
2161
2247
|
this.callback(function(socket) {
|
2162
2248
|
if (!socket) return;
|
2163
|
-
var messages = Faye.map(envelopes, function(e) { return e.message });
|
2164
2249
|
socket.send(Faye.toJSON(messages));
|
2250
|
+
Faye.Promise.fulfill(promise, socket);
|
2165
2251
|
}, this);
|
2252
|
+
|
2166
2253
|
this.connect();
|
2254
|
+
|
2255
|
+
return {
|
2256
|
+
abort: function() { promise.then(function(ws) { ws.close() }) }
|
2257
|
+
};
|
2167
2258
|
},
|
2168
2259
|
|
2169
2260
|
connect: function() {
|
@@ -2204,28 +2295,25 @@ Faye.Transport.WebSocket = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2204
2295
|
delete self._pending;
|
2205
2296
|
|
2206
2297
|
if (wasConnected) {
|
2207
|
-
self.
|
2298
|
+
self._handleError(pending, true);
|
2208
2299
|
} else if (self._everConnected) {
|
2209
|
-
self.
|
2300
|
+
self._handleError(pending);
|
2210
2301
|
} else {
|
2211
2302
|
self.setDeferredStatus('failed');
|
2212
2303
|
}
|
2213
2304
|
};
|
2214
2305
|
|
2215
2306
|
socket.onmessage = function(event) {
|
2216
|
-
var
|
2217
|
-
|
2218
|
-
envelope;
|
2307
|
+
var replies = JSON.parse(event.data);
|
2308
|
+
if (!replies) return;
|
2219
2309
|
|
2220
|
-
|
2221
|
-
messages = [].concat(messages);
|
2310
|
+
replies = [].concat(replies);
|
2222
2311
|
|
2223
|
-
for (var i = 0, n =
|
2224
|
-
if (
|
2225
|
-
|
2226
|
-
if (envelope) envelopes.push(envelope);
|
2312
|
+
for (var i = 0, n = replies.length; i < n; i++) {
|
2313
|
+
if (replies[i].successful === undefined) continue;
|
2314
|
+
self._pending.remove(replies[i]);
|
2227
2315
|
}
|
2228
|
-
self.
|
2316
|
+
self._receive(replies);
|
2229
2317
|
};
|
2230
2318
|
},
|
2231
2319
|
|
@@ -2236,7 +2324,8 @@ Faye.Transport.WebSocket = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2236
2324
|
|
2237
2325
|
_createSocket: function() {
|
2238
2326
|
var url = Faye.Transport.WebSocket.getSocketUrl(this.endpoint),
|
2239
|
-
|
2327
|
+
headers = Faye.copyObject(this._dispatcher.headers),
|
2328
|
+
options = {headers: headers, ca: this._dispatcher.ca};
|
2240
2329
|
|
2241
2330
|
options.headers['Cookie'] = this._getCookies();
|
2242
2331
|
|
@@ -2248,7 +2337,7 @@ Faye.Transport.WebSocket = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2248
2337
|
_ping: function() {
|
2249
2338
|
if (!this._socket) return;
|
2250
2339
|
this._socket.send('[]');
|
2251
|
-
this.addTimeout('ping', this.
|
2340
|
+
this.addTimeout('ping', this._dispatcher.timeout / 2, this._ping, this);
|
2252
2341
|
}
|
2253
2342
|
|
2254
2343
|
}), {
|
@@ -2257,9 +2346,9 @@ Faye.Transport.WebSocket = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2257
2346
|
'https:': 'wss:'
|
2258
2347
|
},
|
2259
2348
|
|
2260
|
-
create: function(
|
2261
|
-
var sockets =
|
2262
|
-
sockets[endpoint.href] = sockets[endpoint.href] || new this(
|
2349
|
+
create: function(dispatcher, endpoint) {
|
2350
|
+
var sockets = dispatcher.transports.websocket = dispatcher.transports.websocket || {};
|
2351
|
+
sockets[endpoint.href] = sockets[endpoint.href] || new this(dispatcher, endpoint);
|
2263
2352
|
return sockets[endpoint.href];
|
2264
2353
|
},
|
2265
2354
|
|
@@ -2269,28 +2358,28 @@ Faye.Transport.WebSocket = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2269
2358
|
return Faye.URI.stringify(endpoint);
|
2270
2359
|
},
|
2271
2360
|
|
2272
|
-
isUsable: function(
|
2273
|
-
this.create(
|
2361
|
+
isUsable: function(dispatcher, endpoint, callback, context) {
|
2362
|
+
this.create(dispatcher, endpoint).isUsable(callback, context);
|
2274
2363
|
}
|
2275
2364
|
});
|
2276
2365
|
|
2277
2366
|
Faye.extend(Faye.Transport.WebSocket.prototype, Faye.Deferrable);
|
2278
2367
|
Faye.Transport.register('websocket', Faye.Transport.WebSocket);
|
2279
2368
|
|
2280
|
-
if (Faye.Event)
|
2369
|
+
if (Faye.Event && Faye.ENV.onbeforeunload !== undefined)
|
2281
2370
|
Faye.Event.on(Faye.ENV, 'beforeunload', function() {
|
2282
2371
|
Faye.Transport.WebSocket._unloaded = true;
|
2283
2372
|
});
|
2284
2373
|
|
2285
2374
|
Faye.Transport.EventSource = Faye.extend(Faye.Class(Faye.Transport, {
|
2286
|
-
initialize: function(
|
2287
|
-
Faye.Transport.prototype.initialize.call(this,
|
2375
|
+
initialize: function(dispatcher, endpoint) {
|
2376
|
+
Faye.Transport.prototype.initialize.call(this, dispatcher, endpoint);
|
2288
2377
|
if (!Faye.ENV.EventSource) return this.setDeferredStatus('failed');
|
2289
2378
|
|
2290
|
-
this._xhr = new Faye.Transport.XHR(
|
2379
|
+
this._xhr = new Faye.Transport.XHR(dispatcher, endpoint);
|
2291
2380
|
|
2292
2381
|
endpoint = Faye.copyObject(endpoint);
|
2293
|
-
endpoint.pathname += '/' +
|
2382
|
+
endpoint.pathname += '/' + dispatcher.clientId;
|
2294
2383
|
|
2295
2384
|
var socket = new EventSource(Faye.URI.stringify(endpoint)),
|
2296
2385
|
self = this;
|
@@ -2302,7 +2391,7 @@ Faye.Transport.EventSource = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2302
2391
|
|
2303
2392
|
socket.onerror = function() {
|
2304
2393
|
if (self._everConnected) {
|
2305
|
-
self.
|
2394
|
+
self._handleError([]);
|
2306
2395
|
} else {
|
2307
2396
|
self.setDeferredStatus('failed');
|
2308
2397
|
socket.close();
|
@@ -2310,7 +2399,7 @@ Faye.Transport.EventSource = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2310
2399
|
};
|
2311
2400
|
|
2312
2401
|
socket.onmessage = function(event) {
|
2313
|
-
self.
|
2402
|
+
self._receive(JSON.parse(event.data));
|
2314
2403
|
};
|
2315
2404
|
|
2316
2405
|
this._socket = socket;
|
@@ -2328,34 +2417,34 @@ Faye.Transport.EventSource = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2328
2417
|
this.errback(function() { callback.call(context, false) });
|
2329
2418
|
},
|
2330
2419
|
|
2331
|
-
encode: function(
|
2332
|
-
return this._xhr.encode(
|
2420
|
+
encode: function(messages) {
|
2421
|
+
return this._xhr.encode(messages);
|
2333
2422
|
},
|
2334
2423
|
|
2335
|
-
request: function(
|
2336
|
-
this._xhr.request(
|
2424
|
+
request: function(messages) {
|
2425
|
+
return this._xhr.request(messages);
|
2337
2426
|
}
|
2338
2427
|
|
2339
2428
|
}), {
|
2340
|
-
isUsable: function(
|
2341
|
-
var id =
|
2429
|
+
isUsable: function(dispatcher, endpoint, callback, context) {
|
2430
|
+
var id = dispatcher.clientId;
|
2342
2431
|
if (!id) return callback.call(context, false);
|
2343
2432
|
|
2344
|
-
Faye.Transport.XHR.isUsable(
|
2433
|
+
Faye.Transport.XHR.isUsable(dispatcher, endpoint, function(usable) {
|
2345
2434
|
if (!usable) return callback.call(context, false);
|
2346
|
-
this.create(
|
2435
|
+
this.create(dispatcher, endpoint).isUsable(callback, context);
|
2347
2436
|
}, this);
|
2348
2437
|
},
|
2349
2438
|
|
2350
|
-
create: function(
|
2351
|
-
var sockets =
|
2352
|
-
id =
|
2439
|
+
create: function(dispatcher, endpoint) {
|
2440
|
+
var sockets = dispatcher.transports.eventsource = dispatcher.transports.eventsource || {},
|
2441
|
+
id = dispatcher.clientId;
|
2353
2442
|
|
2354
2443
|
endpoint = Faye.copyObject(endpoint);
|
2355
2444
|
endpoint.pathname += '/' + (id || '');
|
2356
2445
|
var url = Faye.URI.stringify(endpoint);
|
2357
2446
|
|
2358
|
-
sockets[url] = sockets[url] || new this(
|
2447
|
+
sockets[url] = sockets[url] || new this(dispatcher, endpoint);
|
2359
2448
|
return sockets[url];
|
2360
2449
|
}
|
2361
2450
|
});
|
@@ -2364,58 +2453,58 @@ Faye.extend(Faye.Transport.EventSource.prototype, Faye.Deferrable);
|
|
2364
2453
|
Faye.Transport.register('eventsource', Faye.Transport.EventSource);
|
2365
2454
|
|
2366
2455
|
Faye.Transport.XHR = Faye.extend(Faye.Class(Faye.Transport, {
|
2367
|
-
encode: function(
|
2368
|
-
var messages = Faye.map(envelopes, function(e) { return e.message });
|
2456
|
+
encode: function(messages) {
|
2369
2457
|
return Faye.toJSON(messages);
|
2370
2458
|
},
|
2371
2459
|
|
2372
|
-
request: function(
|
2373
|
-
var
|
2460
|
+
request: function(messages) {
|
2461
|
+
var href = this.endpoint.href,
|
2374
2462
|
xhr = Faye.ENV.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest(),
|
2375
2463
|
self = this;
|
2376
2464
|
|
2377
|
-
xhr.open('POST',
|
2465
|
+
xhr.open('POST', href, true);
|
2378
2466
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
2379
2467
|
xhr.setRequestHeader('Pragma', 'no-cache');
|
2380
2468
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
2381
2469
|
|
2382
|
-
var headers = this.
|
2470
|
+
var headers = this._dispatcher.headers;
|
2383
2471
|
for (var key in headers) {
|
2384
2472
|
if (!headers.hasOwnProperty(key)) continue;
|
2385
2473
|
xhr.setRequestHeader(key, headers[key]);
|
2386
2474
|
}
|
2387
2475
|
|
2388
2476
|
var abort = function() { xhr.abort() };
|
2389
|
-
Faye.Event.on(Faye.ENV, 'beforeunload', abort);
|
2477
|
+
if (Faye.ENV.onbeforeunload !== undefined) Faye.Event.on(Faye.ENV, 'beforeunload', abort);
|
2390
2478
|
|
2391
2479
|
xhr.onreadystatechange = function() {
|
2392
2480
|
if (!xhr || xhr.readyState !== 4) return;
|
2393
2481
|
|
2394
|
-
var
|
2395
|
-
status
|
2396
|
-
text
|
2397
|
-
successful
|
2482
|
+
var replies = null,
|
2483
|
+
status = xhr.status,
|
2484
|
+
text = xhr.responseText,
|
2485
|
+
successful = (status >= 200 && status < 300) || status === 304 || status === 1223;
|
2398
2486
|
|
2399
|
-
Faye.Event.detach(Faye.ENV, 'beforeunload', abort);
|
2487
|
+
if (Faye.ENV.onbeforeunload !== undefined) Faye.Event.detach(Faye.ENV, 'beforeunload', abort);
|
2400
2488
|
xhr.onreadystatechange = function() {};
|
2401
2489
|
xhr = null;
|
2402
2490
|
|
2403
|
-
if (!successful) return self.
|
2491
|
+
if (!successful) return self._handleError(messages);
|
2404
2492
|
|
2405
2493
|
try {
|
2406
|
-
|
2494
|
+
replies = JSON.parse(text);
|
2407
2495
|
} catch (e) {}
|
2408
2496
|
|
2409
|
-
if (
|
2410
|
-
self.
|
2497
|
+
if (replies)
|
2498
|
+
self._receive(replies);
|
2411
2499
|
else
|
2412
|
-
self.
|
2500
|
+
self._handleError(messages);
|
2413
2501
|
};
|
2414
2502
|
|
2415
|
-
xhr.send(this.encode(
|
2503
|
+
xhr.send(this.encode(messages));
|
2504
|
+
return xhr;
|
2416
2505
|
}
|
2417
2506
|
}), {
|
2418
|
-
isUsable: function(
|
2507
|
+
isUsable: function(dispatcher, endpoint, callback, context) {
|
2419
2508
|
callback.call(context, Faye.URI.isSameOrigin(endpoint));
|
2420
2509
|
}
|
2421
2510
|
});
|
@@ -2423,15 +2512,14 @@ Faye.Transport.XHR = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2423
2512
|
Faye.Transport.register('long-polling', Faye.Transport.XHR);
|
2424
2513
|
|
2425
2514
|
Faye.Transport.CORS = Faye.extend(Faye.Class(Faye.Transport, {
|
2426
|
-
encode: function(
|
2427
|
-
var messages = Faye.map(envelopes, function(e) { return e.message });
|
2515
|
+
encode: function(messages) {
|
2428
2516
|
return 'message=' + encodeURIComponent(Faye.toJSON(messages));
|
2429
2517
|
},
|
2430
2518
|
|
2431
|
-
request: function(
|
2519
|
+
request: function(messages) {
|
2432
2520
|
var xhrClass = Faye.ENV.XDomainRequest ? XDomainRequest : XMLHttpRequest,
|
2433
2521
|
xhr = new xhrClass(),
|
2434
|
-
headers = this.
|
2522
|
+
headers = this._dispatcher.headers,
|
2435
2523
|
self = this,
|
2436
2524
|
key;
|
2437
2525
|
|
@@ -2452,29 +2540,30 @@ Faye.Transport.CORS = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2452
2540
|
};
|
2453
2541
|
|
2454
2542
|
xhr.onload = function() {
|
2455
|
-
var
|
2543
|
+
var replies = null;
|
2456
2544
|
try {
|
2457
|
-
|
2545
|
+
replies = JSON.parse(xhr.responseText);
|
2458
2546
|
} catch (e) {}
|
2459
2547
|
|
2460
2548
|
cleanUp();
|
2461
2549
|
|
2462
|
-
if (
|
2463
|
-
self.
|
2550
|
+
if (replies)
|
2551
|
+
self._receive(replies);
|
2464
2552
|
else
|
2465
|
-
self.
|
2553
|
+
self._handleError(messages);
|
2466
2554
|
};
|
2467
2555
|
|
2468
2556
|
xhr.onerror = xhr.ontimeout = function() {
|
2469
2557
|
cleanUp();
|
2470
|
-
self.
|
2558
|
+
self._handleError(messages);
|
2471
2559
|
};
|
2472
2560
|
|
2473
2561
|
xhr.onprogress = function() {};
|
2474
|
-
xhr.send(this.encode(
|
2562
|
+
xhr.send(this.encode(messages));
|
2563
|
+
return xhr;
|
2475
2564
|
}
|
2476
2565
|
}), {
|
2477
|
-
isUsable: function(
|
2566
|
+
isUsable: function(dispatcher, endpoint, callback, context) {
|
2478
2567
|
if (Faye.URI.isSameOrigin(endpoint))
|
2479
2568
|
return callback.call(context, false);
|
2480
2569
|
|
@@ -2492,17 +2581,15 @@ Faye.Transport.CORS = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2492
2581
|
Faye.Transport.register('cross-origin-long-polling', Faye.Transport.CORS);
|
2493
2582
|
|
2494
2583
|
Faye.Transport.JSONP = Faye.extend(Faye.Class(Faye.Transport, {
|
2495
|
-
encode: function(
|
2496
|
-
var messages = Faye.map(envelopes, function(e) { return e.message });
|
2584
|
+
encode: function(messages) {
|
2497
2585
|
var url = Faye.copyObject(this.endpoint);
|
2498
2586
|
url.query.message = Faye.toJSON(messages);
|
2499
2587
|
url.query.jsonp = '__jsonp' + Faye.Transport.JSONP._cbCount + '__';
|
2500
2588
|
return Faye.URI.stringify(url);
|
2501
2589
|
},
|
2502
2590
|
|
2503
|
-
request: function(
|
2504
|
-
var
|
2505
|
-
head = document.getElementsByTagName('head')[0],
|
2591
|
+
request: function(messages) {
|
2592
|
+
var head = document.getElementsByTagName('head')[0],
|
2506
2593
|
script = document.createElement('script'),
|
2507
2594
|
callbackName = Faye.Transport.JSONP.getCallbackName(),
|
2508
2595
|
endpoint = Faye.copyObject(this.endpoint),
|
@@ -2511,17 +2598,28 @@ Faye.Transport.JSONP = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2511
2598
|
endpoint.query.message = Faye.toJSON(messages);
|
2512
2599
|
endpoint.query.jsonp = callbackName;
|
2513
2600
|
|
2514
|
-
|
2601
|
+
var cleanup = function() {
|
2515
2602
|
if (!Faye.ENV[callbackName]) return false;
|
2516
2603
|
Faye.ENV[callbackName] = undefined;
|
2517
2604
|
try { delete Faye.ENV[callbackName] } catch (e) {}
|
2518
2605
|
script.parentNode.removeChild(script);
|
2519
|
-
|
2606
|
+
};
|
2607
|
+
|
2608
|
+
Faye.ENV[callbackName] = function(replies) {
|
2609
|
+
cleanup();
|
2610
|
+
self._receive(replies);
|
2520
2611
|
};
|
2521
2612
|
|
2522
2613
|
script.type = 'text/javascript';
|
2523
2614
|
script.src = Faye.URI.stringify(endpoint);
|
2524
2615
|
head.appendChild(script);
|
2616
|
+
|
2617
|
+
script.onerror = function() {
|
2618
|
+
cleanup();
|
2619
|
+
self._handleError(messages);
|
2620
|
+
};
|
2621
|
+
|
2622
|
+
return {abort: cleanup};
|
2525
2623
|
}
|
2526
2624
|
}), {
|
2527
2625
|
_cbCount: 0,
|
@@ -2531,11 +2629,11 @@ Faye.Transport.JSONP = Faye.extend(Faye.Class(Faye.Transport, {
|
|
2531
2629
|
return '__jsonp' + this._cbCount + '__';
|
2532
2630
|
},
|
2533
2631
|
|
2534
|
-
isUsable: function(
|
2632
|
+
isUsable: function(dispatcher, endpoint, callback, context) {
|
2535
2633
|
callback.call(context, true);
|
2536
2634
|
}
|
2537
2635
|
});
|
2538
2636
|
|
2539
2637
|
Faye.Transport.register('callback-polling', Faye.Transport.JSONP);
|
2540
2638
|
|
2541
|
-
})();
|
2639
|
+
})();
|