strelka 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +3293 -3058
  5. data/History.rdoc +17 -0
  6. data/Manifest.txt +3 -0
  7. data/Rakefile +2 -2
  8. data/contrib/hoetemplate/lib/file_name.rb.erb +3 -2
  9. data/contrib/hoetemplate/spec/file_name_spec.rb.erb +1 -1
  10. data/examples/apps/auth-demo +1 -2
  11. data/examples/apps/auth-demo2 +1 -2
  12. data/examples/apps/sessions-demo +1 -2
  13. data/examples/gen-config.rb +1 -2
  14. data/lib/strelka.rb +92 -17
  15. data/lib/strelka/app.rb +7 -6
  16. data/lib/strelka/app/auth.rb +5 -5
  17. data/lib/strelka/app/errors.rb +1 -1
  18. data/lib/strelka/app/filters.rb +1 -1
  19. data/lib/strelka/app/negotiation.rb +1 -1
  20. data/lib/strelka/app/parameters.rb +1 -1
  21. data/lib/strelka/app/restresources.rb +14 -21
  22. data/lib/strelka/app/routing.rb +5 -6
  23. data/lib/strelka/app/sessions.rb +3 -1
  24. data/lib/strelka/app/templating.rb +1 -1
  25. data/lib/strelka/authprovider.rb +1 -1
  26. data/lib/strelka/authprovider/basic.rb +1 -0
  27. data/lib/strelka/authprovider/hostaccess.rb +1 -0
  28. data/lib/strelka/behavior/plugin.rb +2 -2
  29. data/lib/strelka/cli.rb +2 -1
  30. data/lib/strelka/command/config.rb +2 -1
  31. data/lib/strelka/command/discover.rb +2 -1
  32. data/lib/strelka/command/start.rb +2 -1
  33. data/lib/strelka/constants.rb +1 -1
  34. data/lib/strelka/cookie.rb +1 -1
  35. data/lib/strelka/cookieset.rb +1 -1
  36. data/lib/strelka/discovery.rb +1 -1
  37. data/lib/strelka/httprequest.rb +4 -4
  38. data/lib/strelka/httprequest/acceptparams.rb +1 -1
  39. data/lib/strelka/httprequest/auth.rb +3 -1
  40. data/lib/strelka/httprequest/negotiation.rb +1 -1
  41. data/lib/strelka/httprequest/session.rb +3 -1
  42. data/lib/strelka/httpresponse.rb +2 -3
  43. data/lib/strelka/httpresponse/negotiation.rb +1 -1
  44. data/lib/strelka/httpresponse/session.rb +1 -1
  45. data/lib/strelka/mixins.rb +26 -5
  46. data/lib/strelka/multipartparser.rb +3 -3
  47. data/lib/strelka/paramvalidator.rb +4 -4
  48. data/lib/strelka/plugins.rb +14 -5
  49. data/lib/strelka/router.rb +1 -1
  50. data/lib/strelka/router/default.rb +1 -1
  51. data/lib/strelka/router/exclusive.rb +1 -1
  52. data/lib/strelka/session.rb +1 -0
  53. data/lib/strelka/session/db.rb +1 -0
  54. data/lib/strelka/session/default.rb +1 -0
  55. data/lib/strelka/testing.rb +454 -14
  56. data/lib/strelka/websocketserver.rb +150 -36
  57. data/lib/strelka/websocketserver/heartbeat.rb +163 -0
  58. data/lib/strelka/websocketserver/routing.rb +46 -19
  59. data/spec/constants.rb +1 -1
  60. data/spec/helpers.rb +15 -6
  61. data/spec/strelka/app/auth_spec.rb +5 -3
  62. data/spec/strelka/app/errors_spec.rb +2 -2
  63. data/spec/strelka/app/filters_spec.rb +2 -2
  64. data/spec/strelka/app/negotiation_spec.rb +2 -2
  65. data/spec/strelka/app/parameters_spec.rb +5 -5
  66. data/spec/strelka/app/restresources_spec.rb +8 -6
  67. data/spec/strelka/app/routing_spec.rb +3 -3
  68. data/spec/strelka/app/sessions_spec.rb +4 -2
  69. data/spec/strelka/app/templating_spec.rb +2 -2
  70. data/spec/strelka/app_spec.rb +5 -24
  71. data/spec/strelka/authprovider/basic_spec.rb +3 -2
  72. data/spec/strelka/authprovider/hostaccess_spec.rb +3 -2
  73. data/spec/strelka/authprovider_spec.rb +3 -2
  74. data/spec/strelka/cli_spec.rb +7 -4
  75. data/spec/strelka/cookie_spec.rb +2 -2
  76. data/spec/strelka/cookieset_spec.rb +2 -2
  77. data/spec/strelka/discovery_spec.rb +2 -2
  78. data/spec/strelka/exceptions_spec.rb +2 -2
  79. data/spec/strelka/httprequest/acceptparams_spec.rb +2 -2
  80. data/spec/strelka/httprequest/auth_spec.rb +3 -2
  81. data/spec/strelka/httprequest/negotiation_spec.rb +2 -2
  82. data/spec/strelka/httprequest/session_spec.rb +3 -2
  83. data/spec/strelka/httprequest_spec.rb +7 -2
  84. data/spec/strelka/httpresponse/negotiation_spec.rb +6 -5
  85. data/spec/strelka/httpresponse/session_spec.rb +3 -2
  86. data/spec/strelka/httpresponse_spec.rb +4 -3
  87. data/spec/strelka/mixins_spec.rb +85 -2
  88. data/spec/strelka/multipartparser_spec.rb +5 -4
  89. data/spec/strelka/paramvalidator_spec.rb +15 -10
  90. data/spec/strelka/plugins_spec.rb +24 -2
  91. data/spec/strelka/router/default_spec.rb +2 -2
  92. data/spec/strelka/router/exclusive_spec.rb +2 -2
  93. data/spec/strelka/router_spec.rb +2 -2
  94. data/spec/strelka/session/db_spec.rb +3 -2
  95. data/spec/strelka/session/default_spec.rb +3 -2
  96. data/spec/strelka/session_spec.rb +3 -2
  97. data/spec/strelka/testing_spec.rb +772 -0
  98. data/spec/strelka/websocketserver/heartbeat_spec.rb +19 -0
  99. data/spec/strelka/websocketserver/routing_spec.rb +31 -29
  100. data/spec/strelka/websocketserver_spec.rb +210 -75
  101. data/spec/strelka_spec.rb +172 -2
  102. metadata +43 -36
  103. metadata.gz.sig +0 -0
