faye 1.0.4 → 1.1.0
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +12 -4
- data/README.md +10 -9
- data/lib/faye-browser-min.js +2 -1
- data/lib/faye-browser-min.js.map +1 -1
- data/lib/faye-browser.js +566 -343
- data/lib/faye.rb +10 -10
- data/lib/faye/adapters/rack_adapter.rb +29 -10
- 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 +2 -4
- data/lib/faye/protocol/client.rb +101 -154
- 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 +11 -22
- data/lib/faye/protocol/socket.rb +0 -1
- data/lib/faye/protocol/subscription.rb +0 -1
- data/lib/faye/transport/http.rb +41 -28
- data/lib/faye/transport/local.rb +4 -5
- data/lib/faye/transport/transport.rb +57 -36
- data/lib/faye/transport/web_socket.rb +38 -26
- data/lib/faye/util/namespace.rb +0 -1
- metadata +26 -10
- 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 :cookies, :endpoint, :headers, :max_request_size, :proxy, :retry, :transports, :ws_extensions
|
20
|
+
|
21
|
+
def initialize(client, endpoint, options)
|
22
|
+
super()
|
23
|
+
|
24
|
+
@client = client
|
25
|
+
@endpoint = Faye.parse_url(endpoint)
|
26
|
+
@alternates = options[:endpoints] || {}
|
27
|
+
|
28
|
+
@cookies = CookieJar::Jar.new
|
29
|
+
@disabled = []
|
30
|
+
@envelopes = {}
|
31
|
+
@headers = {}
|
32
|
+
@retry = options[:retry] || DEFAULT_RETRY
|
33
|
+
@scheduler = options[:scheduler] || Faye::Scheduler
|
34
|
+
@state = 0
|
35
|
+
@transports = {}
|
36
|
+
@ws_extensions = []
|
37
|
+
|
38
|
+
@proxy = options[:proxy] || {}
|
39
|
+
@proxy = {:origin => @proxy} if String === @proxy
|
40
|
+
|
41
|
+
[*options[:websocket_extensions]].each do |extension|
|
42
|
+
add_websocket_extension(extension)
|
43
|
+
end
|
44
|
+
|
45
|
+
@alternates.each do |type, url|
|
46
|
+
@alternates[type] = Faye.parse_url(url)
|
47
|
+
end
|
48
|
+
|
49
|
+
@max_request_size = MAX_REQUEST_SIZE
|
50
|
+
end
|
51
|
+
|
52
|
+
def endpoint_for(connection_type)
|
53
|
+
@alternates[connection_type] || @endpoint
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_websocket_extension(extension)
|
57
|
+
@ws_extensions << extension
|
58
|
+
end
|
59
|
+
|
60
|
+
def disable(feature)
|
61
|
+
@disabled << feature
|
62
|
+
end
|
63
|
+
|
64
|
+
def set_header(name, value)
|
65
|
+
@headers[name.to_s] = value.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
def close
|
69
|
+
transport = @transport
|
70
|
+
@transport = nil
|
71
|
+
transport.close if transport
|
72
|
+
end
|
73
|
+
|
74
|
+
def connection_types
|
75
|
+
Transport.connection_types
|
76
|
+
end
|
77
|
+
|
78
|
+
def select_transport(transport_types)
|
79
|
+
Transport.get(self, transport_types, @disabled) do |transport|
|
80
|
+
debug('Selected ? transport for ?', transport.connection_type, transport.endpoint)
|
81
|
+
|
82
|
+
next if transport == @transport
|
83
|
+
@transport.close if @transport
|
84
|
+
|
85
|
+
@transport = transport
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def send_message(message, timeout, options = {})
|
90
|
+
return unless @transport
|
91
|
+
|
92
|
+
id = message['id']
|
93
|
+
attempts = options[:attempts]
|
94
|
+
deadline = options[:deadline] && Time.now.to_f + options[:deadline]
|
95
|
+
envelope = @envelopes[id]
|
96
|
+
|
97
|
+
unless envelope
|
98
|
+
scheduler = @scheduler.new(message, :timeout => timeout, :interval => @retry, :attempts => attempts, :deadline => deadline)
|
99
|
+
envelope = @envelopes[id] = Envelope.new(message, scheduler, nil, nil)
|
100
|
+
end
|
101
|
+
|
102
|
+
send_envelope(envelope)
|
103
|
+
end
|
104
|
+
|
105
|
+
def send_envelope(envelope)
|
106
|
+
return if envelope.request or envelope.timer
|
107
|
+
|
108
|
+
message = envelope.message
|
109
|
+
scheduler = envelope.scheduler
|
110
|
+
|
111
|
+
unless scheduler.deliverable?
|
112
|
+
scheduler.abort!
|
113
|
+
@envelopes.delete(message['id'])
|
114
|
+
return
|
115
|
+
end
|
116
|
+
|
117
|
+
envelope.timer = EventMachine.add_timer(scheduler.timeout) do
|
118
|
+
handle_error(message)
|
119
|
+
end
|
120
|
+
|
121
|
+
scheduler.send!
|
122
|
+
envelope.request = @transport.send_message(message)
|
123
|
+
end
|
124
|
+
private :send_envelope
|
125
|
+
|
126
|
+
def handle_response(reply)
|
127
|
+
envelope = @envelopes.delete(reply['id'])
|
128
|
+
|
129
|
+
if reply.has_key?('successful') and envelope
|
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,6 +6,8 @@ module Faye
|
|
6
6
|
include Logging
|
7
7
|
include Extensible
|
8
8
|
|
9
|
+
META_METHODS = %w[handshake connect disconnect subscribe unsubscribe]
|
10
|
+
|
9
11
|
attr_reader :engine
|
10
12
|
|
11
13
|
def initialize(options = {})
|
@@ -14,7 +16,7 @@ module Faye
|
|
14
16
|
engine_opts[:timeout] = @options[:timeout]
|
15
17
|
@engine = Faye::Engine.get(engine_opts)
|
16
18
|
|
17
|
-
info
|
19
|
+
info('Created new server: ?', @options)
|
18
20
|
end
|
19
21
|
|
20
22
|
def close
|
@@ -26,14 +28,14 @@ module Faye
|
|
26
28
|
@engine.open_socket(client_id, Socket.new(self, socket, env))
|
27
29
|
end
|
28
30
|
|
29
|
-
def close_socket(client_id)
|
30
|
-
@engine.
|
31
|
+
def close_socket(client_id, close = true)
|
32
|
+
@engine.flush_connection(client_id, close)
|
31
33
|
end
|
32
34
|
|
33
35
|
def process(messages, env, &callback)
|
34
36
|
local = env.nil?
|
35
37
|
messages = [messages].flatten
|
36
|
-
info
|
38
|
+
info('Processing messages: ? (local: ?)', messages, local)
|
37
39
|
|
38
40
|
return callback.call([]) if messages.size == 0
|
39
41
|
processed, responses = 0, []
|
@@ -42,7 +44,7 @@ module Faye
|
|
42
44
|
responses.concat(replies)
|
43
45
|
processed += 1
|
44
46
|
responses.compact!
|
45
|
-
info
|
47
|
+
info('Returning replies: ?', responses)
|
46
48
|
callback.call(responses) if processed == messages.size
|
47
49
|
end
|
48
50
|
|
@@ -51,7 +53,7 @@ module Faye
|
|
51
53
|
gather_replies.call(replies) if expected == 0
|
52
54
|
|
53
55
|
replies.each_with_index do |reply, i|
|
54
|
-
debug
|
56
|
+
debug('Processing reply: ?', reply)
|
55
57
|
pipe_through_extensions(:outgoing, reply, env) do |message|
|
56
58
|
replies[i] = message
|
57
59
|
extended += 1
|
@@ -81,7 +83,7 @@ module Faye
|
|
81
83
|
|
82
84
|
def handle(message, local = false, &callback)
|
83
85
|
return callback.call([]) if !message
|
84
|
-
info
|
86
|
+
info('Handling message: ? (local: ?)', message, local)
|
85
87
|
|
86
88
|
channel_name = message['channel']
|
87
89
|
error = message['error']
|
@@ -92,7 +94,6 @@ module Faye
|
|
92
94
|
error = Faye::Error.channel_invalid(channel_name)
|
93
95
|
end
|
94
96
|
|
95
|
-
message.delete('clientId')
|
96
97
|
@engine.publish(message) unless error
|
97
98
|
|
98
99
|
response = make_response(message)
|
@@ -102,10 +103,9 @@ module Faye
|
|
102
103
|
end
|
103
104
|
|
104
105
|
def handle_meta(message, local, &callback)
|
105
|
-
method
|
106
|
-
client_id = message['clientId']
|
106
|
+
method = Channel.parse(message['channel'])[1]
|
107
107
|
|
108
|
-
unless method
|
108
|
+
unless META_METHODS.include?(method)
|
109
109
|
response = make_response(message)
|
110
110
|
response['error'] = Faye::Error.channel_forbidden(message['channel'])
|
111
111
|
response['successful'] = false
|
@@ -119,16 +119,6 @@ module Faye
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
-
def method_for(message)
|
123
|
-
case message['channel']
|
124
|
-
when Channel::HANDSHAKE then :handshake
|
125
|
-
when Channel::CONNECT then :connect
|
126
|
-
when Channel::SUBSCRIBE then :subscribe
|
127
|
-
when Channel::UNSUBSCRIBE then :unsubscribe
|
128
|
-
when Channel::DISCONNECT then :disconnect
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
122
|
def advize(response, connection_type)
|
133
123
|
return unless [Channel::HANDSHAKE, Channel::CONNECT].include?(response['channel'])
|
134
124
|
|
@@ -299,4 +289,3 @@ module Faye
|
|
299
289
|
|
300
290
|
end
|
301
291
|
end
|
302
|
-
|
data/lib/faye/protocol/socket.rb
CHANGED
data/lib/faye/transport/http.rb
CHANGED
@@ -1,65 +1,78 @@
|
|
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
|
+
def build_params(content)
|
32
|
+
params = {
|
31
33
|
:head => {
|
32
|
-
'Content-Length'
|
33
|
-
'Content-Type'
|
34
|
-
'
|
35
|
-
|
36
|
-
}.merge(@client.headers),
|
34
|
+
'Content-Length' => content.bytesize,
|
35
|
+
'Content-Type' => 'application/json',
|
36
|
+
'Host' => @endpoint.host + (@endpoint.port ? ":#{@endpoint.port}" : '')
|
37
|
+
}.merge(@dispatcher.headers),
|
37
38
|
|
38
39
|
:body => content,
|
39
40
|
:timeout => -1 # for em-http-request < 1.0
|
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
50
|
version = EventMachine::HttpRequest::VERSION.split('.')[0].to_i
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
options = {:inactivity_timeout => 0}
|
52
|
+
|
53
|
+
if @proxy[:origin]
|
54
|
+
uri = URI.parse(@proxy[:origin])
|
55
|
+
options[:proxy] = {:host => uri.host, :port => uri.port}
|
56
|
+
if uri.user
|
57
|
+
options[:proxy][:authorization] = [uri.user, uri.password]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
if version >= 1
|
62
|
+
client = EventMachine::HttpRequest.new(@endpoint.to_s, options)
|
63
|
+
else
|
64
|
+
client = EventMachine::HttpRequest.new(@endpoint.to_s)
|
65
|
+
end
|
53
66
|
|
54
67
|
client.post(params)
|
55
68
|
end
|
56
69
|
|
57
|
-
def handle_response(
|
58
|
-
|
59
|
-
if
|
60
|
-
receive(
|
70
|
+
def handle_response(messages, response)
|
71
|
+
replies = MultiJson.load(response) rescue nil
|
72
|
+
if replies
|
73
|
+
receive(replies)
|
61
74
|
else
|
62
|
-
handle_error(
|
75
|
+
handle_error(messages)
|
63
76
|
end
|
64
77
|
end
|
65
78
|
end
|