1
0
Fork 0
mirror of https://github.com/puma/puma.git synced 2022-11-09 13:48:40 -05:00
puma--puma/lib/puma/minissl.rb

261 lines
6.2 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
2017-11-20 08:24:02 -05:00
begin
require 'io/wait'
rescue LoadError
end
2013-03-18 19:20:17 -04:00
module Puma
module MiniSSL
class Socket
def initialize(socket, engine)
@socket = socket
@engine = engine
@peercert = nil
2013-03-18 19:20:17 -04:00
end
2012-08-22 19:53:25 -04:00
2013-03-18 19:20:17 -04:00
def to_io
@socket
end
2012-08-22 19:53:25 -04:00
2018-03-05 23:52:34 -05:00
def closed?
@socket.closed?
end
2013-03-18 19:20:17 -04:00
def readpartial(size)
while true
output = @engine.read
return output if output
2012-08-22 19:53:25 -04:00
2013-03-18 19:20:17 -04:00
data = @socket.readpartial(size)
@engine.inject(data)
output = @engine.read
2012-08-23 01:34:10 -04:00
2013-03-18 19:20:17 -04:00
return output if output
2012-08-23 01:34:10 -04:00
2013-03-18 19:20:17 -04:00
while neg_data = @engine.extract
@socket.write neg_data
end
2012-08-23 01:34:10 -04:00
end
end
def engine_read_all
output = @engine.read
while output and additional_output = @engine.read
output << additional_output
end
output
end
def read_nonblock(size, *_)
# *_ is to deal with keyword args that were added
# at some point (and being used in the wild)
2013-03-18 19:20:17 -04:00
while true
output = engine_read_all
2013-03-18 19:20:17 -04:00
return output if output
2012-08-23 01:34:10 -04:00
2017-11-20 08:24:02 -05:00
begin
data = @socket.read_nonblock(size, exception: false)
if data == :wait_readable || data == :wait_writable
if @socket.to_io.respond_to?(data)
@socket.to_io.__send__(data)
elsif data == :wait_readable
IO.select([@socket.to_io])
else
IO.select(nil, [@socket.to_io])
end
elsif !data
return nil
2017-11-20 08:24:02 -05:00
else
break
end
2017-11-20 08:24:02 -05:00
end while true
2012-08-23 01:34:10 -04:00
2013-03-18 19:20:17 -04:00
@engine.inject(data)
output = engine_read_all
2012-08-22 19:53:25 -04:00
2013-03-18 19:20:17 -04:00
return output if output
2012-08-22 19:53:25 -04:00
2013-03-18 19:20:17 -04:00
while neg_data = @engine.extract
@socket.write neg_data
end
2012-08-22 19:53:25 -04:00
end
end
2013-03-18 19:20:17 -04:00
def write(data)
return 0 if data.empty?
need = data.bytesize
2012-08-22 19:53:25 -04:00
2013-03-18 19:20:17 -04:00
while true
wrote = @engine.write data
enc = @engine.extract
2012-08-22 19:53:25 -04:00
while enc
@socket.write enc
enc = @engine.extract
2013-03-18 19:20:17 -04:00
end
2012-08-22 19:53:25 -04:00
2013-03-18 19:20:17 -04:00
need -= wrote
2012-08-22 19:53:25 -04:00
return data.bytesize if need == 0
2012-08-22 19:53:25 -04:00
data = data[wrote..-1]
2013-03-18 19:20:17 -04:00
end
2012-08-22 19:53:25 -04:00
end
2013-03-18 19:20:17 -04:00
alias_method :syswrite, :write
2016-09-17 17:16:14 -04:00
alias_method :<<, :write
2012-09-10 11:50:43 -04:00
# This is a temporary fix to deal with websockets code using
# write_nonblock. The problem with implementing it properly
# is that it means we'd have to have the ability to rewind
# an engine because after we write+extract, the socket
# write_nonblock call might raise an exception and later
# code would pass the same data in, but the engine would think
# it had already written the data in. So for the time being
# (and since write blocking is quite rare), go ahead and actually
# block in write_nonblock.
def write_nonblock(data, *_)
write data
end
2013-03-18 19:20:17 -04:00
def flush
@socket.flush
end
2012-08-23 00:43:40 -04:00
def read_and_drop(timeout = 1)
return :timeout unless IO.select([@socket], nil, nil, timeout)
return :eof unless read_nonblock(1024)
:drop
rescue Errno::EAGAIN
# do nothing
:eagain
end
def should_drop_bytes?
@engine.init? || !@engine.shutdown
end
def close
begin
# Read any drop any partially initialized sockets and any received bytes during shutdown.
# Don't let this socket hold this loop forever.
# If it can't send more packets within 1s, then give up.
while should_drop_bytes?
return if [:timeout, :eof].include?(read_and_drop(1))
end
rescue IOError, SystemCallError
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
# nothing
ensure
@socket.close
end
2013-03-18 19:20:17 -04:00
end
2012-08-23 00:43:40 -04:00
2013-03-18 19:20:17 -04:00
def peeraddr
@socket.peeraddr
end
def peercert
return @peercert if @peercert
raw = @engine.peercert
return nil unless raw
@peercert = OpenSSL::X509::Certificate.new raw
end
end
if defined?(JRUBY_VERSION)
class SSLError < StandardError
# Define this for jruby even though it isn't used.
end
def self.check; end
2012-08-23 00:43:40 -04:00
end
2012-08-22 19:53:25 -04:00
2013-03-18 19:20:17 -04:00
class Context
attr_accessor :verify_mode
if defined?(JRUBY_VERSION)
# jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
attr_reader :keystore
attr_accessor :keystore_pass
attr_accessor :ssl_cipher_list
def keystore=(keystore)
raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore
@keystore = keystore
end
2016-07-24 17:29:23 -04:00
def check
raise "Keystore not configured" unless @keystore
end
else
# non-jruby Context properties
attr_reader :key
attr_reader :cert
attr_reader :ca
attr_accessor :ssl_cipher_filter
def key=(key)
raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
@key = key
end
def cert=(cert)
raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
@cert = cert
end
def ca=(ca)
raise ArgumentError, "No such ca file '#{ca}'" unless File.exist? ca
@ca = ca
end
2016-07-24 17:29:23 -04:00
def check
raise "Key not configured" unless @key
raise "Cert not configured" unless @cert
end
2013-03-18 19:20:17 -04:00
end
end
2012-08-23 00:43:40 -04:00
2013-03-18 19:20:17 -04:00
VERIFY_NONE = 0
VERIFY_PEER = 1
VERIFY_FAIL_IF_NO_PEER_CERT = 2
2012-08-23 00:43:40 -04:00
2013-03-18 19:20:17 -04:00
class Server
def initialize(socket, ctx)
@socket = socket
@ctx = ctx
end
2012-08-22 19:53:25 -04:00
2013-03-18 19:20:17 -04:00
def to_io
@socket
end
2012-08-22 19:53:25 -04:00
2013-03-18 19:20:17 -04:00
def accept
2016-07-24 17:29:23 -04:00
@ctx.check
2013-03-18 19:20:17 -04:00
io = @socket.accept
engine = Engine.server @ctx
2012-08-22 19:53:25 -04:00
2013-03-18 19:20:17 -04:00
Socket.new io, engine
end
2012-08-23 00:43:40 -04:00
2013-03-18 19:20:17 -04:00
def accept_nonblock
2016-07-24 17:29:23 -04:00
@ctx.check
2013-03-18 19:20:17 -04:00
io = @socket.accept_nonblock
engine = Engine.server @ctx
2012-09-10 11:50:43 -04:00
2013-03-18 19:20:17 -04:00
Socket.new io, engine
end
2012-09-10 11:50:43 -04:00
2013-03-18 19:20:17 -04:00
def close
@socket.close unless @socket.closed? # closed? call is for Windows
2013-03-18 19:20:17 -04:00
end
2012-08-23 00:43:40 -04:00
end
2012-08-22 19:53:25 -04:00
end
end