@@ -1,6 +1,8 @@
1
1
  # -*- ruby -*-
2
2
  # vim: set nosta noet ts=4 sw=4:
3
- # encoding: utf-8
3
+ # frozen-string-literal: true
4
+
5
+ require 'set'
4
6
 
5
7
  require 'mongrel2/handler'
6
8
  require 'mongrel2/websocket'
@@ -27,15 +29,17 @@ require 'strelka/discovery'
27
29
  # idle_timeout 15.0
28
30
  #
29
31
  # # When a websocket is set up, add a new user to the table, but without a nick.
30
- # on_handshake do |frame|
31
- # @users[ frame.socket_id ] = nil
32
- # return frame.response # accept the connection
32
+ # def handle_websocket_handshake( request )
33
+ # @users[ request.socket_id ] = nil
34
+ # return request.response # accept the connection
33
35
  # end
34
36
  #
37
+ # plugin :routing
38
+ #
35
39
  # # Handle incoming commands, which should be text frames
36
- # on_text do |frame|
37
- # senderid = frame.socket_id
38
- # data = frame.payload.read
40
+ # on_text do |request|
41
+ # senderid = request.socket_id
42
+ # data = request.payload.read
39
43
  #
40
44
  # # If the input starts with '/', it's a command (e.g., /quit, /nick, etc.)
41
45
  # output = nil
@@ -45,7 +49,7 @@ require 'strelka/discovery'
45
49
  # output = self.say( senderid, data )
46
50
  # end
47
51
  #
48
- # response = frame.response
52
+ # response = request.response
49
53
  # response.puts( output )
50
54
  # return response
