OpenSSL
IO
buffering mix-in module.
This module allows an OpenSSL::SSL::SSLSocket
to behave like an IO
.
You typically won’t use this module directly, you can see it implemented in OpenSSL::SSL::SSLSocket
.
Default size to read from or write to the SSLSocket for buffer operations.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 63
def initialize(*)
super
@eof = false
@rbuffer = Buffer.new
@sync = @io.sync
end
Creates an instance of OpenSSL’s buffering IO
module.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 413
def <<(s)
do_write(s)
self
end
Writes s to the stream. s will be converted to a String
using .to_s
method.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 474
def close
flush rescue nil
sysclose
end
Closes the SSLSocket and flushes any unwritten data.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 91
def consume_rbuff(size=nil)
if @rbuffer.empty?
nil
else
size = @rbuffer.size unless size
ret = @rbuffer[0, size]
@rbuffer[0, size] = ""
ret
end
end
Consumes size bytes from the buffer
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 337
def do_write(s)
@wbuffer = Buffer.new unless defined? @wbuffer
@wbuffer << s
@wbuffer.force_encoding(Encoding::BINARY)
@sync ||= false
if @sync or @wbuffer.size > BLOCK_SIZE
until @wbuffer.empty?
begin
nwrote = syswrite(@wbuffer)
rescue Errno::EAGAIN
retry
end
@wbuffer[0, nwrote] = ""
end
end
end
Writes s to the buffer. When the buffer is full or sync
is true the buffer is flushed to the underlying socket.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 250
def each(eol=$/)
while line = self.gets(eol)
yield line
end
end
Executes the block for every line in the stream where lines are separated by eol.
See also gets
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 291
def each_byte # :yields: byte
while c = getc
yield(c.ord)
end
end
Calls the given block once for each byte in the stream.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 322
def eof?
fill_rbuff if !@eof && @rbuffer.empty?
@eof && @rbuffer.empty?
end
Returns true if the stream is at file which means there is no more data to be read.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 78
def fill_rbuff
begin
@rbuffer << self.sysread(BLOCK_SIZE)
rescue Errno::EAGAIN
retry
rescue EOFError
@eof = true
end
end
Fills the buffer from the underlying SSLSocket
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 462
def flush
osync = @sync
@sync = true
do_write ""
return self
ensure
@sync = osync
end
Flushes buffered data to the SSLSocket.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 284
def getc
read(1)
end
Reads one character from the stream. Returns nil if called at end of file.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 226
def gets(eol=$/, limit=nil)
idx = @rbuffer.index(eol)
until @eof
break if idx
fill_rbuff
idx = @rbuffer.index(eol)
end
if eol.is_a?(Regexp)
size = idx ? idx+$&.size : nil
else
size = idx ? idx+eol.size : nil
end
if size && limit && limit >= 0
size = [size, limit].min
end
consume_rbuff(size)
end
Reads the next “line” from the stream. Lines are separated by eol. If limit is provided the result will not be longer than the given number of bytes.
eol may be a String
or Regexp
.
Unlike IO#gets
the line read will not be assigned to +$_+.
Unlike IO#gets
the separator must be provided if a limit is provided.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 441
def print(*args)
s = Buffer.new
args.each{ |arg| s << arg.to_s }
do_write(s)
nil
end
Writes args to the stream.
See IO#print
for full details.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 454
def printf(s, *args)
do_write(s % args)
nil
end
Formats and writes to the stream converting parameters under control of the format string.
See Kernel#sprintf
for format string details.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 423
def puts(*args)
s = Buffer.new
if args.empty?
s << "\n"
end
args.each{|arg|
s << arg.to_s
s.sub!(/(?<!\n)\z/, "\n")
}
do_write(s)
nil
end
Writes args to the stream along with a record separator.
See IO#puts
for full details.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 110
def read(size=nil, buf=nil)
if size == 0
if buf
buf.clear
return buf
else
return ""
end
end
until @eof
break if size && size <= @rbuffer.size
fill_rbuff
end
ret = consume_rbuff(size) || ""
if buf
buf.replace(ret)
ret = buf
end
(size && ret.empty?) ? nil : ret
end
Reads size bytes from the stream. If buf is provided it must reference a string which will receive the data.
See IO#read
for full details.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 195
def read_nonblock(maxlen, buf=nil, exception: true)
if maxlen == 0
if buf
buf.clear
return buf
else
return ""
end
end
if @rbuffer.empty?
return sysread_nonblock(maxlen, buf, exception: exception)
end
ret = consume_rbuff(maxlen)
if buf
buf.replace(ret)
ret = buf
end
ret
end
Reads at most maxlen bytes in the non-blocking manner.
When no data can be read without blocking it raises OpenSSL::SSL::SSLError
extended by IO::WaitReadable
or IO::WaitWritable
.
IO::WaitReadable
means SSL
needs to read internally so read_nonblock
should be called again when the underlying IO
is readable.
IO::WaitWritable
means SSL
needs to write internally so read_nonblock
should be called again after the underlying IO
is writable.
OpenSSL::Buffering#read_nonblock
needs two rescue clause as follows:
# emulates blocking read (readpartial). begin result = ssl.read_nonblock(maxlen) rescue IO::WaitReadable IO.select([io]) retry rescue IO::WaitWritable IO.select(nil, [io]) retry end
Note that one reason that read_nonblock
writes to the underlying IO
is when the peer requests a new TLS/SSL handshake. See openssl the FAQ for more details. www.openssl.org/support/faq.html
By specifying a keyword argument exception to false
, you can indicate that read_nonblock
should not raise an IO::Wait*able exception, but return the symbol :wait_writable
or :wait_readable
instead. At EOF, it will return nil
instead of raising EOFError
.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 301
def readchar
raise EOFError if eof?
getc
end
Reads a one-character string from the stream. Raises an EOFError
at end of file.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 275
def readline(eol=$/)
raise EOFError if eof?
gets(eol)
end
Reads a line from the stream which is separated by eol.
Raises EOFError
if at end of file.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 262
def readlines(eol=$/)
ary = []
while line = self.gets(eol)
ary << line
end
ary
end
Reads lines from the stream which are separated by eol.
See also gets
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 137
def readpartial(maxlen, buf=nil)
if maxlen == 0
if buf
buf.clear
return buf
else
return ""
end
end
if @rbuffer.empty?
begin
return sysread(maxlen, buf)
rescue Errno::EAGAIN
retry
end
end
ret = consume_rbuff(maxlen)
if buf
buf.replace(ret)
ret = buf
end
ret
end
Reads at most maxlen bytes from the stream. If buf is provided it must reference a string which will receive the data.
See IO#readpartial
for full details.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 314
def ungetc(c)
@rbuffer[0,0] = c.chr
end
Pushes character c back onto the stream such that a subsequent buffered character read will return it.
Unlike IO#getc
multiple bytes may be pushed back onto the stream.
Has no effect on unbuffered reads (such as sysread).
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 360
def write(*s)
s.inject(0) do |written, str|
do_write(str)
written + str.bytesize
end
end
Writes s to the stream. If the argument is not a String
it will be converted using .to_s
method. Returns the number of bytes written.
# File tmp/rubies/ruby-3.0.5/ext/openssl/lib/openssl/buffering.rb, line 404
def write_nonblock(s, exception: true)
flush
syswrite_nonblock(s, exception: exception)
end
Writes s in the non-blocking manner.
If there is buffered data, it is flushed first. This may block.
write_nonblock
returns number of bytes written to the SSL
connection.
When no data can be written without blocking it raises OpenSSL::SSL::SSLError
extended by IO::WaitReadable
or IO::WaitWritable
.
IO::WaitReadable
means SSL
needs to read internally so write_nonblock
should be called again after the underlying IO
is readable.
IO::WaitWritable
means SSL
needs to write internally so write_nonblock
should be called again after underlying IO
is writable.
So OpenSSL::Buffering#write_nonblock
needs two rescue clause as follows.
# emulates blocking write. begin result = ssl.write_nonblock(str) rescue IO::WaitReadable IO.select([io]) retry rescue IO::WaitWritable IO.select(nil, [io]) retry end
Note that one reason that write_nonblock
reads from the underlying IO
is when the peer requests a new TLS/SSL handshake. See the openssl FAQ for more details. www.openssl.org/support/faq.html
By specifying a keyword argument exception to false
, you can indicate that write_nonblock
should not raise an IO::Wait*able exception, but return the symbol :wait_writable
or :wait_readable
instead.