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 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