rcomet 0.0.2
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.
- data/.gitignore +22 -0
- data/History.rdoc +10 -0
- data/LICENSE +340 -0
- data/README.rdoc +98 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/bin/rcomet +3 -0
- data/examples/chart/README.rdoc +18 -0
- data/examples/chart/chart-cometd.rb +18 -0
- data/examples/chart/chart.rb +19 -0
- data/examples/chart/static/chart.rhtml +75 -0
- data/examples/chat/README.rdoc +18 -0
- data/examples/chat/chat-cometd.rb +30 -0
- data/examples/chat/chat.rb +31 -0
- data/examples/chat/static/chat.rhtml +75 -0
- data/examples/chat/static/login.rhtml +20 -0
- data/examples/chat/static/style.css +61 -0
- data/examples/soapbox/README.rdoc +30 -0
- data/examples/soapbox/client.rb +36 -0
- data/examples/soapbox/soapbox-cometd.rb +14 -0
- data/examples/soapbox/soapbox.rb +19 -0
- data/examples/soapbox/static/soapbox.rhtml +92 -0
- data/examples/soapbox/static/style.css +42 -0
- data/lib/rcomet/channel.rb +76 -0
- data/lib/rcomet/client.rb +188 -0
- data/lib/rcomet/core_ext.rb +5 -0
- data/lib/rcomet/rack_adapter.rb +289 -0
- data/lib/rcomet/server.rb +57 -0
- data/lib/rcomet/user.rb +61 -0
- data/lib/rcomet.rb +15 -0
- data/rcomet.gemspec +90 -0
- data/spec/rcomet_spec.rb +7 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +120 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'uri'
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'rcomet'
|
|
6
|
+
require 'rcomet/channel'
|
|
7
|
+
|
|
8
|
+
module RComet
|
|
9
|
+
class Client
|
|
10
|
+
def initialize( uri_or_string )
|
|
11
|
+
@uri = uri_or_string
|
|
12
|
+
@uri = URI.parse(@uri) if @uri.class == String
|
|
13
|
+
@clientId = nil
|
|
14
|
+
# @interval = nil
|
|
15
|
+
@connection = nil
|
|
16
|
+
@subscriptions = {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Request Response
|
|
20
|
+
# MUST include: * channel MUST include: * channel
|
|
21
|
+
# * clientId * successful
|
|
22
|
+
# * connectionType * clientId
|
|
23
|
+
# MAY include: * ext MAY include: * error
|
|
24
|
+
# * id * advice
|
|
25
|
+
# * ext
|
|
26
|
+
# * id
|
|
27
|
+
# * timestamp
|
|
28
|
+
def connect
|
|
29
|
+
@connection.kill unless @connection.nil?
|
|
30
|
+
@connection = Thread.new {
|
|
31
|
+
faild = false
|
|
32
|
+
while true
|
|
33
|
+
id = RComet.random(32)
|
|
34
|
+
message = {
|
|
35
|
+
"channel" => RComet::Channel::CONNECT,
|
|
36
|
+
"clientId" => @clientId,
|
|
37
|
+
"connectionType" => "long-polling",
|
|
38
|
+
"id" => id
|
|
39
|
+
}
|
|
40
|
+
r = send( message )
|
|
41
|
+
|
|
42
|
+
if r[0]["id"] == id and r[0]["successful"] == true
|
|
43
|
+
@subscriptions[r[1]["channel"]].call( r[1]["data"])
|
|
44
|
+
elsif r[0]["successful"] == false
|
|
45
|
+
faild = true
|
|
46
|
+
break
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if faild
|
|
51
|
+
handshake()
|
|
52
|
+
connect()
|
|
53
|
+
end
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Request Response
|
|
58
|
+
# MUST include: * channel MUST include: * channel
|
|
59
|
+
# * clientId * successful
|
|
60
|
+
# MAY include: * ext * clientId
|
|
61
|
+
# * id MAY include: * error
|
|
62
|
+
# * ext
|
|
63
|
+
# * id
|
|
64
|
+
def disconnect
|
|
65
|
+
unless @connection.nil?
|
|
66
|
+
@connection.kill
|
|
67
|
+
message = {
|
|
68
|
+
"channel" => RComet::Channel::DISCONNECT,
|
|
69
|
+
"clientId" => @clientId,
|
|
70
|
+
"id" => RComet.random(32)
|
|
71
|
+
}
|
|
72
|
+
r = send( message )
|
|
73
|
+
## TODO : Check response
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Request
|
|
78
|
+
# MUST include: * channel
|
|
79
|
+
# * version
|
|
80
|
+
# * supportedConnectionTypes
|
|
81
|
+
# MAY include: * minimumVersion
|
|
82
|
+
# * ext
|
|
83
|
+
# * id
|
|
84
|
+
#
|
|
85
|
+
# Success Response Failed Response
|
|
86
|
+
# MUST include: * channel MUST include: * channel
|
|
87
|
+
# * version * successful
|
|
88
|
+
# * supportedConnectionTypes * error
|
|
89
|
+
# * clientId MAY include: * supportedConnectionTypes
|
|
90
|
+
# * successful * advice
|
|
91
|
+
# MAY include: * minimumVersion * version
|
|
92
|
+
# * advice * minimumVersion
|
|
93
|
+
# * ext * ext
|
|
94
|
+
# * id * id
|
|
95
|
+
# * authSuccessful
|
|
96
|
+
def handshake
|
|
97
|
+
id = RComet.random(32)
|
|
98
|
+
message = {
|
|
99
|
+
"channel" => RComet::Channel::HANDSHAKE,
|
|
100
|
+
"version" => RComet::BAYEUX_VERSION,
|
|
101
|
+
"supportedConnectionTypes" => [ "long-polling", "callback-polling" ],
|
|
102
|
+
"id" => id
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
response = send( message )[0]
|
|
106
|
+
if response["successful"] and response["id"] == id
|
|
107
|
+
@clientId = response["clientId"]
|
|
108
|
+
# @interval = response["advice"]["interval"]
|
|
109
|
+
else
|
|
110
|
+
raise
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Request Response
|
|
115
|
+
# MUST include: * channel MUST include: * channel
|
|
116
|
+
# * data * successful
|
|
117
|
+
# MAY include: * clientId MAY include: * id
|
|
118
|
+
# * id * error
|
|
119
|
+
# * ext * ext
|
|
120
|
+
def publish( channel, data )
|
|
121
|
+
message = {
|
|
122
|
+
"channel" => channel,
|
|
123
|
+
"data" => data,
|
|
124
|
+
"clientId" => @clientId,
|
|
125
|
+
"id" => RComet.random(32)
|
|
126
|
+
}
|
|
127
|
+
r = send(message)[0]
|
|
128
|
+
## TODO : Check response
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Request Response
|
|
132
|
+
# MUST include: * channel MUST include: * channel
|
|
133
|
+
# * clientId * successful
|
|
134
|
+
# * subscription * clientId
|
|
135
|
+
# MAY include: * ext * subscription
|
|
136
|
+
# * id MAY include: * error
|
|
137
|
+
# * advice
|
|
138
|
+
# * ext
|
|
139
|
+
# * id
|
|
140
|
+
# * timestamp
|
|
141
|
+
def subscribe( channel, &block )
|
|
142
|
+
@subscriptions[channel] = block if block_given?
|
|
143
|
+
|
|
144
|
+
message = {
|
|
145
|
+
"channel" => RComet::Channel::SUBSCRIBE,
|
|
146
|
+
"clientId" => @clientId,
|
|
147
|
+
"subscription" => channel,
|
|
148
|
+
"id" => RComet.random(32)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
r = send(message)
|
|
152
|
+
## TODO : Check response
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Request Response
|
|
156
|
+
# MUST include: * channel MUST include: * channel
|
|
157
|
+
# * clientId * successful
|
|
158
|
+
# * subscription * clientId
|
|
159
|
+
# MAY include: * ext * subscription
|
|
160
|
+
# * id MAY include: * error
|
|
161
|
+
# * advice
|
|
162
|
+
# * ext
|
|
163
|
+
# * id
|
|
164
|
+
# * timestamp
|
|
165
|
+
def unsubscribe( channels )
|
|
166
|
+
channels = [channels] unless channels.class == Array
|
|
167
|
+
channels.each do |c|
|
|
168
|
+
@subscriptions.delete(c)
|
|
169
|
+
end
|
|
170
|
+
message = {
|
|
171
|
+
"channel" => RComet::Channel::UNSUBSCRIBE,
|
|
172
|
+
"clientId" => @clientId,
|
|
173
|
+
"subscription" => channels,
|
|
174
|
+
"id" => RComet.random(32)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
r = send(message)
|
|
178
|
+
## TODO : Check response
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
private
|
|
182
|
+
def send( message )
|
|
183
|
+
res = Net::HTTP.post_form( @uri, { "message" => [message].to_json } )
|
|
184
|
+
return JSON.parse( res.body )
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# The Rack::Adapter class allow you to use RComet as a Rack middleware
|
|
2
|
+
#
|
|
3
|
+
# Example :
|
|
4
|
+
#
|
|
5
|
+
# map '/comet' do
|
|
6
|
+
# run RComet::RackAdapter :mount => "/comet" do
|
|
7
|
+
# # ...
|
|
8
|
+
# end
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
require 'rack'
|
|
12
|
+
require 'json'
|
|
13
|
+
|
|
14
|
+
require 'rcomet'
|
|
15
|
+
require 'rcomet/core_ext'
|
|
16
|
+
require 'rcomet/server'
|
|
17
|
+
require 'rcomet/channel'
|
|
18
|
+
require 'rcomet/user'
|
|
19
|
+
|
|
20
|
+
module RComet
|
|
21
|
+
class RackAdapter #:nodoc:
|
|
22
|
+
def initialize(app = nil, options = nil, &block)
|
|
23
|
+
@app = app if app.respond_to?(:call)
|
|
24
|
+
@options = [app, options].grep(Hash).first
|
|
25
|
+
@channels = RComet::ChannelSet.new
|
|
26
|
+
@users = Hash.new
|
|
27
|
+
@timeout = nil
|
|
28
|
+
|
|
29
|
+
instance_eval(&block) if block
|
|
30
|
+
return @app
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def call(env)
|
|
34
|
+
request = Rack::Request.new(env)
|
|
35
|
+
|
|
36
|
+
if request.params.empty?
|
|
37
|
+
[404, {'Content-Type' => 'text/html'}, ""]
|
|
38
|
+
else
|
|
39
|
+
messages = JSON.parse(request.params['message'])
|
|
40
|
+
jsonp = request.params['jsonp'] || JSONP_CALLBACK
|
|
41
|
+
get = request.get?
|
|
42
|
+
|
|
43
|
+
process( jsonp, messages, get )
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def channel
|
|
48
|
+
@channels
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def timeout( &block )
|
|
52
|
+
@handler = block if block_given?
|
|
53
|
+
@handler
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def process( jsonp, messages, get )
|
|
57
|
+
replies = []
|
|
58
|
+
messages.each do |message|
|
|
59
|
+
reply = nil
|
|
60
|
+
case message['channel']
|
|
61
|
+
when RComet::Channel::HANDSHAKE
|
|
62
|
+
reply = handshake( message )
|
|
63
|
+
when RComet::Channel::CONNECT
|
|
64
|
+
reply = connect( message )
|
|
65
|
+
when RComet::Channel::DISCONNECT
|
|
66
|
+
reply = disconnect( message )
|
|
67
|
+
when RComet::Channel::SUBSCRIBE
|
|
68
|
+
reply = subscribe( message )
|
|
69
|
+
when RComet::Channel::UNSUBSCRIBE
|
|
70
|
+
reply = unsubscribe( message )
|
|
71
|
+
else
|
|
72
|
+
reply = handle( message )
|
|
73
|
+
end
|
|
74
|
+
if reply.class == Array
|
|
75
|
+
replies.concat( reply )
|
|
76
|
+
else
|
|
77
|
+
replies << reply
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
response = JSON.generate(replies)
|
|
82
|
+
type = {'Content-Type' => 'text/json'}
|
|
83
|
+
if get
|
|
84
|
+
response = "#{jsonp}(#{response});"
|
|
85
|
+
type = {'Content-Type' => 'text/javascript'}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
[200, type, [response]]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def handshake( message )
|
|
92
|
+
begin
|
|
93
|
+
user = User.new(self)
|
|
94
|
+
end while @users.has_key?(user.id)
|
|
95
|
+
@users[user.id] = user
|
|
96
|
+
|
|
97
|
+
response = {
|
|
98
|
+
'channel' => RComet::Channel::HANDSHAKE,
|
|
99
|
+
'version' => RComet::BAYEUX_VERSION,
|
|
100
|
+
'minimumVersion' => RComet::BAYEUX_VERSION,
|
|
101
|
+
'supportedConnectionTypes' => ['long-polling','callback-polling'],
|
|
102
|
+
'clientId' => user.id,
|
|
103
|
+
'successful' => true
|
|
104
|
+
}
|
|
105
|
+
response << { 'id' => message['id'] } if message.has_key?('id')
|
|
106
|
+
|
|
107
|
+
return response
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def connect( message )
|
|
111
|
+
# Initialize response
|
|
112
|
+
response = {
|
|
113
|
+
'channel' => RComet::Channel::CONNECT,
|
|
114
|
+
'clientId' => message['clientId']
|
|
115
|
+
}
|
|
116
|
+
response << { 'id' => message['id'] } if message.has_key?('id')
|
|
117
|
+
|
|
118
|
+
# Get user for clientId
|
|
119
|
+
user = @users[message['clientId']]
|
|
120
|
+
if user
|
|
121
|
+
# Ok, connect user
|
|
122
|
+
user.connected = true
|
|
123
|
+
time = Time.new
|
|
124
|
+
response << {
|
|
125
|
+
'successful' => true,
|
|
126
|
+
'timestamp' =>"#{time.hour}:#{time.min}:#{time.sec} #{time.year}"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if user.has_channel?
|
|
130
|
+
response = user.wait( response )
|
|
131
|
+
end
|
|
132
|
+
user.connected = false
|
|
133
|
+
else
|
|
134
|
+
# User does not exist!
|
|
135
|
+
response << {
|
|
136
|
+
'successful' => false,
|
|
137
|
+
'error' => "402:#{message['clientId']}:Unknown Client ID"
|
|
138
|
+
}
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
return response
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def disconnect( message )
|
|
145
|
+
# Initialize response message
|
|
146
|
+
response = {
|
|
147
|
+
'channel' => RComet::Channel::DISCONNECT,
|
|
148
|
+
'clientId' => message['clientId']
|
|
149
|
+
}
|
|
150
|
+
response << { 'id' => message['id'] } if message.has_key?('id')
|
|
151
|
+
|
|
152
|
+
# Get user for clientId
|
|
153
|
+
user = @users[message['clientId']]
|
|
154
|
+
if user
|
|
155
|
+
# Ok, disconnect user
|
|
156
|
+
user.connected = false
|
|
157
|
+
@users.delete( message['clientId'] )
|
|
158
|
+
|
|
159
|
+
# Complete reponse
|
|
160
|
+
response << {
|
|
161
|
+
'successful' => true
|
|
162
|
+
}
|
|
163
|
+
else
|
|
164
|
+
# User does nit exist!
|
|
165
|
+
response << {
|
|
166
|
+
'successful' => false,
|
|
167
|
+
'error' => "402:#{message['clientId']}:Unknown Client ID"
|
|
168
|
+
}
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
return response
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def subscribe( message )
|
|
175
|
+
response = {
|
|
176
|
+
'channel' => RComet::Channel::SUBSCRIBE,
|
|
177
|
+
'clientId' => message['clientId']
|
|
178
|
+
}
|
|
179
|
+
response << { 'id' => message['id'] } if message.has_key?('id')
|
|
180
|
+
|
|
181
|
+
# Get user for clientId
|
|
182
|
+
user = @users[message['clientId']]
|
|
183
|
+
if user
|
|
184
|
+
# Get channel
|
|
185
|
+
channel = @channels[message['subscription']]
|
|
186
|
+
if channel
|
|
187
|
+
user.subscribe( channel )
|
|
188
|
+
response << {
|
|
189
|
+
'successful' => true,
|
|
190
|
+
'subscription' => message['subscription']
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
unless channel.data.nil?
|
|
194
|
+
response = [response]
|
|
195
|
+
response << {
|
|
196
|
+
'channel' => message['subscription'],
|
|
197
|
+
'id' => (message['id'].to_i+1).to_s,
|
|
198
|
+
'data' => channel.data
|
|
199
|
+
}
|
|
200
|
+
end
|
|
201
|
+
else
|
|
202
|
+
#Channel doesn't exist
|
|
203
|
+
response << {
|
|
204
|
+
'successful' => false,
|
|
205
|
+
'subscription' => message['subscription'],
|
|
206
|
+
'error' => "404:#{message['subscription']}:Unknown Channel"
|
|
207
|
+
}
|
|
208
|
+
end
|
|
209
|
+
else
|
|
210
|
+
response << {
|
|
211
|
+
'successful' => false,
|
|
212
|
+
'error' => "402:#{message['clientId']}:Unknown Client ID"
|
|
213
|
+
}
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
return response
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def unsubscribe( message )
|
|
220
|
+
# Initialize response
|
|
221
|
+
response = {
|
|
222
|
+
'channel' => RComet::Channel::UNSUBSCRIBE,
|
|
223
|
+
'clientId' => message['clientId']
|
|
224
|
+
}
|
|
225
|
+
response << { 'id' => message['id'] } if message.has_key?('id')
|
|
226
|
+
|
|
227
|
+
# Get user for clientId
|
|
228
|
+
user = @users[message['clientId']]
|
|
229
|
+
if user
|
|
230
|
+
# Get channel
|
|
231
|
+
channel = @channels[message['subscription']]
|
|
232
|
+
if channel
|
|
233
|
+
user.unsubscribe( channel )
|
|
234
|
+
response << {
|
|
235
|
+
'successful' => true,
|
|
236
|
+
'subscription' => message['subscription']
|
|
237
|
+
}
|
|
238
|
+
else
|
|
239
|
+
#Channel doesn't exist
|
|
240
|
+
response << {
|
|
241
|
+
'successful' => false,
|
|
242
|
+
'subscription' => message['subscription'],
|
|
243
|
+
'error' => "404:#{message['subscription']}:Unknown Channel"
|
|
244
|
+
}
|
|
245
|
+
end
|
|
246
|
+
else
|
|
247
|
+
response << {
|
|
248
|
+
'successful' => false,
|
|
249
|
+
'error' => "402:#{message['clientId']}:Unknown Client ID"
|
|
250
|
+
}
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
return response
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def handle( message )
|
|
257
|
+
# Initialize response
|
|
258
|
+
response = {
|
|
259
|
+
'channel' => message['channel']
|
|
260
|
+
}
|
|
261
|
+
response << { 'id' => message['id'] } if message.has_key?('id')
|
|
262
|
+
|
|
263
|
+
c = channel[message['channel']]
|
|
264
|
+
if c.nil?
|
|
265
|
+
response << {
|
|
266
|
+
'successful' => false,
|
|
267
|
+
'error' => "404:#{message['channel']}:Unknown Channel"
|
|
268
|
+
}
|
|
269
|
+
else
|
|
270
|
+
response << {
|
|
271
|
+
'successful' => true
|
|
272
|
+
}
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
Thread.new do
|
|
276
|
+
unless c.nil?
|
|
277
|
+
if c.handler.nil?
|
|
278
|
+
c.data( message['data'] )
|
|
279
|
+
else
|
|
280
|
+
c.handler.call( message )
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
return
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
return response
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rcomet/rack_adapter'
|
|
3
|
+
require 'logger'
|
|
4
|
+
|
|
5
|
+
module RComet
|
|
6
|
+
class Server
|
|
7
|
+
# Create a new Comet server
|
|
8
|
+
def initialize( options = {}, &block )
|
|
9
|
+
@conf = {
|
|
10
|
+
:host => "0.0.0.0",
|
|
11
|
+
:port => 8990,
|
|
12
|
+
:mount => "/comet",
|
|
13
|
+
:log => $stdout,
|
|
14
|
+
:server => :webrick
|
|
15
|
+
}.merge( options )
|
|
16
|
+
|
|
17
|
+
if block_given?
|
|
18
|
+
@comet = RComet::RackAdapter.new( &block )
|
|
19
|
+
else
|
|
20
|
+
@comet = RComet::RackAdapter.new( )
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Start the Comet server
|
|
25
|
+
def start
|
|
26
|
+
route = { @conf[:mount] => @comet }
|
|
27
|
+
app = Rack::URLMap.new(route)
|
|
28
|
+
app = Rack::ContentLength.new(app)
|
|
29
|
+
app = Rack::CommonLogger.new(app, Logger.new(@conf[:log]))
|
|
30
|
+
|
|
31
|
+
@main_loop = Thread.new do
|
|
32
|
+
case @conf[:server].to_sym
|
|
33
|
+
when :mongrel
|
|
34
|
+
puts "** Starting Mongrel on #{@conf[:host]}:#{@conf[:port]}"
|
|
35
|
+
@server = Rack::Handler::Mongrel.run( app, {:Port => @conf[:port], :Host => @conf[:host]} )
|
|
36
|
+
when :webrick
|
|
37
|
+
puts "** Starting WEBrick on #{@conf[:host]}:#{@conf[:port]}"
|
|
38
|
+
@server = Rack::Handler::WEBrick.run( app, {:Port => @conf[:port], :BindAddress => @conf[:host]} )
|
|
39
|
+
when :thin
|
|
40
|
+
puts "** Starting Thin on #{@conf[:host]}:#{@conf[:port]}"
|
|
41
|
+
@server = Rack::Handler::Thin.run( app, {:Port => @conf[:port], :Host => @conf[:host]} )
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
puts "Thread end !"
|
|
45
|
+
exit
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
at_exit {
|
|
49
|
+
puts "Quitting..."
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def method_missing(method_name, *args, &block) #:nodoc:
|
|
54
|
+
@comet.send(method_name,*args, &block)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/rcomet/user.rb
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module RComet
|
|
2
|
+
class User
|
|
3
|
+
attr_reader :id
|
|
4
|
+
attr_accessor :connected
|
|
5
|
+
|
|
6
|
+
# Create a new Comet user
|
|
7
|
+
def initialize(adapter)
|
|
8
|
+
@connected = false
|
|
9
|
+
@id = RComet.random(64)
|
|
10
|
+
|
|
11
|
+
@channels = Hash.new
|
|
12
|
+
@event_mutex = Mutex.new
|
|
13
|
+
@messages = []
|
|
14
|
+
@adapter = adapter
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def wait( messages ) #:nodoc:
|
|
18
|
+
@continue = @messages.empty?
|
|
19
|
+
@messages << messages
|
|
20
|
+
while @continue; end
|
|
21
|
+
|
|
22
|
+
messages = @messages.clone
|
|
23
|
+
@messages = []
|
|
24
|
+
return messages
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def send( message ) #:nodoc:
|
|
28
|
+
if @connected == false
|
|
29
|
+
## ADD TIMEOUT !
|
|
30
|
+
unless @adapter.timeout.nil?
|
|
31
|
+
@adapter.timeout.call(self)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
@messages << message
|
|
36
|
+
@continue = false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Subscribe to a given channel
|
|
40
|
+
def subscribe( channel )
|
|
41
|
+
channel.add_user( self )
|
|
42
|
+
@channels[channel.path] = channel
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Unsubscribe to a given channel
|
|
46
|
+
def unsubscribe( channel )
|
|
47
|
+
c = @channels.delete(channel)
|
|
48
|
+
c.delete_user(self) if c
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def unsubscribe_all( )
|
|
52
|
+
@channels.each do |c|
|
|
53
|
+
unsubscribe(c)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def has_channel? #:nodoc:
|
|
58
|
+
return( not @channels.empty? )
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/rcomet.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module RComet
|
|
2
|
+
VERSION = '0.0.2'
|
|
3
|
+
|
|
4
|
+
BAYEUX_VERSION = '1.0'
|
|
5
|
+
JSONP_CALLBACK = 'jsonpcallback'
|
|
6
|
+
CONNECTION_TYPES = %w[long-polling callback-polling]
|
|
7
|
+
|
|
8
|
+
def self.random(size) #:nodoc:
|
|
9
|
+
id = ''
|
|
10
|
+
size.times do |i|
|
|
11
|
+
id << ?A+rand(50)
|
|
12
|
+
end
|
|
13
|
+
return id
|
|
14
|
+
end
|
|
15
|
+
end
|