socketry 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|