51
55
  # end
@@ -62,15 +66,92 @@ class Strelka::WebSocketServer < Mongrel2::Handler
62
66
  log_to :strelka
63
67
 
64
68
 
69
+ ### Overridden from Mongrel2::Handler -- use the value returned from .default_appid if
70
+ ### one is not specified.
71
+ def self::run( appid=nil )
72
+ appid ||= self.default_appid
73
+ self.log.info "Starting up with appid %p." % [ appid ]
74
+ super( appid )
75
+ end
76
+
77
+
78
+ ### Calculate a default application ID for the class based on either its ID
79
+ ### constant or its name and return it.
80
+ def self::default_appid
81
+ self.log.info "Looking up appid for %p" % [ self.class ]
82
+ appid = nil
83
+
84
+ if self.const_defined?( :ID )
85
+ appid = self.const_get( :ID )
86
+ self.log.info " app has an ID: %p" % [ appid ]
87
+ else
88
+ appid = ( self.name || "anonymous#{self.object_id}" ).downcase
89
+ appid.gsub!( /[^[:alnum:]]+/, '-' )
90
+ self.log.info " deriving one from the class name: %p" % [ appid ]
91
+ end
92
+
93
+ return appid
94
+ end
95
+
96
+
97
+ ### Return an instance of the App configured for the handler in the currently-loaded
98
+ ### Mongrel2 config that corresponds to the #default_appid.
99
+ def self::default_app_instance
100
+ appid = self.default_appid
101
+ return self.app_instance_for( appid )
102
+ end
103
+
104
+
105
+
106
+ #################################################################
107
+ ### I N S T A N C E M E T H O D S
108
+ #################################################################
109
+
110
+ ### Dump the application stack when a new instance is created.
111
+ def initialize( * )
112
+ self.class.dump_application_stack
113
+
114
+ @connections = Hash.new {|h, k| h[k] = Set.new }
115
+ @connection_times = Hash.new {|h, k| h[k] = Hash.new }
116
+
117
+ super
118
+ end
119
+
120
+
121
+ ######
122
+ public
123
+ ######
124
+
125
+ ##
126
+ # A Hash of sender ID => Set of connection IDs.
127
+ attr_reader :connections
128
+
129
+ ##
130
+ # A Hash of [sender ID, connection ID] keys => connection Times
131
+ attr_reader :connection_times
132
+
133
+
134
+
135
+ ### Run the app -- overriden to set the process name to something interesting.
136
+ def run
137
+ procname = "%s %s: %p %s" % [ RUBY_ENGINE, RUBY_VERSION, self.class, self.conn ]
138
+ $0 = procname
139
+
140
+ super
141
+ end
142
+
143
+
65
144
  ### Handle a WebSocket frame in +request+. If not overridden, WebSocket connections are
66
145
  ### closed with a policy error status.
67
- def handle_websocket( frame )
146
+ def handle_websocket( request )
68
147
  response = nil
69
148
 
70
- # Dispatch the frame
149
+ self.connection_times[ request.sender_id ][ request.conn_id ] = Time.now
150
+
151
+ # Dispatch the request
71
152
  response = catch( :close_websocket ) do
72
- self.log.debug "Incoming WEBSOCKET frame (%p):%s" % [ frame, frame.headers.path ]
73
- self.handle_frame( frame )
153
+ self.log.debug "Incoming WEBSOCKET request (%p):%s" % [ request, request.headers.path ]
154
+ self.handle_websocket_request( request )
74
155
  end
75
156
 
76
157
  return response
@@ -78,8 +159,13 @@ class Strelka::WebSocketServer < Mongrel2::Handler
78
159
 
79
160
 
80
161
  ### Handle a WebSocket handshake HTTP +request+.
162
+ ### :TODO: Register/check for supported Sec-WebSocket-Protocol.
81
163
  def handle_websocket_handshake( handshake )
