bane 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +16 -6
- data/Rakefile +1 -1
- data/TODO +5 -8
- data/bin/bane +4 -12
- data/examples/multiple_behaviors.rb +7 -6
- data/examples/readme_example.rb +2 -1
- data/examples/single_behavior.rb +2 -1
- data/lib/bane.rb +18 -5
- data/lib/bane/arguments_parser.rb +73 -0
- data/lib/bane/behavior_maker.rb +39 -0
- data/lib/bane/behaviors/responders/close_after_pause.rb +21 -0
- data/lib/bane/behaviors/responders/close_immediately.rb +14 -0
- data/lib/bane/behaviors/responders/deluge_response.rb +27 -0
- data/lib/bane/behaviors/responders/echo_response.rb +16 -0
- data/lib/bane/behaviors/responders/exported.rb +9 -0
- data/lib/bane/behaviors/responders/fixed_response.rb +25 -0
- data/lib/bane/behaviors/responders/for_each_line.rb +18 -0
- data/lib/bane/behaviors/responders/http_refuse_all_credentials.rb +31 -0
- data/lib/bane/behaviors/responders/never_respond.rb +27 -0
- data/lib/bane/behaviors/responders/newline_response.rb +18 -0
- data/lib/bane/behaviors/responders/random_response.rb +24 -0
- data/lib/bane/behaviors/responders/slow_response.rb +32 -0
- data/lib/bane/behaviors/servers/exported.rb +18 -0
- data/lib/bane/behaviors/servers/responder_server.rb +50 -0
- data/lib/bane/behaviors/servers/timeout_in_listen_queue.rb +51 -0
- data/lib/bane/command_line_configuration.rb +22 -79
- data/lib/bane/extensions.rb +7 -0
- data/test/bane/acceptance_test.rb +65 -0
- data/test/bane/arguments_parser_test.rb +76 -0
- data/test/bane/bane_test.rb +12 -0
- data/test/bane/behaviors/responders/close_after_pause_test.rb +30 -0
- data/test/bane/behaviors/responders/close_immediately_test.rb +14 -0
- data/test/bane/behaviors/responders/deluge_response_test.rb +20 -0
- data/test/bane/behaviors/responders/echo_response_test.rb +16 -0
- data/test/bane/behaviors/responders/fixed_response_test.rb +14 -0
- data/test/bane/behaviors/responders/for_each_line_test.rb +29 -0
- data/test/bane/behaviors/responders/http_refuse_all_credentials_test.rb +18 -0
- data/test/bane/behaviors/responders/never_respond_test.rb +31 -0
- data/test/bane/behaviors/responders/newline_response_test.rb +14 -0
- data/test/bane/behaviors/responders/random_response_test.rb +15 -0
- data/test/bane/behaviors/responders/slow_response_test.rb +21 -0
- data/test/bane/behaviors/servers/responder_server_test.rb +77 -0
- data/test/bane/behaviors/servers/timeout_in_listen_queue_test.rb +23 -0
- data/test/bane/command_line_configuration_test.rb +54 -72
- data/test/bane/extensions_test.rb +17 -0
- data/test/bane/fake_connection_test.rb +1 -1
- data/test/bane/launchable_role_tests.rb +20 -0
- data/test/bane/naive_http_response_test.rb +1 -1
- data/test/test_helper.rb +42 -1
- metadata +143 -99
- data/lib/bane/behavior_server.rb +0 -47
- data/lib/bane/behaviors.rb +0 -171
- data/lib/bane/compatibility.rb +0 -36
- data/lib/bane/configuration_parser.rb +0 -89
- data/lib/bane/service_registry.rb +0 -21
- data/test/bane/behavior_server_test.rb +0 -59
- data/test/bane/behaviors_test.rb +0 -135
- data/test/bane/configuration_parser_test.rb +0 -111
- data/test/bane/integration_test.rb +0 -92
- data/test/bane/service_registry_test.rb +0 -20
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Y2M3MGZkYWY0NDNhNjZlYTY5M2I2MGViMDljNjY3YzRiYmY2MDg3OA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OWY2YTY3ZDFmNDU4Y2I1MzEwZjViNDIzZGU0YzMzNzk4ZjdiMmIxOA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ODE5MDJlYzkyMzNmYzllYzRlMTAwY2RjYjNhYzlkNjA1NTU0ODRiZmNhOGZh
|
10
|
+
NjczN2E4ZjM4YjcyMjUyYTBlODUxNjVkM2NiODc4MjIyM2Q2MjE3OWNmYzk0
|
11
|
+
YmFjZmVlNzVmYjMyOGE4ZmJhZmYzYzU3MTljOWMyODc5YjRiYjM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MmI5ZjZmYjM5YTczYzQ0NjFmYjNlMTBlY2I5ZmJiY2ZkMmQ4MDM5MjczYjgw
|
14
|
+
MTJkMWQxOTVkOTExN2FiMjVjNjUxOGRlOWU1YTc3OGUxMjUxNDM1Mjk1Zjkw
|
15
|
+
NDU5YzgzYzE2OGQ0MTY4OTdlY2RjYzNlNDM0ZDEyZmEyMGI3YzI=
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Bane
|
2
2
|
|
3
|
+
[![Build Status](https://secure.travis-ci.org/danielwellman/bane.png)](http://travis-ci.org/danielwellman/bane) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/danielwellman/bane)
|
4
|
+
|
3
5
|
Bane is a test harness used to test your application's interaction with other servers. It is based upon the material from Michael Nygard's ["Release It!"](http://www.pragprog.com/titles/mnee/release-it) book as described in the "Test Harness" chapter.
|
4
6
|
|
5
7
|
## Why Use Bane?
|
@@ -8,12 +10,14 @@ If you are building an application, you may depend on third-party servers or web
|
|
8
10
|
|
9
11
|
## Setup
|
10
12
|
|
11
|
-
Bane is available as a gem. Install it with
|
13
|
+
Bane is available as a Ruby gem. Install it with
|
12
14
|
|
13
15
|
`gem install bane`
|
14
16
|
|
15
17
|
Note that Bane installs an executable, `bane`. Simply invoke `bane` with no arguments to get a usage description.
|
16
18
|
|
19
|
+
Bane requires Ruby 1.9 or later. If you would like to use a Ruby 1.8.7-compatible version, install version 0.3.0.
|
20
|
+
|
17
21
|
## Usage
|
18
22
|
|
19
23
|
Bane is designed with a few usage scenarios in mind:
|
@@ -40,13 +44,14 @@ require 'bane'
|
|
40
44
|
include Bane
|
41
45
|
|
42
46
|
launcher = Launcher.new(
|
43
|
-
[BehaviorServer.new(3000, Behaviors::FixedResponse.new(:
|
47
|
+
[BehaviorServer.new(3000, Behaviors::Responders::FixedResponse.new(message: "Shall we play a game?"))])
|
44
48
|
launcher.start
|
45
49
|
launcher.join
|
46
50
|
```
|
47
51
|
|
48
|
-
See the `examples`directory for more examples. For a list of options supported by the
|
49
|
-
included behaviors, see the source for the behaviors in `
|
52
|
+
See the `examples` directory for more examples. For a list of options supported by the
|
53
|
+
included behaviors, see the source for the behaviors in `lib/bane/behaviors`; note that you will find both
|
54
|
+
servers (that bind to sockets directly) and responders (that assume a running TCP server and communicate with a socket connection).
|
50
55
|
|
51
56
|
## Listening on all hosts
|
52
57
|
|
@@ -60,7 +65,7 @@ By default, the socket behaviors that send any data will close the connection im
|
|
60
65
|
|
61
66
|
For example, if you want to send a static response and then close the connection immediately, use `FixedResponse`. If you want to keep the connection open and respond to every line of input with the same data, use `FixedResponseForEachLine`. Note that these behaviors will never close the connection; they will happily respond to every line of input until you stop Bane.
|
62
67
|
|
63
|
-
If you are implementing a new behavior, you should consider whether or not you would like to provide another variation which keeps a connection open and responds after every line of input. If so, create the basic behavior which responds and closes the connection immediately, then create another behavior which includes the `ForEachLine` module. See the source in `lib/bane/behaviors.rb` for some examples.
|
68
|
+
If you are implementing a new behavior, you should consider whether or not you would like to provide another variation which keeps a connection open and responds after every line of input. If so, create the basic behavior which responds and closes the connection immediately, then create another behavior which includes the `ForEachLine` module. See the source in `lib/bane/behaviors/responders/fixed_response.rb` for some examples.
|
64
69
|
|
65
70
|
## Background
|
66
71
|
|
@@ -83,7 +88,7 @@ See the implementation of HttpRefuseAllCredentials for a simple example of an HT
|
|
83
88
|
* The service can send a response of HTML instead of the expected XML.
|
84
89
|
|
85
90
|
The following behaviors are not yet supported. These require the ability to manipulate
|
86
|
-
TCP packets at a low level
|
91
|
+
TCP packets at a low level, which may require a C or C++ extension or raw sockets.
|
87
92
|
|
88
93
|
* The connection can be refused.
|
89
94
|
* The request can sit in a listen queue until the caller times out.
|
@@ -93,3 +98,8 @@ TCP packets at a low level; which may require a C or C++ extension.
|
|
93
98
|
* The connection can be established, but packets could be lost causing retransmit delays
|
94
99
|
* The connection can be established, but the remote end never acknowledges receiving a packet, causing endless retransmits
|
95
100
|
|
101
|
+
## Support
|
102
|
+
|
103
|
+
<a href="http://www.cyrusinnovation.com/"><img src="http://www.cyrusinnovation.com/marketing/logo.png" alt="Cyrus"></a>
|
104
|
+
|
105
|
+
Thank you to [Cyrus](http://www.cyrusinnovation.com/) for supporting the development of this project.
|
data/Rakefile
CHANGED
@@ -22,7 +22,7 @@ Jeweler::Tasks.new do |gem|
|
|
22
22
|
other servers. It is based upon the material from Michael Nygard's "Release
|
23
23
|
It!" book as described in the "Test Harness" chapter.
|
24
24
|
END
|
25
|
-
gem.authors = ["Daniel Wellman"]
|
25
|
+
gem.authors = ["Daniel Wellman", "Joe Leo"]
|
26
26
|
gem.email = "dan@danielwellman.com"
|
27
27
|
gem.files = FileList[ 'lib/**/*', 'bin/*', 'test/**/*', 'examples/*',
|
28
28
|
'Rakefile' ]
|
data/TODO
CHANGED
@@ -1,21 +1,18 @@
|
|
1
1
|
Features:
|
2
|
-
- Quieter exits on Ctrl-C (than plain stack trace)
|
3
2
|
|
4
3
|
Design questions / Ideas:
|
5
|
-
- Make BasicBehavior a module called Behavior, and include that in each behavior?
|
6
|
-
- Is there a natural separation in Configuration between parsing arguments and instantiating/finding behaviors?
|
7
|
-
In particular, mocking "new" on a class seems to be a smell -- at least because I keep forgetting that 'new' must
|
8
|
-
return an object, and my mocks didn't do that (see CommandLineConfigurationTest, and the messier ConfigurationParserTest)
|
9
|
-
- Explore the ServiceRegistry usage -- should "find" behaviors be there? Printing?
|
10
|
-
- Decide if the current behaviors should be tied more directly to BehaviorServer, via subclassing or some other way.
|
11
4
|
- Figure out where the logger configuration logic belongs in the Launcher/BehaviorServer relationship
|
12
5
|
- Should the default logger go to STDERR or STDOUT?
|
13
|
-
- Break the Behaviors out into several files and test files?
|
14
6
|
- Log every request to the server/behavior, in addition to open, close. For this to work, it would have to be the
|
15
7
|
behavior's responsibility, since GServer#serve gets called only once for the lifetime of the connection.
|
8
|
+
- If we extract a commong logger, we might use that to test for proper disconnection in NeverRespondTest#test_disconnects_after_client_closes_connection
|
16
9
|
|
17
10
|
Future Behaviors:
|
18
11
|
- Create a more configurable version of the DelugeResponse which allows for a header, footer, content and times to repeat.
|
19
12
|
- Write the remaining bad HTTP behaviors. In addition, we may want to replace the NaiveHttpResponse with something
|
20
13
|
from the standard Ruby library, so that there's less code in this project, and so we know that we're
|
21
14
|
following the HTTP protocol.
|
15
|
+
- Have a tiny listen queue (queue size 0) and a connection never gets into the queue
|
16
|
+
- Have a giant listen queue (queue size max?) and only one thread processing -- times out in the listen queue
|
17
|
+
- Close the write part of the connection, but not the read
|
18
|
+
- Close the read part of the connection, but not the write
|
data/bin/bane
CHANGED
@@ -1,22 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
4
3
|
require 'bane'
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
rescue Bane::ConfigurationError => ce
|
10
|
-
puts ce.message
|
11
|
-
puts config.usage
|
5
|
+
parser = Bane::CommandLineConfiguration.new(Bane.find_makeables)
|
6
|
+
servers = parser.process(ARGV) do |error_message|
|
7
|
+
puts error_message
|
12
8
|
exit 1
|
13
9
|
end
|
14
10
|
|
15
|
-
if servers.empty?
|
16
|
-
puts config.usage
|
17
|
-
exit 0
|
18
|
-
end
|
19
|
-
|
20
11
|
launcher = Bane::Launcher.new(servers)
|
21
12
|
launcher.start
|
13
|
+
trap("SIGINT") { Thread.new { launcher.stop; exit } }
|
22
14
|
launcher.join
|
@@ -2,18 +2,19 @@ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
|
2
2
|
require 'bane'
|
3
3
|
|
4
4
|
include Bane
|
5
|
+
include Behaviors
|
5
6
|
|
6
7
|
# This example creates several behavior listening on distinct ports.
|
7
8
|
# Note the FixedResponse port specifies to listen to all hosts (0.0.0.0), all
|
8
9
|
# other servers listen to localhost only by default (127.0.0.1).
|
9
10
|
|
10
|
-
close_immediately =
|
11
|
-
never_respond =
|
12
|
-
fixed_response =
|
11
|
+
close_immediately = Responders::CloseImmediately.new
|
12
|
+
never_respond = Responders::NeverRespond.new
|
13
|
+
fixed_response = Responders::FixedResponse.new(message: "OK")
|
13
14
|
|
14
|
-
launcher = Launcher.new([
|
15
|
-
|
16
|
-
|
15
|
+
launcher = Launcher.new([Servers::ResponderServer.new(3000, close_immediately),
|
16
|
+
Servers::ResponderServer.new(8000, never_respond),
|
17
|
+
Servers::ResponderServer.new(8080, fixed_response, Servers::ALL_INTERFACES)])
|
17
18
|
launcher.start
|
18
19
|
# To run until interrupt, use the following line:
|
19
20
|
#launcher.join
|
data/examples/readme_example.rb
CHANGED
@@ -2,8 +2,9 @@ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
|
2
2
|
require 'bane'
|
3
3
|
|
4
4
|
include Bane
|
5
|
+
include Behaviors
|
5
6
|
|
6
7
|
launcher = Launcher.new(
|
7
|
-
[
|
8
|
+
[Servers::ResponderServer.new(3000, Responders::FixedResponse.new(message: "Shall we play a game?"))])
|
8
9
|
launcher.start
|
9
10
|
launcher.join
|
data/examples/single_behavior.rb
CHANGED
@@ -2,11 +2,12 @@ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
|
2
2
|
require 'bane'
|
3
3
|
|
4
4
|
include Bane
|
5
|
+
include Behaviors
|
5
6
|
|
6
7
|
# This example creates a single behavior listening on port 3000.
|
7
8
|
# Note that the behavior, CloseAfterPause, specifies a default duration to pause - 60 seconds.
|
8
9
|
|
9
|
-
behavior =
|
10
|
+
behavior = Servers::ResponderServer.new(3000, Responders::CloseAfterPause.new(duration: 60))
|
10
11
|
launcher = Launcher.new([behavior])
|
11
12
|
launcher.start
|
12
13
|
# To run until interrupt, use the following line:
|
data/lib/bane.rb
CHANGED
@@ -1,8 +1,21 @@
|
|
1
|
-
require 'bane/
|
2
|
-
require 'bane/
|
3
|
-
require 'bane/behaviors'
|
1
|
+
require 'bane/extensions'
|
2
|
+
require 'bane/behavior_maker'
|
3
|
+
require 'bane/behaviors/responders/for_each_line'
|
4
|
+
require 'bane/behaviors/responders/close_after_pause'
|
5
|
+
require 'bane/behaviors/responders/close_immediately'
|
6
|
+
require 'bane/behaviors/responders/deluge_response'
|
7
|
+
require 'bane/behaviors/responders/echo_response'
|
8
|
+
require 'bane/behaviors/responders/fixed_response'
|
9
|
+
require 'bane/behaviors/responders/http_refuse_all_credentials'
|
10
|
+
require 'bane/behaviors/responders/never_respond'
|
11
|
+
require 'bane/behaviors/responders/newline_response'
|
12
|
+
require 'bane/behaviors/responders/random_response'
|
13
|
+
require 'bane/behaviors/responders/slow_response'
|
14
|
+
require 'bane/behaviors/responders/exported'
|
15
|
+
require 'bane/behaviors/servers/responder_server'
|
16
|
+
require 'bane/behaviors/servers/timeout_in_listen_queue'
|
17
|
+
require 'bane/behaviors/servers/exported'
|
4
18
|
require 'bane/launcher'
|
5
|
-
require 'bane/
|
19
|
+
require 'bane/arguments_parser'
|
6
20
|
require 'bane/command_line_configuration'
|
7
|
-
require 'bane/configuration_parser'
|
8
21
|
require 'bane/naive_http_response'
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Bane
|
4
|
+
class ArgumentsParser
|
5
|
+
def initialize(makeable_names)
|
6
|
+
@makeable_names = makeable_names
|
7
|
+
@options = {host: default_host}
|
8
|
+
@option_parser = init_option_parser
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse(args)
|
12
|
+
@option_parser.parse!(args)
|
13
|
+
|
14
|
+
raise ConfigurationError, "Missing arguments" if args.empty?
|
15
|
+
|
16
|
+
port = parse_port(args[0])
|
17
|
+
behaviors = args.drop(1)
|
18
|
+
ParsedArguments.new(port, @options[:host], behaviors)
|
19
|
+
rescue OptionParser::InvalidOption => io
|
20
|
+
raise ConfigurationError, io.message
|
21
|
+
end
|
22
|
+
|
23
|
+
def usage
|
24
|
+
@option_parser.help
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def init_option_parser
|
30
|
+
OptionParser.new do |opts|
|
31
|
+
opts.banner = 'Usage: bane [options] port [behaviors]'
|
32
|
+
opts.separator ''
|
33
|
+
opts.on('-l', '--listen-on-localhost',
|
34
|
+
"Listen on localhost, (#{default_host}). [default]") do
|
35
|
+
@options[:host] = default_host
|
36
|
+
end
|
37
|
+
opts.on('-a', '--listen-on-all-hosts', "Listen on all interfaces, (#{all_interfaces})") do
|
38
|
+
@options[:host] = all_interfaces
|
39
|
+
end
|
40
|
+
opts.separator ''
|
41
|
+
opts.separator 'All behaviors:'
|
42
|
+
opts.separator @makeable_names.sort.map { |title| " - #{title}" }.join("\n")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse_port(port)
|
47
|
+
Integer(port)
|
48
|
+
rescue ArgumentError
|
49
|
+
raise ConfigurationError, "Invalid port number: #{port}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def all_interfaces
|
53
|
+
Behaviors::Servers::ALL_INTERFACES
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_host
|
57
|
+
Behaviors::Servers::LOCALHOST
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class ParsedArguments
|
62
|
+
|
63
|
+
attr_reader :port, :host, :behaviors
|
64
|
+
|
65
|
+
def initialize(port, host, behaviors)
|
66
|
+
@host = host
|
67
|
+
@port = port
|
68
|
+
@behaviors = behaviors
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Bane
|
2
|
+
|
3
|
+
class BehaviorMaker
|
4
|
+
def initialize(makeables)
|
5
|
+
@makeables = makeables
|
6
|
+
end
|
7
|
+
|
8
|
+
def create(behavior_names, starting_port, host)
|
9
|
+
behavior_names
|
10
|
+
.map { |behavior| makeables.fetch(behavior) { raise UnknownBehaviorError.new(behavior) } }
|
11
|
+
.map.with_index { |maker, index| maker.make(starting_port + index, host) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_all(starting_port, host)
|
15
|
+
makeables.sort.map.with_index { |name_maker_pair, index| name_maker_pair.last.make(starting_port + index, host) }
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :makeables
|
21
|
+
end
|
22
|
+
|
23
|
+
class UnknownBehaviorError < RuntimeError
|
24
|
+
def initialize(name)
|
25
|
+
super "Unknown behavior: #{name}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class ResponderMaker
|
30
|
+
def initialize(responder)
|
31
|
+
@responer = responder
|
32
|
+
end
|
33
|
+
|
34
|
+
def make(port, host)
|
35
|
+
Behaviors::Servers::ResponderServer.new(port, @responer.new, host)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Bane
|
2
|
+
module Behaviors
|
3
|
+
module Responders
|
4
|
+
|
5
|
+
# Accepts a connection, pauses a fixed duration, then closes the connection.
|
6
|
+
#
|
7
|
+
# Options:
|
8
|
+
# - duration: The number of seconds to wait before disconnect. Default: 30
|
9
|
+
class CloseAfterPause
|
10
|
+
def initialize(options = {})
|
11
|
+
@options = {duration: 30}.merge(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def serve(io)
|
15
|
+
sleep(@options[:duration])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Bane
|
2
|
+
module Behaviors
|
3
|
+
module Responders
|
4
|
+
|
5
|
+
# Sends a large response. Response consists of a repeated 'x' character.
|
6
|
+
#
|
7
|
+
# Options
|
8
|
+
# - length: The size in bytes of the response to send. Default: 1,000,000 bytes
|
9
|
+
class DelugeResponse
|
10
|
+
def initialize(options = {})
|
11
|
+
@options = {length: 1_000_000}.merge(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def serve(io)
|
15
|
+
length = @options[:length]
|
16
|
+
|
17
|
+
length.times { io.write('x') }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class DelugeResponseForEachLine < DelugeResponse
|
22
|
+
include ForEachLine
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Bane
|
2
|
+
module Behaviors
|
3
|
+
module Responders
|
4
|
+
|
5
|
+
# Sends a static response.
|
6
|
+
#
|
7
|
+
# Options:
|
8
|
+
# - message: The response message to send. Default: "Hello, world!"
|
9
|
+
class FixedResponse
|
10
|
+
def initialize(options = {})
|
11
|
+
@options = {message: "Hello, world!"}.merge(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def serve(io)
|
15
|
+
io.write @options[:message]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class FixedResponseForEachLine < FixedResponse
|
20
|
+
include ForEachLine
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|