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.
@@ -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 'Passing through ? extensions: ?', stage, message
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
-
@@ -55,4 +55,3 @@ module Faye
55
55
 
56
56
  end
57
57
  end
58
-
@@ -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
@@ -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 'Created new server: ?', @options
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.flush(client_id)
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 'Processing messages: ? (local: ?)', messages, local
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 'Returning replies: ?', responses
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 'Processing reply: ?', reply
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 'Handling message: ? (local: ?)', message, local
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.delete('clientId')
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 = Channel.parse(message['channel'])[1]
108
- client_id = message['clientId']
108
+ method = method_for(message)
109
109
 
110
- unless META_METHODS.include?(method)
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
-
@@ -22,4 +22,3 @@ module Faye
22
22
 
23
23
  end
24
24
  end
25
-
@@ -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, &@callback)
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
-
@@ -1,65 +1,75 @@
1
1
  module Faye
2
2
 
3
3
  class Transport::Http < Transport
4
- def self.usable?(client, endpoint, &callback)
4
+ def self.usable?(dispatcher, endpoint, &callback)
5
5
  callback.call(URI === endpoint)
6
6
  end
7
7
 
8
- def encode(envelopes)
9
- Faye.to_json(envelopes.map { |e| e.message })
8
+ def encode(messages)
9
+ Faye.to_json(messages)
10
10
  end
11
11
 
12
- def request(envelopes)
13
- content = encode(envelopes)
14
- params = build_params(@endpoint, content)
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, envelopes)
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(envelopes)
23
+ handle_error(messages)
24
24
  end
25
+
26
+ request
25
27
  end
26
28
 
27
29
  private
28
30
 
29
- def build_params(uri, content)
30
- {
31
- :head => {
32
- 'Content-Length' => content.bytesize,
33
- 'Content-Type' => 'application/json',
34
- 'Cookie' => get_cookies,
35
- 'Host' => uri.host
36
- }.merge(@client.headers),
37
-
38
- :body => content,
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
- version = EventMachine::HttpRequest::VERSION.split('.')[0].to_i
45
- client = if version >= 1
46
- options = { # for em-http-request >= 1.0
47
- :inactivity_timeout => 0 # connection inactivity (post-setup) timeout (0 = disable timeout)
48
- }
49
- EventMachine::HttpRequest.new(@endpoint.to_s, options)
50
- else
51
- EventMachine::HttpRequest.new(@endpoint.to_s)
52
- end
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(response, envelopes)
58
- message = MultiJson.load(response) rescue nil
59
- if message
60
- receive(envelopes, message)
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(envelopes)
72
+ handle_error(messages)
63
73
  end
64
74
  end
65
75
  end