82
- self.log.warn "Incoming WEBSOCKET_HANDSHAKE request (%p)" % [ request.headers.path ]
164
+ self.log.info "Incoming WEBSOCKET_HANDSHAKE request (%p)" % [ handshake.headers.path ]
165
+ self.connections[ handshake.sender_id ].add( handshake.conn_id )
166
+ self.connection_times[ handshake.sender_id ][ handshake.conn_id ] = Time.now
167
+ self.log.debug " connections: %p" % [ self.connections ]
168
+
83
169
  return handshake.response( handshake.protocols.first )
84
170
  end
85
171
 
@@ -87,59 +173,87 @@ class Strelka::WebSocketServer < Mongrel2::Handler
87
173
  ### Handle a disconnect notice from Mongrel2 via the given +request+. Its return value
88
174
  ### is ignored.
89
175
  def handle_disconnect( request )
90
- self.log.info "Unhandled disconnect notice."
176
+ self.log.info "Connection %d closed." % [ request.conn_id ]
177
+ self.connection_times[ request.sender_id ].delete( request.conn_id )
178
+ self.connections.delete( request.sender_id )
179
+ self.log.debug " connections remaining: %p" % [ self.connections ]
180
+
91
181
  return nil
92
182
  end
93
183
 
94
184
 
185
+ ### Return the Time of the last frame from the client associated with the given
186
+ ### +request+.
187
+ def last_connection_time( request )
188
+ table = self.connection_times[ request.sender_id ] or return nil
189
+ return table[ request.conn_id ]
190
+ end
191
+
192
+
193
+ ### Send the specified +frame+ to all current connections, except those listed in
194
+ ### +except+. The +except+ argument is a single [sender_id, conn_id] tuple.
195
+ def broadcast( frame, except: nil )
196
+ self.connections.each do |sender_id, conn_ids|
197
+ id_list = conn_ids.to_a.
198
+ reject {|cid| except&.first == sender_id && except&.last == cid }
199
+
200
+ self.log.debug "Broadcasting to %d connections for sender %s" %
201
+ [ conn_ids.length, sender_id ]
202
+
203
+ self.conn.broadcast( sender_id, id_list, frame.to_s )
204
+ end
205
+ end
206
+
207
+
95
208
  #########
96
209
  protected
97
210
  #########
98
211
 
99
- ### Default frame handler.
100
- def handle_frame( frame )
101
- if frame.control?
102
- self.handle_control_frame( frame )
212
+ ### Default request handler.
213
+ def handle_websocket_request( request )
214
+ if request.control?
215
+ self.handle_control_request( request )
103
216
  else
104
- self.handle_content_frame( frame )
217
+ self.handle_content_request( request )
105
218
  end
106
219
  end
107
220
 
108
221
 
109
- ### Throw a :close_websocket frame that will close the current connection.
110
- def close_with( frame, reason )
222
+ ### Throw a response with a 'close' frame that will close the current
223
+ ### connection.
224
+ def close_with( request, reason )
111
225
  self.log.debug "Closing the connection: %p" % [ reason ]
112
226
 
113
- # Make a CLOSE frame
114
- frame = frame.response( :close )
115
- frame.set_status( reason )
227
+ # Make a CLOSE response
228
+ response = request.response( :close )
229
+ response.set_status( reason )
116
230
 
117
- throw :close_websocket, frame
231
+ throw :close_websocket, response
118
232
  end
119
233
 
120
234
 
121
- ### Handle an incoming control frame.
122
- def handle_control_frame( frame )
123
- self.log.debug "Handling control frame: %p" % [ frame ]
235
+ ### Handle an incoming request with a control frame.
236
+ def handle_control_request( request )
237
+ self.log.debug "Handling control request: %p" % [ request ]
124
238
 
125
- case frame.opcode
239
+ case request.opcode
126
240
  when :ping
127
- return frame.response
241
+ return request.response
128
242
  when :pong
129
243
  return nil
130
244
  when :close
131
- self.conn.reply_close( frame )
245
+ self.conn.reply_close( request )
132
246
  return nil
133
247
  else
134
- self.close_with( frame, CLOSE_BAD_DATA_TYPE )
248
+ self.close_with( request, CLOSE_BAD_DATA_TYPE )
135
249
  end
136
250
  end
137
251
 
138
252
 
