ionian 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,6 @@
1
1
  require 'ionian/extension/io'
2
2
  require 'socket'
3
+ require 'ipaddr'
3
4
 
4
5
  module Ionian
5
6
  module Extension
@@ -23,20 +24,223 @@ module Ionian
23
24
  def initialize_ionian_socket
24
25
  end
25
26
 
26
- # Returns true if the TCP_NODELAY flag is enabled (Nagle disabled).
27
- # Otherwise false.
27
+ # Returns true if local address reuse is allowed.
28
+ # ( SO_REUSEADDR )
29
+ def reuse_addr
30
+ param = self.getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR)
31
+ .data.unpack('i').first
32
+ param > 0 ? true : false
33
+ end
34
+
35
+ alias_method :reuse_addr?, :reuse_addr
36
+
37
+ # Allows local address reuse if true.
38
+ # ( SO_REUSEADDR )
39
+ def reuse_addr=(value)
40
+ param = value ? 1 : 0
41
+ self.setsockopt ::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, [param].pack('i')
42
+ end
43
+
44
+ # Returns the time to live (hop limit).
45
+ # ( IP_TTL )
46
+ def ttl
47
+ self.getsockopt(::Socket::IPPROTO_IP, ::Socket::IP_TTL)
48
+ .data.unpack('i').first
49
+ end
50
+
51
+ alias_method :ttl?, :ttl
52
+
53
+ # Sets the time to live (hop limit).
54
+ # ( IP_TTL )
55
+ def ttl=(value)
56
+ self.setsockopt ::Socket::IPPROTO_IP, ::Socket::IP_TTL, [value].pack('i')
57
+ end
58
+
59
+ # Returns true if the Nagle algorithm is disabled.
60
+ # ( TCP_NODELAY )
28
61
  def no_delay
29
- nagle_disabled = self.getsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY).data.ord
30
- nagle_disabled > 0 ? true : false
62
+ param = self.getsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY)
63
+ .data.unpack('i').first
64
+ param > 0 ? true : false
31
65
  end
32
66
 
33
- # Setting to true enables the TCP_NODELAY flag (disables Nagle).
34
- # Setting to false disables the flag (enables Nagle).
67
+ alias_method :no_delay?, :no_delay
68
+
69
+ # Disables the Nagle algorithm if true.
70
+ # ( TCP_NODELAY )
35
71
  def no_delay=(value)
