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,23 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
class OpenSSL::SSL::SSLServer
|
4
|
+
# Guard this in case stdlib ever implements it
|
5
|
+
unless method_defined?(:accept_nonblock)
|
6
|
+
# Non-blocking version of accept, stolen directly from the blocking
|
7
|
+
# version, OpenSSL::SSL::SSLServer#accept.
|
8
|
+
def accept_nonblock
|
9
|
+
sock = @svr.accept_nonblock
|
10
|
+
|
11
|
+
begin
|
12
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
|
13
|
+
ssl.sync_close = true
|
14
|
+
ssl.accept if @start_immediately
|
15
|
+
ssl
|
16
|
+
rescue OpenSSL::SSL::SSLError => ex
|
17
|
+
sock.close
|
18
|
+
raise ex
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
# @return [String] `self` with 8-bit unsigned characters.
|
4
|
+
def repack
|
5
|
+
unpack( 'C*' ).pack( 'C*' )
|
6
|
+
end
|
7
|
+
|
8
|
+
# Forces `self` to UTF-8 and replaces invalid characters.
|
9
|
+
def force_utf8!
|
10
|
+
force_encoding( 'utf-8' )
|
11
|
+
encode!( 'utf-16be', invalid: :replace, undef: :replace ).encode( 'utf-8' )
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String] Copy of `self`, {#force_utf8! forced to UTF-8}.
|
15
|
+
def force_utf8
|
16
|
+
dup.force_utf8!
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Bool]
|
20
|
+
# `true` if `self` is binary, `false` if regular text.
|
21
|
+
def binary?
|
22
|
+
encoding == Encoding::ASCII_8BIT ||
|
23
|
+
index( "\x00" ) ||
|
24
|
+
count( "\x00-\x7F", "^ -~\t\r\n").fdiv( length ) > 0.3
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'raptor-io/ruby'
|
3
|
+
|
4
|
+
# A basic class for specific transports to inherit from. Analogous to
|
5
|
+
# stdlib's BasicSocket
|
6
|
+
class RaptorIO::Socket
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
require 'raptor-io/socket/error'
|
10
|
+
require 'raptor-io/socket/switch_board'
|
11
|
+
require 'raptor-io/socket/tcp'
|
12
|
+
require 'raptor-io/socket/tcp/ssl'
|
13
|
+
require 'raptor-io/socket/tcp_server'
|
14
|
+
require 'raptor-io/socket/tcp_server/ssl'
|
15
|
+
|
16
|
+
# Like IO.select, but smarter
|
17
|
+
#
|
18
|
+
# OpenSSL does its own buffering which can result in a consumed TCP
|
19
|
+
# buffer, leading `IO.select` to think that the SSLSocket has no more
|
20
|
+
# data to provide, when that's not the case, effectively making
|
21
|
+
# `IO.select` block forever, even though the SSLSocket's buffer has
|
22
|
+
# not yet been consumed.
|
23
|
+
#
|
24
|
+
# We work around this by attempting a non-blocking read of one byte on
|
25
|
+
# each of the `read_array`, and putting the byte back with
|
26
|
+
# `Socket#ungetc` if it worked, or running it through the the real
|
27
|
+
# `IO.select` if it doesn't.
|
28
|
+
#
|
29
|
+
# @see http://bugs.ruby-lang.org/issues/8875
|
30
|
+
# @see http://jira.codehaus.org/browse/JRUBY-6874
|
31
|
+
# @param read_array [Array] (see IO.select)
|
32
|
+
# @param write_array [Array] (see IO.select)
|
33
|
+
# @param error_array [Array] (see IO.select)
|
34
|
+
# @param timeout [Fixnum,nil] (see IO.select)
|
35
|
+
#
|
36
|
+
# @return [Array] An Array containing three arrays of IO objects that
|
37
|
+
# are ready for reading, ready for writing, or have pending errors,
|
38
|
+
# respectively.
|
39
|
+
# @return [nil] If optional `timeout` is given and `timeout` seconds
|
40
|
+
# elapse before any data is available
|
41
|
+
def self.select(read_array=[], write_array=[], error_array=[], timeout=nil)
|
42
|
+
read_array ||= []
|
43
|
+
write_array ||= []
|
44
|
+
error_array ||= []
|
45
|
+
|
46
|
+
readers_with_data = []
|
47
|
+
|
48
|
+
selectable_readers = read_array.dup.delete_if do |reader|
|
49
|
+
begin
|
50
|
+
# If this socket doesn't have a read_nonblock method, then it's
|
51
|
+
# a server of some kind and we have to run it through the real
|
52
|
+
# select to see if it can {TCPServer#accept accept}.
|
53
|
+
next false unless reader.respond_to? :read_nonblock
|
54
|
+
|
55
|
+
byte = reader.read_nonblock(1)
|
56
|
+
rescue IO::WaitReadable, IO::WaitWritable
|
57
|
+
# Then this thing needs to go through the real select to be able
|
58
|
+
# to tell if it has data.
|
59
|
+
#
|
60
|
+
# Note that {IO::WaitWritable} is needed here because OpenSSL
|
61
|
+
# sockets can block for writing when calling `read*` because of
|
62
|
+
# session renegotiation and the like.
|
63
|
+
false
|
64
|
+
rescue EOFError
|
65
|
+
# Then this thing has an empty read buffer and there's no more
|
66
|
+
# on the wire. We mark it as having data so a subsequent
|
67
|
+
# read or read_nonblock will raise EOFError appropriately.
|
68
|
+
readers_with_data << reader
|
69
|
+
true
|
70
|
+
else
|
71
|
+
# Then this thing has data already in its read buffer and we can
|
72
|
+
# skip the real select for it.
|
73
|
+
reader.ungetc(byte)
|
74
|
+
readers_with_data << reader
|
75
|
+
true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
if readers_with_data.any?
|
80
|
+
if selectable_readers.any? || write_array.any? || error_array.any?
|
81
|
+
#$stderr.puts(" ----- Selecting readers:")
|
82
|
+
#pp selectable_readers
|
83
|
+
# Then see if anything has data right now by using a 0 timeout
|
84
|
+
r,w,e = IO.select(selectable_readers, write_array, error_array, 0)
|
85
|
+
|
86
|
+
real = [
|
87
|
+
readers_with_data | (r || []),
|
88
|
+
w || [],
|
89
|
+
e || []
|
90
|
+
]
|
91
|
+
else
|
92
|
+
# Then there's nothing selectable and we can just return stuff
|
93
|
+
# that has buffered data
|
94
|
+
real = [ readers_with_data, [], [] ]
|
95
|
+
end
|
96
|
+
else
|
97
|
+
# Then wait the given timeout, regardless of whether the arrays
|
98
|
+
# are empty
|
99
|
+
real = IO.select(read_array, write_array, error_array, timeout)
|
100
|
+
end
|
101
|
+
|
102
|
+
#$stderr.puts '------ RaptorIO::Socket.select result ------'
|
103
|
+
#pp real
|
104
|
+
return real
|
105
|
+
end
|
106
|
+
|
107
|
+
class << self
|
108
|
+
|
109
|
+
# Captures Ruby exceptions and converts them to RaptorIO Errors.
|
110
|
+
#
|
111
|
+
# @param [Block] block Block to run.
|
112
|
+
def translate_errors( &block )
|
113
|
+
block.call
|
114
|
+
rescue ::Errno::EPIPE, ::Errno::ECONNRESET => e
|
115
|
+
raise RaptorIO::Socket::Error::BrokenPipe, e.to_s
|
116
|
+
rescue ::Errno::ECONNREFUSED => e
|
117
|
+
raise RaptorIO::Socket::Error::ConnectionRefused, e.to_s
|
118
|
+
end
|
119
|
+
|
120
|
+
# Delegates to `::Socket.getaddrinfo`.
|
121
|
+
def getaddrinfo( *args )
|
122
|
+
begin
|
123
|
+
::Socket.getaddrinfo( *args )
|
124
|
+
# OSX raises SocketError.
|
125
|
+
rescue ::SocketError, ::Errno::ENOENT => e
|
126
|
+
raise RaptorIO::Socket::Error::CouldNotResolve.new( e.to_s )
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Delegate to Ruby Socket.
|
131
|
+
def method_missing(meth, *args, &block)
|
132
|
+
#$stderr.puts("Socket.method_missing(#{meth}, #{args.inspect}")
|
133
|
+
if ::Socket.respond_to?(meth)
|
134
|
+
translate_errors do
|
135
|
+
::Socket.__send__(meth, *args, &block)
|
136
|
+
end
|
137
|
+
else
|
138
|
+
super
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def respond_to_missing?(meth, include_private=false)
|
143
|
+
::Socket.respond_to?(meth, include_private)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Options for this socket.
|
148
|
+
#
|
149
|
+
# @return [Hash<Symbol,Object>]
|
150
|
+
attr_accessor :options
|
151
|
+
|
152
|
+
# @!method to_io
|
153
|
+
# Used by Kernel.select
|
154
|
+
# @return [IO]
|
155
|
+
def_delegator :@socket, :to_io, :to_io
|
156
|
+
|
157
|
+
# @param socket [IO] An already-connected socket.
|
158
|
+
# @param options [Hash] Options (see {#options}).
|
159
|
+
def initialize( socket, options = {} )
|
160
|
+
@socket = socket
|
161
|
+
@options = options
|
162
|
+
end
|
163
|
+
|
164
|
+
# @!method closed?
|
165
|
+
def_delegator :@socket, :closed?, :closed?
|
166
|
+
|
167
|
+
# @!method close
|
168
|
+
def_delegator :@socket, :close, :close
|
169
|
+
|
170
|
+
# Whether this socket is an SSL stream.
|
171
|
+
def ssl?
|
172
|
+
false
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
require 'raptor-io/socket'
|
3
|
+
require 'ipaddr'
|
4
|
+
|
5
|
+
###
|
6
|
+
#
|
7
|
+
# Provides the basic interface that a derived class must implement
|
8
|
+
# in order to be a routable socket creator.
|
9
|
+
#
|
10
|
+
# See {RaptorIO::Socket::Comm::Local} for an implementation using sockets
|
11
|
+
# created with standard Ruby Socket classes.
|
12
|
+
#
|
13
|
+
# Subclasses must implement the following methods:
|
14
|
+
#
|
15
|
+
# * `resolve`
|
16
|
+
# * `create_tcp`
|
17
|
+
# * `create_tcp_server`
|
18
|
+
# * `create_udp`
|
19
|
+
# * `create_udp_server`
|
20
|
+
# * `support_ipv6?`
|
21
|
+
#
|
22
|
+
###
|
23
|
+
class RaptorIO::Socket::Comm
|
24
|
+
require 'raptor-io/socket/comm/local'
|
25
|
+
require 'raptor-io/socket/comm/socks'
|
26
|
+
require 'raptor-io/socket/comm/sapni'
|
27
|
+
|
28
|
+
# @param uri [URI]
|
29
|
+
def self.from_uri(uri, opts = {})
|
30
|
+
raise ArgumentError unless uri.kind_of? URI
|
31
|
+
|
32
|
+
prev_comm = opts[:prev_comm] || RaptorIO::Socket::Comm::Local.new
|
33
|
+
|
34
|
+
comm = case uri.scheme.downcase
|
35
|
+
when "sapni"
|
36
|
+
uri.port ||= 3299
|
37
|
+
RaptorIO::Socket::Comm::SAPNI.new(
|
38
|
+
sap_host: uri.host,
|
39
|
+
sap_port: uri.port,
|
40
|
+
sap_comm: prev_comm,
|
41
|
+
)
|
42
|
+
when "socks"
|
43
|
+
uri.port ||= 1080
|
44
|
+
RaptorIO::Socket::Comm::SOCKS.new(
|
45
|
+
socks_host: uri.host,
|
46
|
+
socks_port: uri.port,
|
47
|
+
socks_comm: prev_comm,
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
comm
|
52
|
+
end
|
53
|
+
|
54
|
+
# Creates a socket on this Comm based on the supplied uniform
|
55
|
+
# parameters.
|
56
|
+
#
|
57
|
+
# @option options :switch_board [SwitchBoard]
|
58
|
+
# @option options :port [Fixnum] Optional based on proto
|
59
|
+
# @option options :protocol [Symbol]
|
60
|
+
# * `:tcp`
|
61
|
+
# * `:udp`
|
62
|
+
#
|
63
|
+
# @return [RaptorIO::Socket]
|
64
|
+
def create( options )
|
65
|
+
options = options.dup
|
66
|
+
options[:peer_host] = IPAddr.parse(options[:peer_host])
|
67
|
+
|
68
|
+
case options.delete(:protocol)
|
69
|
+
when :tcp
|
70
|
+
options[:server] ? create_tcp_server(options) : create_tcp(options)
|
71
|
+
|
72
|
+
when :udp
|
73
|
+
options[:server] ? create_udp_server(options) : create_udp(options)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Resolves a hostname to an IP address using this comm.
|
78
|
+
#
|
79
|
+
# @abstract
|
80
|
+
#
|
81
|
+
# @param [String] hostname
|
82
|
+
def resolve( hostname )
|
83
|
+
raise NotImplementedError
|
84
|
+
end
|
85
|
+
|
86
|
+
# Resolves an IP address to a hostname using this comm.
|
87
|
+
#
|
88
|
+
# @abstract
|
89
|
+
#
|
90
|
+
# @param ip_address [String]
|
91
|
+
def reverse_resolve( ip_address )
|
92
|
+
raise NotImplementedError
|
93
|
+
end
|
94
|
+
|
95
|
+
# Connect to a host over TCP.
|
96
|
+
#
|
97
|
+
# @abstract
|
98
|
+
#
|
99
|
+
# @option options :peer_host [String,IPAddr]
|
100
|
+
# @option options :peer_port [Fixnum]
|
101
|
+
# @option options :local_host [String,IPAddr]
|
102
|
+
# @option options :local_port [Fixnum]
|
103
|
+
# @return [RaptorIO::Socket::TCP]
|
104
|
+
def create_tcp(options)
|
105
|
+
raise NotImplementedError
|
106
|
+
end
|
107
|
+
|
108
|
+
# Create a UDP socket bound to the given :peer_host
|
109
|
+
#
|
110
|
+
# @abstract
|
111
|
+
#
|
112
|
+
# @option options :peer_host [String,IPAddr]
|
113
|
+
# @option options :peer_port [Fixnum]
|
114
|
+
# @option options :local_host [String,IPAddr]
|
115
|
+
# @option options :local_port [Fixnum]
|
116
|
+
def create_udp(options)
|
117
|
+
raise NotImplementedError
|
118
|
+
end
|
119
|
+
|
120
|
+
# Create a TCP server listening on :local_port
|
121
|
+
#
|
122
|
+
# @abstract
|
123
|
+
#
|
124
|
+
# @option options :local_host [String,IPAddr]
|
125
|
+
# @option options :local_port [Fixnum]
|
126
|
+
# @option options :ssl_context [OpenSSL::SSL::Context]
|
127
|
+
def create_tcp_server(options)
|
128
|
+
raise NotImplementedError
|
129
|
+
end
|
130
|
+
|
131
|
+
# Create a UDP server listening on :local_port
|
132
|
+
#
|
133
|
+
# @abstract
|
134
|
+
#
|
135
|
+
# @option options :local_host [String,IPAddr]
|
136
|
+
# @option options :local_port [Fixnum]
|
137
|
+
def create_udp_server(options)
|
138
|
+
raise NotImplementedError
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
end
|
143
|
+
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
require 'timeout'
|
3
|
+
require 'socket'
|
4
|
+
require 'resolv'
|
5
|
+
|
6
|
+
# Local communication using Ruby `::Socket`s
|
7
|
+
class RaptorIO::Socket::Comm::Local < RaptorIO::Socket::Comm
|
8
|
+
|
9
|
+
# Determine whether we support IPv6
|
10
|
+
#
|
11
|
+
# We attempt to discover this by creating an unbound UDP socket with
|
12
|
+
# the AF_INET6 address family
|
13
|
+
def support_ipv6?
|
14
|
+
return @supports_ipv6 unless @supports_ipv6.nil?
|
15
|
+
|
16
|
+
@supports_ipv6 = false
|
17
|
+
|
18
|
+
if ::Socket.const_defined?('AF_INET6')
|
19
|
+
begin
|
20
|
+
::Socket.new(::Socket::AF_INET6, ::Socket::SOCK_DGRAM, ::Socket::IPPROTO_UDP).close
|
21
|
+
@supports_ipv6 = true
|
22
|
+
rescue
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
@supports_ipv6
|
27
|
+
end
|
28
|
+
|
29
|
+
# Resolves a hostname to an IP address using this comm.
|
30
|
+
#
|
31
|
+
# @param hostname [String]
|
32
|
+
def resolve( hostname )
|
33
|
+
::Resolv.getaddress hostname
|
34
|
+
end
|
35
|
+
|
36
|
+
# Resolves an IP address to a hostname using this comm.
|
37
|
+
#
|
38
|
+
# @param [String] ip_address
|
39
|
+
def reverse_resolve( ip_address )
|
40
|
+
::Resolv.getname ip_address
|
41
|
+
end
|
42
|
+
|
43
|
+
# Connect to `:peer_host`
|
44
|
+
#
|
45
|
+
# @option (see Comm#create_tcp)
|
46
|
+
# @return [Socket::TCP]
|
47
|
+
# @raise [RaptorIO::Socket::Error::ConnectTimeout]
|
48
|
+
def create_tcp( options )
|
49
|
+
phost = IPAddr.parse( options[:peer_host] )
|
50
|
+
|
51
|
+
# Passing an explicit ::Socket::IPPROTO_TCP is broken on jruby
|
52
|
+
# See https://github.com/jruby/jruby/issues/785
|
53
|
+
socket = ::Socket.new(phost.family, ::Socket::SOCK_STREAM, 0)
|
54
|
+
socket.do_not_reverse_lookup = true
|
55
|
+
|
56
|
+
if options[:local_port] || options[:local_host]
|
57
|
+
socket.bind(::Socket.pack_sockaddr_in(options[:local_port], options[:local_host]))
|
58
|
+
end
|
59
|
+
|
60
|
+
begin
|
61
|
+
socket.connect_nonblock(::Socket.pack_sockaddr_in(options[:peer_port], phost.to_s))
|
62
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET
|
63
|
+
raise RaptorIO::Socket::Error::ConnectionRefused
|
64
|
+
rescue Errno::EINPROGRESS
|
65
|
+
# This should almost always be raised with a call to
|
66
|
+
# connect_nonblock. When the socket finishes connecting it
|
67
|
+
# becomes available for writing.
|
68
|
+
res = select(nil, [socket], nil, options[:connect_timeout] || 2)
|
69
|
+
if res.nil?
|
70
|
+
raise RaptorIO::Socket::Error::ConnectionTimeout
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if options[:ssl_context]
|
75
|
+
RaptorIO::Socket::TCP::SSL.new(socket, options)
|
76
|
+
else
|
77
|
+
RaptorIO::Socket::TCP.new(socket, options)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Listen locally on `:local_port`
|
82
|
+
#
|
83
|
+
# @option (see Comm#create_tcp_server)
|
84
|
+
def create_tcp_server( options )
|
85
|
+
socket = TCPServer.new( options[:local_host], options[:local_port] )
|
86
|
+
|
87
|
+
if (options[:context] = options.delete(:ssl_context))
|
88
|
+
RaptorIO::Socket::TCPServer::SSL.new( socket, options )
|
89
|
+
else
|
90
|
+
RaptorIO::Socket::TCPServer.new( socket, options )
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|