139
- ### Handle an incoming content frame.
140
- def handle_content_frame( frame )
141
- self.log.warn "Unhandled frame type %p" % [ frame.opcode ]
142
- self.close_with( frame, CLOSE_BAD_DATA_TYPE )
253
+ ### Handle an incoming request with a content frame.
254
+ def handle_content_request( request )
255
+ self.log.warn "Unhandled request type %p" % [ request.opcode ]
256
+ self.close_with( request, CLOSE_BAD_DATA_TYPE )
143
257
  end
144
258
 
145
259
 
@@ -0,0 +1,163 @@
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # frozen-string-literal: true
4
+
5
+ require 'strelka' unless defined?( Strelka )
6
+ require 'strelka/websocketserver' unless defined?( Strelka::WebSocketServer )
7
+ require 'strelka/plugin' unless defined?( Strelka::Plugin )
8
+
9
+ # Heartbeat logic for Strelka WebSocketServers.
10
+ #
11
+ # To ping connected clients, and disconnect them after 3 failed pings:
12
+ #
13
+ # class ChatServer < Strelka::WebSocketServer
14
+ # plugin :heartbeat
15
+ #
16
+ # heartbeat_rate 5.0
17
+ # idle_timeout 15.0
18
+ # end
19
+ #
20
+ module Strelka::WebSocketServer::Heartbeat
21
+ extend Loggability,
22
+ Strelka::Plugin
23
+ include Strelka::Constants,
24
+ Mongrel2::WebSocket::Constants
25
+
26
+
27
+ # The default number of seconds between heartbeat events
28
+ DEFAULT_HEARTBEAT_RATE = 5.0
29
+
30
+ # The default number of seconds between events before a client is disconnected
31
+ DEFAULT_IDLE_TIMEOUT = 15.0
32
+
33
+
34
+ # Loggability API -- set up logging under the 'strelka' log host
35
+ log_to :strelka
36
+
37
+ # Plugins API -- set up load order
38
+ run_outside :routing
39
+
40
+
41
+ # Class methods to add to servers with a heartbeat.
42
+ module ClassMethods # :nodoc:
43
+
44
+ @heartbeat_rate = DEFAULT_HEARTBEAT_RATE
45
+ @idle_timeout = DEFAULT_IDLE_TIMEOUT
46
+
47
+
48
+ ### Get/set the number of seconds between heartbeat events.
49
+ def heartbeat_rate( new_rate=nil )
50
+ @heartbeat_rate = new_rate.to_f if new_rate
51
+ return @heartbeat_rate
52
+ end
53
+
54
+
55
+ ### Get/set the number of seconds between events before a client is disconnected
56
+ def idle_timeout( new_timeout=nil )
57
+ @idle_timeout = new_timeout if new_timeout
58
+ return @idle_timeout
59
+ end
60
+
61
+
62
+ ### Inheritance hook -- inheriting classes inherit their parents' routes table.
63
+ def inherited( subclass )
64
+ super
65
+ subclass.instance_variable_set( :@idle_timeout, self.idle_timeout.dup )
66
+ subclass.instance_variable_set( :@heartbeat_rate, self.heartbeat_rate.dup )
67
+ end
68
+
69
+ end # module ClassMethods
70
+
71
+
72
+ ######
73
+ public
74
+ ######
75
+
76
+ ### Called by Mongrel2::Handler when it starts accepting requests. Overridden
77
+ ### to start up the heartbeat thread.
78
+ def start_accepting_requests
79
+ self.start_heartbeat
80
+ super
81
+ end
82
+
83
+
84
+ ### Called by Mongrel2::Handler when the server is restarted. Overridden to
85
+ ### restart the heartbeat thread.
86
+ def restart
87
+ self.stop_heartbeat
88
+ super
89
+ self.start_heartbeat
90
+ end
91
+
92
+
93
+ ### Called by Mongrel2::Handler when the server is shut down. Overridden to
94
+ ### stop the heartbeat thread.
95
+ def shutdown
96
+ self.stop_heartbeat
97
+ super
98
+ end
99
+
100
+
101
+ ### Start a thread that will periodically ping connected sockets and remove any
102
+ ### connections that don't reply
103
+ def start_heartbeat
104
+ self.log.info "Starting heartbeat timer."
105
+ @heartbeat_timer = self.reactor.add_periodic_timer( self.class.heartbeat_rate ) do
106
+ self.cull_idle_sockets
107
+ self.ping_all_sockets
108
+ end
109
+ end
110
+
111
+
112
+ ### Tell the heartbeat thread to exit.
113
+ def stop_heartbeat
114
+ self.reactor.remove_timer( @heartbeat_timer )
115
+ end
116
+
117
+
118
+ ### Disconnect any sockets that haven't sent any frames for at least
119
+ ### SOCKET_IDLE_TIMEOUT seconds.
120
+ def cull_idle_sockets
121
+ self.log.debug "Culling idle sockets."
122
+
123
+ earliest = Time.now - self.class.idle_timeout
124
+
125
+ self.connection_times.each do |sender_id, times|
126
+ times.each do |conn_id, lastframe|
127
+ next unless earliest > lastframe
128
+
129
+ self.log.warn "Timing out connection %s:%d: last seen %0.3fs ago." %
130
+ [ sender_id, conn_id, Time.now - lastframe ]
131
+
132
+ # Make a CLOSE frame
133
+ frame = Mongrel2::WebSocket::Frame.close
134
+ frame.set_status( CLOSE_EXCEPTION )
135
+
136
+ # Use the connection directly so we can send a frame and close the
137
+ # connection
138
+ self.conn.send( sender_id, conn_id, frame.to_s )
139
+ self.conn.send_close( sender_id, conn_id )
140
+ end
141
+ end
142
+ end
143
+
144
+
145
+ ### Send a PING frame to all connected sockets.
146
+ def ping_all_sockets
147
+ return if self.connections.empty?
148
+
149
+ self.log.debug "Pinging %d connected sockets." % [ self.connections.length ]
150
+ self.connections.each do |sender_id, conn_ids|
151
+ frame = Mongrel2::WebSocket::Frame.new( sender_id, conn_id, '', {}, 'heartbeat' )
152
+ frame.opcode = :ping
153
+ frame.fin = true
154
+
155
+ self.log.debug " %s/%d: PING" % [ sender_id, conn_id ]
156
+ self.conn.reply( frame )
157
+ end
158
+
159
+ self.log.debug " done with pings."
160
+ end
161
+
162
+ end # module Strelka::WebSocketServer::Heartbeat
163
+
@@ -1,6 +1,6 @@
1
1
  # -*- ruby -*-
2
2
  # vim: set nosta noet ts=4 sw=4:
3
- # encoding: utf-8
3
+ # frozen-string-literal: true
4
4
 
5
5
  require 'strelka' unless defined?( Strelka )
6
6
  require 'strelka/websocketserver' unless defined?( Strelka::WebSocketServer )
@@ -10,20 +10,36 @@ require 'strelka/plugin' unless defined?( Strelka::Plugin )
10
10
  #
11
11
  # For a protocol that defines its own opcodes:
12
12
  #
13
- # class ChatServer
13
+ # class ChatServer < Strelka::WebSocketServer
14
14
  # plugin :routing
15
15
  #
16
+ # on_handshake do |request|
17
+ # # ...
18
+ # end
19
+ # on_text do |request|
20
+ # # ...
21
+ # end
22
+ # end
23
+ #
24
+ # class CustomChatServer < Strelka::WebSocketServer
25
+ # plugin :routing
16
26
  # opcodes :nick => 7,
17
27
  # :emote => 8
18
28
  #
19
- # on_text do |frame|
20
- # # ...
29
+ # on_nick do |request|
30
+ # nick = request.payload.read
31
+ # self.set_nick( request.socket_id, nick )
32
+ # return request.response( "Okay, nick set to #{nick}.", :text )
21
33
  # end
22
34
  #
23
- # on_nick do |frame|
24
- # self.set_nick( frame.socket_id, frame.payload.read )
35
+ # on_emote do |request|
36
+ # emote = request.payload.read
37
+ # nick = self.nick_for( request.socket_id )
38
+ # msg = "%s %s" % [ nick, emote ]
39
+ # self.broadcast( msg )
40
+ # return nil
25
41
  # end
