module OpenSSL::Buffering

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.

Constants

BLOCK_SIZE

Default size to read from or write to the SSLSocket for buffer operations.

Attributes

sync[RW]

The "sync mode" of the SSLSocket.

See IO#sync for full details.

Public Class Methods

new(*) click to toggle source

Creates an instance of OpenSSL's buffering IO module.

Calls superclass method
# File ext/openssl/lib/openssl/buffering.rb, line 43
def initialize(*)
 super
 @eof = false
 @rbuffer = ""
 @sync = @io.sync
end

Public Instance Methods

<<(s) click to toggle source

Writes s to the stream. s will be converted to a String using String#to_s.

# File ext/openssl/lib/openssl/buffering.rb, line 389
def << (s)
 do_write(s)
 self
end
close() click to toggle source

Closes the SSLSocket and flushes any unwritten data.

# File ext/openssl/lib/openssl/buffering.rb, line 452
def close
 flush rescue nil
 sysclose
end
each(eol=$/) { |line| ... } click to toggle source

Executes the block for every line in the stream where lines are separated by eol.

See also gets

# File ext/openssl/lib/openssl/buffering.rb, line 227
def each(eol=$/)
 while line = self.gets(eol)
 yield line
 end
end
Also aliased as: each_line
each_byte() { |byte| ... } click to toggle source

Calls the given block once for each byte in the stream.

# File ext/openssl/lib/openssl/buffering.rb, line 268
def each_byte # :yields: byte
 while c = getc
 yield(c.ord)
 end
end
each_line(eol=$/)
Alias for: each
eof()
Alias for: eof?
eof?() click to toggle source

Returns true if the stream is at file which means there is no more data to be read.

# File ext/openssl/lib/openssl/buffering.rb, line 299
def eof?
 fill_rbuff if !@eof && @rbuffer.empty?
 @eof && @rbuffer.empty?
end
Also aliased as: eof
flush() click to toggle source

Flushes buffered data to the SSLSocket.

# File ext/openssl/lib/openssl/buffering.rb, line 440
def flush
 osync = @sync
 @sync = true
 do_write ""
 return self
ensure
 @sync = osync
end
getc() click to toggle source

Reads one character from the stream. Returns nil if called at end of file.

# File ext/openssl/lib/openssl/buffering.rb, line 261
def getc
 read(1)
end
gets(eol=$/, limit=nil) click to toggle source

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 ext/openssl/lib/openssl/buffering.rb, line 203
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 limit and limit >= 0
 size = [size, limit].min
 end
 consume_rbuff(size)
end
print(*args) click to toggle source

Writes args to the stream.

See IO#print for full details.

printf(s, *args) click to toggle source

Formats and writes to the stream converting parameters under control of the format string.

See Kernel#sprintf for format string details.

# File ext/openssl/lib/openssl/buffering.rb, line 432
def printf(s, *args)
 do_write(s % args)
 nil
end
puts(*args) click to toggle source

Writes args to the stream along with a record separator.

See IO#puts for full details.

# File ext/openssl/lib/openssl/buffering.rb, line 399
def puts(*args)
 s = ""
 if args.empty?
 s << "\n"
 end
 args.each{|arg|
 s << arg.to_s
 if $/ && /\n\z/ !~ s
 s << "\n"
 end
 }
 do_write(s)
 nil
end
read(size=nil, buf=nil) click to toggle source

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 ext/openssl/lib/openssl/buffering.rb, line 90
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
read_nonblock(maxlen, buf=nil, exception: true) click to toggle source

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.

#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

# File ext/openssl/lib/openssl/buffering.rb, line 171
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
 raise EOFError if ret.empty?
 ret
end
readchar() click to toggle source

Reads a one-character string from the stream. Raises an EOFError at end of file.

# File ext/openssl/lib/openssl/buffering.rb, line 278
def readchar
 raise EOFError if eof?
 getc
end
readline(eol=$/) click to toggle source

Reads a line from the stream which is separated by eol.

Raises EOFError if at end of file.

# File ext/openssl/lib/openssl/buffering.rb, line 252
def readline(eol=$/)
 raise EOFError if eof?
 gets(eol)
end
readlines(eol=$/) click to toggle source

Reads lines from the stream which are separated by eol.

See also gets

# File ext/openssl/lib/openssl/buffering.rb, line 239
def readlines(eol=$/)
 ary = []
 while line = self.gets(eol)
 ary << line
 end
 ary
end
readpartial(maxlen, buf=nil) click to toggle source

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 ext/openssl/lib/openssl/buffering.rb, line 117
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
 raise EOFError if ret.empty?
 ret
end
ungetc(c) click to toggle source

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 ext/openssl/lib/openssl/buffering.rb, line 291
def ungetc(c)
 @rbuffer[0,0] = c.chr
end
write(s) click to toggle source

Writes s to the stream. If the argument is not a string it will be converted using String#to_s. Returns the number of bytes written.

# File ext/openssl/lib/openssl/buffering.rb, line 342
def write(s)
 do_write(s)
 s.bytesize
end
write_nonblock(s, exception: true) click to toggle source

Writes str 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 #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

# File ext/openssl/lib/openssl/buffering.rb, line 380
def write_nonblock(s, exception: true)
 flush
 syswrite_nonblock(s, exception: exception)
end

Private Instance Methods

consume_rbuff(size=nil) click to toggle source

Consumes size bytes from the buffer

# File ext/openssl/lib/openssl/buffering.rb, line 71
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
do_write(s) click to toggle source

Writes s to the buffer. When the buffer is full or sync is true the buffer is flushed to the underlying socket.

# File ext/openssl/lib/openssl/buffering.rb, line 314
def do_write(s)
 @wbuffer = "" unless defined? @wbuffer
 @wbuffer << s
 @wbuffer.force_encoding(Encoding::BINARY)
 @sync ||= false
 if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/)
 remain = idx ? idx + $/.size : @wbuffer.length
 nwritten = 0
 while remain > 0
 str = @wbuffer[nwritten,remain]
 begin
 nwrote = syswrite(str)
 rescue Errno::EAGAIN
 retry
 end
 remain -= nwrote
 nwritten += nwrote
 end
 @wbuffer[0,nwritten] = ""
 end
end
fill_rbuff() click to toggle source

Fills the buffer from the underlying SSLSocket

# File ext/openssl/lib/openssl/buffering.rb, line 58
def fill_rbuff
 begin
 @rbuffer << self.sysread(BLOCK_SIZE)
 rescue Errno::EAGAIN
 retry
 rescue EOFError
 @eof = true
 end
end