socketry 0.4.0 → 0.5.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/.rubocop.yml +2 -2
- data/.travis.yml +1 -1
- data/CHANGES.md +10 -0
- data/Gemfile +1 -1
- data/README.md +24 -4
- data/lib/socketry/exceptions.rb +4 -1
- data/lib/socketry/resolver/resolv.rb +1 -1
- data/lib/socketry/ssl/server.rb +12 -23
- data/lib/socketry/ssl/socket.rb +34 -6
- data/lib/socketry/udp/datagram.rb +4 -4
- data/lib/socketry/version.rb +1 -1
- data/socketry.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 294dcb634b53400b9dbf5d7ea235dc90edac0112
|
4
|
+
data.tar.gz: cda272965c718f2e3bbf40d53b8f2e24f5fd817a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc8b7123299b7044881fa4164f586bac63da56d85605ff0cb1b1402ba41d1d5dc5fd163379410f5ffcbe48e4fcd4a5b64695ce095ef2b38aa4e23732afa7a2a5
|
7
|
+
data.tar.gz: 82278d2ed2b89ede744d2c257473a1284d5c4505d356102cb799d02b08ff741771d22092c53e8234ae6ff76d67d03b589fdde2f2e2dec68c9d3dfdebcfb65c0f
|
data/.rubocop.yml
CHANGED
@@ -37,7 +37,7 @@ Metrics/ClassLength:
|
|
37
37
|
Max: 200
|
38
38
|
|
39
39
|
Metrics/CyclomaticComplexity:
|
40
|
-
Max:
|
40
|
+
Max: 15
|
41
41
|
|
42
42
|
Metrics/MethodLength:
|
43
43
|
Max: 50
|
@@ -46,7 +46,7 @@ Metrics/ParameterLists:
|
|
46
46
|
Enabled: false
|
47
47
|
|
48
48
|
Metrics/PerceivedComplexity:
|
49
|
-
Max:
|
49
|
+
Max: 15
|
50
50
|
|
51
51
|
#
|
52
52
|
# Lint
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 0.5.0 (2016-11-26)
|
2
|
+
|
3
|
+
* Require Ruby 2.2.6+ (earlier 2.2 versions had async I/O bugs)
|
4
|
+
* Extract Socketry::SSL::Socket#accept into its own method
|
5
|
+
* Simplify Socketry::SSL::Socket#from_socket API
|
6
|
+
* Raise Socketry::SSL::CertificateVerifyError for certificate verification errors
|
7
|
+
* Specs and bugfixes for Socketry::SSL::Server
|
8
|
+
* Rename Socketry::UDP::Datagram accessors to `remote_host`, `remote_addr`, and `remote_port`
|
9
|
+
* Update to RuboCop 0.45.0
|
10
|
+
|
1
11
|
## 0.4.0 (2016-11-25)
|
2
12
|
|
3
13
|
* Specs and bugfixes for SSL sockets
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -25,12 +25,8 @@ While Socketry provides a synchronous, blocking API similar to Ruby's own
|
|
25
25
|
`TCPSocket` and `UDPSocket` classes, behind the scenes it uses non-blocking I/O
|
26
26
|
to implement thread-safe timeouts.
|
27
27
|
|
28
|
-
Highly modular and pluggable, Socketry also provides the flexibility to
|
29
|
-
seamlessly leverage [Celluloid::IO] for event-driven I/O.
|
30
|
-
|
31
28
|
[timeout.rb]: http://ruby-doc.org/stdlib-2.3.1/libdoc/timeout/rdoc/Timeout.html
|
32
29
|
[unsafe multithreaded behaviors]: http://blog.headius.com/2008/02/ruby-threadraise-threadkill-timeoutrb.html
|
33
|
-
[Celluloid::IO]: https://github.com/celluloid/celluloid-io
|
34
30
|
|
35
31
|
## Installation
|
36
32
|
|
@@ -74,6 +70,30 @@ for more detailed documentation and usage notes.
|
|
74
70
|
[YARD API documentation](http://www.rubydoc.info/gems/socketry/)
|
75
71
|
is also available.
|
76
72
|
|
73
|
+
## Supported Ruby Versions
|
74
|
+
|
75
|
+
This library aims to support and is [tested against][travis] the following Ruby
|
76
|
+
versions:
|
77
|
+
|
78
|
+
* Ruby 2.2.6+
|
79
|
+
* Ruby 2.3.x
|
80
|
+
* JRuby 9.1.6.0+
|
81
|
+
|
82
|
+
If something doesn't work on one of these versions, it's a bug.
|
83
|
+
|
84
|
+
This library may inadvertently work (or seem to work) on other Ruby versions,
|
85
|
+
however support will only be provided for the versions listed above.
|
86
|
+
|
87
|
+
If you would like this library to support another Ruby version or
|
88
|
+
implementation, you may volunteer to be a maintainer. Being a maintainer
|
89
|
+
entails making sure all tests run and pass on that implementation. When
|
90
|
+
something breaks on your implementation, you will be responsible for providing
|
91
|
+
patches in a timely fashion. If critical issues for a particular implementation
|
92
|
+
exist at the time of a major release, support for that Ruby version may be
|
93
|
+
dropped.
|
94
|
+
|
95
|
+
[travis]: http://travis-ci.org/socketry/socketry
|
96
|
+
|
77
97
|
## Contributing
|
78
98
|
|
79
99
|
* Fork this repository on github
|
data/lib/socketry/exceptions.rb
CHANGED
@@ -31,7 +31,10 @@ module Socketry
|
|
31
31
|
# Errors related to SSL
|
32
32
|
Error = Class.new(Socketry::Error)
|
33
33
|
|
34
|
+
# Certificate could not be verified
|
35
|
+
CertificateVerifyError = Class.new(Socketry::SSL::Error)
|
36
|
+
|
34
37
|
# Hostname verification error
|
35
|
-
HostnameError = Class.new(
|
38
|
+
HostnameError = Class.new(CertificateVerifyError)
|
36
39
|
end
|
37
40
|
end
|
data/lib/socketry/ssl/server.rb
CHANGED
@@ -12,17 +12,13 @@ module Socketry
|
|
12
12
|
hostname_or_port,
|
13
13
|
port = nil,
|
14
14
|
ssl_socket_class: OpenSSL::SSL::SSLSocket,
|
15
|
-
ssl_context: OpenSSL::SSL::SSLContext.new,
|
16
15
|
ssl_params: nil,
|
17
16
|
**args
|
18
17
|
)
|
19
|
-
raise TypeError, "invalid SSL context (#{ssl_context.class})" unless ssl_context.is_a?(OpenSSL::SSL::SSLContext)
|
20
18
|
raise TypeError, "expected Hash, got #{ssl_params.class}" if ssl_params && !ssl_params.is_a?(Hash)
|
21
19
|
|
22
20
|
@ssl_socket_class = ssl_socket_class
|
23
|
-
@
|
24
|
-
@ssl_context.set_params(ssl_params) if ssl_params && !ssl_params.empty?
|
25
|
-
@ssl_context.freeze
|
21
|
+
@ssl_params = ssl_params
|
26
22
|
|
27
23
|
super(hostname_or_port, port, **args)
|
28
24
|
end
|
@@ -36,28 +32,21 @@ module Socketry
|
|
36
32
|
# to ensure a slow/malicious connection can't cause a denial-of-service
|
37
33
|
# attack against the server.
|
38
34
|
#
|
39
|
-
# @param timeout [Numeric, NilClass] seconds to wait before aborting the accept
|
35
|
+
# @param timeout [Numeric, NilClass] (default nil, unlimited) seconds to wait before aborting the accept
|
36
|
+
#
|
40
37
|
# @return [Socketry::SSL::Socket]
|
41
38
|
def accept(timeout: nil, **args)
|
42
|
-
|
43
|
-
ssl_socket = @ssl_socket_class.new(ruby_socket, @ssl_context)
|
39
|
+
tcp_socket = super(timeout: timeout, **args)
|
44
40
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
raise Socketry::TimeoutError, "failed to complete handshake after #{timeout} seconds"
|
53
|
-
end
|
41
|
+
ssl_socket = Socketry::SSL::Socket.new(
|
42
|
+
read_timeout: @read_timeout,
|
43
|
+
write_timeout: @write_timeout,
|
44
|
+
resolver: @resolver,
|
45
|
+
ssl_socket_class: @ssl_socket_class,
|
46
|
+
ssl_params: @ssl_params
|
47
|
+
)
|
54
48
|
|
55
|
-
|
56
|
-
read_timeout: @read_timeout,
|
57
|
-
write_timeout: @write_timeout,
|
58
|
-
resolver: @resolver,
|
59
|
-
socket_class: @socket_class
|
60
|
-
).from_socket(ruby_socket)
|
49
|
+
ssl_socket.accept(tcp_socket, timeout: timeout)
|
61
50
|
end
|
62
51
|
end
|
63
52
|
end
|
data/lib/socketry/ssl/socket.rb
CHANGED
@@ -15,6 +15,7 @@ module Socketry
|
|
15
15
|
# @param ssl_socket_class [Object] Class which provides the underlying SSL implementation
|
16
16
|
# @param ssl_context [OpenSSL::SSL::SSLContext] SSL configuration object
|
17
17
|
# @param ssL_params [Hash] Parameter hash to set on the given SSL context
|
18
|
+
#
|
18
19
|
# @return [Socketry::SSL::Socket]
|
19
20
|
def initialize(
|
20
21
|
ssl_socket_class: OpenSSL::SSL::SSLSocket,
|
@@ -44,6 +45,7 @@ module Socketry
|
|
44
45
|
# @param timeout [Numeric] Number of seconds to wait before aborting connect
|
45
46
|
# @param enable_sni [true, false] (default: true) Enables Server Name Indication (SNI)
|
46
47
|
# @param verify_hostname [true, false] (default: true) Ensure server's hostname matches cert
|
48
|
+
#
|
47
49
|
# @raise [Socketry::AddressError] an invalid address was given
|
48
50
|
# @raise [Socketry::TimeoutError] connect operation timed out
|
49
51
|
# @raise [Socketry::SSL::Error] an error occurred negotiating an SSL connection
|
@@ -59,8 +61,9 @@ module Socketry
|
|
59
61
|
)
|
60
62
|
super(remote_addr, remote_port, local_addr: local_addr, local_port: local_port, timeout: timeout)
|
61
63
|
|
62
|
-
@ssl_socket =
|
64
|
+
@ssl_socket = @ssl_socket_class.new(@socket, @ssl_context)
|
63
65
|
@ssl_socket.hostname = remote_addr if enable_sni
|
66
|
+
@ssl_socket.sync_close = true
|
64
67
|
|
65
68
|
begin
|
66
69
|
@ssl_socket.connect_nonblock
|
@@ -71,6 +74,7 @@ module Socketry
|
|
71
74
|
retry if @socket.wait_writable(timeout)
|
72
75
|
raise Socketry::TimeoutError, "connection to #{remote_addr}:#{remote_port} timed out"
|
73
76
|
rescue OpenSSL::SSL::SSLError => ex
|
77
|
+
raise Socketry::SSL::CertificateVerifyError, ex.message if ex.message.include?("certificate verify failed")
|
74
78
|
raise Socketry::SSL::Error, ex.message, ex.backtrace
|
75
79
|
end
|
76
80
|
|
@@ -89,17 +93,39 @@ module Socketry
|
|
89
93
|
raise ex
|
90
94
|
end
|
91
95
|
|
96
|
+
# Accept an SSL connection from a Socketry or Ruby socket
|
97
|
+
#
|
98
|
+
# @param tcp_socket [TCPSocket, Socketry::TCP::Socket] raw TCP socket to begin SSL handshake with
|
99
|
+
# @param timeout [Numeric, NilClass] (default nil, unlimited) seconds to wait before aborting the accept
|
100
|
+
#
|
101
|
+
# @return [self]
|
102
|
+
def accept(tcp_socket, timeout: nil)
|
103
|
+
tcp_socket = IO.try_convert(tcp_socket) || raise(TypeError, "couldn't convert #{tcp_socket.class} to IO")
|
104
|
+
ssl_socket = @ssl_socket_class.new(tcp_socket, @ssl_context)
|
105
|
+
|
106
|
+
begin
|
107
|
+
ssl_socket.accept_nonblock
|
108
|
+
rescue IO::WaitReadable
|
109
|
+
retry if IO.select([tcp_socket], nil, nil, timeout)
|
110
|
+
raise Socketry::TimeoutError, "failed to complete handshake after #{timeout} seconds"
|
111
|
+
rescue IO::WaitWritable
|
112
|
+
retry if IO.select(nil, [tcp_socket], nil, timeout)
|
113
|
+
raise Socketry::TimeoutError, "failed to complete handshake after #{timeout} seconds"
|
114
|
+
end
|
115
|
+
|
116
|
+
from_socket(ssl_socket)
|
117
|
+
end
|
118
|
+
|
92
119
|
# Wrap a Ruby OpenSSL::SSL::SSLSocket (or other low-level SSL socket)
|
93
120
|
#
|
94
|
-
# @param socket [::Socket] (or specified socket_class) low-level socket to wrap
|
95
121
|
# @param ssl_socket [OpenSSL::SSL::SSLSocket] SSL socket class associated with this socket
|
122
|
+
#
|
96
123
|
# @return [self]
|
97
|
-
def from_socket(
|
98
|
-
raise TypeError, "expected #{@socket_class}, got #{socket.class}" unless socket.is_a?(@socket_class)
|
124
|
+
def from_socket(ssl_socket)
|
99
125
|
raise TypeError, "expected #{@ssl_socket_class}, got #{ssl_socket.class}" unless ssl_socket.is_a?(@ssl_socket_class)
|
100
|
-
raise StateError, "already connected" if @socket
|
126
|
+
raise StateError, "already connected" if @socket
|
101
127
|
|
102
|
-
@socket =
|
128
|
+
@socket = ssl_socket.to_io
|
103
129
|
@ssl_socket = ssl_socket
|
104
130
|
@ssl_socket.sync_close = true
|
105
131
|
|
@@ -110,6 +136,7 @@ module Socketry
|
|
110
136
|
#
|
111
137
|
# @param size [Fixnum] number of bytes to attempt to read
|
112
138
|
# @param outbuf [String, NilClass] an optional buffer into which data should be read
|
139
|
+
#
|
113
140
|
# @raise [Socketry::Error] an I/O operation failed
|
114
141
|
# @return [String, :wait_readable] data read, or :wait_readable if operation would block
|
115
142
|
def read_nonblock(size, outbuf: nil)
|
@@ -125,6 +152,7 @@ module Socketry
|
|
125
152
|
# Perform a non-blocking write operation
|
126
153
|
#
|
127
154
|
# @param data [String] number of bytes to attempt to read
|
155
|
+
#
|
128
156
|
# @raise [Socketry::Error] an I/O operation failed
|
129
157
|
# @return [Fixnum, :wait_writable] number of bytes written, or :wait_writable if op would block
|
130
158
|
def write_nonblock(data)
|
@@ -5,14 +5,14 @@ module Socketry
|
|
5
5
|
module UDP
|
6
6
|
# Represents a received UDP message
|
7
7
|
class Datagram
|
8
|
-
attr_reader :message, :sockaddr, :
|
8
|
+
attr_reader :message, :sockaddr, :remote_host, :remote_addr, :remote_port
|
9
9
|
|
10
10
|
def initialize(message, sockaddr)
|
11
11
|
@message = message
|
12
12
|
@sockaddr = sockaddr
|
13
|
-
@
|
14
|
-
@
|
15
|
-
@
|
13
|
+
@remote_port = sockaddr[1]
|
14
|
+
@remote_host = sockaddr[2]
|
15
|
+
@remote_addr = sockaddr[3]
|
16
16
|
end
|
17
17
|
|
18
18
|
def addrinfo
|
data/lib/socketry/version.rb
CHANGED
data/socketry.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.required_ruby_version = ">= 2.2.
|
24
|
+
spec.required_ruby_version = ">= 2.2.6"
|
25
25
|
|
26
26
|
spec.add_runtime_dependency "hitimes", ">= 1.2"
|
27
27
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: socketry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Arcieri
|
@@ -82,7 +82,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
82
|
requirements:
|
83
83
|
- - ">="
|
84
84
|
- !ruby/object:Gem::Version
|
85
|
-
version: 2.2.
|
85
|
+
version: 2.2.6
|
86
86
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
88
|
- - ">="
|
@@ -90,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
90
|
version: '0'
|
91
91
|
requirements: []
|
92
92
|
rubyforge_project:
|
93
|
-
rubygems_version: 2.
|
93
|
+
rubygems_version: 2.4.8
|
94
94
|
signing_key:
|
95
95
|
specification_version: 4
|
96
96
|
summary: High-level wrappers for Ruby sockets with advanced thread-safe timeout support
|