iodine 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +86 -8
- data/lib/iodine.rb +45 -2
- data/lib/iodine/core.rb +9 -37
- data/lib/iodine/http.rb +135 -0
- data/lib/iodine/http/hpack.rb +543 -0
- data/lib/iodine/http/http1.rb +217 -0
- data/lib/iodine/http/http2.rb +465 -0
- data/lib/iodine/http/rack_support.rb +105 -0
- data/lib/iodine/http/request.rb +413 -0
- data/lib/iodine/http/response.rb +355 -0
- data/lib/iodine/http/session.rb +110 -0
- data/lib/iodine/http/websocket_client.rb +224 -0
- data/lib/iodine/http/websocket_handler.rb +40 -0
- data/lib/iodine/http/websockets.rb +319 -0
- data/lib/iodine/io.rb +71 -28
- data/lib/iodine/protocol.rb +74 -25
- data/lib/iodine/settings.rb +44 -3
- data/lib/iodine/ssl_connector.rb +47 -0
- data/lib/iodine/timers.rb +5 -21
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +3 -0
- data/{bin/http_test → manual tests/core_http_test } +6 -2
- data/bin/echo b/data/manual → tests/echo +0 -0
- data/bin/em playground b/data/manual tests/em → playground +0 -0
- data/manual tests/hello_world +56 -0
- metadata +20 -6
- data/lib/iodine/ssl_protocol.rb +0 -108
data/lib/iodine/io.rb
CHANGED
@@ -2,19 +2,25 @@ module Iodine
|
|
2
2
|
|
3
3
|
public
|
4
4
|
|
5
|
-
# Gets the last time at which the IO Reactor was last active (last "tick").
|
5
|
+
# @return [Time] Gets the last time at which the IO Reactor was last active (last "tick").
|
6
6
|
def time
|
7
7
|
@time
|
8
8
|
end
|
9
|
-
|
10
|
-
#
|
9
|
+
# Replaces (or creates) an IO's protocol object.
|
10
|
+
#
|
11
|
+
# Accepts 2 arguments, in the following order:
|
11
12
|
#
|
12
|
-
# accepts:
|
13
13
|
# io:: the raw IO object.
|
14
|
-
# protocol:: a protocol instance - should be a
|
15
|
-
#
|
14
|
+
# protocol:: a protocol instance - should be an instance of a class inheriting from {Iodine::Protocol}. type will NOT be checked - but Iodine could break if there is a type mismatch.
|
15
|
+
# @return [Protocol]
|
16
16
|
def switch_protocol *args
|
17
17
|
@io_in << args
|
18
|
+
args[1]
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Array] Returns an Array with all the currently active connection's Protocol instances.
|
22
|
+
def to_a
|
23
|
+
@ios.values
|
18
24
|
end
|
19
25
|
|
20
26
|
|
@@ -22,14 +28,16 @@ module Iodine
|
|
22
28
|
|
23
29
|
@port = (ARGV.index('-p') && ARGV[ARGV.index('-p') + 1]) || ENV['PORT'] || 3000
|
24
30
|
@bind = (ARGV.index('-ip') && ARGV[ARGV.index('-ip') + 1]) || ENV['IP'] || "0.0.0.0"
|
31
|
+
@ssl = (ARGV.index('ssl') && true) || (@port == 443)
|
25
32
|
@protocol = nil
|
26
33
|
@ssl_context = nil
|
34
|
+
@ssl_protocols = {}
|
27
35
|
@time = Time.now
|
28
36
|
|
29
37
|
@timeout_proc = Proc.new {|prot| prot.timeout?(@time) }
|
30
38
|
@status_loop = Proc.new {|io| @io_out << io if io.closed? || !(io.stat.readable? rescue false) }
|
31
39
|
@close_callback = Proc.new {|prot| prot.on_close if prot }
|
32
|
-
REACTOR = Proc.new do
|
40
|
+
REACTOR = [ (Proc.new do
|
33
41
|
if @queue.empty?
|
34
42
|
#clear any closed IO objects.
|
35
43
|
@time = Time.now
|
@@ -42,43 +50,41 @@ module Iodine
|
|
42
50
|
until @io_out.empty?
|
43
51
|
o_io = @io_out.pop
|
44
52
|
o_io.close unless o_io.closed?
|
45
|
-
|
53
|
+
run @ios.delete(o_io), &@close_callback
|
46
54
|
end
|
47
55
|
# react to IO events
|
48
56
|
begin
|
49
57
|
r = IO.select(@ios.keys, nil, nil, 0.15)
|
50
|
-
r[0].each {|io| queue @ios[io] } if r
|
58
|
+
r[0].each {|io| @queue << [@ios[io]] } if r
|
51
59
|
rescue => e
|
52
60
|
|
53
61
|
end
|
54
62
|
unless @stop && @queue.empty?
|
55
63
|
# @ios.values.each &@timeout_loop
|
56
|
-
@check_timers &&
|
57
|
-
queue REACTOR
|
64
|
+
@check_timers && (@queue << @check_timers)
|
65
|
+
@queue << REACTOR
|
58
66
|
end
|
59
67
|
else
|
60
|
-
queue REACTOR
|
68
|
+
@queue << REACTOR
|
61
69
|
end
|
62
|
-
end
|
70
|
+
end )]
|
63
71
|
# internal helper methods and classes.
|
64
72
|
module Base
|
65
73
|
# the server listener Protocol.
|
66
74
|
class Listener < ::Iodine::Protocol
|
67
75
|
def on_open
|
68
76
|
@protocol = Iodine.protocol
|
77
|
+
@ssl = Iodine.ssl
|
69
78
|
end
|
70
79
|
def call
|
71
80
|
begin
|
72
81
|
n_io = nil
|
73
82
|
loop do
|
74
83
|
n_io = @io.accept_nonblock
|
75
|
-
@protocol.accept(n_io)
|
84
|
+
@protocol.accept(n_io, @ssl)
|
76
85
|
end
|
77
86
|
rescue Errno::EWOULDBLOCK => e
|
78
87
|
|
79
|
-
rescue OpenSSL::SSL::SSLError => e
|
80
|
-
warn "SSL Error - Self-signed Certificate?".freeze
|
81
|
-
n_io.close if n_io && !n_io.closed?
|
82
88
|
rescue => e
|
83
89
|
n_io.close if n_io && !n_io.closed?
|
84
90
|
@stop = true
|
@@ -92,19 +98,56 @@ module Iodine
|
|
92
98
|
## remember to set traps (once) when 'listen' is called.
|
93
99
|
run do
|
94
100
|
next unless @protocol
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
+
if @protocol.is_a?( ::Class ) && @protocol.ancestors.include?( ::Iodine::Protocol )
|
102
|
+
begin
|
103
|
+
@server = ::TCPServer.new(@bind, @port)
|
104
|
+
rescue => e
|
105
|
+
Iodine.fatal e.message
|
106
|
+
Iodine.fatal "Running existing tasks and exiting."
|
107
|
+
@stop = true
|
108
|
+
next
|
109
|
+
end
|
110
|
+
shut_down_proc = Proc.new {|protocol| protocol.on_shutdown ; protocol.close }
|
111
|
+
on_shutdown do
|
112
|
+
@logger << "Stopping to listen on port #{@port} and shutting down.\n"
|
113
|
+
@server.close unless @server.closed?
|
114
|
+
@ios.values.each {|p| run p, &shut_down_proc }
|
115
|
+
end
|
116
|
+
::Iodine::Base::Listener.accept(@server, false)
|
117
|
+
@logger << "Iodine #{VERSION} is listening on port #{@port}#{ ' to SSL/TLS connections.' if @ssl}\n"
|
118
|
+
if @spawn_count && @spawn_count.to_i > 1 && Process.respond_to?(:fork)
|
119
|
+
@logger << "Server will run using #{@spawn_count.to_i} processes - Spawning #{@spawn_count.to_i - 1 } more processes.\n"
|
120
|
+
(@spawn_count.to_i - 1).times do
|
121
|
+
Process.fork do
|
122
|
+
@logger << "Spawned process: #{Process.pid}.\n"
|
123
|
+
on_shutdown { @logger << "Shutting down process #{Process.pid}.\n" }
|
124
|
+
threads = []
|
125
|
+
@queue.clear
|
126
|
+
@queue << REACTOR
|
127
|
+
@thread_count.times { threads << Thread.new { cycle } }
|
128
|
+
unless @stop
|
129
|
+
old_int_trap = trap('INT') { throw :stop; trap('INT', old_int_trap) if old_int_trap }
|
130
|
+
old_term_trap = trap('TERM') { throw :stop; trap('TERM', old_term_trap) if old_term_trap }
|
131
|
+
catch(:stop) { sleep }
|
132
|
+
@stop = true
|
133
|
+
# setup exit timeout.
|
134
|
+
threads.each {|t| Thread.new {sleep 25; t.kill; t.kill } }
|
135
|
+
end
|
136
|
+
threads.each {|t| t.join rescue true }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
@logger << "Press ^C to stop the server.\n"
|
142
|
+
else
|
143
|
+
@logger << "Iodine #{VERSION} is running.\n"
|
144
|
+
on_shutdown do
|
145
|
+
@logger << "Iodine says goodbye.\n"
|
146
|
+
end
|
147
|
+
@logger << "Press ^C to stop the cycling.\n"
|
101
148
|
end
|
102
|
-
@server = ::TCPServer.new(@bind, @port)
|
103
|
-
::Iodine::Base::Listener.accept(@server)
|
104
|
-
@logger << "Iodine #{VERSION} is listening on port #{@port}\n"
|
105
149
|
old_int_trap = trap('INT') { throw :stop; trap('INT', old_int_trap) if old_int_trap }
|
106
150
|
old_term_trap = trap('TERM') { throw :stop; trap('TERM', old_term_trap) if old_term_trap }
|
107
|
-
@
|
108
|
-
queue REACTOR
|
151
|
+
@queue << REACTOR
|
109
152
|
end
|
110
153
|
end
|
data/lib/iodine/protocol.rb
CHANGED
@@ -4,12 +4,12 @@ module Iodine
|
|
4
4
|
#
|
5
5
|
# A new protocol instance will be created for every network connection.
|
6
6
|
#
|
7
|
-
# The recommended use is to inherit this class
|
7
|
+
# The recommended use is to inherit this class and override any of the following:
|
8
8
|
# on_open:: called whenever the Protocol is initialized. Override this to initialize the Protocol object.
|
9
9
|
# on_message(data):: called whenever data is received from the IO. Override this to implement the actual network protocol.
|
10
10
|
# on_close:: called AFTER the Protocol's IO is closed.
|
11
11
|
# on_shutdown:: called when the server's shutdown process had started and BEFORE the Protocol's IO is closed. It allows graceful shutdown for network protocols.
|
12
|
-
# ping::
|
12
|
+
# ping:: called when timeout was reached. see {#set_timeout}
|
13
13
|
#
|
14
14
|
# Once the network protocol class was created, remember to tell Iodine about it:
|
15
15
|
# class MyProtocol << Iodine::Protocol
|
@@ -20,12 +20,14 @@ module Iodine
|
|
20
20
|
#
|
21
21
|
class Protocol
|
22
22
|
|
23
|
-
# returns the
|
23
|
+
# returns the IO object. If the connection uses SSL/TLS, this will return the SSLSocket (not a native IO object).
|
24
|
+
#
|
25
|
+
# Using one of the Protocol methods {#write}, {#read}, {#close} is prefered over direct access.
|
24
26
|
attr_reader :io
|
25
27
|
|
26
28
|
# Sets the timeout in seconds for IO activity (set timeout within {#on_open}).
|
27
29
|
#
|
28
|
-
# After timeout is reached, {#ping} will be
|
30
|
+
# After timeout is reached, {#ping} will be called. The connection will be closed if {#ping} returns `false` or `nil`.
|
29
31
|
def set_timeout seconds
|
30
32
|
@timeout = seconds
|
31
33
|
end
|
@@ -46,11 +48,19 @@ module Iodine
|
|
46
48
|
def on_shutdown
|
47
49
|
end
|
48
50
|
|
49
|
-
# This method is called whenever a timeout has occurred. Either implement a ping or
|
50
|
-
#
|
51
|
-
# A `false` or `nil` return value will cause disconnection
|
51
|
+
# This method is called whenever a timeout has occurred. Either implement a ping or close the connection.
|
52
|
+
# The default implementation closes the connection.
|
52
53
|
def ping
|
53
|
-
|
54
|
+
close
|
55
|
+
end
|
56
|
+
|
57
|
+
#############
|
58
|
+
## functionality and helpers
|
59
|
+
|
60
|
+
|
61
|
+
# returns true id the protocol is using an encrypted connection (the IO is an OpenSSL::SSL::SSLSocket).
|
62
|
+
def ssl?
|
63
|
+
@io.is_a?(OpenSSL::SSL::SSLSocket) # io.npn_protocol
|
54
64
|
end
|
55
65
|
|
56
66
|
|
@@ -65,9 +75,15 @@ module Iodine
|
|
65
75
|
# reads from the IO up to the specified number of bytes (defaults to ~2Mb).
|
66
76
|
def read size = 2_097_152
|
67
77
|
touch
|
68
|
-
@io.recv_nonblock( size )
|
69
|
-
|
78
|
+
ssl? ? read_ssl(size) : @io.recv_nonblock( size )
|
79
|
+
# @io.read_nonblock( size ) # this one is a bit slower...
|
80
|
+
rescue OpenSSL::SSL::SSLErrorWaitReadable, IO::WaitReadable, IO::WaitWritable
|
70
81
|
nil
|
82
|
+
rescue IOError, Errno::ECONNRESET
|
83
|
+
close
|
84
|
+
rescue => e
|
85
|
+
Iodine.warn "Protocol read error: #{e.class.name} #{e.message} (closing connection)"
|
86
|
+
close
|
71
87
|
end
|
72
88
|
|
73
89
|
# this method, writes data to the socket / io object.
|
@@ -79,36 +95,48 @@ module Iodine
|
|
79
95
|
r
|
80
96
|
end
|
81
97
|
rescue => e
|
82
|
-
# GReactor.warn e
|
83
98
|
close
|
84
99
|
end
|
85
100
|
end
|
86
101
|
|
102
|
+
# returns the connection's unique local ID as a Hex string.
|
103
|
+
#
|
104
|
+
# This can be used locally but not across processes.
|
105
|
+
def id
|
106
|
+
@id ||= @io.to_io.to_s(16)
|
107
|
+
end
|
87
108
|
|
88
|
-
#
|
89
|
-
# Iodine receives data using this protocol's IO.
|
109
|
+
# returns an [Enumerable](http://ruby-doc.org/core-2.2.3/Enumerable.html) with all the active connections.
|
90
110
|
#
|
91
|
-
#
|
92
|
-
|
93
|
-
|
94
|
-
|
111
|
+
# if a block is passed, than this method exceutes the block.
|
112
|
+
def self.each
|
113
|
+
if block_given?
|
114
|
+
Iodine.to_a.each {|p| yield(p) if p.is_a?(self) }
|
115
|
+
else
|
116
|
+
( Iodine.to_a.select {|p| p.is_a?(self) } ).each
|
117
|
+
end
|
95
118
|
end
|
96
119
|
|
120
|
+
|
97
121
|
#################
|
98
122
|
## the following are Iodine's "system" methods, used internally. Don't override.
|
99
123
|
|
100
124
|
|
101
125
|
# This method is used by Iodine to initialized the Protocol.
|
102
126
|
#
|
127
|
+
# A new Protocol instance set itself up as the IO's protocol (replacing any previous protocol).
|
128
|
+
#
|
103
129
|
# Normally you won't need to override this method. Override {#on_open} instead.
|
104
130
|
def initialize io
|
105
131
|
@timeout ||= nil
|
106
132
|
@send_locker = Mutex.new
|
107
133
|
@locker = Mutex.new
|
108
134
|
@io = io
|
109
|
-
switch_protocol self
|
110
135
|
touch
|
111
|
-
|
136
|
+
@locker.synchronize do
|
137
|
+
Iodine.switch_protocol @io.to_io, self
|
138
|
+
on_open
|
139
|
+
end
|
112
140
|
end
|
113
141
|
|
114
142
|
# Called by Iodine whenever there is data in the IO's read buffer.
|
@@ -132,24 +160,45 @@ module Iodine
|
|
132
160
|
#
|
133
161
|
# Normally you won't need to override this method. See {#ping}
|
134
162
|
def timeout? time
|
135
|
-
|
163
|
+
ping if @timeout && !@send_locker.locked? && ( (time - @last_active) > @timeout )
|
136
164
|
end
|
137
165
|
|
138
166
|
|
139
167
|
|
168
|
+
|
169
|
+
protected
|
140
170
|
# This method is used by Iodine to create the IO handler whenever a new connection is established.
|
141
171
|
#
|
142
172
|
# Normally you won't need to override this method.
|
143
|
-
def self.accept io
|
144
|
-
self.new(io)
|
173
|
+
def self.accept io, ssl
|
174
|
+
ssl ? SSLConnector.new(io, self) : self.new(io)
|
145
175
|
end
|
146
|
-
|
147
|
-
protected
|
148
|
-
|
149
176
|
# This methos updates the timeout "watch", signifying the IO was active.
|
150
177
|
def touch
|
151
178
|
@last_active = Iodine.time
|
152
179
|
end
|
180
|
+
|
181
|
+
# reads from the IO up to the specified number of bytes (defaults to ~1Mb).
|
182
|
+
def read_ssl size
|
183
|
+
@send_locker.synchronize do
|
184
|
+
data = ''
|
185
|
+
begin
|
186
|
+
(data << @io.read_nonblock(size).to_s) until data.bytesize >= size
|
187
|
+
rescue OpenSSL::SSL::SSLErrorWaitReadable, IO::WaitReadable, IO::WaitWritable
|
188
|
+
|
189
|
+
rescue IOError
|
190
|
+
close
|
191
|
+
rescue => e
|
192
|
+
Iodine.warn "SSL Protocol read error: #{e.class.name} #{e.message} (closing connection)"
|
193
|
+
close
|
194
|
+
end
|
195
|
+
return false if data.to_s.empty?
|
196
|
+
touch
|
197
|
+
data
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
|
153
202
|
end
|
154
203
|
|
155
204
|
end
|
data/lib/iodine/settings.rb
CHANGED
@@ -14,6 +14,15 @@ module Iodine
|
|
14
14
|
@thread_count = count
|
15
15
|
end
|
16
16
|
|
17
|
+
# Sets the number of processes that should be spawned in Server mode. Defaults to 1 (no processes spawned).
|
18
|
+
#
|
19
|
+
# * Forking (spwaning processes) might NOT work on all systems (forking is supported by Ruby on Unix systems).
|
20
|
+
# * Please make sure your code is safe to fork into different processes. For instance, Websocket broadcasting and unicasting won't work across different processes unless synced using an external Pub/Sub service/database such as Redis.
|
21
|
+
# * Forking might cause some tasks (such as time based tasks) to be performed twice (once for each process). This is a feature. To avoid duplicated task performance, use a task (delayed execution) to initialize any tasks you want to perform only once. While the initial time based tasks and the server are shared across processes, the initial task stack will only run on the main process.
|
22
|
+
def processes= count
|
23
|
+
@spawn_count = count
|
24
|
+
end
|
25
|
+
|
17
26
|
# Sets the server port. Defaults to the runtime `-p` argument, or the ENV['PORT'] or 3000 (in this order).
|
18
27
|
def port= port
|
19
28
|
@port = port
|
@@ -23,7 +32,9 @@ module Iodine
|
|
23
32
|
@bind = address
|
24
33
|
end
|
25
34
|
|
26
|
-
# Sets the Protocol the Iodine Server will use. Should be a child of Protocol
|
35
|
+
# Sets the Protocol the Iodine Server will use. Should be a child of {Iodine::Protocol}. Defaults to nil (no server).
|
36
|
+
#
|
37
|
+
# If the protocol passed does NOT inherit from {Iodine::Protocol}, Iodine will cycle without initiating a server until stopped (TimedEvent mode).
|
27
38
|
def protocol= protocol
|
28
39
|
@stop = protocol ? false : true
|
29
40
|
@protocol = protocol
|
@@ -33,15 +44,45 @@ module Iodine
|
|
33
44
|
@protocol
|
34
45
|
end
|
35
46
|
|
36
|
-
# Sets the SSL
|
47
|
+
# Sets the SSL flag, so that Iodine will require that new connection be encrypted.
|
48
|
+
def ssl= required
|
49
|
+
@ssl = required && true
|
50
|
+
end
|
51
|
+
# Returns true if Iodine will require that new connection be encrypted.
|
52
|
+
def ssl
|
53
|
+
@ssl
|
54
|
+
end
|
55
|
+
|
56
|
+
# Sets the SSL Context to be used when using an encrypted connection. Defaults to a self signed certificate and no verification.
|
57
|
+
#
|
58
|
+
# Manually setting the context will automatically set the SSL flag,
|
59
|
+
# so that Iodine will require encryption for new incoming connections.
|
37
60
|
def ssl_context= context
|
61
|
+
@ssl = true
|
38
62
|
@ssl_context = context
|
39
63
|
end
|
40
|
-
|
64
|
+
|
65
|
+
# Gets the SSL Context to be used when using an encrypted connection.
|
41
66
|
def ssl_context
|
42
67
|
@ssl_context ||= init_ssl_context
|
43
68
|
end
|
44
69
|
|
70
|
+
# Sets the an SSL Protocol Hash (`'name' => Protocol`), allowing dynamic Protocol Negotiation.
|
71
|
+
# At the moment only NPN is supported. ALPN support should be established in a future release.
|
72
|
+
#
|
73
|
+
# * please notice that using allowing dynamic Protocol Negotiation could cause unexpected protocol choices when attempting to implement Opportunistic Encryption with {Iodine::SSLConnector}.
|
74
|
+
def ssl_protocols= protocol_hash
|
75
|
+
raise TypeError, "Iodine.ssl_protocols should be a Hash with Strings for keys (protocol identifiers) and Classes as values (Protocol classes)." unless protocol_hash.is_a?(Hash)
|
76
|
+
@ssl = true
|
77
|
+
ssl_context.npn_protocols = protocol_hash.keys
|
78
|
+
@ssl_protocols = protocol_hash
|
79
|
+
end
|
80
|
+
|
81
|
+
# Gets the SSL Protocol Hash used for
|
82
|
+
def ssl_protocols
|
83
|
+
@ssl_protocols
|
84
|
+
end
|
85
|
+
|
45
86
|
|
46
87
|
protected
|
47
88
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Iodine
|
2
|
+
|
3
|
+
# This is a mini-protocol used only to implement the SSL Handshake in a non-blocking manner,
|
4
|
+
# allowing for a hardcoded timeout (which you can monkey patch) of 3 seconds.
|
5
|
+
class SSLConnector < Protocol
|
6
|
+
def initialize io, protocol
|
7
|
+
@protocol = protocol
|
8
|
+
super(io)
|
9
|
+
end
|
10
|
+
TIMEOUT = 3 # hardcoded SSL/TLS handshake timeout
|
11
|
+
def on_open
|
12
|
+
timeout = TIMEOUT
|
13
|
+
@ssl_socket = ::OpenSSL::SSL::SSLSocket.new(@io, ::Iodine.ssl_context)
|
14
|
+
@ssl_socket.sync_close = true
|
15
|
+
end
|
16
|
+
|
17
|
+
# atempt an SSL Handshale
|
18
|
+
def call
|
19
|
+
return if @locker.locked?
|
20
|
+
return unless @locker.try_lock
|
21
|
+
begin
|
22
|
+
@ssl_socket.accept_nonblock
|
23
|
+
rescue ::IO::WaitReadable, ::IO::WaitWritable
|
24
|
+
return
|
25
|
+
rescue ::OpenSSL::SSL::SSLError
|
26
|
+
@e = ::OpenSSL::SSL::SSLError.new "Self-signed Certificate?".freeze
|
27
|
+
close
|
28
|
+
return
|
29
|
+
rescue => e
|
30
|
+
::Iodine.warn "SSL Handshake failed with: #{e.message}".freeze
|
31
|
+
@e = e
|
32
|
+
close
|
33
|
+
return
|
34
|
+
ensure
|
35
|
+
@locker.unlock
|
36
|
+
end
|
37
|
+
( (@ssl_socket.npn_protocol && ::Iodine.ssl_protocols[@ssl_socket.npn_protocol]) || @protocol).new @ssl_socket
|
38
|
+
end
|
39
|
+
def on_close
|
40
|
+
# inform
|
41
|
+
::Iodine.warn "SSL Handshake #{@e ? "failed with: #{@e.message} (#{@e.class.name})" : 'timed-out.'}".freeze
|
42
|
+
# the core @io is already closed, but let's make sure the SSL object is closed as well.
|
43
|
+
@ssl_socket.close unless @ssl_socket.closed?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|