strelka 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ChangeLog +156 -9
- data/History.rdoc +15 -0
- data/IDEAS.rdoc +17 -1
- data/MILESTONES.rdoc +1 -1
- data/Manifest.txt +10 -2
- data/Plugins.rdoc +4 -4
- data/README.rdoc +3 -3
- data/Rakefile +5 -4
- data/bin/strelka +19 -10
- data/contrib/hoetemplate/data/project/apps/file_name_app +1 -0
- data/contrib/hoetemplate/lib/file_name.rb.erb +3 -2
- data/examples/apps/hello-world +1 -0
- data/examples/apps/ws-chat +69 -0
- data/examples/apps/ws-echo +61 -0
- data/examples/gen-config.rb +6 -5
- data/lib/strelka/app/auth.rb +2 -2
- data/lib/strelka/app/errors.rb +1 -1
- data/lib/strelka/app/filters.rb +3 -2
- data/lib/strelka/app/negotiation.rb +2 -2
- data/lib/strelka/app/parameters.rb +1 -2
- data/lib/strelka/app/restresources.rb +3 -2
- data/lib/strelka/app/routing.rb +1 -1
- data/lib/strelka/app/sessions.rb +2 -2
- data/lib/strelka/app/templating.rb +7 -3
- data/lib/strelka/app.rb +5 -145
- data/lib/strelka/behavior/plugin.rb +4 -4
- data/lib/strelka/discovery.rb +211 -0
- data/lib/strelka/httprequest.rb +1 -0
- data/lib/strelka/httpresponse/negotiation.rb +7 -1
- data/lib/strelka/mixins.rb +4 -1
- data/lib/strelka/paramvalidator.rb +1 -1
- data/lib/strelka/plugins.rb +8 -6
- data/lib/strelka/websocketserver/routing.rb +116 -0
- data/lib/strelka/websocketserver.rb +147 -0
- data/lib/strelka.rb +5 -4
- data/spec/{lib/constants.rb → constants.rb} +3 -2
- data/spec/{lib/helpers.rb → helpers.rb} +15 -14
- data/spec/strelka/app/auth_spec.rb +145 -142
- data/spec/strelka/app/errors_spec.rb +20 -26
- data/spec/strelka/app/filters_spec.rb +67 -54
- data/spec/strelka/app/negotiation_spec.rb +8 -14
- data/spec/strelka/app/parameters_spec.rb +23 -29
- data/spec/strelka/app/restresources_spec.rb +98 -100
- data/spec/strelka/app/routing_spec.rb +57 -57
- data/spec/strelka/app/sessions_spec.rb +11 -17
- data/spec/strelka/app/templating_spec.rb +36 -40
- data/spec/strelka/app_spec.rb +48 -147
- data/spec/strelka/authprovider/basic_spec.rb +5 -11
- data/spec/strelka/authprovider/hostaccess_spec.rb +9 -15
- data/spec/strelka/authprovider_spec.rb +3 -9
- data/spec/strelka/cookie_spec.rb +32 -38
- data/spec/strelka/cookieset_spec.rb +31 -37
- data/spec/strelka/discovery_spec.rb +144 -0
- data/spec/strelka/exceptions_spec.rb +2 -8
- data/spec/strelka/httprequest/acceptparams_spec.rb +74 -83
- data/spec/strelka/httprequest/auth_spec.rb +5 -15
- data/spec/strelka/httprequest/negotiation_spec.rb +93 -103
- data/spec/strelka/httprequest/session_spec.rb +12 -22
- data/spec/strelka/httprequest_spec.rb +1 -7
- data/spec/strelka/httpresponse/negotiation_spec.rb +84 -76
- data/spec/strelka/httpresponse/session_spec.rb +25 -35
- data/spec/strelka/httpresponse_spec.rb +20 -26
- data/spec/strelka/mixins_spec.rb +66 -61
- data/spec/strelka/multipartparser_spec.rb +31 -37
- data/spec/strelka/paramvalidator_spec.rb +389 -373
- data/spec/strelka/plugins_spec.rb +17 -23
- data/spec/strelka/router/default_spec.rb +32 -38
- data/spec/strelka/router/exclusive_spec.rb +28 -34
- data/spec/strelka/router_spec.rb +2 -8
- data/spec/strelka/session/db_spec.rb +17 -15
- data/spec/strelka/session/default_spec.rb +22 -28
- data/spec/strelka/session_spec.rb +3 -9
- data/spec/strelka/websocketserver/routing_spec.rb +119 -0
- data/spec/strelka/websocketserver_spec.rb +149 -0
- data/spec/strelka_spec.rb +11 -13
- data.tar.gz.sig +3 -3
- metadata +22 -14
- metadata.gz.sig +0 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
# encoding: utf-8
|
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
|
+
# Frame routing logic for Strelka WebSocketServers.
|
10
|
+
#
|
11
|
+
# For a protocol that defines its own opcodes:
|
12
|
+
#
|
13
|
+
# class ChatServer
|
14
|
+
# plugin :routing
|
15
|
+
#
|
16
|
+
# opcodes :nick => 7,
|
17
|
+
# :emote => 8
|
18
|
+
#
|
19
|
+
# on_text do |frame|
|
20
|
+
# # ...
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# on_nick do |frame|
|
24
|
+
# self.set_nick( frame.socket_id, frame.payload.read )
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
#
|
28
|
+
module Strelka::WebSocketServer::Routing
|
29
|
+
extend Loggability,
|
30
|
+
Strelka::Plugin
|
31
|
+
include Strelka::Constants,
|
32
|
+
Mongrel2::WebSocket::Constants
|
33
|
+
|
34
|
+
|
35
|
+
# Loggability API -- set up logging under the 'strelka' log host
|
36
|
+
log_to :strelka
|
37
|
+
|
38
|
+
# Plugins API -- set up load order
|
39
|
+
# run_inside :templating, :filters, :parameters
|
40
|
+
|
41
|
+
|
42
|
+
# Class methods to add to classes with routing.
|
43
|
+
module ClassMethods # :nodoc:
|
44
|
+
|
45
|
+
# The list of routes to pass to the Router when the application is created
|
46
|
+
attr_reader :op_callbacks
|
47
|
+
@op_callbacks = {}
|
48
|
+
|
49
|
+
# The Hash of opcodes that can be hooked
|
50
|
+
attr_reader :opcode_map
|
51
|
+
@opcode_map = {}
|
52
|
+
|
53
|
+
|
54
|
+
### Declare one or more opcodes in the form:
|
55
|
+
###
|
56
|
+
### {
|
57
|
+
### <bit> => <label>,
|
58
|
+
### }
|
59
|
+
def opcodes( hash )
|
60
|
+
@opcode_map ||= {}
|
61
|
+
@opcode_map.merge!( hash )
|
62
|
+
@opcode_map.each do |bit, label|
|
63
|
+
self.log.debug "Set opcode %p to %#0x" % [ label, bit ]
|
64
|
+
declarative = "on_#{label}"
|
65
|
+
block = self.make_declarative( label )
|
66
|
+
self.log.debug " declaring method %p on %p" % [ declarative, self ]
|
67
|
+
self.class.send( :define_method, declarative, &block )
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
### Make a declarative method for setting the callback for frames with the specified
|
73
|
+
### +opcode+ (Symbol).
|
74
|
+
def make_declarative( opcode )
|
75
|
+
self.log.debug "Making a declarative for %p" % [ opcode ]
|
76
|
+
return lambda do |&block|
|
77
|
+
self.log.debug "Setting handler for %p frames to %p" % [ opcode, block ]
|
78
|
+
methodname = "on_#{opcode}_frame"
|
79
|
+
define_method( methodname, &block )
|
80
|
+
self.op_callbacks[ opcode ] = self.instance_method( methodname )
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
### Inheritance hook -- inheriting classes inherit their parents' routes table.
|
86
|
+
def inherited( subclass )
|
87
|
+
super
|
88
|
+
subclass.instance_variable_set( :@opcode_map, self.opcode_map.dup )
|
89
|
+
subclass.instance_variable_set( :@op_callbacks, self.op_callbacks.dup )
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
### Extension callback -- install default opcode declaratives when the plugin
|
94
|
+
### is registered.
|
95
|
+
def self::extended( mod )
|
96
|
+
super
|
97
|
+
mod.opcodes( Mongrel2::WebSocket::Constants::OPCODE_NAME )
|
98
|
+
end
|
99
|
+
|
100
|
+
end # module ClassMethods
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
### Dispatch the incoming frame to its handler based on its opcode
|
105
|
+
def handle_frame( frame )
|
106
|
+
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 ]
|
109
|
+
|
110
|
+
handler = self.class.op_callbacks[ opname ] or return super
|
111
|
+
|
112
|
+
return handler.bind( self ).call( frame )
|
113
|
+
end
|
114
|
+
|
115
|
+
end # module Strelka::WebSocketServer::Routing
|
116
|
+
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
# encoding: utf-8
|
4
|
+
|
5
|
+
require 'mongrel2/handler'
|
6
|
+
require 'mongrel2/websocket'
|
7
|
+
|
8
|
+
require 'strelka' unless defined?( Strelka )
|
9
|
+
require 'strelka/mixins'
|
10
|
+
require 'strelka/plugins'
|
11
|
+
require 'strelka/discovery'
|
12
|
+
|
13
|
+
|
14
|
+
# WebSocket (RFC 6455) Server base class.
|
15
|
+
#
|
16
|
+
# class ChatServer < Strelka::WebSocketServer
|
17
|
+
#
|
18
|
+
# # Set up a Hash for participating users
|
19
|
+
# def initialize( * )
|
20
|
+
# super
|
21
|
+
# @users = {}
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # Disconnect clients that don't answer a ping
|
25
|
+
# plugin :heartbeat
|
26
|
+
# heartbeat_rate 5.0
|
27
|
+
# idle_timeout 15.0
|
28
|
+
#
|
29
|
+
# # 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
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # Handle incoming commands, which should be text frames
|
36
|
+
# on_text do |frame|
|
37
|
+
# senderid = frame.socket_id
|
38
|
+
# data = frame.payload.read
|
39
|
+
#
|
40
|
+
# # If the input starts with '/', it's a command (e.g., /quit, /nick, etc.)
|
41
|
+
# output = nil
|
42
|
+
# if data.start_with?( '/' )
|
43
|
+
# output = self.command( senderid, data[1..-1] )
|
44
|
+
# else
|
45
|
+
# output = self.say( senderid, data )
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# response = frame.response
|
49
|
+
# response.puts( output )
|
50
|
+
# return response
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# end # class ChatServer
|
54
|
+
#
|
55
|
+
class Strelka::WebSocketServer < Mongrel2::Handler
|
56
|
+
extend Strelka::MethodUtilities,
|
57
|
+
Strelka::PluginLoader,
|
58
|
+
Strelka::Discovery
|
59
|
+
|
60
|
+
|
61
|
+
# Loggability API -- log to the Strelka logger
|
62
|
+
log_to :strelka
|
63
|
+
|
64
|
+
|
65
|
+
### Handle a WebSocket frame in +request+. If not overridden, WebSocket connections are
|
66
|
+
### closed with a policy error status.
|
67
|
+
def handle_websocket( frame )
|
68
|
+
response = nil
|
69
|
+
|
70
|
+
# Dispatch the frame
|
71
|
+
response = catch( :close_websocket ) do
|
72
|
+
self.log.debug "Incoming WEBSOCKET frame (%p):%s" % [ frame, frame.headers.path ]
|
73
|
+
self.handle_frame( frame )
|
74
|
+
end
|
75
|
+
|
76
|
+
return response
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
### Handle a WebSocket handshake HTTP +request+.
|
81
|
+
def handle_websocket_handshake( handshake )
|
82
|
+
self.log.warn "Incoming WEBSOCKET_HANDSHAKE request (%p)" % [ request.headers.path ]
|
83
|
+
return handshake.response( handshake.protocols.first )
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
### Handle a disconnect notice from Mongrel2 via the given +request+. Its return value
|
88
|
+
### is ignored.
|
89
|
+
def handle_disconnect( request )
|
90
|
+
self.log.info "Unhandled disconnect notice."
|
91
|
+
return nil
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
#########
|
96
|
+
protected
|
97
|
+
#########
|
98
|
+
|
99
|
+
### Default frame handler.
|
100
|
+
def handle_frame( frame )
|
101
|
+
if frame.control?
|
102
|
+
self.handle_control_frame( frame )
|
103
|
+
else
|
104
|
+
self.handle_content_frame( frame )
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
### Throw a :close_websocket frame that will close the current connection.
|
110
|
+
def close_with( frame, reason )
|
111
|
+
self.log.debug "Closing the connection: %p" % [ reason ]
|
112
|
+
|
113
|
+
# Make a CLOSE frame
|
114
|
+
frame = frame.response( :close )
|
115
|
+
frame.set_status( reason )
|
116
|
+
|
117
|
+
throw :close_websocket, frame
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
### Handle an incoming control frame.
|
122
|
+
def handle_control_frame( frame )
|
123
|
+
self.log.debug "Handling control frame: %p" % [ frame ]
|
124
|
+
|
125
|
+
case frame.opcode
|
126
|
+
when :ping
|
127
|
+
return frame.response
|
128
|
+
when :pong
|
129
|
+
return nil
|
130
|
+
when :close
|
131
|
+
self.conn.reply_close( frame )
|
132
|
+
return nil
|
133
|
+
else
|
134
|
+
self.close_with( frame, CLOSE_BAD_DATA_TYPE )
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|
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 )
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
end # class Strelka::WebSocketServer
|
147
|
+
|
data/lib/strelka.rb
CHANGED
@@ -24,10 +24,10 @@ module Strelka
|
|
24
24
|
log_as :strelka
|
25
25
|
|
26
26
|
# Library version constant
|
27
|
-
VERSION = '0.
|
27
|
+
VERSION = '0.7.0'
|
28
28
|
|
29
29
|
# Version-control revision constant
|
30
|
-
REVISION = %q$Revision:
|
30
|
+
REVISION = %q$Revision: 2caa91898658 $
|
31
31
|
|
32
32
|
require 'strelka/constants'
|
33
33
|
require 'strelka/exceptions'
|
@@ -36,6 +36,7 @@ module Strelka
|
|
36
36
|
require 'strelka/app'
|
37
37
|
require 'strelka/httprequest'
|
38
38
|
require 'strelka/httpresponse'
|
39
|
+
require 'strelka/discovery'
|
39
40
|
|
40
41
|
|
41
42
|
### Get the library version. If +include_buildnum+ is true, the version string will
|
@@ -67,10 +68,10 @@ module Strelka
|
|
67
68
|
### named +gemname+. Returns the first matching class, or raises an exception if no
|
68
69
|
### app class was found.
|
69
70
|
def self::App( appname, gemname=nil )
|
70
|
-
path, _ = Strelka::
|
71
|
+
path, _ = Strelka::Discovery.find( appname, gemname )
|
71
72
|
raise LoadError, "Can't find the %s app." % [ appname ] unless path
|
72
73
|
|
73
|
-
apps = Strelka::
|
74
|
+
apps = Strelka::Discovery.load( path ) or
|
74
75
|
raise ScriptError "Loading %s didn't define a Strelka::App class." % [ path ]
|
75
76
|
|
76
77
|
return apps.first
|
@@ -8,7 +8,8 @@ require 'strelka' unless defined?( Strelka )
|
|
8
8
|
### A collection of constants used in testing
|
9
9
|
module Strelka::TestConstants # :nodoc:all
|
10
10
|
|
11
|
-
include Strelka::Constants
|
11
|
+
include Strelka::Constants,
|
12
|
+
Mongrel2::WebSocket::Constants
|
12
13
|
|
13
14
|
unless defined?( TEST_HOST )
|
14
15
|
|
@@ -24,7 +25,7 @@ module Strelka::TestConstants # :nodoc:all
|
|
24
25
|
|
25
26
|
# Freeze all testing constants
|
26
27
|
constants.each do |cname|
|
27
|
-
const_get(cname).freeze
|
28
|
+
const_get(cname).freeze if cname.to_s.start_with?( 'TEST_' )
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
@@ -8,11 +8,8 @@ BEGIN {
|
|
8
8
|
srcdir = basedir.parent
|
9
9
|
mongrel2dir = srcdir + 'Mongrel2/lib'
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
$LOAD_PATH.unshift( mongrel2dir.to_s ) unless $LOAD_PATH.include?( mongrel2dir.to_s )
|
14
|
-
$LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
|
15
|
-
$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
|
11
|
+
$LOAD_PATH.unshift( mongrel2dir.to_s ) unless
|
12
|
+
!mongrel2dir.directory? || $LOAD_PATH.include?( mongrel2dir.to_s )
|
16
13
|
}
|
17
14
|
|
18
15
|
# SimpleCov test coverage reporting; enable this using the :coverage rake task
|
@@ -40,7 +37,7 @@ require 'mongrel2/testing'
|
|
40
37
|
require 'strelka'
|
41
38
|
require 'strelka/testing'
|
42
39
|
|
43
|
-
|
40
|
+
require_relative 'constants'
|
44
41
|
|
45
42
|
Loggability.format_with( :color ) if $stdout.tty?
|
46
43
|
|
@@ -103,19 +100,23 @@ module Strelka::SpecHelpers
|
|
103
100
|
end
|
104
101
|
|
105
102
|
|
106
|
-
abort "You need a version of RSpec >= 2.6.0" unless defined?( RSpec )
|
107
|
-
|
108
103
|
### Mock with RSpec
|
109
|
-
RSpec.configure do |
|
104
|
+
RSpec.configure do |config|
|
110
105
|
include Strelka::TestConstants
|
111
106
|
|
112
|
-
|
107
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
108
|
+
config.run_all_when_everything_filtered = true
|
109
|
+
config.filter_run :focus
|
110
|
+
config.order = 'random'
|
111
|
+
config.mock_with( :rspec ) do |mock|
|
112
|
+
mock.syntax = :expect
|
113
|
+
end
|
113
114
|
|
114
|
-
|
115
|
+
config.extend( Strelka::TestConstants )
|
115
116
|
|
116
|
-
|
117
|
-
|
118
|
-
|
117
|
+
config.include( Loggability::SpecHelpers )
|
118
|
+
config.include( Mongrel2::SpecHelpers )
|
119
|
+
config.include( Strelka::SpecHelpers )
|
119
120
|
end
|
120
121
|
|
121
122
|
# vim: set nosta noet ts=4 sw=4:
|