raptor-io 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE +30 -0
  3. data/README.md +51 -0
  4. data/lib/rack/handler/raptor-io.rb +130 -0
  5. data/lib/raptor-io.rb +11 -0
  6. data/lib/raptor-io/error.rb +19 -0
  7. data/lib/raptor-io/protocol.rb +6 -0
  8. data/lib/raptor-io/protocol/error.rb +10 -0
  9. data/lib/raptor-io/protocol/http.rb +34 -0
  10. data/lib/raptor-io/protocol/http/client.rb +685 -0
  11. data/lib/raptor-io/protocol/http/error.rb +16 -0
  12. data/lib/raptor-io/protocol/http/headers.rb +132 -0
  13. data/lib/raptor-io/protocol/http/message.rb +67 -0
  14. data/lib/raptor-io/protocol/http/request.rb +307 -0
  15. data/lib/raptor-io/protocol/http/request/manipulator.rb +117 -0
  16. data/lib/raptor-io/protocol/http/request/manipulators.rb +217 -0
  17. data/lib/raptor-io/protocol/http/request/manipulators/authenticator.rb +110 -0
  18. data/lib/raptor-io/protocol/http/request/manipulators/authenticators/basic.rb +36 -0
  19. data/lib/raptor-io/protocol/http/request/manipulators/authenticators/digest.rb +135 -0
  20. data/lib/raptor-io/protocol/http/request/manipulators/authenticators/negotiate.rb +69 -0
  21. data/lib/raptor-io/protocol/http/request/manipulators/authenticators/ntlm.rb +29 -0
  22. data/lib/raptor-io/protocol/http/request/manipulators/redirect_follower.rb +65 -0
  23. data/lib/raptor-io/protocol/http/response.rb +166 -0
  24. data/lib/raptor-io/protocol/http/server.rb +446 -0
  25. data/lib/raptor-io/ruby.rb +4 -0
  26. data/lib/raptor-io/ruby/hash.rb +24 -0
  27. data/lib/raptor-io/ruby/ipaddr.rb +15 -0
  28. data/lib/raptor-io/ruby/openssl.rb +23 -0
  29. data/lib/raptor-io/ruby/string.rb +27 -0
  30. data/lib/raptor-io/socket.rb +175 -0
  31. data/lib/raptor-io/socket/comm.rb +143 -0
  32. data/lib/raptor-io/socket/comm/local.rb +94 -0
  33. data/lib/raptor-io/socket/comm/sapni.rb +75 -0
  34. data/lib/raptor-io/socket/comm/socks.rb +237 -0
  35. data/lib/raptor-io/socket/comm_chain.rb +30 -0
  36. data/lib/raptor-io/socket/error.rb +45 -0
  37. data/lib/raptor-io/socket/switch_board.rb +183 -0
  38. data/lib/raptor-io/socket/switch_board/route.rb +42 -0
  39. data/lib/raptor-io/socket/tcp.rb +231 -0
  40. data/lib/raptor-io/socket/tcp/ssl.rb +77 -0
  41. data/lib/raptor-io/socket/tcp_server.rb +16 -0
  42. data/lib/raptor-io/socket/tcp_server/ssl.rb +52 -0
  43. data/lib/raptor-io/socket/udp.rb +0 -0
  44. data/lib/raptor-io/version.rb +6 -0
  45. data/lib/tasks/yard.rake +26 -0
  46. data/spec/rack/handler/raptor_spec.rb +140 -0
  47. data/spec/raptor-io/protocol/http/client_spec.rb +671 -0
  48. data/spec/raptor-io/protocol/http/headers_spec.rb +189 -0
  49. data/spec/raptor-io/protocol/http/message_spec.rb +5 -0
  50. data/spec/raptor-io/protocol/http/request/manipulators/authenticator_spec.rb +193 -0
  51. data/spec/raptor-io/protocol/http/request/manipulators/authenticators/basic_spec.rb +32 -0
  52. data/spec/raptor-io/protocol/http/request/manipulators/authenticators/digest_spec.rb +76 -0
  53. data/spec/raptor-io/protocol/http/request/manipulators/authenticators/negotiate_spec.rb +52 -0
  54. data/spec/raptor-io/protocol/http/request/manipulators/authenticators/ntlm_spec.rb +37 -0
  55. data/spec/raptor-io/protocol/http/request/manipulators/redirect_follower_spec.rb +51 -0
  56. data/spec/raptor-io/protocol/http/request/manipulators_spec.rb +202 -0
  57. data/spec/raptor-io/protocol/http/request_spec.rb +965 -0
  58. data/spec/raptor-io/protocol/http/response_spec.rb +236 -0
  59. data/spec/raptor-io/protocol/http/server_spec.rb +345 -0
  60. data/spec/raptor-io/ruby/hash_spec.rb +20 -0
  61. data/spec/raptor-io/ruby/string_spec.rb +20 -0
  62. data/spec/raptor-io/socket/comm/local_spec.rb +50 -0
  63. data/spec/raptor-io/socket/switch_board/route_spec.rb +49 -0
  64. data/spec/raptor-io/socket/switch_board_spec.rb +87 -0
  65. data/spec/raptor-io/socket/tcp/ssl_spec.rb +18 -0
  66. data/spec/raptor-io/socket/tcp_server/ssl_spec.rb +59 -0
  67. data/spec/raptor-io/socket/tcp_server_spec.rb +19 -0
  68. data/spec/raptor-io/socket/tcp_spec.rb +14 -0
  69. data/spec/raptor-io/socket_spec.rb +16 -0
  70. data/spec/raptor-io/version_spec.rb +10 -0
  71. data/spec/spec_helper.rb +56 -0
  72. data/spec/support/fixtures/raptor/protocol/http/request/manipulators/manifoolators/fooer.rb +25 -0
  73. data/spec/support/fixtures/raptor/protocol/http/request/manipulators/niccolo_machiavelli.rb +20 -0
  74. data/spec/support/fixtures/raptor/protocol/http/request/manipulators/options_validator.rb +28 -0
  75. data/spec/support/fixtures/raptor/socket/ssl_server.crt +18 -0
  76. data/spec/support/fixtures/raptor/socket/ssl_server.key +15 -0
  77. data/spec/support/lib/path_helpers.rb +11 -0
  78. data/spec/support/lib/webserver_option_parser.rb +26 -0
  79. data/spec/support/lib/webservers.rb +120 -0
  80. data/spec/support/shared/contexts/with_ssl_server.rb +70 -0
  81. data/spec/support/shared/contexts/with_tcp_server.rb +58 -0
  82. data/spec/support/shared/examples/raptor/comm_examples.rb +26 -0
  83. data/spec/support/shared/examples/raptor/protocols/http/message.rb +106 -0
  84. data/spec/support/shared/examples/raptor/socket_examples.rb +135 -0
  85. data/spec/support/webservers/raptor/protocols/http/client.rb +100 -0
  86. data/spec/support/webservers/raptor/protocols/http/client_close_connection.rb +29 -0
  87. data/spec/support/webservers/raptor/protocols/http/client_https.rb +43 -0
  88. data/spec/support/webservers/raptor/protocols/http/request/manipulators/authenticators/basic.rb +9 -0
  89. data/spec/support/webservers/raptor/protocols/http/request/manipulators/authenticators/digest.rb +22 -0
  90. data/spec/support/webservers/raptor/protocols/http/request/manipulators/redirect_follower.rb +11 -0
  91. metadata +336 -0
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hash do
4
+ describe '#stringify' do
5
+ it 'returns a Hash with keys and values recursively converted to strings' do
6
+ {
7
+ test: 'blah',
8
+ another_hash: {
9
+ stuff: 'test'
10
+ }
11
+ }.stringify.should == {
12
+ 'test' => 'blah',
13
+ 'another_hash' => {
14
+ 'stuff' => 'test'
15
+ }
16
+ }
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe String do
4
+
5
+ describe '#binary?' do
6
+ context 'when the content is' do
7
+ context 'binary' do
8
+ it 'returns true' do
9
+ "\ff\ff\ff".binary?.should be_true
10
+ end
11
+ end
12
+ context 'text' do
13
+ it 'returns false' do
14
+ 'test'.binary?.should be_false
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ require 'raptor-io/socket'
4
+
5
+ describe RaptorIO::Socket::Comm::Local do
6
+ subject(:comm_local) { described_class.new }
7
+
8
+ context "with a server to connect to" do
9
+ let(:tcp_port) { 9999 }
10
+ let(:udp_port) { 9090 }
11
+ before do
12
+ @tcp_server = ::TCPServer.new(tcp_port)
13
+ end
14
+ after do
15
+ @tcp_server.close
16
+ end
17
+
18
+ subject(:comm) { described_class.new }
19
+ it_behaves_like "a comm"
20
+ end
21
+
22
+ describe '#resolve' do
23
+ it 'should resolve a hostname to an IP address' do
24
+ # OSX uses a link-local address on the loopback interface
25
+ # (fe80::1%lo0) to be 'localhost'
26
+ ['127.0.0.1', '::1', 'fe80::1%lo0'].should include(comm_local.resolve('localhost'))
27
+ end
28
+ end
29
+
30
+ describe "#support_ipv6?" do
31
+ # These are ghetto tests. It assumes the implementation for
32
+ # determining support.
33
+ it "should be true if we can create ipv6 sockets" do
34
+ socket_inst = double("sock")
35
+ socket_inst.stub(:close)
36
+
37
+ ::Socket.should_receive(:new).with(::Socket::AF_INET6, ::Socket::SOCK_DGRAM, ::Socket::IPPROTO_UDP).and_return(socket_inst)
38
+ comm_local.support_ipv6?.should be_true
39
+ end
40
+ it "should be false if ::Socket::AF_INET6 is not defined" do
41
+ ::Socket.should_receive(:const_defined?).and_return(false)
42
+ comm_local.support_ipv6?.should be_false
43
+ end
44
+ it "should be false if we cannot create ipv6 sockets" do
45
+ ::Socket.should_receive(:new).and_raise(RuntimeError)
46
+ comm_local.support_ipv6?.should be_false
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ require 'raptor-io/socket'
4
+
5
+ describe RaptorIO::Socket::SwitchBoard::Route do
6
+
7
+ context "class methods" do
8
+ describe ".new" do
9
+ it "should accept an IPAddr for subnet and netmask" do
10
+ expect {
11
+ described_class.new(IPAddr.new("1.2.3.4"), IPAddr.new("255.255.0.0"), nil)
12
+ }.not_to raise_error
13
+ end
14
+ it "should accept a String for subnet and netmask" do
15
+ expect {
16
+ described_class.new("1.2.3.4", "255.255.0.0", nil)
17
+ }.not_to raise_error
18
+ end
19
+ end
20
+ end
21
+
22
+ context "instance methods" do
23
+ subject(:route) do
24
+ RaptorIO::Socket::SwitchBoard::Route.new("1.2.3.4", "255.255.255.0", nil)
25
+ end
26
+
27
+ describe "#==" do
28
+ it "should be equal if attributes are the same" do
29
+ RaptorIO::Socket::SwitchBoard::Route.new("1.2.3.4", "255.255.255.0", nil).should == route
30
+ end
31
+ it "should NOT be equal if any attributes are different" do
32
+ RaptorIO::Socket::SwitchBoard::Route.new("1.2.3.4", "255.0.0.0", nil).should_not == route
33
+ RaptorIO::Socket::SwitchBoard::Route.new("2.2.3.4", "255.255.255.0", nil).should_not == route
34
+ end
35
+ end
36
+
37
+ describe "#<=>" do
38
+ it "should compare the subnet" do
39
+ other = RaptorIO::Socket::SwitchBoard::Route.new("0.0.0.0", "255.255.0.0", nil)
40
+ (route.<=> other).should == 1
41
+ other = RaptorIO::Socket::SwitchBoard::Route.new("0.0.0.0", "255.255.255.0", nil)
42
+ (route.<=> other).should == 0
43
+ other = RaptorIO::Socket::SwitchBoard::Route.new("0.0.0.0", "255.255.255.255", nil)
44
+ (route.<=> other).should == -1
45
+ end
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ require 'raptor-io/socket'
4
+
5
+ describe RaptorIO::Socket::SwitchBoard do
6
+ subject(:switch_board) do
7
+ described_class.new
8
+ end
9
+
10
+ it { should be_an Enumerable }
11
+
12
+ describe "#add_route" do
13
+ it "should add a route" do
14
+ switch_board.routes.should be_empty
15
+ switch_board.add_route("1.2.3.4", "255.255.255.0", nil)
16
+ switch_board.routes.length.should == 1
17
+ end
18
+ it "should accept IPAddrs" do
19
+ switch_board.routes.should be_empty
20
+ switch_board.add_route(IPAddr.new("1.2.3.4"), IPAddr.new("255.255.255.0"), nil)
21
+ switch_board.routes.length.should == 1
22
+ end
23
+ end
24
+
25
+ describe "#each" do
26
+ subject(:switch_board) do
27
+ sb = described_class.new
28
+ 4.times do |i|
29
+ sb.add_route("#{i}.2.3.4", "255.255.255.0", nil)
30
+ end
31
+ sb
32
+ end
33
+
34
+ it "should return all routes" do
35
+ routes = []
36
+ switch_board.each do |r|
37
+ routes << r
38
+ end
39
+ routes.length.should == 4
40
+ routes.should == switch_board.routes
41
+ end
42
+ end
43
+
44
+ describe "#flush_routes" do
45
+ it "should empty the routes" do
46
+ switch_board.routes.should be_empty
47
+ switch_board.add_route("1.1.1.1", "255.255.255.0", nil)
48
+ switch_board.add_route("2.2.2.2", "255.255.255.0", nil)
49
+ switch_board.routes.should_not be_empty
50
+ switch_board.flush_routes
51
+ switch_board.routes.should be_empty
52
+ end
53
+ end
54
+
55
+ describe "#remove_route" do
56
+
57
+ end
58
+
59
+ describe "#best_comm" do
60
+ it "should return the comm for the most specific route" do
61
+ comm0 = double("more specific")
62
+ comm1 = double("less specific")
63
+ comm2 = double("even less specific")
64
+ comm3 = double("different net")
65
+
66
+ switch_board.add_route("1.1.1.0", "255.255.255.0", comm0)
67
+ switch_board.add_route("1.1.0.0", "255.255.0.0", comm1)
68
+ switch_board.add_route("1.0.0.0", "255.0.0.0", comm2)
69
+ switch_board.add_route("2.2.2.0", "255.255.255.255", comm3)
70
+
71
+ switch_board.best_comm("1.1.1.1").should == comm0
72
+ end
73
+
74
+ it "should fall back to less specific routes" do
75
+ comm0 = double("more specific")
76
+ comm1 = double("less specific")
77
+ comm2 = double("different net")
78
+
79
+ switch_board.add_route("1.1.1.0", "255.255.255.0", comm0)
80
+ switch_board.add_route("1.1.0.0", "255.255.0.0", comm1)
81
+
82
+ switch_board.best_comm("1.1.2.1").should == comm1
83
+
84
+ end
85
+ end
86
+
87
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ require 'raptor-io/socket'
3
+
4
+ describe RaptorIO::Socket::TCP::SSL do
5
+ let(:opts) do
6
+ {
7
+ ssl_version: :TLSv1,
8
+ ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE
9
+ }
10
+ end
11
+
12
+ #include_context 'with ssl server'
13
+
14
+ #it_behaves_like "a client socket"
15
+
16
+ end
17
+
18
+
@@ -0,0 +1,59 @@
1
+ =begin
2
+ require 'spec_helper'
3
+ require 'raptor-io/socket'
4
+
5
+ describe RaptorIO::Socket::TCPServer::SSL do
6
+ include_context 'with tcp server'
7
+
8
+ let(:server_cert) { File.read(File.join(fixtures_path, 'raptor', 'socket', 'ssl_server.crt')) }
9
+ let(:server_key) { File.read(File.join(fixtures_path, 'raptor', 'socket', 'ssl_server.key')) }
10
+ let(:server_context) do
11
+ OpenSSL::SSL::SSLContext.new.tap do |context|
12
+ context.cert = OpenSSL::X509::Certificate.new(server_cert)
13
+ context.key = OpenSSL::PKey::RSA.new(server_key)
14
+ end
15
+ end
16
+
17
+ subject(:ssl_server) do
18
+ described_class.new(server_sock, context: server_context )
19
+ end
20
+
21
+ let(:data) { 'test'.force_encoding( 'binary' ) }
22
+
23
+ describe '#accept_nonblock' do
24
+ it 'returns a connected peer as a RaptorIO::Socket::TCP::SSL socket' do
25
+ ssl_server
26
+ ssl_client = OpenSSL::SSL::SSLSocket.new(client_sock)
27
+
28
+ Thread.new do
29
+ Thread.pass
30
+ #$stderr.puts "ssl_client connecting"
31
+ ssl_client.connect
32
+ #$stderr.puts "ssl_client CONNECTED"
33
+ end
34
+
35
+ ssl_peer = nil
36
+
37
+ begin
38
+ #$stderr.puts "ssl_server accepting"
39
+ ssl_peer = ssl_server.accept_nonblock
40
+ #$stderr.puts "ssl_server ACCEPTED"
41
+ rescue IO::WaitReadable
42
+ #$stderr.puts "ssl_server waiting for a client"
43
+ RaptorIO::Socket.select([ssl_server])
44
+ retry
45
+ end
46
+
47
+ #$stderr.puts("ssl_server accepted peer #{ssl_peer}")
48
+ ssl_peer.should be_kind_of RaptorIO::Socket::TCP::SSL
49
+
50
+ select( [], [ssl_peer] )
51
+ ssl_peer.write data
52
+
53
+ select( [ssl_client] )
54
+ ssl_client.read(data.size).should == data
55
+
56
+ end
57
+ end
58
+ end
59
+ =end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'raptor-io/socket'
3
+
4
+ describe RaptorIO::Socket::TCPServer do
5
+ subject { described_class.new(io, opts) }
6
+ let(:opts) { {} }
7
+ let(:io) {
8
+ sio = StringIO.new
9
+ # Bind, listen, and connect all return 0 unless an exception is
10
+ # raised
11
+ sio.stub(:bind).and_return(0)
12
+ sio.stub(:listen).and_return(0)
13
+ sio.stub(:accept).and_return { StringIO.new }
14
+
15
+ sio
16
+ }
17
+
18
+ it_behaves_like 'a server socket'
19
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require 'raptor-io/socket'
3
+
4
+ describe RaptorIO::Socket::TCP do
5
+
6
+ subject { described_class.new(io, opts) }
7
+
8
+ let(:io) { StringIO.new }
9
+ let(:opts) { {} }
10
+
11
+ it_behaves_like 'a client socket'
12
+
13
+ it { should respond_to(:to_ssl) }
14
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ require 'raptor-io/socket'
4
+
5
+ describe RaptorIO::Socket do
6
+ subject { described_class.new(io) }
7
+ let(:io) { StringIO.new }
8
+
9
+ it "should not swallow errors in method_missing" do
10
+ subject.should_not respond_to(:asdfjkl)
11
+ expect {
12
+ subject.asdfjkl
13
+ }.to raise_error(NoMethodError)
14
+ end
15
+ end
16
+
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe "RaptorIO::VERSION" do
4
+ it 'holds the version number of the library' do
5
+ splits = RaptorIO::VERSION.to_s.split( '.' )
6
+
7
+ splits.should be_any
8
+ splits.each { |number| number.should match /\d+/ }
9
+ end
10
+ end
@@ -0,0 +1,56 @@
1
+
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+
5
+ require 'raptor-io'
6
+
7
+ # This file was generated by the `rspec --init` command. Conventionally, all
8
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
9
+ # Require this file using `require "spec_helper"` to ensure that it is only
10
+ # loaded once.
11
+ #
12
+
13
+ # Requires supporting ruby files with custom matchers and macros, etc,
14
+ # in spec/support/ and its subdirectories.
15
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each do |f|
16
+ next if f.include? '/webservers/'
17
+ require f
18
+ end
19
+
20
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
21
+ RSpec.configure do |config|
22
+ config.treat_symbols_as_metadata_keys_with_true_values = true
23
+ config.run_all_when_everything_filtered = true
24
+ config.filter_run :focus
25
+
26
+ # Run specs in random order to surface order dependencies. If you find an
27
+ # order dependency and want to debug it, you can fix the order by providing
28
+ # the seed, which is printed after each run.
29
+ # --seed 1234
30
+ config.order = 'random'
31
+
32
+ config.before( :all ) do
33
+ RaptorIO::Protocol::HTTP::Client.reset
34
+ RaptorIO::Protocol::HTTP::Request::Manipulators.reset
35
+ WebServers.killall
36
+ end
37
+
38
+ config.after( :all ) do
39
+ RaptorIO::Protocol::HTTP::Client.reset
40
+ RaptorIO::Protocol::HTTP::Request::Manipulators.reset
41
+ end
42
+
43
+ config.after( :suite ) do
44
+ RaptorIO::Protocol::HTTP::Client.reset
45
+ RaptorIO::Protocol::HTTP::Request::Manipulators.reset
46
+ WebServers.killall
47
+ end
48
+
49
+ end
50
+
51
+ EXAMPLE_PORT = 12345
52
+
53
+ def example_addr; '127.0.0.1'; end
54
+ def example_port; EXAMPLE_PORT; end
55
+ def example_ssl_port; EXAMPLE_PORT + 1; end
56
+
@@ -0,0 +1,25 @@
1
+ module RaptorIO
2
+ module Protocol::HTTP
3
+ class Request
4
+
5
+ module Manipulators
6
+ module Manifoolators
7
+
8
+ #
9
+ # Test manipulator, shows that manipulators can be namespaced.
10
+ #
11
+ # @author Tasos Laskos <tasos_laskos@rapid7.com>
12
+ #
13
+ class Fooer < Manipulator
14
+
15
+ def run
16
+ request.url += ('foo' * options[:times])
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end