26
- #
42
+ # end
27
43
  #
28
44
  module Strelka::WebSocketServer::Routing
29
45
  extend Loggability,
@@ -42,40 +58,51 @@ module Strelka::WebSocketServer::Routing
42
58
  # Class methods to add to classes with routing.
43
59
  module ClassMethods # :nodoc:
44
60
 
61
+ ##
45
62
  # The list of routes to pass to the Router when the application is created
46
63
  attr_reader :op_callbacks
47
64
  @op_callbacks = {}
48
65
 
49
- # The Hash of opcodes that can be hooked
66
+ ##
67
+ # The Hash of opcode names to numeric opcodes
50
68
  attr_reader :opcode_map
51
69
  @opcode_map = {}
52
70
 
71
+ ##
72
+ # The Hash of numeric opcodes to opcode names
73
+ attr_reader :opcode_names
74
+ @opcode_names = {}
75
+
53
76
 
54
77
  ### Declare one or more opcodes in the form:
55
78
  ###
56
79
  ### {
57
- ### <bit> => <label>,
80
+ ### <label> => <bit>,
58
81
  ### }
59
82
  def opcodes( hash )
60
83
  @opcode_map ||= {}
61
84
  @opcode_map.merge!( hash )
62
- @opcode_map.each do |bit, label|
85
+
86
+ @opcode_names = @opcode_map.invert
87
+
88
+ @opcode_map.each do |label, bit|
63
89
  self.log.debug "Set opcode %p to %#0x" % [ label, bit ]
64
90
  declarative = "on_#{label}"
65
91
  block = self.make_declarative( label )
66
92
  self.log.debug " declaring method %p on %p" % [ declarative, self ]
67
- self.class.send( :define_method, declarative, &block )
93
+ self.class.remove_method( declarative ) if self.class.method_defined?( declarative )
94
+ self.class.define_method( declarative, &block )
68
95
  end
69
96
  end
70
97
 
71
98
 
72
- ### Make a declarative method for setting the callback for frames with the specified
73
- ### +opcode+ (Symbol).
99
+ ### Make a declarative method for setting the callback for requests with frames
100
+ ### with the specified +opcode+ (Symbol).
74
101
  def make_declarative( opcode )
75
102
  self.log.debug "Making a declarative for %p" % [ opcode ]
76
103
  return lambda do |&block|
77
104
  self.log.debug "Setting handler for %p frames to %p" % [ opcode, block ]
78
- methodname = "on_#{opcode}_frame"
105
+ methodname = "on_#{opcode}_request"
79
106
  define_method( methodname, &block )
80
107
  self.op_callbacks[ opcode ] = self.instance_method( methodname )
81
108
  end
@@ -94,7 +121,7 @@ module Strelka::WebSocketServer::Routing
94
121
  ### is registered.
95
122
  def self::extended( mod )
96
123
  super
97
- mod.opcodes( Mongrel2::WebSocket::Constants::OPCODE_NAME )
124
+ mod.opcodes( Mongrel2::WebSocket::Constants::OPCODE )
98
125
  end
99
126
 
100
127
  end # module ClassMethods
@@ -102,14 +129,14 @@ module Strelka::WebSocketServer::Routing
102
129
 
103
130
 
104
131
  ### Dispatch the incoming frame to its handler based on its opcode
105
- def handle_frame( frame )
132
+ def handle_websocket_request( request )
106
133
  self.log.debug "[:routing] Opcode map is: %p" % [ self.class.opcode_map ]
107
- opname = self.class.opcode_map[ frame.numeric_opcode ]
108
- self.log.debug "[:routing] Routing frame: %p" % [ opname ]
134
+ opname = self.class.opcode_names[ request.numeric_opcode ]
135
+ self.log.debug "[:routing] Routing request: %p" % [ opname ]
109
136
 
110
137
  handler = self.class.op_callbacks[ opname ] or return super
111
138
 
112
- return handler.bind( self ).call( frame )
139
+ return handler.bind( self ).call( request )
113
140
  end
114
141
 
115
142
  end # module Strelka::WebSocketServer::Routing