raktr 0.0.1

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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -0
  3. data/LICENSE.md +29 -0
  4. data/README.md +77 -0
  5. data/Rakefile +53 -0
  6. data/lib/raktr/connection/callbacks.rb +71 -0
  7. data/lib/raktr/connection/error.rb +120 -0
  8. data/lib/raktr/connection/peer_info.rb +90 -0
  9. data/lib/raktr/connection/tls.rb +164 -0
  10. data/lib/raktr/connection.rb +339 -0
  11. data/lib/raktr/global.rb +24 -0
  12. data/lib/raktr/iterator.rb +249 -0
  13. data/lib/raktr/queue.rb +89 -0
  14. data/lib/raktr/tasks/base.rb +57 -0
  15. data/lib/raktr/tasks/delayed.rb +33 -0
  16. data/lib/raktr/tasks/one_off.rb +30 -0
  17. data/lib/raktr/tasks/periodic.rb +58 -0
  18. data/lib/raktr/tasks/persistent.rb +29 -0
  19. data/lib/raktr/tasks.rb +105 -0
  20. data/lib/raktr/version.rb +13 -0
  21. data/lib/raktr.rb +707 -0
  22. data/spec/raktr/connection/tls_spec.rb +348 -0
  23. data/spec/raktr/connection_spec.rb +74 -0
  24. data/spec/raktr/iterator_spec.rb +203 -0
  25. data/spec/raktr/queue_spec.rb +91 -0
  26. data/spec/raktr/tasks/base.rb +8 -0
  27. data/spec/raktr/tasks/delayed_spec.rb +71 -0
  28. data/spec/raktr/tasks/one_off_spec.rb +66 -0
  29. data/spec/raktr/tasks/periodic_spec.rb +57 -0
  30. data/spec/raktr/tasks/persistent_spec.rb +54 -0
  31. data/spec/raktr/tasks_spec.rb +155 -0
  32. data/spec/raktr_spec.rb +20 -0
  33. data/spec/raktr_tls_spec.rb +20 -0
  34. data/spec/spec_helper.rb +17 -0
  35. data/spec/support/fixtures/handlers/echo_client.rb +34 -0
  36. data/spec/support/fixtures/handlers/echo_client_tls.rb +10 -0
  37. data/spec/support/fixtures/handlers/echo_server.rb +12 -0
  38. data/spec/support/fixtures/handlers/echo_server_tls.rb +8 -0
  39. data/spec/support/fixtures/pems/cacert.pem +37 -0
  40. data/spec/support/fixtures/pems/client/cert.pem +37 -0
  41. data/spec/support/fixtures/pems/client/foo-cert.pem +39 -0
  42. data/spec/support/fixtures/pems/client/foo-key.pem +51 -0
  43. data/spec/support/fixtures/pems/client/key.pem +51 -0
  44. data/spec/support/fixtures/pems/server/cert.pem +37 -0
  45. data/spec/support/fixtures/pems/server/key.pem +51 -0
  46. data/spec/support/helpers/paths.rb +23 -0
  47. data/spec/support/helpers/utilities.rb +135 -0
  48. data/spec/support/lib/server_option_parser.rb +29 -0
  49. data/spec/support/lib/servers/runner.rb +13 -0
  50. data/spec/support/lib/servers.rb +133 -0
  51. data/spec/support/servers/echo.rb +14 -0
  52. data/spec/support/servers/echo_tls.rb +22 -0
  53. data/spec/support/servers/echo_unix.rb +14 -0
  54. data/spec/support/servers/echo_unix_tls.rb +22 -0
  55. data/spec/support/shared/connection.rb +696 -0
  56. data/spec/support/shared/raktr.rb +834 -0
  57. data/spec/support/shared/task.rb +21 -0
  58. metadata +140 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2aa060f18df13540e73b42a5e9c68bdf86eaf03ae21104da0e827e57252dffe3