36
- disable_nagle = value ? 1 : 0
37
- self.setsockopt ::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, disable_nagle
72
+ param = value ? 1 : 0
73
+ self.setsockopt ::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, [param].pack('i')
74
+ end
75
+
76
+ # Returns true if multiple writes are buffered into a single segment.
77
+ # See #recork.
78
+ # Linux only.
79
+ # ( TCP_CORK )
80
+ def cork
81
+ param = self.getsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_CORK)
82
+ .data.unpack('i').first
83
+ param > 0 ? true : false
84
+ end
85
+
86
+ alias_method :cork?, :cork
87
+
88
+ # Buffers multiple writes into a single segment if true.
89
+ # The segment is sent once the cork flag is disabled,
90
+ # the upper limit on the size of a segment is reached,
91
+ # the socket is closed, or 200ms elapses from the time
92
+ # the first corked byte is written.
93
+ # See #recork.
94
+ # Linux only.
95
+ # ( TCP_CORK )
96
+ def cork=(value)
97
+ param = value ? 1 : 0
98
+ self.setsockopt ::Socket::IPPROTO_TCP, ::Socket::TCP_CORK, [param].pack('i')
99
+ end
100
+
101
+ # Unsets cork to transmit data, then reapplies cork.
102
+ # ( TCP_CORK )
103
+ def recork
104
+ self.setsockopt ::Socket::IPPROTO_TCP, ::Socket::TCP_CORK, [0].pack('i')
105
+ self.setsockopt ::Socket::IPPROTO_TCP, ::Socket::TCP_CORK, [1].pack('i')
106
+ end
107
+
108
+ # Join a multicast group.
109
+ # Address is the class D multicast address (uses remote
110
+ # address if not specified).
111
+ # Interface is the local network interface to receive the
112
+ # multicast traffic on (all interfaces if not specified).
113
+ # ( IP_ADD_MEMBERSHIP )
114
+ def ip_add_membership(address = nil, interface = nil)
115
+ address ||= self.remote_address.ip_address
116
+ interface ||= '0.0.0.0'
117
+
118
+ self.setsockopt \
119
+ ::Socket::IPPROTO_IP,
120
+ ::Socket::IP_ADD_MEMBERSHIP,
121
+ IPAddr.new(address).hton + IPAddr.new(interface).hton
122
+ end
123
+
124
+ # Leave a multicast group.
125
+ # Address is the class D multicast address (uses remote
126
+ # address if not specified).
127
+ # Interface is the local network interface the multicast
128
+ # traffic is received on (all interfaces if not specified).
129
+ # ( IP_DROP_MEMBERSHIP )
130
+ def ip_drop_membership(address = nil, interface = nil)
131
+ address ||= self.remote_address.ip_address
132
+ interface ||= '0.0.0.0'
133
+
134
+ self.setsockopt \
135
+ ::Socket::IPPROTO_IP,
136
+ ::Socket::IP_DROP_MEMBERSHIP,
137
+ IPAddr.new(address).hton + IPAddr.new(interface).hton
138
+ end
139
+
140
+ # Returns the default interface for outgoing multicasts.
141
+ # ( IP_MULTICAST_IF )
142
+ def ip_multicast_if
143
+ self.getsockopt(::Socket::IPPROTO_IP, ::Socket::IP_MULTICAST_IF)
144
+ .data.unpack('CCCC').join('.')
145
+ end
146
+
147
+ # Specify default interface for outgoing multicasts.
148
+ # ( IP_MULTICAST_IF )
149
+ def ip_multicast_if=(interface = nil)
150
+ interface ||= '0.0.0.0'
151
+
152
+ self.setsockopt \
153
+ ::Socket::IPPROTO_IP,
154
+ ::Socket::IP_MULTICAST_IF,
155
+ IPAddr.new(interface).hton
156
+ end
157
+
158
+ # Returns the time to live (hop limit) for outgoing multicasts.
159
+ # ( IP_MULTICAST_TTL )
160
+ def ip_multicast_ttl
161
+ self.getsockopt(::Socket::IPPROTO_IP, ::Socket::IP_MULTICAST_TTL)
162
+ .data.unpack('C').first
163
+ end
164
+
165
+ # Set the time to live (hop limit) for outgoing multicasts.
166
+ # ( IP_MULTICAST_TTL )
167
+ def ip_multicast_ttl=(value)
168
+ self.setsockopt ::Socket::IPPROTO_IP, ::Socket::IP_MULTICAST_TTL, [value].pack('C')
169
+ end
170
+
171
+ # Returns true if loopback of outgoing multicasts is enabled.
172
+ # ( IP_MULTICAST_LOOP )
173
+ def ip_multicast_loop
174
+ param = self.getsockopt(::Socket::IPPROTO_IP, ::Socket::IP_MULTICAST_LOOP)
175
+ .data.unpack('C').first
176
+ param > 0 ? true : false
177
+ end
178
+
179
+ alias_method :ip_multicast_loop?, :ip_multicast_loop
180
+
181
+ # Enables loopback of outgoing multicasts if true.
182
+ # ( IP_MULTICAST_LOOP )
183
+ def ip_multicast_loop=(value)
184
+ param = value ? 1 : 0
185
+ self.setsockopt ::Socket::IPPROTO_IP, ::Socket::IP_MULTICAST_LOOP, [param].pack('C')
186
+ end
187
+
188
+ def ipv6_add_membership
189
+ # TODO: Implement
190
+ false
38
191
  end
39
192
 
193
+ def ipv6_drop_membership
194
+ # TODO: Implement
195
+ false
196
+ end
197
+
198
+ def ipv6_multicast_if
199
+ # TODO: Implement
200
+ false
201
+ end
202
+
203
+ def ipv6_multicast_if=(value)
204
+ # TODO: Implement
205
+ end
206
+
207
+ def ipv6_multicast_hops
208
+ # TODO: Implement
209
+ false
210
+ end
211
+
212
+ def ipv6_multicast_hops=(value)
213
+ # TODO: Implement
214
+ end
215
+
216
+ def ipv6_multicast_loop
217
+ # TODO: Implement
218
+ false
219
+ end
220
+
221
+ alias_method :ipv6_multicast_loop?, :ipv6_multicast_loop
222
+
223
+ def ipv6_multicast_loop=(value)
224
+ # TODO: Implement
225
+ end
226
+
227
+
228
+ class << self
229
+ # Returns true if the given address is within the multicast range.
230
+ def multicast(address)
231
+ address >= '224.0.0.0' and address <= '239.255.255.255' ? true : false
232
+ end
233
+
234
+ alias_method :multicast?, :multicast
235
+ end
236
+
237
+ # Returns true if the socket's address is in the multicast range.
238
+ def multicast
239
+ Ionian::Extension::Socket.multicast self.remote_address.ip_address
240
+ end
241
+
242
+ alias_method :multicast?, :multicast
243
+
40
244
  end
