faye 1.0.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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