faye 1.0.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +386 -196
- data/LICENSE.md +12 -0
- data/README.md +4 -30
- data/build/client/faye-browser-min.js +2 -0
- data/build/client/faye-browser-min.js.map +1 -0
- data/build/client/faye-browser.js +3371 -0
- data/build/client/faye-browser.js.map +1 -0
- data/lib/faye.rb +10 -14
- data/lib/faye/adapters/rack_adapter.rb +64 -34
- data/lib/faye/engines/connection.rb +7 -7
- data/lib/faye/engines/memory.rb +7 -8
- data/lib/faye/engines/proxy.rb +14 -15
- data/lib/faye/error.rb +0 -1
- data/lib/faye/mixins/deferrable.rb +0 -1
- data/lib/faye/mixins/logging.rb +0 -1
- data/lib/faye/mixins/publisher.rb +0 -1
- data/lib/faye/mixins/timeouts.rb +0 -1
- data/lib/faye/protocol/channel.rb +6 -8
- data/lib/faye/protocol/client.rb +109 -161
- data/lib/faye/protocol/dispatcher.rb +170 -0
- data/lib/faye/protocol/extensible.rb +1 -2
- data/lib/faye/protocol/grammar.rb +0 -1
- data/lib/faye/protocol/scheduler.rb +43 -0
- data/lib/faye/protocol/server.rb +23 -14
- data/lib/faye/protocol/socket.rb +0 -1
- data/lib/faye/protocol/subscription.rb +17 -2
- data/lib/faye/transport/http.rb +43 -33
- data/lib/faye/transport/local.rb +6 -5
- data/lib/faye/transport/transport.rb +69 -38
- data/lib/faye/transport/web_socket.rb +47 -41
- data/lib/faye/util/namespace.rb +0 -1
- metadata +59 -29
- data/lib/faye-browser-min.js +0 -3
- data/lib/faye-browser-min.js.map +0 -1
- data/lib/faye-browser.js +0 -2541
- data/lib/faye/protocol/envelope.rb +0 -24
@@ -0,0 +1,170 @@
|
|
1
|
+
module Faye
|
2
|
+
class Dispatcher
|
3
|
+
|
4
|
+
class Envelope < Struct.new(:message, :scheduler, :request, :timer)
|
5
|
+
end
|
6
|
+
|
7
|
+
MAX_REQUEST_SIZE = 2048
|
8
|
+
DEFAULT_RETRY = 5.0
|
9
|
+
|
10
|
+
UP = 1
|
11
|
+
DOWN = 2
|
12
|
+
|
13
|
+
include Publisher
|
14
|
+
include Logging
|
15
|
+
extend Forwardable
|
16
|
+
def_delegators :@transport, :connection_type
|
17
|
+
|
18
|
+
attr_accessor :client_id, :timeout
|
19
|
+
attr_reader :endpoint, :tls, :headers, :cookies, :proxy, :retry
|
20
|
+
attr_reader :max_request_size, :transports, :ws_extensions
|
21
|
+
|
22
|
+
def initialize(client, endpoint, options)
|
23
|
+
super()
|
24
|
+
|
25
|
+
@client = client
|
26
|
+
@endpoint = String === endpoint ? URI(endpoint) : endpoint
|
27
|
+
@alternates = options[:endpoints] || {}
|
28
|
+
|
29
|
+
@cookies = CookieJar::Jar.new
|
30
|
+
@disabled = []
|
31
|
+
@envelopes = {}
|
32
|
+
@headers = {}
|
33
|
+
@retry = options[:retry] || DEFAULT_RETRY
|
34
|
+
@scheduler = options[:scheduler] || Faye::Scheduler
|
35
|
+
@state = 0
|
36
|
+
@transports = {}
|
37
|
+
@ws_extensions = []
|
38
|
+
|
39
|
+
@proxy = options[:proxy] || {}
|
40
|
+
@proxy = { :origin => @proxy } if String === @proxy
|
41
|
+
|
42
|
+
[*options[:websocket_extensions]].each do |extension|
|
43
|
+
add_websocket_extension(extension)
|
44
|
+
end
|
45
|
+
|
46
|
+
@tls = { :verify_peer => true }.merge(options[:tls] || {})
|
47
|
+
|
48
|
+
@alternates.each do |type, url|
|
49
|
+
@alternates[type] = URI(url)
|
50
|
+
end
|
51
|
+
|
52
|
+
@max_request_size = MAX_REQUEST_SIZE
|
53
|
+
end
|
54
|
+
|
55
|
+
def endpoint_for(connection_type)
|
56
|
+
@alternates[connection_type] || @endpoint
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_websocket_extension(extension)
|
60
|
+
@ws_extensions << extension
|
61
|
+
end
|
62
|
+
|
63
|
+
def disable(feature)
|
64
|
+
@disabled << feature
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_header(name, value)
|
68
|
+
@headers[name.to_s] = value.to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
def close
|
72
|
+
transport = @transport
|
73
|
+
@transport = nil
|
74
|
+
transport.close if transport
|
75
|
+
end
|
76
|
+
|
77
|
+
def connection_types
|
78
|
+
Transport.connection_types
|
79
|
+
end
|
80
|
+
|
81
|
+
def select_transport(transport_types)
|
82
|
+
Transport.get(self, transport_types, @disabled) do |transport|
|
83
|
+
debug('Selected ? transport for ?', transport.connection_type, transport.endpoint)
|
84
|
+
|
85
|
+
next if transport == @transport
|
86
|
+
@transport.close if @transport
|
87
|
+
|
88
|
+
@transport = transport
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def send_message(message, timeout, options = {})
|
93
|
+
id = message['id']
|
94
|
+
attempts = options[:attempts]
|
95
|
+
deadline = options[:deadline] && Time.now.to_f + options[:deadline]
|
96
|
+
envelope = @envelopes[id]
|
97
|
+
|
98
|
+
unless envelope
|
99
|
+
scheduler = @scheduler.new(message, :timeout => timeout, :interval => @retry, :attempts => attempts, :deadline => deadline)
|
100
|
+
envelope = @envelopes[id] = Envelope.new(message, scheduler, nil, nil)
|
101
|
+
end
|
102
|
+
|
103
|
+
send_envelope(envelope)
|
104
|
+
end
|
105
|
+
|
106
|
+
def send_envelope(envelope)
|
107
|
+
return unless @transport
|
108
|
+
return if envelope.request or envelope.timer
|
109
|
+
|
110
|
+
message = envelope.message
|
111
|
+
scheduler = envelope.scheduler
|
112
|
+
|
113
|
+
unless scheduler.deliverable?
|
114
|
+
scheduler.abort!
|
115
|
+
@envelopes.delete(message['id'])
|
116
|
+
return
|
117
|
+
end
|
118
|
+
|
119
|
+
envelope.timer = EventMachine.add_timer(scheduler.timeout) do
|
120
|
+
handle_error(message)
|
121
|
+
end
|
122
|
+
|
123
|
+
scheduler.send!
|
124
|
+
envelope.request = @transport.send_message(message)
|
125
|
+
end
|
126
|
+
private :send_envelope
|
127
|
+
|
128
|
+
def handle_response(reply)
|
129
|
+
if reply.has_key?('successful') and envelope = @envelopes.delete(reply['id'])
|
130
|
+
envelope.scheduler.succeed!
|
131
|
+
EventMachine.cancel_timer(envelope.timer) if envelope.timer
|
132
|
+
end
|
133
|
+
|
134
|
+
trigger(:message, reply)
|
135
|
+
|
136
|
+
return if @state == UP
|
137
|
+
@state = UP
|
138
|
+
@client.trigger('transport:up')
|
139
|
+
end
|
140
|
+
|
141
|
+
def handle_error(message, immediate = false)
|
142
|
+
return unless envelope = @envelopes[message['id']]
|
143
|
+
return unless request = envelope.request
|
144
|
+
|
145
|
+
request.callback do |req|
|
146
|
+
req.close if req.respond_to?(:close)
|
147
|
+
end
|
148
|
+
|
149
|
+
scheduler = envelope.scheduler
|
150
|
+
scheduler.fail!
|
151
|
+
|
152
|
+
EventMachine.cancel_timer(envelope.timer)
|
153
|
+
envelope.request = envelope.timer = nil
|
154
|
+
|
155
|
+
if immediate
|
156
|
+
send_envelope(envelope)
|
157
|
+
else
|
158
|
+
envelope.timer = EventMachine.add_timer(scheduler.interval) do
|
159
|
+
envelope.timer = nil
|
160
|
+
send_envelope(envelope)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
return if @state == DOWN
|
165
|
+
@state = DOWN
|
166
|
+
@client.trigger('transport:down')
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
@@ -18,7 +18,7 @@ module Faye
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def pipe_through_extensions(stage, message, env, &callback)
|
21
|
-
debug
|
21
|
+
debug('Passing through ? extensions: ?', stage, message)
|
22
22
|
|
23
23
|
return callback.call(message) unless @extensions
|
24
24
|
extensions = @extensions.dup
|
@@ -43,4 +43,3 @@ module Faye
|
|
43
43
|
|
44
44
|
end
|
45
45
|
end
|
46
|
-
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Faye
|
2
|
+
class Scheduler
|
3
|
+
|
4
|
+
def initialize(message, options)
|
5
|
+
@message = message
|
6
|
+
@options = options
|
7
|
+
@attempts = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def interval
|
11
|
+
@options[:interval]
|
12
|
+
end
|
13
|
+
|
14
|
+
def timeout
|
15
|
+
@options[:timeout]
|
16
|
+
end
|
17
|
+
|
18
|
+
def deliverable?
|
19
|
+
attempts = @options[:attempts]
|
20
|
+
deadline = @options[:deadline]
|
21
|
+
now = Time.now.to_f
|
22
|
+
|
23
|
+
return false if attempts and @attempts >= attempts
|
24
|
+
return false if deadline and now > deadline
|
25
|
+
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def send!
|
30
|
+
@attempts += 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def succeed!
|
34
|
+
end
|
35
|
+
|
36
|
+
def fail!
|
37
|
+
end
|
38
|
+
|
39
|
+
def abort!
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
data/lib/faye/protocol/server.rb
CHANGED
@@ -6,8 +6,6 @@ module Faye
|
|
6
6
|
include Logging
|
7
7
|
include Extensible
|
8
8
|
|
9
|
-
META_METHODS = %w[handshake connect disconnect subscribe unsubscribe]
|
10
|
-
|
11
9
|
attr_reader :engine
|
12
10
|
|
13
11
|
def initialize(options = {})
|
@@ -16,7 +14,7 @@ module Faye
|
|
16
14
|
engine_opts[:timeout] = @options[:timeout]
|
17
15
|
@engine = Faye::Engine.get(engine_opts)
|
18
16
|
|
19
|
-
info
|
17
|
+
info('Created new server: ?', @options)
|
20
18
|
end
|
21
19
|
|
22
20
|
def close
|
@@ -28,14 +26,14 @@ module Faye
|
|
28
26
|
@engine.open_socket(client_id, Socket.new(self, socket, env))
|
29
27
|
end
|
30
28
|
|
31
|
-
def close_socket(client_id)
|
32
|
-
@engine.
|
29
|
+
def close_socket(client_id, close = true)
|
30
|
+
@engine.flush_connection(client_id, close)
|
33
31
|
end
|
34
32
|
|
35
33
|
def process(messages, env, &callback)
|
36
34
|
local = env.nil?
|
37
35
|
messages = [messages].flatten
|
38
|
-
info
|
36
|
+
info('Processing messages: ? (local: ?)', messages, local)
|
39
37
|
|
40
38
|
return callback.call([]) if messages.size == 0
|
41
39
|
processed, responses = 0, []
|
@@ -44,7 +42,7 @@ module Faye
|
|
44
42
|
responses.concat(replies)
|
45
43
|
processed += 1
|
46
44
|
responses.compact!
|
47
|
-
info
|
45
|
+
info('Returning replies: ?', responses)
|
48
46
|
callback.call(responses) if processed == messages.size
|
49
47
|
end
|
50
48
|
|
@@ -53,7 +51,7 @@ module Faye
|
|
53
51
|
gather_replies.call(replies) if expected == 0
|
54
52
|
|
55
53
|
replies.each_with_index do |reply, i|
|
56
|
-
debug
|
54
|
+
debug('Processing reply: ?', reply)
|
57
55
|
pipe_through_extensions(:outgoing, reply, env) do |message|
|
58
56
|
replies[i] = message
|
59
57
|
extended += 1
|
@@ -83,7 +81,7 @@ module Faye
|
|
83
81
|
|
84
82
|
def handle(message, local = false, &callback)
|
85
83
|
return callback.call([]) if !message
|
86
|
-
info
|
84
|
+
info('Handling message: ? (local: ?)', message, local)
|
87
85
|
|
88
86
|
channel_name = message['channel']
|
89
87
|
error = message['error']
|
@@ -94,7 +92,10 @@ module Faye
|
|
94
92
|
error = Faye::Error.channel_invalid(channel_name)
|
95
93
|
end
|
96
94
|
|
97
|
-
message
|
95
|
+
if message['data'].nil?
|
96
|
+
error = Faye::Error.parameter_missing('data')
|
97
|
+
end
|
98
|
+
|
98
99
|
@engine.publish(message) unless error
|
99
100
|
|
100
101
|
response = make_response(message)
|
@@ -104,10 +105,9 @@ module Faye
|
|
104
105
|
end
|
105
106
|
|
106
107
|
def handle_meta(message, local, &callback)
|
107
|
-
method
|
108
|
-
client_id = message['clientId']
|
108
|
+
method = method_for(message)
|
109
109
|
|
110
|
-
unless
|
110
|
+
unless method
|
111
111
|
response = make_response(message)
|
112
112
|
response['error'] = Faye::Error.channel_forbidden(message['channel'])
|
113
113
|
response['successful'] = false
|
@@ -121,6 +121,16 @@ module Faye
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
+
def method_for(message)
|
125
|
+
case message['channel']
|
126
|
+
when Channel::HANDSHAKE then :handshake
|
127
|
+
when Channel::CONNECT then :connect
|
128
|
+
when Channel::SUBSCRIBE then :subscribe
|
129
|
+
when Channel::UNSUBSCRIBE then :unsubscribe
|
130
|
+
when Channel::DISCONNECT then :disconnect
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
124
134
|
def advize(response, connection_type)
|
125
135
|
return unless [Channel::HANDSHAKE, Channel::CONNECT].include?(response['channel'])
|
126
136
|
|
@@ -291,4 +301,3 @@ module Faye
|
|
291
301
|
|
292
302
|
end
|
293
303
|
end
|
294
|
-
|
data/lib/faye/protocol/socket.rb
CHANGED
@@ -9,9 +9,25 @@ module Faye
|
|
9
9
|
@cancelled = false
|
10
10
|
end
|
11
11
|
|
12
|
+
def with_channel(&callback)
|
13
|
+
@with_channel = callback
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(*args)
|
18
|
+
message = args.first
|
19
|
+
|
20
|
+
@callback.call(message['data']) if @callback
|
21
|
+
@with_channel.call(message['channel'], message['data']) if @with_channel
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_proc
|
25
|
+
@to_proc ||= lambda { |*a| call(*a) }
|
26
|
+
end
|
27
|
+
|
12
28
|
def cancel
|
13
29
|
return if @cancelled
|
14
|
-
@client.unsubscribe(@channels,
|
30
|
+
@client.unsubscribe(@channels, self)
|
15
31
|
@cancelled = true
|
16
32
|
end
|
17
33
|
|
@@ -21,4 +37,3 @@ module Faye
|
|
21
37
|
|
22
38
|
end
|
23
39
|
end
|
24
|
-
|
data/lib/faye/transport/http.rb
CHANGED
@@ -1,65 +1,75 @@
|
|
1
1
|
module Faye
|
2
2
|
|
3
3
|
class Transport::Http < Transport
|
4
|
-
def self.usable?(
|
4
|
+
def self.usable?(dispatcher, endpoint, &callback)
|
5
5
|
callback.call(URI === endpoint)
|
6
6
|
end
|
7
7
|
|
8
|
-
def encode(
|
9
|
-
Faye.to_json(
|
8
|
+
def encode(messages)
|
9
|
+
Faye.to_json(messages)
|
10
10
|
end
|
11
11
|
|
12
|
-
def request(
|
13
|
-
content = encode(
|
14
|
-
params = build_params(
|
12
|
+
def request(messages)
|
13
|
+
content = encode(messages)
|
14
|
+
params = build_params(content)
|
15
15
|
request = create_request(params)
|
16
16
|
|
17
17
|
request.callback do
|
18
|
-
handle_response(request.response
|
18
|
+
handle_response(messages, request.response)
|
19
19
|
store_cookies(request.response_header['SET_COOKIE'])
|
20
20
|
end
|
21
21
|
|
22
22
|
request.errback do
|
23
|
-
handle_error(
|
23
|
+
handle_error(messages)
|
24
24
|
end
|
25
|
+
|
26
|
+
request
|
25
27
|
end
|
26
28
|
|
27
29
|
private
|
28
30
|
|
29
|
-
def build_params(
|
30
|
-
{
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
:body
|
39
|
-
:timeout => -1 # for em-http-request < 1.0
|
31
|
+
def build_params(content)
|
32
|
+
headers = {
|
33
|
+
'Content-Length' => content.bytesize,
|
34
|
+
'Content-Type' => 'application/json',
|
35
|
+
'Host' => @endpoint.host + (@endpoint.port ? ":#{ @endpoint.port }" : '')
|
36
|
+
}
|
37
|
+
|
38
|
+
params = {
|
39
|
+
:head => headers.merge(@dispatcher.headers),
|
40
|
+
:body => content
|
40
41
|
}
|
42
|
+
|
43
|
+
cookie = get_cookies
|
44
|
+
params[:head]['Cookie'] = cookie unless cookie == ''
|
45
|
+
|
46
|
+
params
|
41
47
|
end
|
42
48
|
|
43
49
|
def create_request(params)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
options = {
|
51
|
+
:inactivity_timeout => 0,
|
52
|
+
:tls => @dispatcher.tls
|
53
|
+
}
|
54
|
+
|
55
|
+
if @proxy[:origin]
|
56
|
+
uri = URI(@proxy[:origin])
|
57
|
+
options[:proxy] = { :host => uri.host, :port => uri.port }
|
58
|
+
if uri.user
|
59
|
+
options[:proxy][:authorization] = [uri.user, uri.password]
|
60
|
+
end
|
61
|
+
end
|
53
62
|
|
63
|
+
client = EventMachine::HttpRequest.new(@endpoint.to_s, options)
|
54
64
|
client.post(params)
|
55
65
|
end
|
56
66
|
|
57
|
-
def handle_response(
|
58
|
-
|
59
|
-
if
|
60
|
-
receive(
|
67
|
+
def handle_response(messages, response)
|
68
|
+
replies = MultiJson.load(response) rescue nil
|
69
|
+
if replies
|
70
|
+
receive(replies)
|
61
71
|
else
|
62
|
-
handle_error(
|
72
|
+
handle_error(messages)
|
63
73
|
end
|
64
74
|
end
|
65
75
|
end
|