raptor-io 0.0.1
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.
- 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
|