raptor-io 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE +30 -0
- data/README.md +51 -0
- data/lib/rack/handler/raptor-io.rb +130 -0
- data/lib/raptor-io.rb +11 -0
- data/lib/raptor-io/error.rb +19 -0
- data/lib/raptor-io/protocol.rb +6 -0
- data/lib/raptor-io/protocol/error.rb +10 -0
- data/lib/raptor-io/protocol/http.rb +34 -0
- data/lib/raptor-io/protocol/http/client.rb +685 -0
- data/lib/raptor-io/protocol/http/error.rb +16 -0
- data/lib/raptor-io/protocol/http/headers.rb +132 -0
- data/lib/raptor-io/protocol/http/message.rb +67 -0
- data/lib/raptor-io/protocol/http/request.rb +307 -0
- data/lib/raptor-io/protocol/http/request/manipulator.rb +117 -0
- data/lib/raptor-io/protocol/http/request/manipulators.rb +217 -0
- data/lib/raptor-io/protocol/http/request/manipulators/authenticator.rb +110 -0
- data/lib/raptor-io/protocol/http/request/manipulators/authenticators/basic.rb +36 -0
- data/lib/raptor-io/protocol/http/request/manipulators/authenticators/digest.rb +135 -0
- data/lib/raptor-io/protocol/http/request/manipulators/authenticators/negotiate.rb +69 -0
- data/lib/raptor-io/protocol/http/request/manipulators/authenticators/ntlm.rb +29 -0
- data/lib/raptor-io/protocol/http/request/manipulators/redirect_follower.rb +65 -0
- data/lib/raptor-io/protocol/http/response.rb +166 -0
- data/lib/raptor-io/protocol/http/server.rb +446 -0
- data/lib/raptor-io/ruby.rb +4 -0
- data/lib/raptor-io/ruby/hash.rb +24 -0
- data/lib/raptor-io/ruby/ipaddr.rb +15 -0
- data/lib/raptor-io/ruby/openssl.rb +23 -0
- data/lib/raptor-io/ruby/string.rb +27 -0
- data/lib/raptor-io/socket.rb +175 -0
- data/lib/raptor-io/socket/comm.rb +143 -0
- data/lib/raptor-io/socket/comm/local.rb +94 -0
- data/lib/raptor-io/socket/comm/sapni.rb +75 -0
- data/lib/raptor-io/socket/comm/socks.rb +237 -0
- data/lib/raptor-io/socket/comm_chain.rb +30 -0
- data/lib/raptor-io/socket/error.rb +45 -0
- data/lib/raptor-io/socket/switch_board.rb +183 -0
- data/lib/raptor-io/socket/switch_board/route.rb +42 -0
- data/lib/raptor-io/socket/tcp.rb +231 -0
- data/lib/raptor-io/socket/tcp/ssl.rb +77 -0
- data/lib/raptor-io/socket/tcp_server.rb +16 -0
- data/lib/raptor-io/socket/tcp_server/ssl.rb +52 -0
- data/lib/raptor-io/socket/udp.rb +0 -0
- data/lib/raptor-io/version.rb +6 -0
- data/lib/tasks/yard.rake +26 -0
- data/spec/rack/handler/raptor_spec.rb +140 -0
- data/spec/raptor-io/protocol/http/client_spec.rb +671 -0
- data/spec/raptor-io/protocol/http/headers_spec.rb +189 -0
- data/spec/raptor-io/protocol/http/message_spec.rb +5 -0
- data/spec/raptor-io/protocol/http/request/manipulators/authenticator_spec.rb +193 -0
- data/spec/raptor-io/protocol/http/request/manipulators/authenticators/basic_spec.rb +32 -0
- data/spec/raptor-io/protocol/http/request/manipulators/authenticators/digest_spec.rb +76 -0
- data/spec/raptor-io/protocol/http/request/manipulators/authenticators/negotiate_spec.rb +52 -0
- data/spec/raptor-io/protocol/http/request/manipulators/authenticators/ntlm_spec.rb +37 -0
- data/spec/raptor-io/protocol/http/request/manipulators/redirect_follower_spec.rb +51 -0
- data/spec/raptor-io/protocol/http/request/manipulators_spec.rb +202 -0
- data/spec/raptor-io/protocol/http/request_spec.rb +965 -0
- data/spec/raptor-io/protocol/http/response_spec.rb +236 -0
- data/spec/raptor-io/protocol/http/server_spec.rb +345 -0
- data/spec/raptor-io/ruby/hash_spec.rb +20 -0
- data/spec/raptor-io/ruby/string_spec.rb +20 -0
- data/spec/raptor-io/socket/comm/local_spec.rb +50 -0
- data/spec/raptor-io/socket/switch_board/route_spec.rb +49 -0
- data/spec/raptor-io/socket/switch_board_spec.rb +87 -0
- data/spec/raptor-io/socket/tcp/ssl_spec.rb +18 -0
- data/spec/raptor-io/socket/tcp_server/ssl_spec.rb +59 -0
- data/spec/raptor-io/socket/tcp_server_spec.rb +19 -0
- data/spec/raptor-io/socket/tcp_spec.rb +14 -0
- data/spec/raptor-io/socket_spec.rb +16 -0
- data/spec/raptor-io/version_spec.rb +10 -0
- data/spec/spec_helper.rb +56 -0
- data/spec/support/fixtures/raptor/protocol/http/request/manipulators/manifoolators/fooer.rb +25 -0
- data/spec/support/fixtures/raptor/protocol/http/request/manipulators/niccolo_machiavelli.rb +20 -0
- data/spec/support/fixtures/raptor/protocol/http/request/manipulators/options_validator.rb +28 -0
- data/spec/support/fixtures/raptor/socket/ssl_server.crt +18 -0
- data/spec/support/fixtures/raptor/socket/ssl_server.key +15 -0
- data/spec/support/lib/path_helpers.rb +11 -0
- data/spec/support/lib/webserver_option_parser.rb +26 -0
- data/spec/support/lib/webservers.rb +120 -0
- data/spec/support/shared/contexts/with_ssl_server.rb +70 -0
- data/spec/support/shared/contexts/with_tcp_server.rb +58 -0
- data/spec/support/shared/examples/raptor/comm_examples.rb +26 -0
- data/spec/support/shared/examples/raptor/protocols/http/message.rb +106 -0
- data/spec/support/shared/examples/raptor/socket_examples.rb +135 -0
- data/spec/support/webservers/raptor/protocols/http/client.rb +100 -0
- data/spec/support/webservers/raptor/protocols/http/client_close_connection.rb +29 -0
- data/spec/support/webservers/raptor/protocols/http/client_https.rb +43 -0
- data/spec/support/webservers/raptor/protocols/http/request/manipulators/authenticators/basic.rb +9 -0
- data/spec/support/webservers/raptor/protocols/http/request/manipulators/authenticators/digest.rb +22 -0
- data/spec/support/webservers/raptor/protocols/http/request/manipulators/redirect_follower.rb +11 -0
- metadata +336 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module RaptorIO
|
2
|
+
module Protocol::HTTP
|
3
|
+
class Request
|
4
|
+
|
5
|
+
#
|
6
|
+
# Test manipulator.
|
7
|
+
#
|
8
|
+
# @author Tasos Laskos <tasos_laskos@rapid7.com>
|
9
|
+
#
|
10
|
+
class Manipulators::NiccoloMachiavelli < Manipulator
|
11
|
+
|
12
|
+
def run
|
13
|
+
[client, request, options, datastore]
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RaptorIO
|
2
|
+
module Protocol::HTTP
|
3
|
+
class Request
|
4
|
+
|
5
|
+
#
|
6
|
+
# Test manipulator.
|
7
|
+
#
|
8
|
+
# @author Tasos Laskos <tasos_laskos@rapid7.com>
|
9
|
+
#
|
10
|
+
class Manipulators::OptionsValidator < Manipulator
|
11
|
+
|
12
|
+
validate_options do |options, client, request|
|
13
|
+
errors = {}
|
14
|
+
next errors if options[:mandatory_string].is_a? String
|
15
|
+
|
16
|
+
errors[:mandatory_string] = 'Must be string.'
|
17
|
+
errors
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
options[:mandatory_string] * 10
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIC6TCCAlKgAwIBAgIEIRYEajANBgkqhkiG9w0BAQUFADBRMQswCQYDVQQGEwJV
|
3
|
+
UzELMAkGA1UECAwCVFgxDzANBgNVBAcMBkF1c3RpbjEPMA0GA1UECgwGUmFwaWQ3
|
4
|
+
MRMwEQYDVQQDDApyc3BlYy1ob3N0MB4XDTEzMDgxMDAyMjI1MVoXDTEzMDkwOTEy
|
5
|
+
MjI1MVowUTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlRYMQ8wDQYDVQQHDAZBdXN0
|
6
|
+
aW4xDzANBgNVBAoMBlJhcGlkNzETMBEGA1UEAwwKcnNwZWMtaG9zdDCBnzANBgkq
|
7
|
+
hkiG9w0BAQEFAAOBjQAwgYkCgYEAz5QxP0R5i0curO/f0Fgn8mZ/1ygJUUbrgsVp
|
8
|
+
OOU1CarECbZmOU9iEhR/QB2IknOsm0QfVlkCH/gusOZWbQdIaqmrD5mnrgKLHS4N
|
9
|
+
w/hvzg4tgTLtuJvlGq2zFXh9FKoTSV8IiXevc0i9DmbZ0Cx2uSjrx+DYdDTDdq9j
|
10
|
+
1DVuKrECAwEAAaOBzTCByjAJBgNVHRMEAjAAMB0GA1UdDgQWBBQ1Um68pnvPrkGt
|
11
|
+
lJsAT+drEdu7CzATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBLAwfAYD
|
12
|
+
VR0jBHUwc4AUNVJuvKZ7z65BrZSbAE/naxHbuwuhVaRTMFExCzAJBgNVBAYTAlVT
|
13
|
+
MQswCQYDVQQIDAJUWDEPMA0GA1UEBwwGQXVzdGluMQ8wDQYDVQQKDAZSYXBpZDcx
|
14
|
+
EzARBgNVBAMMCnJzcGVjLWhvc3SCBCEWBGowDQYJKoZIhvcNAQEFBQADgYEAw98n
|
15
|
+
wL0Z/FIDflLJyVdFnohOqDadLTr3ms44u2ahSCf1dmXn0fd5784lz8tl44w/HeIO
|
16
|
+
xCJvr33i+9+RS9bzkaogq73mw5spc/ZvBK1ivs6/rw+bH7MURr2htdO4gYsYLUVS
|
17
|
+
baOu/+8LPjopJZEC/sj+b4ez8Fb9ex/7k1BZfnA=
|
18
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,15 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIICXQIBAAKBgQDPlDE/RHmLRy6s79/QWCfyZn/XKAlRRuuCxWk45TUJqsQJtmY5
|
3
|
+
T2ISFH9AHYiSc6ybRB9WWQIf+C6w5lZtB0hqqasPmaeuAosdLg3D+G/ODi2BMu24
|
4
|
+
m+UarbMVeH0UqhNJXwiJd69zSL0OZtnQLHa5KOvH4Nh0NMN2r2PUNW4qsQIDAQAB
|
5
|
+
AoGASP3eN1YXuz8DjbInrHZjTZx3VavxYtAiXnCWaHhIpyaSGqw10+8zGBJ3EI+S
|
6
|
+
B5V/W3Wf41gXJDC8El5cg6gs8RoNz9koRrQ6+Rv5pivkuGsaHZWY2CnVzYfc7pCr
|
7
|
+
wjqwkVgXgFT41LfoOLruppSxYuL8Bts0UT3Q1pSaH31lGsUCQQD7gkp4DAQ+ZS51
|
8
|
+
NccmiVhTuzEYg6TEd29x1arLe9AXrqL3DtN6eDxkSmfc0ZUpL9xudD0nM/L1GEzW
|
9
|
+
D8ygvnxzAkEA00kWGlpKPKMAaF0lXFOzCgjRKtEw7XrDENMdOvh1s1206vtn48OK
|
10
|
+
/88q9twfjzPN6pc/dg4qd0+q9eGCTdE3SwJBANKzAR32uytmango+FDRaNykimnG
|
11
|
+
ByfMAuHzpSTY8aiVVdLxabtEtRsztjUoovQhM2KZII4SGCy6EcyW6c+UJP8CQGfc
|
12
|
+
jZj2uXeFSTYEU9FG88QDAY9itgKHTkx++ud6K6G4dq7sVu2HulR1qlEfdAQZGygu
|
13
|
+
oWuPGyD7cLbd3AgUyHECQQC9wNYpbA4pSC3OBtzIOyIuCi+ROJN7nSx6kNuMm5LZ
|
14
|
+
uX9znuOE6t6Yb0yw8M3Z9LUIKLJ1B9fhjiJb9fD9wCIC
|
15
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
class WebServerOptionParser
|
4
|
+
DEFAULT = {
|
5
|
+
address: '0.0.0.0',
|
6
|
+
port: 4567
|
7
|
+
}
|
8
|
+
|
9
|
+
def self.parse
|
10
|
+
options = {}
|
11
|
+
|
12
|
+
OptionParser.new do |opts|
|
13
|
+
|
14
|
+
opts.on( '-o', '--addr [host]', "set the host (default is #{options[:address]})" ) do |address|
|
15
|
+
options[:address] = address
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on( '-p', '--port [port]', Integer, "set the port (default is #{options[:port]})" ) do |port|
|
19
|
+
options[:port] = port
|
20
|
+
end
|
21
|
+
|
22
|
+
end.parse!
|
23
|
+
|
24
|
+
DEFAULT.merge( options )
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
class WebServers
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
attr_reader :lib
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@lib = File.expand_path( File.dirname( __FILE__ ) + '/../webservers' )
|
11
|
+
@servers = {}
|
12
|
+
|
13
|
+
Dir.glob( File.join( @lib + '/**', '*.rb' ) ) do |path|
|
14
|
+
@servers[normalize_name( File.basename( path, '.rb' ) )] = {
|
15
|
+
port: available_port,
|
16
|
+
path: path
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def start( name )
|
22
|
+
return if up?( name )
|
23
|
+
|
24
|
+
server_info = data_for( name )
|
25
|
+
server_info[:pid] = Process.spawn(
|
26
|
+
'ruby', server_info[:path], '-p', server_info[:port].to_s,
|
27
|
+
:out=>"/dev/null", :err=>"/dev/null"
|
28
|
+
)
|
29
|
+
|
30
|
+
sleep 0.2 while !up?( name )
|
31
|
+
end
|
32
|
+
|
33
|
+
def url_for( name )
|
34
|
+
"#{protocol_for( name )}://#{address_for( name )}:#{port_for( name )}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def address_for( name )
|
38
|
+
'127.0.0.1'
|
39
|
+
end
|
40
|
+
|
41
|
+
def protocol_for( name )
|
42
|
+
'http'
|
43
|
+
end
|
44
|
+
|
45
|
+
def port_for( name )
|
46
|
+
data_for( name )[:port]
|
47
|
+
end
|
48
|
+
|
49
|
+
def target_for( name )
|
50
|
+
WebTarget.new( address_for( name ), port_for( name ) )
|
51
|
+
end
|
52
|
+
|
53
|
+
def data_for( name )
|
54
|
+
@servers[normalize_name( name )]
|
55
|
+
end
|
56
|
+
|
57
|
+
def up?( name )
|
58
|
+
begin
|
59
|
+
::Net::HTTP.get_response( URI.parse( url_for( name ) ) )
|
60
|
+
true
|
61
|
+
rescue Errno::ECONNRESET
|
62
|
+
true
|
63
|
+
rescue => e
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def kill( name )
|
69
|
+
server_info = data_for( name )
|
70
|
+
return if !server_info[:pid]
|
71
|
+
|
72
|
+
begin
|
73
|
+
10.times { Process.kill( 'KILL', server_info[:pid] ) }
|
74
|
+
return false
|
75
|
+
rescue Errno::ESRCH
|
76
|
+
server_info.delete( :pid )
|
77
|
+
return true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def killall
|
82
|
+
@servers.keys.each { |n| kill n }
|
83
|
+
end
|
84
|
+
|
85
|
+
def available_port
|
86
|
+
loop do
|
87
|
+
port = 5555 + rand( 9999 )
|
88
|
+
begin
|
89
|
+
socket = ::Socket.new( :INET, :STREAM, 0 )
|
90
|
+
socket.bind(::Socket.sockaddr_in(port, "127.0.0.1"))
|
91
|
+
socket.close
|
92
|
+
return port
|
93
|
+
rescue Errno::EADDRINUSE => e
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def normalize_name( name )
|
99
|
+
name.to_s.to_sym
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.method_missing( sym, *args, &block )
|
103
|
+
if instance.respond_to?( sym )
|
104
|
+
instance.send( sym, *args, &block )
|
105
|
+
elsif
|
106
|
+
super( sym, *args, &block )
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.respond_to?( m )
|
111
|
+
super( m ) || instance.respond_to?( m )
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def set_data_for( name, data )
|
117
|
+
@servers[normalize_name( name )] = data
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
|
2
|
+
shared_context 'with ssl server' do
|
3
|
+
include_context 'with tcp server'
|
4
|
+
|
5
|
+
let(:server_cert) { File.read(File.join(fixtures_path, 'raptor', 'socket', 'ssl_server.crt')) }
|
6
|
+
let(:server_key) { File.read(File.join(fixtures_path, 'raptor', 'socket', 'ssl_server.key')) }
|
7
|
+
let(:server_context) do
|
8
|
+
OpenSSL::SSL::SSLContext.new.tap do |context|
|
9
|
+
context.cert = OpenSSL::X509::Certificate.new(server_cert)
|
10
|
+
context.key = OpenSSL::PKey::RSA.new(server_key)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:ssl_server_sock) do
|
15
|
+
@ssl_server_sock = OpenSSL::SSL::SSLSocket.new(server_sock, server_context)
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:io) do
|
19
|
+
#$stderr.puts("with_ssl_server :io, starting tcp server thread")
|
20
|
+
server_thread
|
21
|
+
|
22
|
+
#$stderr.puts(":io client_sock connecting")
|
23
|
+
client_sock
|
24
|
+
|
25
|
+
#$stderr.puts("starting ssl accept thread")
|
26
|
+
@ssl_server_thread = Thread.new { ssl_server_sock.accept }
|
27
|
+
#$stderr.puts(" ssl accept thread, #{@ssl_server_thread.inspect}")
|
28
|
+
|
29
|
+
# pass to make sure the server thread gets a slice before we return
|
30
|
+
Thread.pass
|
31
|
+
|
32
|
+
# this should now be a connected ::TCPSocket
|
33
|
+
client_sock
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:server_thread) do
|
37
|
+
@server_thread = Thread.new do
|
38
|
+
begin
|
39
|
+
#$stderr.puts("ssl_server_sock.accept_nonblock")
|
40
|
+
peer = ssl_server_sock.accept_nonblock
|
41
|
+
rescue IO::WaitReadable, IO::WaitWritable
|
42
|
+
#$stderr.puts(":server_sock waiting for a client")
|
43
|
+
select([server_sock], [server_sock])
|
44
|
+
#$stderr.puts(":server_sock retrying")
|
45
|
+
retry
|
46
|
+
end
|
47
|
+
#$stderr.puts(":server_sock accepted peer #{peer.inspect}")
|
48
|
+
peer
|
49
|
+
end
|
50
|
+
@server_thread
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
subject do
|
55
|
+
#$stderr.puts("with_ssl_server subject (#{described_class})")
|
56
|
+
s = described_class.new(io, opts)
|
57
|
+
|
58
|
+
#$stderr.puts("Subject: #{s.inspect}")
|
59
|
+
peer = @ssl_server_thread.value
|
60
|
+
#$stderr.puts("ssl_server_thread.value #{peer}")
|
61
|
+
s
|
62
|
+
end
|
63
|
+
|
64
|
+
after(:each) do
|
65
|
+
@ssl_server_sock.close if @ssl_server_sock
|
66
|
+
@ssl_server_thread.kill if @ssl_server_thread
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
shared_context 'with tcp server' do
|
3
|
+
after(:each) do
|
4
|
+
if @server_sock
|
5
|
+
@server_sock.close rescue nil
|
6
|
+
end
|
7
|
+
if @server_thread
|
8
|
+
@server_thread.kill
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:server_sock) do
|
13
|
+
@server_sock = TCPServer.new(example_addr, example_ssl_port)
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:server_thread) do
|
17
|
+
@server_thread = Thread.new do
|
18
|
+
begin
|
19
|
+
#$stderr.puts("server_sock.accept_nonblock")
|
20
|
+
peer = server_sock.accept_nonblock
|
21
|
+
rescue IO::WaitReadable, IO::WaitWritable
|
22
|
+
#$stderr.puts(":server_sock waiting for a client")
|
23
|
+
select([server_sock], [server_sock])
|
24
|
+
#$stderr.puts(":server_sock retrying")
|
25
|
+
retry
|
26
|
+
end
|
27
|
+
#$stderr.puts(":server_sock accepted peer #{peer.inspect}")
|
28
|
+
peer
|
29
|
+
end
|
30
|
+
@server_thread
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:client_sock) do
|
34
|
+
#$stderr.puts "client_sock connecting"
|
35
|
+
retries = 3
|
36
|
+
begin
|
37
|
+
#$stderr.puts "client_sock trying to connect (#{retries} left)"
|
38
|
+
connected_socket = TCPSocket.new(example_addr, example_ssl_port)
|
39
|
+
rescue Errno::ECONNREFUSED
|
40
|
+
# Give the server thread another chance to get it up
|
41
|
+
Thread.pass
|
42
|
+
sleep 0.1
|
43
|
+
retry if (retries -= 1) > 0
|
44
|
+
raise $!
|
45
|
+
end
|
46
|
+
#$stderr.puts "client_sock connected #{connected_socket}"
|
47
|
+
connected_socket
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:io) do
|
51
|
+
server_thread
|
52
|
+
client_sock
|
53
|
+
server_thread.value.write(data)
|
54
|
+
client_sock
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
# Requires let(:tcp_port) and let(:udp_port) to be set to something
|
3
|
+
shared_examples "a comm" do
|
4
|
+
it { should respond_to(:create_tcp) }
|
5
|
+
it { should respond_to(:create_tcp_server) }
|
6
|
+
it { should respond_to(:resolve) }
|
7
|
+
it { should respond_to(:reverse_resolve) }
|
8
|
+
#pending { should respond_to(:create_udp) }
|
9
|
+
#pending { should respond_to(:create_udp_server) }
|
10
|
+
|
11
|
+
let(:peer_host) { "127.0.0.1" }
|
12
|
+
|
13
|
+
describe "#create_tcp" do
|
14
|
+
it "should create a TCP socket" do
|
15
|
+
sock = comm.create_tcp(peer_host: peer_host, peer_port: tcp_port)
|
16
|
+
sock.should be_a(RaptorIO::Socket::TCP)
|
17
|
+
raddr = sock.remote_address
|
18
|
+
raddr.should be_a(::Addrinfo)
|
19
|
+
raddr.ip_address.should == peer_host
|
20
|
+
raddr.ip_port.should == tcp_port
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
|
@@ -0,0 +1,106 @@
|
|
1
|
+
shared_examples_for 'RaptorIO::Protocol::HTTP::Message' do
|
2
|
+
|
3
|
+
let(:url) { 'http://test.com' }
|
4
|
+
|
5
|
+
describe '#initialize' do
|
6
|
+
it 'sets the instance attributes by the options' do
|
7
|
+
options = {
|
8
|
+
url: url,
|
9
|
+
version: '1.0',
|
10
|
+
headers: {
|
11
|
+
'X-Stuff' => 'Blah'
|
12
|
+
}
|
13
|
+
}
|
14
|
+
r = described_class.new( options )
|
15
|
+
r.version.should == options[:version]
|
16
|
+
r.headers.should == options[:headers]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#version' do
|
21
|
+
it 'defaults to 1.1' do
|
22
|
+
described_class.new( url: url ).version.should == '1.1'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#keep_alive?' do
|
27
|
+
context 'when the protocol version is 1.1 or higher' do
|
28
|
+
context 'and connection-token is set to \'close\'' do
|
29
|
+
it 'returns false' do
|
30
|
+
described_class.new( url: url,
|
31
|
+
version: '1.1',
|
32
|
+
headers: { 'connection' => 'close' }
|
33
|
+
).keep_alive?.should be_false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'returns true' do
|
38
|
+
described_class.new( url: url, version: '1.1' ).keep_alive?.should be_true
|
39
|
+
described_class.new( url: url, version: '1.2' ).keep_alive?.should be_true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when the protocol version is lower than 1.1' do
|
44
|
+
context 'and connection-token is set to \'keep-alive\'' do
|
45
|
+
it 'returns false' do
|
46
|
+
described_class.new( url: url,
|
47
|
+
version: '1.0',
|
48
|
+
headers: { 'connection' => 'keep-alive' }
|
49
|
+
).keep_alive?.should be_true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'returns false' do
|
54
|
+
described_class.new( url: url, version: '1.0' ).keep_alive?.should be_false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#http_1_1?' do
|
60
|
+
context 'when the protocol version is 1.1' do
|
61
|
+
it 'returns true' do
|
62
|
+
described_class.new( url: url, version: '1.1' ).http_1_1?.should be_true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when the protocol version is not 1.1' do
|
67
|
+
it 'returns false' do
|
68
|
+
described_class.new( url: url, version: '1.2' ).http_1_1?.should be_false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#http_1_0?' do
|
74
|
+
context 'when the protocol version is 1.0' do
|
75
|
+
it 'returns true' do
|
76
|
+
described_class.new( url: url, version: '1.0' ).http_1_0?.should be_true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when the protocol version is not 1.0' do
|
81
|
+
it 'returns false' do
|
82
|
+
described_class.new( url: url, version: '1.2' ).http_1_0?.should be_false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '#headers' do
|
88
|
+
context 'when not configured' do
|
89
|
+
it 'defaults to an empty Hash' do
|
90
|
+
described_class.new( url: url ).headers.should == {}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'returns the configured value' do
|
95
|
+
headers = { 'Content-Type' => 'text/plain' }
|
96
|
+
described_class.new( url: url, headers: headers ).headers.should == headers
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '#body' do
|
101
|
+
it 'returns the configured body' do
|
102
|
+
body = 'Stuff...'
|
103
|
+
described_class.new( url: url, body: body ).body.should == body
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|