socketry 0.3.0 → 0.4.0
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 +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +2 -2
- data/CHANGES.md +7 -0
- data/README.md +2 -6
- data/lib/socketry.rb +1 -0
- data/lib/socketry/exceptions.rb +3 -0
- data/lib/socketry/resolver/resolv.rb +2 -0
- data/lib/socketry/ssl/socket.rb +26 -26
- data/lib/socketry/tcp/server.rb +2 -0
- data/lib/socketry/tcp/socket.rb +19 -10
- data/lib/socketry/udp/datagram.rb +29 -0
- data/lib/socketry/udp/socket.rb +88 -33
- data/lib/socketry/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca5c2f89cca095c9a9512b02c3dff7d7af615af2
|
4
|
+
data.tar.gz: 5d4ccc55cb34dcea0bb63986882c52195a1dabc3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7cfa8860c9e4aa4f0685d8d707f433d1d98ecdf56bad86146d8347b5480492ad51ac03e1d18035899e1ac2457f64b77f0822b91a7ca8e24af85a0262ffe1a27
|
7
|
+
data.tar.gz: b63f66204999a96fe018d168d9448de66d1ad97b1793c145af5b41cd4a04e806fce9cf1d84901d99872ffbf0ec9cbbd0cafded0777db4ca14e9a5ea904fff3d9
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.3.
|
1
|
+
2.3.3
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 0.4.0 (2016-11-25)
|
2
|
+
|
3
|
+
* Specs and bugfixes for SSL sockets
|
4
|
+
* Specs and bugfixes for UDP sockets
|
5
|
+
* Add Socketry::UDP::Datagram class
|
6
|
+
* Add Socketry::AddressInUseError exception
|
7
|
+
|
1
8
|
## 0.3.0 (2016-09-24)
|
2
9
|
|
3
10
|
* Implement Socketry::TCP::Socket#read and #write
|
data/README.md
CHANGED
@@ -11,13 +11,9 @@
|
|
11
11
|
[license-image]: https://img.shields.io/badge/license-MIT-blue.svg
|
12
12
|
[license-link]: https://github.com/socketry/socketry/blob/master/LICENSE.txt
|
13
13
|
|
14
|
-
High-level
|
14
|
+
High-level Ruby socket library with support for TCP, UDP, and SSL sockets.
|
15
15
|
|
16
|
-
|
17
|
-
timeout support that can be used with any multithreaded Ruby app. That said,
|
18
|
-
Socketry can also be used to provide asynchronous I/O with [Celluloid::IO].
|
19
|
-
|
20
|
-
[Celluloid::IO]: https://github.com/celluloid/celluloid-io
|
16
|
+
Implements thread-safe timeouts using asynchronous I/O and high-precision monotonic timers.
|
21
17
|
|
22
18
|
## Motivation
|
23
19
|
|
data/lib/socketry.rb
CHANGED
data/lib/socketry/exceptions.rb
CHANGED
@@ -10,6 +10,9 @@ module Socketry
|
|
10
10
|
# Invalid address
|
11
11
|
AddressError = Class.new(Socketry::Error)
|
12
12
|
|
13
|
+
# Address is already in use
|
14
|
+
AddressInUseError = Class.new(Socketry::Error)
|
15
|
+
|
13
16
|
# Timeouts performing an I/O operation
|
14
17
|
TimeoutError = Class.new(Socketry::Error)
|
15
18
|
|
data/lib/socketry/ssl/socket.rb
CHANGED
@@ -26,9 +26,10 @@ module Socketry
|
|
26
26
|
raise TypeError, "expected Hash, got #{ssl_params.class}" if ssl_params && !ssl_params.is_a?(Hash)
|
27
27
|
|
28
28
|
@ssl_socket_class = ssl_socket_class
|
29
|
+
|
29
30
|
@ssl_context = ssl_context
|
30
31
|
@ssl_context.set_params(ssl_params) if ssl_params && !ssl_params.empty?
|
31
|
-
|
32
|
+
|
32
33
|
@ssl_socket = nil
|
33
34
|
|
34
35
|
super(**args)
|
@@ -41,7 +42,8 @@ module Socketry
|
|
41
42
|
# @param local_addr [String] DNS name or IP address to bind to locally
|
42
43
|
# @param local_port [Fixnum] Local TCP port to bind to
|
43
44
|
# @param timeout [Numeric] Number of seconds to wait before aborting connect
|
44
|
-
# @param
|
45
|
+
# @param enable_sni [true, false] (default: true) Enables Server Name Indication (SNI)
|
46
|
+
# @param verify_hostname [true, false] (default: true) Ensure server's hostname matches cert
|
45
47
|
# @raise [Socketry::AddressError] an invalid address was given
|
46
48
|
# @raise [Socketry::TimeoutError] connect operation timed out
|
47
49
|
# @raise [Socketry::SSL::Error] an error occurred negotiating an SSL connection
|
@@ -52,12 +54,13 @@ module Socketry
|
|
52
54
|
local_addr: nil,
|
53
55
|
local_port: nil,
|
54
56
|
timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:connect],
|
57
|
+
enable_sni: true,
|
55
58
|
verify_hostname: true
|
56
59
|
)
|
57
60
|
super(remote_addr, remote_port, local_addr: local_addr, local_port: local_port, timeout: timeout)
|
58
61
|
|
59
62
|
@ssl_socket = OpenSSL::SSL::SSLSocket.new(@socket, @ssl_context)
|
60
|
-
@ssl_socket.hostname = remote_addr
|
63
|
+
@ssl_socket.hostname = remote_addr if enable_sni
|
61
64
|
|
62
65
|
begin
|
63
66
|
@ssl_socket.connect_nonblock
|
@@ -110,23 +113,13 @@ module Socketry
|
|
110
113
|
# @raise [Socketry::Error] an I/O operation failed
|
111
114
|
# @return [String, :wait_readable] data read, or :wait_readable if operation would block
|
112
115
|
def read_nonblock(size, outbuf: nil)
|
113
|
-
ensure_connected
|
114
116
|
case outbuf
|
115
117
|
when String
|
116
|
-
@ssl_socket.read_nonblock(size, outbuf, exception: false)
|
118
|
+
perform { @ssl_socket.read_nonblock(size, outbuf, exception: false) }
|
117
119
|
when NilClass
|
118
|
-
@ssl_socket.read_nonblock(size, exception: false)
|
120
|
+
perform { @ssl_socket.read_nonblock(size, exception: false) }
|
119
121
|
else raise TypeError, "unexpected outbuf class: #{outbuf.class}"
|
120
122
|
end
|
121
|
-
# Some buggy Rubies continue to raise exceptions in these cases
|
122
|
-
rescue IO::WaitReadable
|
123
|
-
:wait_readable
|
124
|
-
# Due to SSL, we may need to write to complete a read (e.g. renegotiation)
|
125
|
-
rescue IO::WaitWritable
|
126
|
-
:wait_writable
|
127
|
-
rescue => ex
|
128
|
-
# TODO: more specific exceptions
|
129
|
-
raise Socketry::Error, ex.message, ex.backtrace
|
130
123
|
end
|
131
124
|
|
132
125
|
# Perform a non-blocking write operation
|
@@ -135,26 +128,33 @@ module Socketry
|
|
135
128
|
# @raise [Socketry::Error] an I/O operation failed
|
136
129
|
# @return [Fixnum, :wait_writable] number of bytes written, or :wait_writable if op would block
|
137
130
|
def write_nonblock(data)
|
131
|
+
perform { @ssl_socket.write_nonblock(data, exception: false) }
|
132
|
+
end
|
133
|
+
|
134
|
+
# Close the socket
|
135
|
+
#
|
136
|
+
# @return [true, false] true if the socket was open, false if closed
|
137
|
+
def close
|
138
|
+
@ssl_socket.close rescue nil
|
139
|
+
super
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
# Perform a non-blocking I/O operation
|
145
|
+
def perform
|
138
146
|
ensure_connected
|
139
|
-
|
147
|
+
yield
|
140
148
|
# Some buggy Rubies continue to raise this exception
|
141
|
-
rescue IO::
|
149
|
+
rescue IO::WaitWritable
|
142
150
|
:wait_writable
|
143
|
-
# Due to SSL, we may need to write to complete a read (e.g. renegotiation)
|
151
|
+
# Due to SSL, we may need to write to complete a read (e.g. handshaking, renegotiation)
|
144
152
|
rescue IO::WaitReadable
|
145
153
|
:wait_readable
|
146
154
|
rescue => ex
|
147
155
|
# TODO: more specific exceptions
|
148
156
|
raise Socketry::Error, ex.message, ex.backtrace
|
149
157
|
end
|
150
|
-
|
151
|
-
# Close the socket
|
152
|
-
#
|
153
|
-
# @return [true, false] true if the socket was open, false if closed
|
154
|
-
def close
|
155
|
-
@ssl_socket.close
|
156
|
-
super
|
157
|
-
end
|
158
158
|
end
|
159
159
|
end
|
160
160
|
end
|
data/lib/socketry/tcp/server.rb
CHANGED
data/lib/socketry/tcp/socket.rb
CHANGED
@@ -7,7 +7,7 @@ module Socketry
|
|
7
7
|
class Socket
|
8
8
|
include Socketry::Timeout
|
9
9
|
|
10
|
-
attr_reader :remote_addr, :remote_port, :local_addr, :local_port
|
10
|
+
attr_reader :addr_fmaily, :remote_addr, :remote_port, :local_addr, :local_port
|
11
11
|
attr_reader :read_timeout, :write_timeout, :resolver, :socket_class
|
12
12
|
|
13
13
|
# Create a Socketry::TCP::Socket with the default options, then connect
|
@@ -15,6 +15,7 @@ module Socketry
|
|
15
15
|
#
|
16
16
|
# @param remote_addr [String] DNS name or IP address of the host to connect to
|
17
17
|
# @param remote_port [Fixnum] TCP port to connect to
|
18
|
+
#
|
18
19
|
# @return [Socketry::TCP::Socket]
|
19
20
|
def self.connect(remote_addr, remote_port, **args)
|
20
21
|
new.connect(remote_addr, remote_port, **args)
|
@@ -27,6 +28,7 @@ module Socketry
|
|
27
28
|
# @param timer [Object] A timekeeping object to use for measuring timeouts
|
28
29
|
# @param resolver [Object] A resolver object to use for resolving DNS names
|
29
30
|
# @param socket_class [Object] Underlying socket class which implements I/O ops
|
31
|
+
#
|
30
32
|
# @return [Socketry::TCP::Socket]
|
31
33
|
def initialize(
|
32
34
|
read_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:read],
|
@@ -41,7 +43,7 @@ module Socketry
|
|
41
43
|
@socket_class = socket_class
|
42
44
|
@resolver = resolver
|
43
45
|
|
44
|
-
@
|
46
|
+
@addr_family = nil
|
45
47
|
@socket = nil
|
46
48
|
|
47
49
|
@remote_addr = nil
|
@@ -59,9 +61,10 @@ module Socketry
|
|
59
61
|
# @param local_addr [String] DNS name or IP address to bind to locally
|
60
62
|
# @param local_port [Fixnum] Local TCP port to bind to
|
61
63
|
# @param timeout [Numeric] Number of seconds to wait before aborting connect
|
62
|
-
#
|
64
|
+
#
|
63
65
|
# @raise [Socketry::AddressError] an invalid address was given
|
64
66
|
# @raise [Socketry::TimeoutError] connect operation timed out
|
67
|
+
#
|
65
68
|
# @return [self]
|
66
69
|
def connect(
|
67
70
|
remote_addr,
|
@@ -84,14 +87,12 @@ module Socketry
|
|
84
87
|
local_addr = @resolver.resolve(local_addr, timeout: time_remaining(timeout)) if local_addr
|
85
88
|
raise ArgumentError, "expected IPAddr from resolver, got #{remote_addr.class}" unless remote_addr.is_a?(IPAddr)
|
86
89
|
|
87
|
-
if remote_addr.ipv4?
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
else raise Socketry::AddressError, "unsupported IP address family: #{remote_addr}"
|
92
|
-
end
|
90
|
+
@addr_family = if remote_addr.ipv4? then ::Socket::AF_INET
|
91
|
+
elsif remote_addr.ipv6? then ::Socket::AF_INET6
|
92
|
+
else raise Socketry::AddressError, "unsupported IP address family: #{remote_addr}"
|
93
|
+
end
|
93
94
|
|
94
|
-
socket = @socket_class.new(@
|
95
|
+
socket = @socket_class.new(@addr_family, ::Socket::SOCK_STREAM, 0)
|
95
96
|
socket.bind Addrinfo.tcp(local_addr.to_s, local_port) if local_addr
|
96
97
|
remote_sockaddr = ::Socket.sockaddr_in(remote_port, remote_addr.to_s)
|
97
98
|
|
@@ -143,7 +144,9 @@ module Socketry
|
|
143
144
|
#
|
144
145
|
# @param size [Fixnum] number of bytes to attempt to read
|
145
146
|
# @param outbuf [String, NilClass] an optional buffer into which data should be read
|
147
|
+
#
|
146
148
|
# @raise [Socketry::Error] an I/O operation failed
|
149
|
+
#
|
147
150
|
# @return [String, :wait_readable] data read, or :wait_readable if operation would block
|
148
151
|
def read_nonblock(size, outbuf: nil)
|
149
152
|
ensure_connected
|
@@ -188,7 +191,9 @@ module Socketry
|
|
188
191
|
# @param size [Fixnum] number of bytes to attempt to read
|
189
192
|
# @param outbuf [String] an output buffer to read data into
|
190
193
|
# @param timeout [Numeric] Number of seconds to wait for read operation to complete
|
194
|
+
#
|
191
195
|
# @raise [Socketry::Error] an I/O operation failed
|
196
|
+
#
|
192
197
|
# @return [String, :eof] bytes read, or :eof if socket closed while reading
|
193
198
|
def read(size, outbuf: String.new, timeout: @write_timeout)
|
194
199
|
outbuf.clear
|
@@ -212,7 +217,9 @@ module Socketry
|
|
212
217
|
# Perform a non-blocking write operation
|
213
218
|
#
|
214
219
|
# @param data [String] data to write to the socket
|
220
|
+
#
|
215
221
|
# @raise [Socketry::Error] an I/O operation failed
|
222
|
+
#
|
216
223
|
# @return [Fixnum, :wait_writable] number of bytes written, or :wait_writable if op would block
|
217
224
|
def write_nonblock(data)
|
218
225
|
ensure_connected
|
@@ -249,7 +256,9 @@ module Socketry
|
|
249
256
|
#
|
250
257
|
# @param data [String] data to write to the socket
|
251
258
|
# @param timeout [Numeric] Number of seconds to wait for write operation to complete
|
259
|
+
#
|
252
260
|
# @raise [Socketry::Error] an I/O operation failed
|
261
|
+
#
|
253
262
|
# @return [Fixnum] number of bytes written, or :eof if socket closed during writing
|
254
263
|
def write(data, timeout: @write_timeout)
|
255
264
|
total_written = data.size
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Socketry
|
4
|
+
# User Datagram Protocol: "fire-and-forget" packet protocol
|
5
|
+
module UDP
|
6
|
+
# Represents a received UDP message
|
7
|
+
class Datagram
|
8
|
+
attr_reader :message, :sockaddr, :host, :addr, :port
|
9
|
+
|
10
|
+
def initialize(message, sockaddr)
|
11
|
+
@message = message
|
12
|
+
@sockaddr = sockaddr
|
13
|
+
@port = sockaddr[1]
|
14
|
+
@host = sockaddr[2]
|
15
|
+
@addr = sockaddr[3]
|
16
|
+
end
|
17
|
+
|
18
|
+
def addrinfo
|
19
|
+
addr_family = case @sockaddr[0]
|
20
|
+
when "AF_INET" then ::Socket::AF_INET
|
21
|
+
when "AF_INET6" then ::Socket::AF_INET6
|
22
|
+
else raise Socketry::AddressError, "unsupported IP address family: #{@sockaddr[0]}"
|
23
|
+
end
|
24
|
+
|
25
|
+
Addrinfo.new(@sockaddr, addr_family, ::Socket::SOCK_DGRAM)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/socketry/udp/socket.rb
CHANGED
@@ -7,31 +7,39 @@ module Socketry
|
|
7
7
|
class Socket
|
8
8
|
include Socketry::Timeout
|
9
9
|
|
10
|
-
attr_reader :read_timeout, :write_timeout, :resolver, :socket_class
|
10
|
+
attr_reader :addr_family, :read_timeout, :write_timeout, :resolver, :socket_class
|
11
11
|
|
12
12
|
# Create a UDP socket matching the given socket's address family
|
13
13
|
#
|
14
|
-
# @param remote_addr [String]
|
14
|
+
# @param remote_addr [String] Address to connect/bind to
|
15
|
+
# @param resolver [Object] Resolver object to use for resolving DNS names
|
16
|
+
#
|
15
17
|
# @return [Socketry::UDP::Socket]
|
16
18
|
def self.from_addr(remote_addr, resolver: Socketry::Resolver::DEFAULT_RESOLVER)
|
17
19
|
addr = resolver.resolve(remote_addr)
|
18
|
-
if addr.ipv4?
|
19
|
-
|
20
|
-
elsif addr.ipv6?
|
21
|
-
new(family: :ipv6)
|
20
|
+
if addr.ipv4? then new(addr_family: :ipv4)
|
21
|
+
elsif addr.ipv6? then new(addr_family: :ipv6)
|
22
22
|
else raise Socketry::AddressError, "unsupported IP address family: #{addr}"
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
#
|
26
|
+
# Create a UDP server bound to the given address and port
|
27
|
+
#
|
28
|
+
# @param local_addr [String] Local DNS name or IP address to listen on
|
29
|
+
# @param local_port [Fixnum] Local UDP port to listen on
|
30
|
+
# @param resolver [Object] Resolver object to use for resolving DNS names
|
27
31
|
#
|
28
32
|
# @return [Socketry::UDP::Socket]
|
29
|
-
def self.bind(
|
30
|
-
from_addr(
|
33
|
+
def self.bind(local_addr, local_port, resolver: Socketry::Resolver::DEFAULT_RESOLVER)
|
34
|
+
from_addr(local_addr, resolver: resolver).bind(local_addr, local_port)
|
31
35
|
end
|
32
36
|
|
33
37
|
# Connect to the given address and port
|
34
38
|
#
|
39
|
+
# @param remote_addr [String] DNS name or IP address of the host to connect to
|
40
|
+
# @param remote_port [Fixnum] UDP port to connect to
|
41
|
+
# @param resolver [Object] Resolver object to use for resolving DNS names
|
42
|
+
#
|
35
43
|
# @return [Socketry::UDP::Socket]
|
36
44
|
def self.connect(remote_addr, remote_port, resolver: Socketry::Resolver::DEFAULT_RESOLVER)
|
37
45
|
from_addr(remote_addr, resolver: resolver).connect(remote_addr, remote_port)
|
@@ -39,26 +47,32 @@ module Socketry
|
|
39
47
|
|
40
48
|
# Create a new UDP socket
|
41
49
|
#
|
50
|
+
# @param addr_family [:ipv4, :ipv6] (default :ipv4) address family for this socket
|
51
|
+
# @param read_timeout [Numeric] Seconds to wait before an uncompleted read errors
|
52
|
+
# @param write_timeout [Numeric] Seconds to wait before an uncompleted write errors
|
53
|
+
# @param timer [Object] Time interval object to use for measuring timeouts
|
54
|
+
# @param resolver [Object] Resolver object to use for resolving DNS names
|
55
|
+
# @param socket_class [Object] Underlying socket class which implements I/O ops
|
56
|
+
#
|
57
|
+
# @raise [ArgumentError] an invalid argument was given
|
58
|
+
#
|
42
59
|
# @return [Socketry::UDP::Socket]
|
43
60
|
def initialize(
|
44
|
-
|
61
|
+
addr_family: :ipv4,
|
45
62
|
read_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:read],
|
46
63
|
write_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:write],
|
47
64
|
timer: Socketry::Timeout::DEFAULT_TIMER.new,
|
48
65
|
resolver: Socketry::Resolver::DEFAULT_RESOLVER,
|
49
66
|
socket_class: ::UDPSocket
|
50
67
|
)
|
51
|
-
case
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
@address_family = address_family
|
58
|
-
else raise ArgumentError, "invalid address family: #{address_family.inspect}"
|
59
|
-
end
|
68
|
+
@addr_family = case addr_family
|
69
|
+
when :ipv4 then ::Socket::AF_INET
|
70
|
+
when :ipv6 then ::Socket::AF_INET6
|
71
|
+
when ::Socket::AF_INET, ::Socket::AF_INET6 then addr_family
|
72
|
+
else raise ArgumentError, "invalid address family: #{addr_family.inspect}"
|
73
|
+
end
|
60
74
|
|
61
|
-
@socket = socket_class.new(@
|
75
|
+
@socket = socket_class.new(@addr_family)
|
62
76
|
@read_timeout = read_timeout
|
63
77
|
@write_timeout = write_timeout
|
64
78
|
@resolver = resolver
|
@@ -66,22 +80,29 @@ module Socketry
|
|
66
80
|
start_timer(timer)
|
67
81
|
end
|
68
82
|
|
69
|
-
#
|
83
|
+
# Start a UDP server bound to a particular address and port
|
84
|
+
#
|
85
|
+
# @param local_addr [String] Local DNS name or IP address to listen on
|
86
|
+
# @param local_port [Fixnum] Local UDP port to listen on
|
70
87
|
#
|
71
88
|
# @return [self]
|
72
|
-
def bind(
|
73
|
-
@socket.bind(@resolver.resolve(
|
89
|
+
def bind(local_addr, local_port)
|
90
|
+
@socket.bind(@resolver.resolve(local_addr).to_s, local_port)
|
74
91
|
self
|
92
|
+
rescue Errno::EADDRINUSE => ex
|
93
|
+
raise AddressInUseError, ex.message, ex.backtrace
|
75
94
|
rescue => ex
|
76
|
-
# TODO: more specific exceptions
|
77
95
|
raise Socketry::Error, ex.message, ex.backtrace
|
78
96
|
end
|
79
97
|
|
80
|
-
#
|
98
|
+
# Make a UDP client connection to the given address and port
|
99
|
+
#
|
100
|
+
# @param remote_addr [String] DNS name or IP address of the host to connect to
|
101
|
+
# @param remote_port [Fixnum] UDP port to connect to
|
81
102
|
#
|
82
103
|
# @return [self]
|
83
104
|
def connect(remote_addr, remote_port)
|
84
|
-
@socket.connect(@resolver.resolve(remote_addr), remote_port)
|
105
|
+
@socket.connect(@resolver.resolve(remote_addr).to_s, remote_port)
|
85
106
|
self
|
86
107
|
rescue => ex
|
87
108
|
# TODO: more specific exceptions
|
@@ -90,9 +111,11 @@ module Socketry
|
|
90
111
|
|
91
112
|
# Perform a non-blocking receive
|
92
113
|
#
|
93
|
-
# @
|
114
|
+
# @param maxlen [Fixnum] Maximum packet length to receive
|
115
|
+
#
|
116
|
+
# @return [Socketry::UDP::Datagram, :wait_readable] Received datagram or indication to wait
|
94
117
|
def recvfrom_nonblock(maxlen)
|
95
|
-
|
118
|
+
Socketry::UDP::Datagram.new(*@socket.recvfrom_nonblock(maxlen))
|
96
119
|
rescue ::IO::WaitReadable
|
97
120
|
:wait_readable
|
98
121
|
rescue => ex
|
@@ -102,7 +125,10 @@ module Socketry
|
|
102
125
|
|
103
126
|
# Perform a blocking receive
|
104
127
|
#
|
105
|
-
# @
|
128
|
+
# @param maxlen [Fixnum] Maximum packet length to receive
|
129
|
+
# @param timeout [Numeric] Number of seconds to wait for recvfrom operation to complete
|
130
|
+
#
|
131
|
+
# @return [String] Received data
|
106
132
|
def recvfrom(maxlen, timeout: @read_timeout)
|
107
133
|
set_timeout(timeout)
|
108
134
|
|
@@ -112,19 +138,48 @@ module Socketry
|
|
112
138
|
raise Socketry::TimeoutError, "recvfrom timed out after #{timeout} seconds"
|
113
139
|
end
|
114
140
|
ensure
|
115
|
-
clear_timeout(
|
141
|
+
clear_timeout(timeout)
|
116
142
|
end
|
117
143
|
|
118
144
|
result
|
119
145
|
end
|
120
146
|
|
121
|
-
# Send
|
122
|
-
|
123
|
-
|
147
|
+
# Send a UDP packet to a remote host
|
148
|
+
#
|
149
|
+
# @param msg [String] Data to write to the remote host/port
|
150
|
+
# @param host [String] Remote host to send data to. May be omitted if `connect` was called previously
|
151
|
+
# @param port [Fixnum] UDP port to send data to. May be omitted if `connect` was called previously
|
152
|
+
#
|
153
|
+
# @return [Fixum] Number of bytes sent
|
154
|
+
def send(msg, host: nil, port: nil)
|
155
|
+
host = @resolver.resolve(host).to_s if host
|
156
|
+
if host || port
|
157
|
+
@socket.send(msg, 0, host, port)
|
158
|
+
else
|
159
|
+
@socket.send(msg, 0)
|
160
|
+
end
|
124
161
|
rescue => ex
|
125
162
|
# TODO: more specific exceptions
|
126
163
|
raise Socketry::Error, ex.message, ex.backtrace
|
127
164
|
end
|
165
|
+
|
166
|
+
# Close the socket
|
167
|
+
#
|
168
|
+
# @return [true, false] true if the socket was open, false if closed
|
169
|
+
def close
|
170
|
+
return false if closed?
|
171
|
+
@socket.close
|
172
|
+
true
|
173
|
+
ensure
|
174
|
+
@socket = nil
|
175
|
+
end
|
176
|
+
|
177
|
+
# Is the socket closed?
|
178
|
+
#
|
179
|
+
# @return [true, false] do we locally think the socket is closed?
|
180
|
+
def closed?
|
181
|
+
@socket.nil?
|
182
|
+
end
|
128
183
|
end
|
129
184
|
end
|
130
185
|
end
|
data/lib/socketry/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: socketry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Arcieri
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hitimes
|
@@ -66,6 +66,7 @@ files:
|
|
66
66
|
- lib/socketry/tcp/server.rb
|
67
67
|
- lib/socketry/tcp/socket.rb
|
68
68
|
- lib/socketry/timeout.rb
|
69
|
+
- lib/socketry/udp/datagram.rb
|
69
70
|
- lib/socketry/udp/socket.rb
|
70
71
|
- lib/socketry/version.rb
|
71
72
|
- socketry.gemspec
|
@@ -89,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
90
|
version: '0'
|
90
91
|
requirements: []
|
91
92
|
rubyforge_project:
|
92
|
-
rubygems_version: 2.5.
|
93
|
+
rubygems_version: 2.5.2
|
93
94
|
signing_key:
|
94
95
|
specification_version: 4
|
95
96
|
summary: High-level wrappers for Ruby sockets with advanced thread-safe timeout support
|