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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ca5c2f89cca095c9a9512b02c3dff7d7af615af2
4
- data.tar.gz: 5d4ccc55cb34dcea0bb63986882c52195a1dabc3
3
+ metadata.gz: 294dcb634b53400b9dbf5d7ea235dc90edac0112
4
+ data.tar.gz: cda272965c718f2e3bbf40d53b8f2e24f5fd817a
5
5
  SHA512:
6
- metadata.gz: c7cfa8860c9e4aa4f0685d8d707f433d1d98ecdf56bad86146d8347b5480492ad51ac03e1d18035899e1ac2457f64b77f0822b91a7ca8e24af85a0262ffe1a27
7
- data.tar.gz: b63f66204999a96fe018d168d9448de66d1ad97b1793c145af5b41cd4a04e806fce9cf1d84901d99872ffbf0ec9cbbd0cafded0777db4ca14e9a5ea904fff3d9
6
+ metadata.gz: fc8b7123299b7044881fa4164f586bac63da56d85605ff0cb1b1402ba41d1d5dc5fd163379410f5ffcbe48e4fcd4a5b64695ce095ef2b38aa4e23732afa7a2a5
7
+ data.tar.gz: 82278d2ed2b89ede744d2c257473a1284d5c4505d356102cb799d02b08ff741771d22092c53e8234ae6ff76d67d03b589fdde2f2e2dec68c9d3dfdebcfb65c0f
@@ -37,7 +37,7 @@ Metrics/ClassLength:
37
37
  Max: 200
38
38
 
39
39
  Metrics/CyclomaticComplexity:
40
- Max: 10
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: 10
49
+ Max: 15
50
50
 
51
51
  #
52
52
  # Lint
@@ -4,7 +4,7 @@ sudo: false
4
4
  bundler_args: --without development doc
5
5
 
6
6
  rvm:
7
- - 2.2
7
+ - 2.2.6
8
8
  - 2.3.3
9
9
  - jruby-9.1.6.0
10
10
 
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
@@ -11,7 +11,7 @@ end
11
11
 
12
12
  group :test do
13
13
  gem "rspec", "~> 3", require: false
14
- gem "rubocop", "0.42.0", require: false
14
+ gem "rubocop", "0.45.0", require: false
15
15
  gem "coveralls", require: false
16
16
  end
17
17
 
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
@@ -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(Socketry::SSL::Error)
38
+ HostnameError = Class.new(CertificateVerifyError)
36
39
  end
37
40
  end
@@ -45,7 +45,7 @@ module Socketry
45
45
  when Integer, Float
46
46
  @resolver.timeouts = timeout
47
47
  when NilClass
48
- # no timeout
48
+ nil # no timeout
49
49
  else raise TypeError, "expected Numeric, got #{timeout.class}"
50
50
  end
51
51
 
@@ -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
- @ssl_context = ssl_context
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
- ruby_socket = super(timeout: timeout, **args).to_io
43
- ssl_socket = @ssl_socket_class.new(ruby_socket, @ssl_context)
39
+ tcp_socket = super(timeout: timeout, **args)
44
40
 
45
- begin
46
- ssl_socket.accept_nonblock
47
- rescue IO::WaitReadable
48
- retry if IO.select([ruby_socket], nil, nil, timeout)
49
- raise Socketry::TimeoutError, "failed to complete handshake after #{timeout} seconds"
50
- rescue IO::WaitWritable
51
- retry if IO.select(nil, [ruby_socket], nil, timeout)
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
- Socketry::SSL::Socket.new(
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
@@ -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 = OpenSSL::SSL::SSLSocket.new(@socket, @ssl_context)
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(socket, ssl_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 && @socket != socket
126
+ raise StateError, "already connected" if @socket
101
127
 
102
- @socket = 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, :host, :addr, :port
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
- @port = sockaddr[1]
14
- @host = sockaddr[2]
15
- @addr = sockaddr[3]
13
+ @remote_port = sockaddr[1]
14
+ @remote_host = sockaddr[2]
15
+ @remote_addr = sockaddr[3]
16
16
  end
17
17
 
18
18
  def addrinfo
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Socketry
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
@@ -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.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.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.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.5.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