41
245
  end
42
246
  end
data/lib/ionian/socket.rb CHANGED
@@ -6,22 +6,43 @@ module Ionian
6
6
  class Socket
7
7
 
8
8
  # Args:
9
- # host: IP or hostname to connect to.
10
- # port: Connection's port number. Default is 23. Unused by :unix protocol.
11
- # protocol: Type of socket to create. :tcp, :udp, :unix. Default is :tcp.
9
+ # host: IP or hostname to connect to.
10
+ # port: Connection's port number. Default is 23. Unused by :unix protocol.
11
+ # protocol: Type of socket to create. :tcp, :udp, :unix. Default is :tcp.
12
+ # :udp will be automatically selected for addresses in the multicast range.
12
13
  # persistent: The socket remains open after data is sent if this is true.
13
14
  # The socket closes after data is sent and a packet is received
14
15
  # if this is false. Default is true.
16
+ # bind_port: Local UDP port to bind to for receiving data, if different than
17
+ # the remote port being connected to.
18
+ # reuse_addr: Set true to enable the SO_REUSEADDR flag. Allows local address reuse.
19
+ # no_delay: Set true to enable the TCP_NODELAY flag. Disables Nagle algorithm.
20
+ # cork: Set true to enable the TCP_CORK flag. Buffers multiple writes
21
+ # into one segment.
15
22
  # expression: Overrides the #read_match regular expression for received data.
16
23
  def initialize(**kwargs)
17
24
  @socket = nil
18
25
 
26
+ # TODO: Should be able to parse the port out of host.
27
+ # :port should override this parsed value.
28
+
19
29
  @host = kwargs.fetch :host
20
- @port = kwargs.fetch :port, 23
21
- @protocol = kwargs.fetch :protocol, :tcp
30
+ @port = kwargs.fetch :port, 23
31
+ @bind_port = kwargs.fetch :bind_port, @port
32
+
33
+ # Automatically select UDP for the multicast range. Otherwise default to TCP.
34
+ default_protocol = :tcp
35
+ default_protocol = :udp if Ionian::Extension::Socket.multicast? @host
36
+ default_protocol = :unix if @host.start_with? '/'
37
+
38
+ @protocol = kwargs.fetch :protocol, default_protocol
22
39
  @persistent = kwargs.fetch :persistent, true
23
40
  @expression = kwargs.fetch :expression, nil
24
41
 
42
+ @reuse_addr = kwargs.fetch :reuse_addr, false
43
+ @no_delay = kwargs.fetch :no_delay, false
44
+ @cork = kwargs.fetch :cork, false
45
+
25
46
  create_socket if @persistent
26
47
  end
27
48
 
@@ -123,15 +144,30 @@ module Ionian
123
144
  case @protocol
124
145
  when :tcp
125
146
  @socket = ::TCPSocket.new @host, @port
147
+ @socket.extend Ionian::Extension::Socket
148
+ @socket.no_delay = true if @no_delay
149
+ @socket.cork = true if @cork
150
+
126
151
  when :udp
127
152
  @socket = ::UDPSocket.new
128
- @socket.bind '', @port
153
+ @socket.extend Ionian::Extension::Socket
154
+
155
+ @socket.reuse_addr = true if
156
+ @reuse_addr or Ionian::Extension::Socket.multicast? @host
157
+
158
+ @socket.bind ::Socket::INADDR_ANY, @bind_port
129
159
  @socket.connect @host, @port
160
+
161
+ @socket.ip_add_membership if Ionian::Extension::Socket.multicast? @host
162
+
130
163
  when :unix
131
164
  @socket = ::UNIXSocket.new @host
165
+ @socket.extend Ionian::Extension::Socket
132
166
  end
133
167
 
134
- @socket.extend Ionian::Extension::Socket
168
+ # TODO: Implement SO_LINGER flag for non-persistent sockets;
169
+ # especially send-and-forget.
170
+
135
171
  @socket.expression = @expression if @expression
136
172
 
137
173
  initialize_socket_methods
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ionian
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex McLain
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-03 00:00:00.000000000 Z
11
+ date: 2013-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake