# frozen_string_literal: false
# 'OpenSSL for Ruby 2' project
# Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
# This program is licensed under the same licence as Ruby.
# (See the file 'LICENCE'.)
# 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.
module OpenSSL::Buffering
# The "sync mode" of the SSLSocket.
# See IO#sync for full details.
# Default size to read from or write to the SSLSocket for buffer operations.
# Creates an instance of OpenSSL's buffering IO module.
# Fills the buffer from the underlying SSLSocket
@rbuffer << self.sysread(BLOCK_SIZE)
# Consumes _size_ bytes from the buffer
def consume_rbuff(size=nil)
size = @rbuffer.size unless size
# 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.
def read(size=nil, buf=nil)
break if size && size <= @rbuffer.size
ret = consume_rbuff(size) || ""
(size && ret.empty?) ? nil : ret
# 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.
def readpartial(maxlen, buf=nil)
return sysread(maxlen, buf)
ret = consume_rbuff(maxlen)
# 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).
# result = ssl.read_nonblock(maxlen)
# rescue IO::WaitReadable
# rescue IO::WaitWritable
# 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. http://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.
def read_nonblock(maxlen, buf=nil, exception: true)
return sysread_nonblock(maxlen, buf, exception: exception)
ret = consume_rbuff(maxlen)
# 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
# _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.
def gets(eol=$/, limit=nil)
idx = @rbuffer.index(eol)
idx = @rbuffer.index(eol)
size = idx ? idx+$&.size : nil
size = idx ? idx+eol.size : nil
if size && limit && limit >= 0
# Executes the block for every line in the stream where lines are separated
while line = self.gets(eol)
# Reads lines from the stream which are separated by _eol_.
while line = self.gets(eol)
# Reads a line from the stream which is separated by _eol_.
# Raises EOFError if at end of file.
# Reads one character from the stream. Returns nil if called at end of
# Calls the given block once for each byte in the stream.
def each_byte # :yields: byte
# Reads a one-character string from the stream. Raises an EOFError at 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).
# Returns true if the stream is at file which means there is no more data to
fill_rbuff if !@eof && @rbuffer.empty?
# Writes _s_ to the buffer. When the buffer is full or #sync is true the
# buffer is flushed to the underlying socket.
@wbuffer = "" unless defined? @wbuffer
@wbuffer.force_encoding(Encoding::BINARY)
if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/)
remain = idx ? idx + $/.size : @wbuffer.length
str = @wbuffer[nwritten,remain]
@wbuffer[0,nwritten] = ""
# 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.
s.inject(0) do |written, str|
# 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.
# result = ssl.write_nonblock(str)
# rescue IO::WaitReadable
# rescue IO::WaitWritable
# 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. http://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.
def write_nonblock(s, exception: true)
syswrite_nonblock(s, exception: exception)
# Writes _s_ to the stream. _s_ will be converted to a String using
# Writes _args_ to the stream along with a record separator.
# See IO#puts for full details.
# Writes _args_ to the stream.
# See IO#print for full details.
args.each{ |arg| s << arg.to_s }
# Formats and writes to the stream converting parameters under control of
# See Kernel#sprintf for format string details.
# Flushes buffered data to the SSLSocket.
# Closes the SSLSocket and flushes any unwritten data.