strelka 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|