danielwellman / bane Goto Github PK
View Code? Open in Web Editor NEWA test harness for socket connections
License: BSD 2-Clause "Simplified" License
A test harness for socket connections
License: BSD 2-Clause "Simplified" License
Running "bane" with no arguments should print all the known behaviors and a usage message, but it fails in Ruby 1.8.6.
$ ruby --version
ruby 1.8.6 (2008-03-03 patchlevel 114) [x86_64-linux]
$ bane
Usage: bane port_number
All behaviors:
/usr/lib64/ruby/gems/1.8/gems/bane-0.1.0/bin/bane:10: wrong argument type Symbol (expected Proc) (TypeError)
from /usr/bin/bane:19:in `load'
from /usr/bin/bane:19
Bane currently defaults to listening to only connections from localhost (127.0.0.1). It would be helpful if Bane could also listen on all incoming connections, even from remote machines (0.0.0.0).
It's not clear to me if 0.0.0.0 should be the new default or enabled via a command-line option. Listening to all incoming connections by default may be a security concern, but I'm not entirely sure. While it means malicious parties could connect to Bane, the types of responses Bane sends are meant to be malicious themselves (e.g. never hanging up the connection). If there are security problems with the underlying TCP server (currently Ruby's GServer) then these will be publicly exposed.
One behavior suggested in Release It! is:
The request can sit in a listen queue until the caller times out
After reading Jesse Storimer's "Working with TCP Sockets" book, I believe this is possible to implement entirely in Ruby.
See the video: What happens with the listen backlog is full?
Also see the second man page for listen: man 2 listen
and note the backlog
parameter.
My proposed idea is to create a TCPServer on the given port, listen with a queue of one, and then have Bane immediately connect to that socket -- effectively filling the listen queue. There may be some other way to simulate this, but I'm not yet sure.
Note that I first thought we could pass in zero as the backlog size, but as far as I can tell, passing in zero does not necessarily mean "no backlog", but rather "implementation specific" behavior. See one example man page for listen.
I suspect this will require changes to BehaviorServer
, or the creation of some different types of BehaviorServers. Specifically, BehaviorServer currently is a GServer, but from what I see of the GServer source code, it supports a maxConnections
option. However, it looks to me like this does not actually specify a backlog size to listen(). Instead, it looks to me like GServer keeps track of the open connection count, and waits until the count is below the maxConnections option. I'm not exactly sure what this will do, but I suspect it will not actually use the backlog like we'd like.
To do this, I suspect this Behavior will need to specify startup options for the server - which means making the Behaviors more coupled to the server. That is, right now behaviors cannot control the socket-level options of a server, they are only notified when a connection has been established by the BehaviorServer. To implement the low-level TCP packet-munging I'd like for the other lower level behaviors (e.g. "The remote end can send nothing but RESET packets") this may be required anyway.
extensions.rb defines the method unqualified_name
on the Class object. In insolation, this is fine, however if Bane is used by other libraries (say, in integration tests) this method name might clash with another implementation of unqualified_name
. In particular, I'm concerned about ActiveSupport which is widely used.
To use a name which might be more familiar to ActiveSupport, we could define
class.demodulize_name
(which would be defined on Module)
This uses a name which is probably familiar to anyone who uses ActiveSupport, since it has a 'demodulize' method on String.
As a bonus, 'demodulize_name' is also unused in ActiveSupport -- and didn't turn up any Google search results when we looked today.
Bane does not work when installed on Windows machines.
Bane only ships with a "bin/bane" executable. For Windows, this needs a corresponding extension that the OS will recognize.
To do this, it seems like extracting the launcher code in bin/bane into a class would avoid duplicating the logic in the Windows and Unix/Mac launcher script.
Bane is currently using the Test::Unit version included with Ruby 1.8.7.
Let's upgrade either to a more modern version of Test::Unit or MiniTest.
The integration tests fail on Ruby 1.9.1 (as tested in Ubuntu) with a "Port already in use" error.
It appears that the first integration test passes and sends the stop request to the Bane servers. However the port is still in use by the time the second integration test attempts to start a new server on the same port. As a first attempt, putting in a "sleep 1" in setup fixes the problem, which makes me think there is a problem in how the threads are stopped in Bane::Launcher.
This behavior is not present in Ruby 1.8.6 or 1.8.7 on the same installation of Ubuntu (using rvm).
See the stack trace below for an example of the failure:
wellman@wellman-uvm:~/dev/ruby/bane$ ruby test/bane/integration_test.rb
Loaded suite test/bane/integration_test
Started
.E
Finished in 0.008375 seconds.
synchronize' /home/wellman/dev/ruby/bane/lib/bane/configuration.rb:25:in
start'block in start' /home/wellman/dev/ruby/bane/lib/bane/configuration.rb:10:in
map'start' /home/wellman/dev/ruby/bane/lib/bane/launcher.rb:12:in
start'run_server_with' test/bane/integration_test.rb:9:in
test_uses_specified_port_and_server'2 tests, 1 assertions, 0 failures, 1 errors, 0 skips
wellman@wellman-uvm:~/dev/ruby/bane$
The second and third integration tests occasionally fail due to attempting to start a server as the test port is already in use.
This appears to be happening because the Bane BehaviorServers (a.k.a. GServer) do not stop immediately, and new servers are being started on the same port. There is a race condition between stopping and starting the server.
Upon investigation, it appears that GServer's stop instance method does not stop the server immediately (it is a graceful shutdown, eventually stopping the server), whereas GServer's stop class method does.
Error:
test_uses_behavior_options(BaneIntegrationTest):
RuntimeError: Port already in use: 127.0.0.1:4000!
internal:prelude:10:in synchronize' /home/vagrant/builds/danielwellman/bane/lib/bane/launcher.rb:11:in
block in art'
/home/vagrant/builds/danielwellman/bane/lib/bane/launcher.rb:11:in each' /home/vagrant/builds/danielwellman/bane/lib/bane/launcher.rb:11:in
start'
/home/vagrant/builds/danielwellman/bane/test/bane/integration_test.rb:45:in un_server_with'
/home/vagrant/builds/danielwellman/bane/test/bane/integration_test.rb:21:in est_uses_behavior_options'
/home/vagrant/.rvm/gems/ruby-1.9.3-p194/gems/mocha-0.12.lib/mocha/monkey_patching/mini_test/version_230_to_2101.rb:28:in `run'
Error:
test_uses_specified_port_and_server(BaneIntegrationTest):
RuntimeError: Port already in use: 127.0.0.1:4000!
internal:prelude:10:in synchronize' /home/vagrant/builds/danielwellman/bane/lib/bane/launcher.rb:11:in
block in art'
/home/vagrant/builds/danielwellman/bane/lib/bane/launcher.rb:11:in each' /home/vagrant/builds/danielwellman/bane/lib/bane/launcher.rb:11:in
start'
/home/vagrant/builds/danielwellman/bane/test/bane/integration_test.rb:45:in un_server_with'
/home/vagrant/builds/danielwellman/bane/test/bane/integration_test.rb:9:in est_uses_specified_port_and_server'
/home/vagrant/.rvm/gems/ruby-1.9.3-p194/gems/mocha-0.12.2/lib/mocha/monkey_patching/mini_test/version_230_to_2101.rb:28:in `run'
With Ruby 1.8.7 now retired, shall we remove support for this version from Bane?
This would mean removing the compatibility.rb extensions which add 1.9-era methods to String and Symbol.
A unit test fails on certain versions of Ruby (1.8.7? 1.9.1?) when running the SlowResponse unit test. This behavior uses each_char, which does not work for all versions of Ruby.
See http://javazquez.com/juan/2008/06/25/handling-rubys-stringeach_char-iterator/
More here: https://github.com/grosser/wwtd?utm_source=rubyweekly&utm_medium=email.
This is a non-production enhancement.
In ruby 1.9.2 p0, the tests which use Telnet in BaneIntegrationTest fail due to a NoMethodError:
daniel-wellmans-macbook:bane wellman$ rvm 1.9.2
daniel-wellmans-macbook:bane wellman$ ruby test/bane/integration_test.rb
Loaded suite test/bane/integration_test
Started
.EE
Finished in 0.052258 seconds.
1) Error:
test_uses_behavior_options(BaneIntegrationTest):
NoMethodError: undefined method `close' for #<Net::Telnet:0x000001011d7a70>
test/bane/integration_test.rb:64:in `ensure in telnet_to'
test/bane/integration_test.rb:64:in `telnet_to'
test/bane/integration_test.rb:23:in `block in test_uses_behavior_options'
test/bane/integration_test.rb:47:in `run_server_with'
test/bane/integration_test.rb:22:in `test_uses_behavior_options'
2) Error:
test_uses_specified_port_and_server(BaneIntegrationTest):
RuntimeError: Port already in use: 127.0.0.1:4000!
<internal:prelude>:10:in `synchronize'
/Users/wellman/dev/ruby/bane/lib/bane/launcher.rb:13:in `block in start'
/Users/wellman/dev/ruby/bane/lib/bane/launcher.rb:11:in `each'
/Users/wellman/dev/ruby/bane/lib/bane/launcher.rb:11:in `start'
test/bane/integration_test.rb:46:in `run_server_with'
test/bane/integration_test.rb:10:in `test_uses_specified_port_and_server'
3 tests, 1 assertions, 0 failures, 2 errors, 0 skips
When running bane on the command line using Ruby 2.0, sending a SIGINT causes an exception to be thrown:
joe@warpaint:/dev/bane$ ruby -v/dev/bane$ ./bin/bane 8080
ruby 2.0.0dev (2012-12-01 trunk 38126) [i686-linux]
joe@warpaint:
[Sun Dec 30 12:43:11 2012] CloseImmediately 127.0.0.1:8080 start
[Sun Dec 30 12:43:11 2012] CloseAfterPause 127.0.0.1:8081 start
[Sun Dec 30 12:43:11 2012] FixedResponse 127.0.0.1:8082 start[Sun Dec 30 12:43:11 2012] NewlineResponse 127.0.0.1:8084 start[Sun Dec 30 12:43:11 2012] FixedResponseForEachLine 127.0.0.1:8083 start
[Sun Dec 30 12:43:11 2012] NewlineResponseForEachLine 127.0.0.1:8085 start
[Sun Dec 30 12:43:11 2012] SlowResponseForEachLine 127.0.0.1:8089 start
[Sun Dec 30 12:43:11 2012] RandomResponseForEachLine 127.0.0.1:8087 start
[Sun Dec 30 12:43:11 2012] SlowResponse 127.0.0.1:8088 start
[Sun Dec 30 12:43:11 2012] RandomResponse 127.0.0.1:8086 start
[Sun Dec 30 12:43:11 2012] NeverRespond 127.0.0.1:8090 start
[Sun Dec 30 12:43:11 2012] DelugeResponse 127.0.0.1:8091 start
[Sun Dec 30 12:43:11 2012] DelugeResponseForEachLine 127.0.0.1:8092 start
[Sun Dec 30 12:43:11 2012] HttpRefuseAllCredentials 127.0.0.1:8093 start
^C[Sun Dec 30 12:43:12 2012] CloseImmediately 127.0.0.1:8080 stop
[Sun Dec 30 12:43:12 2012] CloseAfterPause 127.0.0.1:8081 stop
[Sun Dec 30 12:43:12 2012] HttpRefuseAllCredentials 127.0.0.1:8093 stop[Sun Dec 30 12:43:12 2012] DelugeResponse 127.0.0.1:8091 stop[Sun Dec 30 12:43:12 2012] SlowResponseForEachLine 127.0.0.1:8089 stop[Sun Dec 30 12:43:12 2012] SlowResponse 127.0.0.1:8088 stop[Sun Dec 30 12:43:12 2012] NewlineResponseForEachLine 127.0.0.1:8085 stop[Sun Dec 30 12:43:12 2012] FixedResponseForEachLine 127.0.0.1:8083 stop[Sun Dec 30 12:43:12 2012] FixedResponse 127.0.0.1:8082 stop[Sun Dec 30 12:43:12 2012] NewlineResponse 127.0.0.1:8084 stop[Sun Dec 30 12:43:12 2012] NeverRespond 127.0.0.1:8090 stop
[Sun Dec 30 12:43:12 2012] RandomResponse 127.0.0.1:8086 stop
[Sun Dec 30 12:43:12 2012] DelugeResponseForEachLine 127.0.0.1:8092 stop
[Sun Dec 30 12:43:12 2012] RandomResponseForEachLine 127.0.0.1:8087 stop
/home/joe/.rvm/rubies/ruby-2.0.0-preview2/lib/ruby/2.0.0/gserver.rb:116:in synchronize': can't be called from trap context (ThreadError) from /home/joe/.rvm/rubies/ruby-2.0.0-preview2/lib/ruby/2.0.0/gserver.rb:116:in
stop'
from /home/joe/dev/bane/lib/bane/launcher.rb:20:in block in stop' from /home/joe/dev/bane/lib/bane/launcher.rb:19:in
each'
from /home/joe/dev/bane/lib/bane/launcher.rb:19:in stop' from ./bin/bane:22:in
block in
call' from /home/joe/.rvm/rubies/ruby-2.0.0-preview2/lib/ruby/2.0.0/gserver.rb:140:in
join'join' from /home/joe/dev/bane/lib/bane/launcher.rb:15:in
block in join'each' from /home/joe/dev/bane/lib/bane/launcher.rb:15:in
join'Further investigation revealed the following discussion:
https://bugs.ruby-lang.org/issues/6416#change-33951
and subsequent patch:
https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/37852
If I'm reading this correctly, this patch was submitted to reduce the chances of deadlocking a system upon SIGINT.
I'd like to submit a patch to this after a little more research. Simply changing a line in the bane executable as follows:
trap("SIGINT") { exit! }
will keep the exception from being thrown but it's unclear if that's sufficient to clean up the threads. Also, it would be good to get a test around this behavior (currently all tests pass under Ruby 2.0).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.