tkellem 0.9.0.beta3 → 0.9.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/tkellem/bouncer.rb +14 -30
- data/lib/tkellem/bouncer_connection.rb +58 -42
- data/lib/tkellem/daemon.rb +18 -7
- data/lib/tkellem/irc_server.rb +124 -0
- data/lib/tkellem/migrations/001_init_db.rb +22 -39
- data/lib/tkellem/migrations/002_at_connect_columns.rb +4 -21
- data/lib/tkellem/migrations/003_settings.rb +9 -18
- data/lib/tkellem/models/host.rb +2 -2
- data/lib/tkellem/models/listen_address.rb +1 -13
- data/lib/tkellem/models/network.rb +7 -11
- data/lib/tkellem/models/network_user.rb +6 -22
- data/lib/tkellem/models/setting.rb +3 -3
- data/lib/tkellem/models/user.rb +6 -13
- data/lib/tkellem/plugins/backlog.rb +31 -43
- data/lib/tkellem/socket_server.rb +7 -15
- data/lib/tkellem/tkellem_bot.rb +19 -11
- data/lib/tkellem/tkellem_server.rb +70 -77
- data/lib/tkellem/version.rb +1 -1
- data/lib/tkellem.rb +10 -1
- data/spec/bouncer_connection_spec.rb +37 -0
- data/spec/irc_server_spec.rb +145 -0
- data/spec/spec_helper.rb +17 -4
- data/tkellem.gemspec +5 -6
- metadata +31 -43
- data/lib/tkellem/celluloid_tools.rb +0 -131
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tkellem/irc_server'
|
3
|
+
|
4
|
+
include Tkellem
|
5
|
+
|
6
|
+
describe Bouncer, "connection" do
|
7
|
+
before do
|
8
|
+
EM.stub!(:add_timer).and_return(nil)
|
9
|
+
end
|
10
|
+
|
11
|
+
def make_server
|
12
|
+
network = Network.create!(:hosts => [Host.create!(:address => 'localhost', :port => 4321)], :name => 'test')
|
13
|
+
b = Bouncer.new(NetworkUser.create!(:user => User.new(:username => 'speccer'), :network => network))
|
14
|
+
b
|
15
|
+
end
|
16
|
+
|
17
|
+
def send_welcome(s, &just_before_last)
|
18
|
+
s.should_receive(:send_msg).with("USER speccer somehost tkellem :speccer@tkellem")
|
19
|
+
s.should_receive(:send_msg).with("NICK speccer")
|
20
|
+
s.should_receive(:send_msg).with("AWAY :Away")
|
21
|
+
s.connection_established(nil)
|
22
|
+
s.server_msg(IrcMessage.parse("001 blah blah"))
|
23
|
+
s.server_msg(IrcMessage.parse("002 more blah"))
|
24
|
+
s.server_msg(IrcMessage.parse("003 even more blah"))
|
25
|
+
just_before_last && just_before_last.call
|
26
|
+
s.server_msg(IrcMessage.parse("376 :end of MOTD"))
|
27
|
+
end
|
28
|
+
|
29
|
+
def connected_server
|
30
|
+
s = make_server
|
31
|
+
send_welcome(s)
|
32
|
+
s.connected?.should be_true
|
33
|
+
s
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should connect to the server on creation" do
|
37
|
+
s = make_server
|
38
|
+
s.connected?.should_not be_true
|
39
|
+
s.should_receive(:send_msg).with("USER speccer somehost tkellem :speccer@tkellem")
|
40
|
+
s.should_receive(:send_msg).with("NICK speccer")
|
41
|
+
s.connection_established(nil)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should pong" do
|
45
|
+
s = connected_server
|
46
|
+
s.should_receive(:send_msg).with("PONG tkellem!tkellem :HAI")
|
47
|
+
s.server_msg(IrcMessage.parse(":speccer!test@host ping :HAI"))
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should ignore blank lines" do
|
51
|
+
b = mock("bouncer", :trace => nil)
|
52
|
+
b.should_receive(:failsafe).and_yield
|
53
|
+
s = em(IrcServerConnection).new('connected', b, false)
|
54
|
+
b.should_receive(:server_msg).never
|
55
|
+
s.receive_line("")
|
56
|
+
end
|
57
|
+
|
58
|
+
def tk_server
|
59
|
+
$tk_server ||= TkellemServer.new
|
60
|
+
end
|
61
|
+
|
62
|
+
after(:each) do
|
63
|
+
$tk_server.stop if $tk_server
|
64
|
+
$tk_server = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def network_user(opts = {})
|
68
|
+
opts[:user] ||= @user ||= User.create!(:username => 'speccer', :password => 'test123')
|
69
|
+
opts[:network] ||= @network ||= Network.create!(:name => 'localhost')
|
70
|
+
@network_user ||= NetworkUser.create!(opts)
|
71
|
+
end
|
72
|
+
|
73
|
+
def bouncer(opts = {})
|
74
|
+
tk_server
|
75
|
+
network_user
|
76
|
+
@bouncer = $tk_server.bouncers.values.last
|
77
|
+
if opts[:connect]
|
78
|
+
@server_conn = em(IrcServerConnection).new(nil, @bouncer, false)
|
79
|
+
@server_conn.stub!(:send_data)
|
80
|
+
@bouncer.connection_established(@server_conn)
|
81
|
+
@bouncer.send :ready!
|
82
|
+
end
|
83
|
+
@bouncer
|
84
|
+
end
|
85
|
+
|
86
|
+
def client_connection(opts = {})
|
87
|
+
@client ||= em(BouncerConnection).new(tk_server, false)
|
88
|
+
if opts[:connect]
|
89
|
+
end
|
90
|
+
@client
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should force the client nick on connect" do
|
94
|
+
network_user(:nick => 'mynick')
|
95
|
+
bouncer(:connect => true)
|
96
|
+
@bouncer.server_msg(m ":mynick JOIN #t1")
|
97
|
+
client_connection
|
98
|
+
@client.should_receive(:send_msg).with(":some_other_nick NICK mynick")
|
99
|
+
@client.should_receive(:send_msg).with(":mynick JOIN #t1")
|
100
|
+
@client.receive_line("PASS test123")
|
101
|
+
@client.receive_line("NICK some_other_nick")
|
102
|
+
@client.receive_line("USER #{@user.username}@#{@network.name} a b :c")
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should attempt another nick if the default is taken" do
|
106
|
+
network_user(:nick => 'mynick')
|
107
|
+
bouncer
|
108
|
+
@server_conn = em(IrcServerConnection).new(nil, @bouncer, false)
|
109
|
+
@server_conn.stub!(:send_data)
|
110
|
+
@bouncer.connection_established(@server_conn)
|
111
|
+
@server_conn.should_receive(:send_data).with("NICK mynick_\r\n")
|
112
|
+
@bouncer.server_msg(m ":server 433 * mynick :Nickname already in use")
|
113
|
+
@bouncer.nick.should == 'mynick_'
|
114
|
+
@bouncer.send :ready!
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should change nicks if a client sends nick after connecting" do
|
118
|
+
network_user(:nick => 'mynick')
|
119
|
+
bouncer(:connect => true)
|
120
|
+
@bouncer.server_msg(m ":mynick JOIN #t1")
|
121
|
+
client_connection
|
122
|
+
@client.should_receive(:send_msg).with(":mynick JOIN #t1")
|
123
|
+
@client.receive_line("PASS test123")
|
124
|
+
@client.receive_line("NICK mynick")
|
125
|
+
@client.receive_line("USER #{@user.username}@#{@network.name} a b :c")
|
126
|
+
@bouncer.nick.should == 'mynick'
|
127
|
+
@bouncer.client_msg(@client, m("NICK some_other"))
|
128
|
+
@bouncer.nick.should == 'some_other'
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should change nicks if a server forces nick change" do
|
132
|
+
network_user(:nick => 'mynick')
|
133
|
+
bouncer(:connect => true)
|
134
|
+
@bouncer.server_msg(m ":mynick JOIN #t1")
|
135
|
+
client_connection
|
136
|
+
@client.should_receive(:send_msg).with(":mynick JOIN #t1")
|
137
|
+
@client.receive_line("PASS test123")
|
138
|
+
@client.receive_line("NICK mynick")
|
139
|
+
@client.receive_line("USER #{@user.username}@#{@network.name} a b :c")
|
140
|
+
@bouncer.nick.should == 'mynick'
|
141
|
+
@client.should_receive(:send_msg).with(m ":mynick NICK some_other")
|
142
|
+
@bouncer.server_msg(m ":mynick NICK some_other")
|
143
|
+
@bouncer.nick.should == 'some_other'
|
144
|
+
end
|
145
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,17 +5,30 @@ require 'tkellem'
|
|
5
5
|
require 'rspec'
|
6
6
|
|
7
7
|
Tkellem::EasyLogger.logger = Logger.new("test.log")
|
8
|
+
ActiveRecord::Base.logger = Tkellem::EasyLogger.logger
|
8
9
|
|
9
|
-
|
10
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
11
|
+
ActiveRecord::Migration.verbose = false
|
12
|
+
ActiveRecord::Migrator.migrate(File.expand_path("../../lib/tkellem/migrations", __FILE__), nil)
|
10
13
|
|
11
14
|
RSpec.configure do |config|
|
12
|
-
config.around(:each) do |
|
13
|
-
|
14
|
-
|
15
|
+
config.around(:each) do |test|
|
16
|
+
EM.run do
|
17
|
+
ActiveRecord::Base.transaction do
|
18
|
+
test.run
|
19
|
+
raise ActiveRecord::Rollback
|
20
|
+
end
|
21
|
+
EM.stop
|
15
22
|
end
|
16
23
|
end
|
17
24
|
|
18
25
|
def m(line)
|
19
26
|
IrcMessage.parse(line)
|
20
27
|
end
|
28
|
+
|
29
|
+
def em(mod)
|
30
|
+
c = Class.new
|
31
|
+
c.send(:include, mod)
|
32
|
+
c
|
33
|
+
end
|
21
34
|
end
|
data/tkellem.gemspec
CHANGED
@@ -17,12 +17,11 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.default_executable = %q{tkellem}
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
|
-
s.add_dependency "
|
21
|
-
s.add_dependency "
|
22
|
-
s.add_dependency "
|
23
|
-
s.add_dependency "
|
24
|
-
s.add_dependency "sqlite3", "1.3.6"
|
20
|
+
s.add_dependency "eventmachine", "~> 1.0.3"
|
21
|
+
s.add_dependency "activerecord", "~> 4.0.0.rc2"
|
22
|
+
s.add_dependency "sqlite3", "~> 1.3.3"
|
23
|
+
s.add_dependency "rails-observers"
|
25
24
|
|
26
|
-
s.add_development_dependency "rake"
|
27
25
|
s.add_development_dependency "rspec", "~> 2.5"
|
26
|
+
s.add_development_dependency "simplecov"
|
28
27
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tkellem
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.0.
|
4
|
+
version: 0.9.0.beta4
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,16 +9,16 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
12
|
+
date: 2013-06-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: eventmachine
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.
|
21
|
+
version: 1.0.3
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,15 +26,15 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.
|
29
|
+
version: 1.0.3
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
31
|
+
name: activerecord
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
none: false
|
34
34
|
requirements:
|
35
35
|
- - ~>
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version: 0.
|
37
|
+
version: 4.0.0.rc2
|
38
38
|
type: :runtime
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -42,57 +42,57 @@ dependencies:
|
|
42
42
|
requirements:
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version: 0.
|
45
|
+
version: 4.0.0.rc2
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
47
|
+
name: sqlite3
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
49
49
|
none: false
|
50
50
|
requirements:
|
51
|
-
- -
|
51
|
+
- - ~>
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 3.
|
53
|
+
version: 1.3.3
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
none: false
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 3.
|
61
|
+
version: 1.3.3
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
63
|
+
name: rails-observers
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
65
65
|
none: false
|
66
66
|
requirements:
|
67
|
-
- - '
|
67
|
+
- - ! '>='
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
69
|
+
version: '0'
|
70
70
|
type: :runtime
|
71
71
|
prerelease: false
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
|
-
- - '
|
75
|
+
- - ! '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version:
|
77
|
+
version: '0'
|
78
78
|
- !ruby/object:Gem::Dependency
|
79
|
-
name:
|
79
|
+
name: rspec
|
80
80
|
requirement: !ruby/object:Gem::Requirement
|
81
81
|
none: false
|
82
82
|
requirements:
|
83
|
-
- -
|
83
|
+
- - ~>
|
84
84
|
- !ruby/object:Gem::Version
|
85
|
-
version:
|
86
|
-
type: :
|
85
|
+
version: '2.5'
|
86
|
+
type: :development
|
87
87
|
prerelease: false
|
88
88
|
version_requirements: !ruby/object:Gem::Requirement
|
89
89
|
none: false
|
90
90
|
requirements:
|
91
|
-
- -
|
91
|
+
- - ~>
|
92
92
|
- !ruby/object:Gem::Version
|
93
|
-
version:
|
93
|
+
version: '2.5'
|
94
94
|
- !ruby/object:Gem::Dependency
|
95
|
-
name:
|
95
|
+
name: simplecov
|
96
96
|
requirement: !ruby/object:Gem::Requirement
|
97
97
|
none: false
|
98
98
|
requirements:
|
@@ -107,22 +107,6 @@ dependencies:
|
|
107
107
|
- - ! '>='
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: '0'
|
110
|
-
- !ruby/object:Gem::Dependency
|
111
|
-
name: rspec
|
112
|
-
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
|
-
requirements:
|
115
|
-
- - ~>
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '2.5'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
|
-
requirements:
|
123
|
-
- - ~>
|
124
|
-
- !ruby/object:Gem::Version
|
125
|
-
version: '2.5'
|
126
110
|
description:
|
127
111
|
email:
|
128
112
|
- brian@codekitchen.net
|
@@ -152,9 +136,9 @@ files:
|
|
152
136
|
- lib/tkellem.rb
|
153
137
|
- lib/tkellem/bouncer.rb
|
154
138
|
- lib/tkellem/bouncer_connection.rb
|
155
|
-
- lib/tkellem/celluloid_tools.rb
|
156
139
|
- lib/tkellem/daemon.rb
|
157
140
|
- lib/tkellem/irc_message.rb
|
141
|
+
- lib/tkellem/irc_server.rb
|
158
142
|
- lib/tkellem/migrations/001_init_db.rb
|
159
143
|
- lib/tkellem/migrations/002_at_connect_columns.rb
|
160
144
|
- lib/tkellem/migrations/003_settings.rb
|
@@ -173,7 +157,9 @@ files:
|
|
173
157
|
- lib/tkellem/version.rb
|
174
158
|
- resources/bot_command_descriptions.yml
|
175
159
|
- resources/setting_descriptions.yml
|
160
|
+
- spec/bouncer_connection_spec.rb
|
176
161
|
- spec/irc_message_spec.rb
|
162
|
+
- spec/irc_server_spec.rb
|
177
163
|
- spec/spec_helper.rb
|
178
164
|
- tkellem.gemspec
|
179
165
|
homepage: http://github.com/codekitchen/tkellem
|
@@ -190,7 +176,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
190
176
|
version: '0'
|
191
177
|
segments:
|
192
178
|
- 0
|
193
|
-
hash: -
|
179
|
+
hash: -629410189197339585
|
194
180
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
195
181
|
none: false
|
196
182
|
requirements:
|
@@ -204,5 +190,7 @@ signing_key:
|
|
204
190
|
specification_version: 3
|
205
191
|
summary: IRC bouncer with multi-client support
|
206
192
|
test_files:
|
193
|
+
- spec/bouncer_connection_spec.rb
|
207
194
|
- spec/irc_message_spec.rb
|
195
|
+
- spec/irc_server_spec.rb
|
208
196
|
- spec/spec_helper.rb
|
@@ -1,131 +0,0 @@
|
|
1
|
-
require 'openssl'
|
2
|
-
require 'celluloid/io'
|
3
|
-
|
4
|
-
module Tkellem
|
5
|
-
module CelluloidTools
|
6
|
-
|
7
|
-
# Generates a new SSL context with a new cert and key
|
8
|
-
# Great for easily getting up and running, but not necessarily a good idea for
|
9
|
-
# production use
|
10
|
-
def self.generate_ssl_ctx
|
11
|
-
key = OpenSSL::PKey::RSA.new(2048)
|
12
|
-
|
13
|
-
dn = OpenSSL::X509::Name.parse("/CN=tkellem-auto")
|
14
|
-
cert = OpenSSL::X509::Certificate.new
|
15
|
-
cert.version = 2
|
16
|
-
cert.serial = 1
|
17
|
-
cert.subject = dn
|
18
|
-
cert.issuer = dn
|
19
|
-
cert.public_key = key.public_key
|
20
|
-
cert.not_before = Time.now
|
21
|
-
cert.not_after = Time.now + 94670777 # 3 years
|
22
|
-
cert.sign(key, OpenSSL::Digest::SHA1.new)
|
23
|
-
|
24
|
-
ctx = OpenSSL::SSL::SSLContext.new
|
25
|
-
ctx.key = key
|
26
|
-
ctx.cert = cert
|
27
|
-
|
28
|
-
ctx
|
29
|
-
end
|
30
|
-
|
31
|
-
class BackoffSupervisor < ::Celluloid::SupervisionGroup
|
32
|
-
attr_reader :registry
|
33
|
-
|
34
|
-
def restart_actor(actor, reason)
|
35
|
-
member = @members.find do |member|
|
36
|
-
member.actor == actor
|
37
|
-
end
|
38
|
-
raise "a group member went missing. This shouldn't be!" unless member
|
39
|
-
|
40
|
-
if reason
|
41
|
-
end
|
42
|
-
|
43
|
-
member.restart(reason)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
class Listener < Struct.new(:server, :callback)
|
48
|
-
include Celluloid::IO
|
49
|
-
|
50
|
-
def self.start(*args, &callback)
|
51
|
-
listener = self.new(*args)
|
52
|
-
listener.callback = callback
|
53
|
-
listener.async.run
|
54
|
-
listener
|
55
|
-
end
|
56
|
-
|
57
|
-
def run
|
58
|
-
loop { handle_connection(server.accept) }
|
59
|
-
end
|
60
|
-
|
61
|
-
def handle_connection(socket)
|
62
|
-
callback.(socket)
|
63
|
-
end
|
64
|
-
|
65
|
-
finalizer :close
|
66
|
-
|
67
|
-
def close
|
68
|
-
server.try(:close) unless server.try(:closed?)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
class TCPListener < Listener
|
73
|
-
def initialize(host, port)
|
74
|
-
self.server = TCPServer.new(host, port)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
class SSLListener < Listener
|
79
|
-
def initialize(host, port)
|
80
|
-
self.server = SSLServer.new(TCPServer.new(host, port), CelluloidTools.generate_ssl_ctx)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
class UnixListener < Listener
|
85
|
-
def initialize(socket_path)
|
86
|
-
self.server = UNIXServer.new(socket_path)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
module LineReader
|
91
|
-
def self.included(k)
|
92
|
-
k.send :finalizer, :close_connection
|
93
|
-
end
|
94
|
-
|
95
|
-
def readline
|
96
|
-
@delimiter ||= "\r\n"
|
97
|
-
@readline_buffer ||= ''
|
98
|
-
loop do
|
99
|
-
if idx = @readline_buffer.index(@delimiter)
|
100
|
-
postidx = idx + @delimiter.size
|
101
|
-
line = @readline_buffer[0, postidx]
|
102
|
-
@readline_buffer = @readline_buffer[postidx..-1]
|
103
|
-
return line
|
104
|
-
else
|
105
|
-
@socket.readpartial(4096, @readline_buffer)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def close_connection
|
111
|
-
@socket.close if @socket && !@socket.closed?
|
112
|
-
end
|
113
|
-
|
114
|
-
def run
|
115
|
-
loop do
|
116
|
-
line = readline.force_encoding(Encoding::UTF_8)
|
117
|
-
receive_line(line)
|
118
|
-
end
|
119
|
-
rescue EOFError, IOError, OpenSSL::SSL::SSLError
|
120
|
-
unbind
|
121
|
-
end
|
122
|
-
|
123
|
-
def receive_line(line)
|
124
|
-
end
|
125
|
-
|
126
|
-
def unbind
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
end
|
131
|
-
end
|