4
+ data.tar.gz: 5f1dd3fa7626e764f5212424c8e25b26a16bb52d4d00a2d2e6e4e5684575123a
5
+ SHA512:
6
+ metadata.gz: 8889f36f41d78a38dbd5afb95af6f4c2836e2056bb00451dabb10141c19d34c1ce02a7809c54e3c807713ab0b3bb94e6d969eb423878ab2c8514db703c868f85
7
+ data.tar.gz: fa3426f952f1980e9916aeaa1af050da734fec2c8de94a8e352c5223271e4309f66abd9fba46399d5470394ad9b4d65801620c4b060e9b4db1c0775d23a8859e
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ # ChangeLog
data/LICENSE.md ADDED
@@ -0,0 +1,29 @@
1
+ # License
2
+
3
+ Copyright (C) 2022, Ecsypno <https://ecsypno.com/>
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without modification,
7
+ are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice,
10
+ this list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ * Neither the name of the copyright holder nor the names of its contributors
17
+ may be used to endorse or promote products derived from this software
18
+ without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # Raktr
2
+
3
+ <table>
4
+ <tr>
5
+ <th>Version</th>
6
+ <td>0.0.1</td>
7
+ </tr>
8
+ <tr>
9
+ <th>Github page</th>
10
+ <td><a href="http://github.com/qadron/raktr">http://github.com/qadron/raktr</a></td>
11
+ <tr/>
12
+ <tr>
13
+ <th>Code Documentation</th>
14
+ <td><a href="http://rubydoc.info/github/qadron/raktr/">http://rubydoc.info/github/qadron/raktr/</a></td>
15
+ </tr>
16
+ <tr>
17
+ <th>Author</th>
18
+ <td><a href="http://twitter.com/Zap0tek">Tasos Laskos</a></td>
19
+ </tr>
20
+ <tr>
21
+ <th>Copyright</th>
22
+ <td>2022 <a href="https://ecsypno.com">Ecsypno</a></td>
23
+ </tr>
24
+ <tr>
25
+ <th>License</th>
26
+ <td><a href="file.LICENSE.html">3-clause BSD</a></td>
27
+ </tr>
28
+ </table>
29
+
30
+ ## Synopsis
31
+
32
+ Raktr is a simple, lightweight, pure-Ruby implementation of the
33
+ [Reactor](http://en.wikipedia.org/wiki/Reactor_pattern) pattern, mainly focused
34
+ on network connections -- and less so on generic tasks.
35
+
36
+ ## Features
37
+
38
+ - Extremely lightweight.
39
+ - Very simple design.
40
+ - Support for TCP/IP and UNIX-domain sockets.
41
+ - TLS encryption.
42
+ - Pure-Ruby.
43
+ - Multi-platform.
44
+
45
+ ## Supported platforms
46
+
47
+ - Rubies:
48
+ - MRI >= 1.9
49
+ - Rubinius
50
+ - JRuby
51
+ - Operating Systems:
52
+ - Linux
53
+ - OSX
54
+ - Windows
55
+
56
+ ## Examples
57
+
58
+ For examples please see the `examples/` directory.
59
+
60
+ ## Installation
61
+
62
+ gem install raktr
63
+
64
+ ## Running the Specs
65
+
66
+ rake spec
67
+
68
+ ## Bug reports/Feature requests
69
+
70
+ Please send your feedback using GitHub's issue system at
71
+ [http://github.com/qadron/raktr/issues](http://github.com/qadron/raktr/issues).
72
+
73
+
74
+ ## License
75
+
76
+ Raktr is provided under the 3-clause BSD license.
77
+ See the [LICENSE](https://github.com/qadron/raktr/blob/master/LICENSE.md) file for more information.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ =begin
2
+
3
+ This file is part of the Raktr project and may be subject to
4
+ redistribution and commercial restrictions. Please see the Raktr
5
+ web site for more information on licensing and terms of use.
6
+
7
+ =end
8
+
9
+ require 'rubygems'
10
+ require File.expand_path( File.dirname( __FILE__ ) ) + '/lib/raktr/version'
11
+
12
+ begin
13
+ require 'rspec'
14
+ require 'rspec/core/rake_task'
15
+
16
+ RSpec::Core::RakeTask.new
17
+ rescue
18
+ end
19
+
20
+ task default: [ :build, :spec ]
21
+
22
+ desc 'Generate docs'
23
+ task :docs do
24
+ outdir = '../raktr-docs'
25
+ sh "rm -rf #{outdir}"
26
+ sh "mkdir -p #{outdir}"
27
+
28
+ sh "yardoc -o #{outdir}"
29
+
30
+ sh 'rm -rf .yardoc'
31
+ end
32
+
33
+ desc 'Clean up'
34
+ task :clean do
35
+ sh 'rm *.gem || true'
36
+ end
37
+
38
+ desc 'Build the gem.'
39
+ task build: [ :clean ] do
40
+ sh "gem build raktr.gemspec"
41
+ end
42
+
43
+ desc 'Build and install the gem.'
44
+ task install: [ :build ] do
45
+ sh "gem install raktr-#{Raktr::VERSION}.gem"
46
+ end
47
+
48
+ desc 'Push a new version to Rubygems'
49
+ task publish: [ :build ] do
50
+ sh "git tag -a v#{Raktr::VERSION} -m 'Version #{Raktr::VERSION}'"
51
+ sh "gem push raktr-#{Raktr::VERSION}.gem"
52
+ end
53
+ task release: [ :publish ]
@@ -0,0 +1,71 @@
1
+ =begin
2
+
3
+ This file is part of the Raktr project and may be subject to
4
+ redistribution and commercial restrictions. Please see the Raktr
5
+ web site for more information on licensing and terms of use.
6
+
7
+ =end
8
+
9
+ class Raktr
10
+ class Connection
11
+
12
+ # Callbacks to be invoked per event.
13
+ #
14
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
15
+ module Callbacks
16
+
17
+ # Called after the connection has been established.
18
+ #
19
+ # @abstract
20
+ def on_connect
21
+ end
22
+
23
+ # Called after the connection has been attached to a {#raktr}.
24
+ #
25
+ # @abstract
26
+ def on_attach
27
+ end
28
+
29
+ # Called right the connection is detached from the {#raktr}.
30
+ #
31
+ # @abstract
32
+ def on_detach
33
+ end
34
+
35
+ # @note If a connection could not be established no {#socket} may be
36
+ # available.
37
+ #
38
+ # Called when the connection gets closed.
39
+ #
40
+ # @param [Exception] reason
41
+ # Reason for the close.
42
+ #
43
+ # @abstract
44
+ def on_close( reason )
45
+ end
46
+
47
+ # Called when data are available.
48
+ #
49
+ # @param [String] data
50
+ # Incoming data.
51
+ #
52
+ # @abstract
53
+ def on_read( data )
54
+ end
55
+
56
+ # Called after each {#write} call.
57
+ #
58
+ # @abstract
59
+ def on_write
60
+ end
61
+
62
+ # Called after the {#write buffered data} have all been sent to the peer.
63
+ #
64
+ # @abstract
65
+ def on_flush
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,120 @@
1
+ =begin
2
+
3
+ This file is part of the Raktr project and may be subject to
4
+ redistribution and commercial restrictions. Please see the Raktr
5
+ web site for more information on licensing and terms of use.
6
+
7
+ =end
8
+
9
+ class Raktr
10
+ class Connection
11
+
12
+ # {Connection} error namespace.
13
+ #
14
+ # All {Connection} errors inherit from and live under it.
15
+ #
16
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
17
+ class Error < Raktr::Error
18
+
19
+ class << self
20
+ # Captures Ruby exceptions and converts them to an appropriate
21
+ # subclass of {Error}.
22
+ #
23
+ # @param [Block] block Block to run.
24
+ def translate( &block )
25
+ block.call
26
+ rescue IOError, Errno::ENOTCONN, Errno::ENOTSOCK => e
27
+ raise_with_proper_backtrace( e, Closed )
28
+ rescue SocketError, Errno::ENOENT => e
29
+ raise_with_proper_backtrace( e, HostNotFound )
30
+ rescue Errno::EPIPE => e
31
+ raise_with_proper_backtrace( e, BrokenPipe )
32
+ rescue Errno::ECONNREFUSED,
33
+ # JRuby throws Errno::EADDRINUSE when trying to connect to a
34
+ # non-existent server.
35
+ Errno::EADDRINUSE => e
36
+ raise_with_proper_backtrace( e, Refused )
37
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED => e
38
+ raise_with_proper_backtrace( e, Reset )
39
+ rescue Errno::EACCES => e
40
+ raise_with_proper_backtrace( e, Permission )
41
+ rescue Errno::EBADF => e
42
+ raise_with_proper_backtrace( e, BadSocket )
43
+
44
+ # Catch and forward these before handling OpenSSL::OpenSSLError because
45
+ # all SSL errors inherit from it, including OpenSSL::SSL::SSLErrorWaitReadable
46
+ # and OpenSSL::SSL::SSLErrorWaitWritable which also inherit from
47
+ # IO::WaitReadable and IO::WaitWritable and need special treatment.
48
+ rescue IO::WaitReadable, IO::WaitWritable, Errno::EINPROGRESS
49
+ raise
50
+
51
+ # We're mainly interested in translating SSL handshake errors but there
52
+ # aren't any specific exceptions for these.
53
+ #
54
+ # Why make things easy and clean, right?
55
+ rescue OpenSSL::SSL::SSLError, OpenSSL::OpenSSLError => e
56
+ raise_with_proper_backtrace( e, SSL )
57
+ end
58
+
59
+ def raise_with_proper_backtrace( ruby, raktr )
60
+ e = raktr.new( ruby.to_s )
61
+ e.set_backtrace ruby.backtrace
62
+ raise e
63
+ end
64
+ end
65
+
66
+ # Like a `SocketError.getaddrinfo` exception.
67
+ #
68
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
69
+ class HostNotFound < Error
70
+ end
71
+
72
+ # Like a `Errno::EACCES` exception.
73
+ #
74
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
75
+ class Permission < Error
76
+ end
77
+
78
+ # Like a `Errno::ECONNREFUSED` exception.
79
+ #
80
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
81
+ class Refused < Error
82
+ end
83
+
84
+ # Like a `Errno::ECONNRESET` exception.
85
+ #
86
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
87
+ class Reset < Error
88
+ end
89
+
90
+ # Like a `Errno::EPIPE` exception.
91
+ #
92
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
93
+ class BrokenPipe < Error
94
+ end
95
+
96
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
97
+ class Timeout < Error
98
+ end
99
+
100
+ # Like a `IOError` exception.
101
+ #
102
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
103
+ class Closed < Error
104
+ end
105
+
106
+ # Like a `Errno::EBADF` exception.
107
+ #
108
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
109
+ class BadSocket < Error
110
+ end
111
+
112
+ # Like a `OpenSSL::OpenSSLError` exception.
113
+ #
114
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
115
+ class SSL < Error
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,90 @@
1
+ =begin
2
+
3
+ This file is part of the Raktr project and may be subject to
4
+ redistribution and commercial restrictions. Please see the Raktr
5
+ web site for more information on licensing and terms of use.
6
+
7
+ =end
8
+
9
+ class Raktr
10
+ class Connection
11
+
12
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
13
+ module PeerInfo
14
+
15
+ # @param [Bool] resolve
16
+ # Resolve IP address to hostname.
17
+ # @return [Hash]
18
+ # Peer address information:
19
+ #
20
+ # * IP socket:
21
+ # * Without `resolve`:
22
+ #
23
+ # {
24
+ # protocol: 'AF_INET',
25
+ # port: 10314,
26
+ # hostname: '127.0.0.1',
27
+ # ip_address: '127.0.0.1'
28
+ # }
29
+ #
30
+ # * With `resolve`:
31
+ #
32
+ # {
33
+ # protocol: 'AF_INET',
34
+ # port: 10314,
35
+ # hostname: 'localhost',
36
+ # ip_address: '127.0.0.1'
37
+ # }
38
+ #
39
+ # * UNIX-domain socket:
40
+ #
41
+ # {
42
+ # protocol: 'AF_UNIX',
43
+ # path: '/tmp/my-socket'
44
+ # }
45
+ def peer_address_info( resolve = false )
46
+ if Raktr.supports_unix_sockets? && to_io.is_a?( UNIXSocket )
47
+ {
48
+ protocol: to_io.peeraddr.first,
49
+ path: to_io.path
50
+ }
51
+ else
52
+ protocol, port, hostname, ip_address = to_io.peeraddr( resolve )
53
+
54
+ {
55
+ protocol: protocol,
56
+ port: port,
57
+ hostname: hostname,
58
+ ip_address: ip_address
59
+ }
60
+ end
61
+ end
62
+
63
+ # @return [String]
64
+ # Peer's IP address or socket path.
65
+ def peer_address
66
+ peer_ip_address || peer_address_info[:path]
67
+ end
68
+
69
+ # @return [String]
70
+ # Peer's IP address.
71
+ def peer_ip_address
72
+ peer_address_info[:ip_address]
73
+ end
74
+
75
+ # @return [String]
76
+ # Peer's hostname.
77
+ def peer_hostname
78
+ peer_address_info(true)[:hostname]
79
+ end
80
+
81
+ # @return [String]
82
+ # Peer's port.
83
+ def peer_port
84
+ peer_address_info[:port]
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,164 @@
1
+ =begin
2
+
3
+ This file is part of the Raktr project and may be subject to
4
+ redistribution and commercial restrictions. Please see the Raktr
5
+ web site for more information on licensing and terms of use.
6
+
7
+ =end
8
+
9
+ class Raktr
10
+ class Connection
11
+
12
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
13
+ module TLS
14
+
15
+ # Converts the {#socket} to an SSL one.
16
+ #
17
+ # @param [Hash] options
18
+ # @option [String] :certificate
19
+ # Path to a PEM certificate.
20
+ # @option [String] :private_key
21
+ # Path to a PEM private key.
22
+ # @option [String] :ca
23
+ # Path to a PEM CA.
24
+ def start_tls( options = {} )
25
+ if @socket.is_a? OpenSSL::SSL::SSLSocket
26
+ @ssl_context = @socket.context
27
+ return
28
+ end
29
+
30
+ @ssl_context = OpenSSL::SSL::SSLContext.new
31
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
32
+
33
+ if options[:certificate] && options[:private_key]
34
+
35
+ @ssl_context.cert =
36
+ OpenSSL::X509::Certificate.new( File.open( options[:certificate] ) )
37
+ @ssl_context.key =
38
+ OpenSSL::PKey::RSA.new( File.open( options[:private_key] ) )
39
+
40
+ @ssl_context.ca_file = options[:ca]
41
+ @ssl_context.verify_mode =
42
+ OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
43
+
44
+ elsif @role == :server
45
+ @ssl_context.key = OpenSSL::PKey::RSA.new( 2048 )
46
+ @ssl_context.cert = OpenSSL::X509::Certificate.new
47
+ @ssl_context.cert.subject = OpenSSL::X509::Name.new( [['CN', 'localhost']] )
48
+ @ssl_context.cert.issuer = @ssl_context.cert.subject
49
+ @ssl_context.cert.public_key = @ssl_context.key
50
+ @ssl_context.cert.not_before = Time.now
51
+ @ssl_context.cert.not_after = Time.now + 60 * 60 * 24
52
+ @ssl_context.cert.version = 2
53
+ @ssl_context.cert.serial = 1
54
+
55
+ @ssl_context.cert.sign( @ssl_context.key, OpenSSL::Digest::SHA1.new )
56
+ end
57
+
58
+ if @role == :server
59
+ @socket = OpenSSL::SSL::SSLServer.new( @socket, @ssl_context )
60
+ else
61
+ @socket = OpenSSL::SSL::SSLSocket.new( @socket, @ssl_context )
62
+ @socket.sync_close = true
63
+
64
+ # We've switched to SSL, a connection needs to be re-established
65
+ # via the SSL handshake.
66
+ @connected = false
67
+
68
+ _connect if unix?
69
+ end
70
+
71
+ @socket
72
+ end
73
+
74
+ # Performs an SSL handshake in addition to a plaintext connect operation.
75
+ #
76
+ # @private
77
+ def _connect
78
+ return if @ssl_connected
79
+
80
+ Error.translate do
81
+ @plaintext_connected ||= super
82
+ return if !@plaintext_connected
83
+
84
+ # Mark the connection as not connected due to the pending SSL handshake.
85
+ @connected = false
86
+
87
+ @socket.connect_nonblock
88
+ @ssl_connected = @connected = true
89
+ end
90
+ rescue IO::WaitReadable, IO::WaitWritable, Errno::EINPROGRESS
91
+ rescue Error => e
92
+ close e
93
+ end
94
+
95
+ # First checks if there's a pending SSL #accept operation when this
96
+ # connection is a server handler which has been passed an accepted
97
+ # plaintext connection.
98
+ #
99
+ # @private
100
+ def _write( *args )
101
+ return ssl_accept if accept?
102
+
103
+ super( *args )
104
+ end
105
+
106
+ # First checks if there's a pending SSL #accept operation when this
107
+ # connection is a server handler which has been passed an accepted
108
+ # plaintext connection.
109
+ #
110
+ # @private
111
+ def _read
112
+ return ssl_accept if accept?
113
+
114
+ super
115
+ rescue OpenSSL::SSL::SSLErrorWaitReadable
116
+ end
117
+
118
+ private
119
+
120
+ def ssl_accept
121
+ Error.translate do
122
+ @accepted = !!@socket.accept_nonblock
123
+ end
124
+ rescue IO::WaitReadable, IO::WaitWritable
125
+ rescue Error => e
126
+ close e
127
+ false
128
+ end
129
+
130
+ def accept?
131
+ return false if @accepted
132
+ return false if role != :server || !@socket.is_a?( OpenSSL::SSL::SSLSocket )
133
+
134
+ true
135
+ end
136
+
137
+ # Accepts a new SSL client connection.
138
+ #
139
+ # @return [OpenSSL::SSL::SSLSocket, nil]
140
+ # New connection or `nil` if the socket isn't ready to accept new
141
+ # connections yet.
142
+ #
143
+ # @private
144
+ def socket_accept
145
+ Error.translate do
146
+ socket = to_io.accept_nonblock
147
+
148
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(
149
+ socket,
150
+ @ssl_context
151
+ )
152
+ ssl_socket.sync_close = true
153
+ ssl.accept if @start_immediately
154
+ ssl_socket
155
+ end
156
+ rescue IO::WaitReadable, IO::WaitWritable
157
+ rescue Error => e
158
+ close e
159
+ end
160
+
161
+ end
162
+
163
+ end
164
+ end