Skip to content

Instantly share code, notes, and snippets.

@tagomoris
Last active January 11, 2017 03:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tagomoris/3cbd5ccbe77d83c86db2756c096b227f to your computer and use it in GitHub Desktop.
Save tagomoris/3cbd5ccbe77d83c86db2756c096b227f to your computer and use it in GitHub Desktop.
Trying to use SSLSocket for servers on Cool.io
$ ruby hoge.rb
{:here=>"creating socket"}
{:here=>"creating sslsocket"}
:here=>"socket.new"}
{:here=>"connecting"}
{:here=>"calling super"}
{:here=>"super called"}
{:here=>"on_connect"}
{:here=>"trying accept", :accepted=>false}
{:here=>"failed to nonblock accept", :error=>OpenSSL::SSL::SSLErrorWaitReadable, :msg=>"read would block"}
{:here=>"trying accept", :accepted=>false}
{:here=>"successfully accepted"}
:here=>"connected"}
{:here=>"writing data"}
{:here=>"reading data"}
:here=>"on_data", :accepted=>true, :data=>"yaaaaaaaaaaaaaaaay\n"}
{:here=>"writing response"}
{:here=>"write done, detaching"}
{:here=>"closing socket"}
{:here=>"detached"}
{:here=>"data", :data=>"yaaaaaaaaaaaaaaaay\n"}
require 'cool.io'
require 'fcntl'
require 'openssl'
loop = Coolio::Loop.new
sock = TCPServer.new("127.0.0.1", 12101)
sock.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
class SSLHandler < Coolio::Socket
def initialize(sock)
p(here: "socket.new")
@sslsock = OpenSSL::SSL::SSLSocket.new(sock, create_ssl_context)
@write_buffer = ''.force_encoding('ascii-8bit')
@accepted = false
p(here: "calling super")
super(@sslsock)
p(here: "super called")
rescue => e
p(here: "rescue in initialize", error: e.class, msg: e.message)
end
def create_ssl_context
opts = {
private_key_length: 2048,
country: 'US',
state: 'CA',
locality: 'Mountain View',
common_name: 'localhost',
}
key = OpenSSL::PKey::RSA.generate(opts[:private_key_length])
issuer = subject = OpenSSL::X509::Name.new
subject.add_entry('C', opts[:country])
subject.add_entry('ST', opts[:state])
subject.add_entry('L', opts[:locality])
subject.add_entry('CN', opts[:common_name])
digest = OpenSSL::Digest::SHA1.new
cert = OpenSSL::X509::Certificate.new
cert.not_before = Time.at(0)
cert.not_after = Time.now + 5 * 365 * 86400 # 5 years after
cert.public_key = key
cert.serial = 1
cert.issuer = issuer
cert.subject = subject
cert.sign(key, digest)
# cert, key
ctx = OpenSSL::SSL::SSLContext.new('TLSv1_2')
ctx.cert = cert
ctx.key = key
ctx
end
def try_accept
p(here: "trying accept", accepted: @accepted)
return if @accepted
begin
@sslsock.accept_nonblock
p(here: "successfully accepted")
@accepted = true
rescue IO::WaitReadable, IO::WaitWritable => e
# wait for next try
p(here: "failed to nonblock accept", error: e.class, msg: e.message)
# p(here: "trying blocking accept")
# @sslsock.accept
# p(here: "succeeded to accept in blocking mode")
# @accepted = true
rescue OpenSSL::SSL::SSLError
# varification error or something else
p(here: "failed to nonblock accept", error: e.class, msg: e.message)
p(here: "going to close this socket...")
# TODO: disconnect
begin
self.detach
rescue => e
p(here: "unexpected error while closing unverified connection", error: e.class, msg: e.message)
end
end
end
def on_connect # tcp connection
p(here: "on_connect")
# try_accept
end
def on_readable
if @accepted
super
else
try_accept
end
end
def on_read(data)
p(here: "on_data", accepted: @accepted, data: data)
if @accepted
p(here: "writing response")
write(data)
else
try_accept
end
end
def write(data)
@write_buffer << data
schedule_write
data.size
end
def on_writable # overwrite Coolio#on_writable
begin
# @_write_buffer.write_to(@_io)
size = @sslsock.write_nonblock(@write_buffer)
@write_buffer.slice!(0, size)
rescue IO::WaitWritable, IO::WaitReadable
return
rescue Errno::EINTR
return
# SystemCallError catches Errno::EPIPE & Errno::ECONNRESET amongst others.
rescue SystemCallError, IOError, SocketError
return close
end
if @write_buffer.empty?
disable_write_watcher
on_write_complete
end
end
def on_write_complete
p(here: "write done, detaching")
close # detaches @_io if attached
p(here: "detached")
end
def close
p(here: "closing socket")
super
end
end
server = Coolio::TCPServer.new(sock, nil, SSLHandler)
loop.attach(server)
main = Thread.new{ loop.run }
sleep 1
p(here: "creating socket")
sock = TCPSocket.new("127.0.0.1", 12101)
p(here: "creating sslsocket")
conn = OpenSSL::SSL::SSLSocket.new(sock)
p(here: "connecting")
conn.connect
p(here: "connected")
p(here: "writing data")
conn.write "yaaaaaaaaaaaaaaaay\n"
p(here: "reading data")
data = conn.read
p(here: "data", data: data)
conn.close
server.detach
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment