bane 0.2.0 → 0.3.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.
- data/README.md +30 -30
- data/Rakefile +31 -40
- data/TODO +18 -10
- data/bin/bane +15 -10
- data/examples/multiple_behaviors.rb +24 -0
- data/examples/readme_example.rb +9 -0
- data/examples/single_behavior.rb +18 -0
- data/lib/bane.rb +2 -2
- data/lib/bane/{delegating_gserver.rb → behavior_server.rb} +11 -6
- data/lib/bane/behaviors.rb +29 -20
- data/lib/bane/command_line_configuration.rb +96 -0
- data/lib/bane/configuration_parser.rb +19 -4
- data/lib/bane/launcher.rb +7 -8
- data/lib/bane/service_registry.rb +4 -0
- data/test/bane/{delegating_gserver_test.rb → behavior_server_test.rb} +19 -16
- data/test/bane/behaviors_test.rb +32 -36
- data/test/bane/command_line_configuration_test.rb +100 -0
- data/test/bane/configuration_parser_test.rb +42 -55
- data/test/bane/fake_connection_test.rb +1 -1
- data/test/bane/integration_test.rb +45 -20
- data/test/bane/naive_http_response_test.rb +1 -1
- data/test/bane/service_registry_test.rb +1 -1
- metadata +63 -44
- data/examples/simple_port_and_class_as_constant.rb +0 -9
- data/examples/simple_port_and_class_as_string.rb +0 -7
- data/examples/specify_behavior_options.rb +0 -16
- data/examples/specify_ports.rb +0 -15
- data/lib/bane/configuration.rb +0 -50
- data/test/bane/configuration_test.rb +0 -52
- data/test/bane/launcher_test.rb +0 -16
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Bane
|
4
|
+
class CommandLineConfiguration
|
5
|
+
def initialize()
|
6
|
+
@options = { :host => BehaviorServer::DEFAULT_HOST }
|
7
|
+
@option_parser = init_option_parser
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(args)
|
11
|
+
parse_options(@options, args)
|
12
|
+
|
13
|
+
return [] if (args.empty?)
|
14
|
+
|
15
|
+
port = parse_port(args[0])
|
16
|
+
behaviors = parse_behaviors(args.drop(1))
|
17
|
+
|
18
|
+
behaviors = ServiceRegistry.all_servers if behaviors.empty?
|
19
|
+
LinearPortMappedBehaviorConfiguration.new(port, behaviors, @options[:host]).servers
|
20
|
+
end
|
21
|
+
|
22
|
+
def usage
|
23
|
+
@option_parser.help
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def init_option_parser
|
29
|
+
OptionParser.new do |opts|
|
30
|
+
opts.banner = "Usage: bane [options] port [behaviors]"
|
31
|
+
opts.separator ""
|
32
|
+
opts.on("-l", "--listen-on-localhost",
|
33
|
+
"Listen on localhost, (#{BehaviorServer::DEFAULT_HOST}). [default]") do
|
34
|
+
@options[:host] = BehaviorServer::DEFAULT_HOST
|
35
|
+
end
|
36
|
+
opts.on("-a", "--listen-on-all-hosts", "Listen on all interfaces, (#{BehaviorServer::ALL_INTERFACES})") do
|
37
|
+
@options[:host] = BehaviorServer::ALL_INTERFACES
|
38
|
+
end
|
39
|
+
opts.separator ""
|
40
|
+
opts.separator "All behaviors:"
|
41
|
+
opts.separator ServiceRegistry.all_server_names.map { |title| " - #{title}" }.join("\n")
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse_options(options, args)
|
47
|
+
@option_parser.parse!(args)
|
48
|
+
rescue OptionParser::InvalidOption => io
|
49
|
+
raise ConfigurationError, io.message
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_port(port)
|
53
|
+
Integer(port)
|
54
|
+
rescue ArgumentError => ae
|
55
|
+
raise ConfigurationError, "Invalid port number: #{port}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_behaviors(behavior_names)
|
59
|
+
behavior_names.map { |behavior| find(behavior) }
|
60
|
+
rescue UnknownBehaviorError => ube
|
61
|
+
raise ConfigurationError, ube.message
|
62
|
+
end
|
63
|
+
|
64
|
+
def find(behavior)
|
65
|
+
raise UnknownBehaviorError.new(behavior) unless Behaviors.const_defined?(behavior)
|
66
|
+
Behaviors.const_get(behavior)
|
67
|
+
end
|
68
|
+
|
69
|
+
class LinearPortMappedBehaviorConfiguration
|
70
|
+
def initialize(port, behaviors, host)
|
71
|
+
@starting_port = port
|
72
|
+
@behaviors = behaviors
|
73
|
+
@host = host
|
74
|
+
end
|
75
|
+
|
76
|
+
def servers
|
77
|
+
configurations = []
|
78
|
+
@behaviors.each_with_index do |behavior, index|
|
79
|
+
configurations << BehaviorServer.new(@starting_port + index, behavior.new, @host)
|
80
|
+
end
|
81
|
+
configurations
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
class ConfigurationError < RuntimeError; end
|
89
|
+
|
90
|
+
class UnknownBehaviorError < RuntimeError
|
91
|
+
def initialize(name)
|
92
|
+
super "Unknown behavior: #{name}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -4,8 +4,8 @@ module Bane
|
|
4
4
|
|
5
5
|
attr_reader :configurations
|
6
6
|
|
7
|
-
|
8
7
|
def initialize(*args)
|
8
|
+
warn_about_deprecation()
|
9
9
|
@configurations = []
|
10
10
|
|
11
11
|
@configurations = case args[0]
|
@@ -18,6 +18,10 @@ module Bane
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
def warn_about_deprecation
|
22
|
+
warn "Kernel#Configuration() and ConfigurationParser are deprecated. Please directly construct an array of BehaviorServers to pass to Launcher."
|
23
|
+
end
|
24
|
+
|
21
25
|
private
|
22
26
|
|
23
27
|
def map_integer_and_behavior_arguments(*args)
|
@@ -31,7 +35,7 @@ module Bane
|
|
31
35
|
|
32
36
|
def setup_linear_ports(port, behavior_classes)
|
33
37
|
behavior_classes.each_with_index do |behavior, index|
|
34
|
-
@configurations <<
|
38
|
+
@configurations << create_server(port + index, find(behavior))
|
35
39
|
end
|
36
40
|
@configurations
|
37
41
|
end
|
@@ -59,16 +63,27 @@ module Bane
|
|
59
63
|
case value
|
60
64
|
when Module
|
61
65
|
behavior = value
|
62
|
-
|
66
|
+
create_server(port, behavior)
|
63
67
|
when Hash
|
64
68
|
behavior = value.delete(:behavior)
|
65
69
|
options = value
|
66
|
-
|
70
|
+
create_server(port, behavior, options)
|
67
71
|
else
|
68
72
|
raise ConfigurationError, "Unknown configuration option: #{value.inspect}"
|
69
73
|
end
|
70
74
|
end
|
71
75
|
|
76
|
+
def create_server(port, behavior, options = {})
|
77
|
+
BehaviorServer.new(port, behavior.new(options), BehaviorServer::DEFAULT_HOST)
|
78
|
+
end
|
79
|
+
|
72
80
|
end
|
73
81
|
|
82
|
+
end
|
83
|
+
|
84
|
+
# Helper method to easily create configuration.
|
85
|
+
module Kernel
|
86
|
+
def Configuration(*args)
|
87
|
+
Bane::ConfigurationParser.new(*args).configurations
|
88
|
+
end
|
74
89
|
end
|
data/lib/bane/launcher.rb
CHANGED
@@ -2,24 +2,23 @@ module Bane
|
|
2
2
|
|
3
3
|
class Launcher
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
-
@
|
8
|
-
@running_servers = []
|
5
|
+
def initialize(servers, logger = $stderr)
|
6
|
+
@servers = servers
|
7
|
+
@servers.each { |server| server.stdlog = logger }
|
9
8
|
end
|
10
9
|
|
11
10
|
def start
|
12
|
-
@
|
11
|
+
@servers.each { |server| server.start }
|
13
12
|
end
|
14
13
|
|
15
14
|
def join
|
16
|
-
@
|
15
|
+
@servers.each { |server| server.join }
|
17
16
|
end
|
18
17
|
|
19
18
|
def stop
|
20
|
-
@
|
19
|
+
@servers.each { |server| server.stop }
|
21
20
|
end
|
22
21
|
|
23
22
|
end
|
24
|
-
|
23
|
+
|
25
24
|
end
|
@@ -1,30 +1,32 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/../test_helper'
|
1
|
+
require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
|
2
2
|
require 'mocha'
|
3
3
|
|
4
|
-
class
|
4
|
+
class BehaviorServerTest < Test::Unit::TestCase
|
5
5
|
include Bane
|
6
6
|
|
7
7
|
IRRELEVANT_IO_STREAM = nil
|
8
8
|
IRRELEVANT_OPTIONS = {}
|
9
|
+
IRRELEVANT_HOST = "1.1.1.1"
|
10
|
+
IRRELEVANT_BEHAVIOR = nil
|
9
11
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
behavior.expects(:serve).with(anything(), is_a(Hash))
|
12
|
+
def test_initializes_server_on_specified_port
|
13
|
+
server = BehaviorServer.new(6000, IRRELEVANT_BEHAVIOR)
|
14
|
+
assert_equal 6000, server.port
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
+
def test_initializes_server_on_specified_hostname
|
18
|
+
server = BehaviorServer.new(IRRELEVANT_PORT, IRRELEVANT_BEHAVIOR, "hostname")
|
19
|
+
assert_equal "hostname", server.host
|
17
20
|
end
|
18
21
|
|
19
|
-
def
|
22
|
+
def test_delegates_serve_call_to_behavior
|
23
|
+
io = mock()
|
20
24
|
behavior = mock()
|
21
|
-
|
22
|
-
initialized_options = {:expected => :options}
|
23
|
-
server = DelegatingGServer.new(IRRELEVANT_PORT, behavior, initialized_options)
|
25
|
+
server = BehaviorServer.new(IRRELEVANT_PORT, behavior)
|
24
26
|
|
25
|
-
behavior.expects(:serve).with(
|
27
|
+
behavior.expects(:serve).with(io)
|
26
28
|
|
27
|
-
server.serve(
|
29
|
+
server.serve(io)
|
28
30
|
end
|
29
31
|
|
30
32
|
def test_connection_log_messages_use_short_behavior_name_to_shorten_log_messages
|
@@ -45,8 +47,9 @@ class DelegatingGserverTest < Test::Unit::TestCase
|
|
45
47
|
|
46
48
|
def assert_log_message_uses_short_behavior_name_for(method)
|
47
49
|
logger = StringIO.new
|
48
|
-
server =
|
49
|
-
|
50
|
+
server = BehaviorServer.new(IRRELEVANT_PORT, Bane::Behaviors::CloseImmediately.new)
|
51
|
+
server.stdlog = logger
|
52
|
+
|
50
53
|
yield server
|
51
54
|
|
52
55
|
assert_match /CloseImmediately/, logger.string, "Log for #{method} should contain class short name"
|
data/test/bane/behaviors_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/../test_helper'
|
1
|
+
require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
|
2
2
|
require 'timeout'
|
3
3
|
require 'mocha'
|
4
4
|
|
@@ -11,72 +11,80 @@ class BehaviorsTest < Test::Unit::TestCase
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_fixed_response_sends_the_specified_message
|
14
|
-
query_server(
|
14
|
+
query_server(FixedResponse.new(:message => "Test Message"))
|
15
15
|
|
16
16
|
assert_equal "Test Message", response
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_newline_response_sends_only_a_newline_character
|
20
|
-
query_server(
|
20
|
+
query_server(NewlineResponse.new)
|
21
21
|
|
22
22
|
assert_equal "\n", response
|
23
23
|
end
|
24
24
|
|
25
25
|
def test_deluge_response_sends_one_million_bytes_by_default
|
26
|
-
query_server(
|
26
|
+
query_server(DelugeResponse.new)
|
27
27
|
|
28
28
|
assert_response_length 1_000_000
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_deluge_response_accepts_length_parameter
|
32
|
-
query_server(
|
32
|
+
query_server(DelugeResponse.new(:length => 1))
|
33
33
|
|
34
34
|
assert_response_length 1
|
35
35
|
end
|
36
36
|
|
37
37
|
def test_close_immediately_sends_no_response
|
38
|
-
query_server(
|
38
|
+
query_server(CloseImmediately.new)
|
39
39
|
|
40
40
|
assert_empty_response()
|
41
41
|
end
|
42
42
|
|
43
43
|
def test_never_respond_never_sends_a_response
|
44
|
-
server =
|
44
|
+
server = NeverRespond.new
|
45
45
|
|
46
46
|
assert_raise Timeout::Error do
|
47
|
-
Timeout::timeout(
|
47
|
+
Timeout::timeout(1) { query_server(server) }
|
48
48
|
end
|
49
49
|
assert_empty_response
|
50
50
|
end
|
51
51
|
|
52
|
-
def
|
53
|
-
server =
|
52
|
+
def test_close_after_pause_sleeps_30_seconds_by_default
|
53
|
+
server = CloseAfterPause.new
|
54
54
|
server.expects(:sleep).with(30)
|
55
|
-
|
56
|
-
query_server(server)
|
57
55
|
|
58
|
-
|
56
|
+
query_server(server)
|
59
57
|
end
|
60
58
|
|
59
|
+
|
61
60
|
def test_close_after_pause_accepts_duration_parameter
|
62
|
-
server =
|
61
|
+
server = CloseAfterPause.new(:duration => 1)
|
62
|
+
server.expects(:sleep).with(1)
|
63
63
|
|
64
|
-
|
64
|
+
query_server(server)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_close_after_pause_sends_nothing
|
68
|
+
server = CloseAfterPause.new
|
69
|
+
server.stubs(:sleep)
|
70
|
+
|
71
|
+
query_server(server)
|
65
72
|
end
|
66
73
|
|
67
74
|
def test_slow_response_sends_a_message_slowly
|
68
|
-
server = create(SlowResponse)
|
69
75
|
message = "Hi!"
|
70
76
|
delay = 0.5
|
71
|
-
max_delay = (message.length + 1) * delay
|
72
77
|
|
73
|
-
|
78
|
+
server = SlowResponse.new(:pause_duration => delay, :message => message)
|
79
|
+
server.expects(:sleep).with(delay).at_least(message.length)
|
80
|
+
|
81
|
+
query_server(server)
|
74
82
|
|
75
83
|
assert_equal message, response
|
76
84
|
end
|
77
85
|
|
78
86
|
def test_random_response_sends_a_nonempty_response
|
79
|
-
query_server(
|
87
|
+
query_server(RandomResponse.new)
|
80
88
|
|
81
89
|
assert (!response.empty?), "Should have served a nonempty response"
|
82
90
|
end
|
@@ -84,7 +92,7 @@ class BehaviorsTest < Test::Unit::TestCase
|
|
84
92
|
def test_refuse_all_http_credentials_sends_401_response_code
|
85
93
|
@fake_connection.will_send("GET /some/irrelevant/path HTTP/1.1")
|
86
94
|
|
87
|
-
server =
|
95
|
+
server = HttpRefuseAllCredentials.new
|
88
96
|
query_server(server)
|
89
97
|
|
90
98
|
assert @fake_connection.read_all_queries?, "Should have read the HTTP query before sending response"
|
@@ -96,11 +104,11 @@ class BehaviorsTest < Test::Unit::TestCase
|
|
96
104
|
end
|
97
105
|
|
98
106
|
def test_for_each_line_reads_a_line_before_responding
|
99
|
-
server =
|
107
|
+
server = Bane::Behaviors::FixedResponseForEachLine.new({:message => "Dynamic"})
|
100
108
|
|
101
109
|
@fake_connection.will_send "irrelevant\n"
|
102
110
|
|
103
|
-
query_server(server
|
111
|
+
query_server(server)
|
104
112
|
assert_equal "Dynamic", response
|
105
113
|
|
106
114
|
assert @fake_connection.read_all_queries?
|
@@ -108,12 +116,8 @@ class BehaviorsTest < Test::Unit::TestCase
|
|
108
116
|
|
109
117
|
private
|
110
118
|
|
111
|
-
def
|
112
|
-
|
113
|
-
end
|
114
|
-
|
115
|
-
def query_server(server, options = {})
|
116
|
-
server.serve(@fake_connection, options)
|
119
|
+
def query_server(server)
|
120
|
+
server.serve(@fake_connection)
|
117
121
|
end
|
118
122
|
|
119
123
|
def response
|
@@ -128,12 +132,4 @@ class BehaviorsTest < Test::Unit::TestCase
|
|
128
132
|
assert_equal expected_length, response.length, "Response was the wrong length"
|
129
133
|
end
|
130
134
|
|
131
|
-
def within(duration)
|
132
|
-
begin
|
133
|
-
Timeout::timeout(duration) { yield }
|
134
|
-
rescue Timeout::Error
|
135
|
-
flunk "Test took too long - should have completed within #{duration} seconds."
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
135
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
|
2
|
+
require 'mocha'
|
3
|
+
|
4
|
+
class CommandLineConfigurationTest < Test::Unit::TestCase
|
5
|
+
include Bane
|
6
|
+
|
7
|
+
IRRELEVANT_BEHAVIOR = "CloseImmediately"
|
8
|
+
|
9
|
+
def test_creates_specified_behavior_on_given_port
|
10
|
+
expect_server_created_with(:port => 3000, :behavior => Behaviors::CloseImmediately)
|
11
|
+
|
12
|
+
create_configuration_for([3000, "CloseImmediately"])
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_creates_all_known_behavior_if_only_port_specified
|
16
|
+
expect_server_created_with :port => 4000, :behavior => Behaviors::CloseImmediately
|
17
|
+
expect_server_created_with :port => 4001, :behavior => Behaviors::NeverRespond
|
18
|
+
|
19
|
+
ServiceRegistry.stubs(:all_servers).returns([Behaviors::CloseImmediately, Behaviors::NeverRespond])
|
20
|
+
|
21
|
+
create_configuration_for([4000])
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_creates_multiple_behaviors_starting_on_given_port
|
25
|
+
expect_server_created_with :port => 3000, :behavior => Behaviors::CloseImmediately
|
26
|
+
expect_server_created_with :port => 3001, :behavior => Behaviors::CloseAfterPause
|
27
|
+
|
28
|
+
create_configuration_for([3000, "CloseImmediately", "CloseAfterPause"])
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_dash_l_option_sets_listen_host_to_localhost
|
32
|
+
expect_server_created_with :host => BehaviorServer::DEFAULT_HOST
|
33
|
+
|
34
|
+
create_configuration_for(["-l", IRRELEVANT_PORT, IRRELEVANT_BEHAVIOR])
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_listen_on_localhost_sets_listen_host_to_localhost
|
38
|
+
expect_server_created_with :host => BehaviorServer::DEFAULT_HOST
|
39
|
+
|
40
|
+
create_configuration_for(["--listen-on-localhost", IRRELEVANT_PORT, IRRELEVANT_BEHAVIOR])
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_dash_a_option_sets_listen_host_to_all_interfaces
|
44
|
+
expect_server_created_with :host => BehaviorServer::ALL_INTERFACES
|
45
|
+
|
46
|
+
create_configuration_for(["-a", IRRELEVANT_PORT, IRRELEVANT_BEHAVIOR])
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_listen_on_all_hosts_option_sets_listen_host_to_all_interfaces
|
50
|
+
expect_server_created_with :host => BehaviorServer::ALL_INTERFACES
|
51
|
+
|
52
|
+
create_configuration_for(["--listen-on-all-hosts", IRRELEVANT_PORT, IRRELEVANT_BEHAVIOR])
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_no_arguments_returns_empty_configuration
|
56
|
+
assert(create_configuration_for([]).empty?,
|
57
|
+
"Should have returned no configurations for empty arguments")
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_non_integer_port_fails_with_error_message
|
61
|
+
assert_invaild_arguments_fail_matching_message(["text_instead_of_an_integer"], /Invalid Port Number/i,
|
62
|
+
"Should have indicated the port was invalid.")
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_unknown_behavior_fails_with_unknown_behavior_message
|
66
|
+
assert_invaild_arguments_fail_matching_message([IRRELEVANT_PORT, "AnUknownBehavior"], /Unknown Behavior/i,
|
67
|
+
"Should have indicated the given behavior is unknown.")
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_invalid_option_fails_with_error_message
|
71
|
+
assert_invaild_arguments_fail_matching_message(["--unknown-option", IRRELEVANT_PORT], /Invalid Option/i,
|
72
|
+
"Should have indicated the --uknown-option switch was unknown.")
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def create_configuration_for(array)
|
79
|
+
CommandLineConfiguration.new().parse(array)
|
80
|
+
end
|
81
|
+
|
82
|
+
def unique_behavior
|
83
|
+
Class.new
|
84
|
+
end
|
85
|
+
|
86
|
+
def expect_server_created_with(arguments)
|
87
|
+
arguments = { :port => anything(), :host => anything() }.merge(arguments)
|
88
|
+
behavior_matcher = arguments[:behavior] ? instance_of(arguments[:behavior]) : anything()
|
89
|
+
BehaviorServer.expects(:new).with(arguments[:port], behavior_matcher, arguments[:host])
|
90
|
+
end
|
91
|
+
|
92
|
+
def assert_invaild_arguments_fail_matching_message(arguments, message_matcher, assertion_failure_message)
|
93
|
+
create_configuration_for(arguments)
|
94
|
+
fail "Should have failed"
|
95
|
+
rescue ConfigurationError => ce
|
96
|
+
assert_match(message_matcher, ce.message, assertion_failure_message)
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
end
|