mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00

(OpenSSL::Buffering#write_nonblock): new method. * ext/openssl/ossl_ssl.c (ossl_ssl_write_nonblock): new method. (ossl_ssl_write_internal): defined. (ossl_ssl_write): use ossl_ssl_write_internal. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@23020 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
339 lines
6.3 KiB
Ruby
339 lines
6.3 KiB
Ruby
=begin
|
|
= $RCSfile$ -- Buffering mix-in module.
|
|
|
|
= Info
|
|
'OpenSSL for Ruby 2' project
|
|
Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
|
|
All rights reserved.
|
|
|
|
= Licence
|
|
This program is licenced under the same licence as Ruby.
|
|
(See the file 'LICENCE'.)
|
|
|
|
= Version
|
|
$Id$
|
|
=end
|
|
|
|
module OpenSSL
|
|
module Buffering
|
|
include Enumerable
|
|
attr_accessor :sync
|
|
BLOCK_SIZE = 1024*16
|
|
|
|
def initialize(*args)
|
|
@eof = false
|
|
@rbuffer = ""
|
|
@sync = @io.sync
|
|
end
|
|
|
|
#
|
|
# for reading.
|
|
#
|
|
private
|
|
|
|
def fill_rbuff
|
|
begin
|
|
@rbuffer << self.sysread(BLOCK_SIZE)
|
|
rescue Errno::EAGAIN
|
|
retry
|
|
rescue EOFError
|
|
@eof = true
|
|
end
|
|
end
|
|
|
|
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
|
|
|
|
public
|
|
|
|
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
|
|
|
|
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
|
|
|
|
# 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 after
|
|
# underlying IO is readable.
|
|
#
|
|
# IO::WaitWritable means SSL needs to write internally.
|
|
# So read_nonblock should be called again after
|
|
# underlying IO is writable.
|
|
#
|
|
# So 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 write to a underlying IO
|
|
# is the peer requests a new TLS/SSL handshake.
|
|
# See openssl FAQ for more details.
|
|
# http://www.openssl.org/support/faq.html
|
|
#
|
|
def read_nonblock(maxlen, buf=nil)
|
|
if maxlen == 0
|
|
if buf
|
|
buf.clear
|
|
return buf
|
|
else
|
|
return ""
|
|
end
|
|
end
|
|
if @rbuffer.empty?
|
|
return sysread_nonblock(maxlen, buf)
|
|
end
|
|
ret = consume_rbuff(maxlen)
|
|
if buf
|
|
buf.replace(ret)
|
|
ret = buf
|
|
end
|
|
raise EOFError if ret.empty?
|
|
ret
|
|
end
|
|
|
|
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
|
|
|
|
def each(eol=$/)
|
|
while line = self.gets(eol)
|
|
yield line
|
|
end
|
|
end
|
|
alias each_line each
|
|
|
|
def readlines(eol=$/)
|
|
ary = []
|
|
while line = self.gets(eol)
|
|
ary << line
|
|
end
|
|
ary
|
|
end
|
|
|
|
def readline(eol=$/)
|
|
raise EOFError if eof?
|
|
gets(eol)
|
|
end
|
|
|
|
def getc
|
|
c = read(1)
|
|
c ? c[0] : nil
|
|
end
|
|
|
|
def each_byte
|
|
while c = getc
|
|
yield(c)
|
|
end
|
|
end
|
|
|
|
def readchar
|
|
raise EOFError if eof?
|
|
getc
|
|
end
|
|
|
|
def ungetc(c)
|
|
@rbuffer[0,0] = c.chr
|
|
end
|
|
|
|
def eof?
|
|
fill_rbuff if !@eof && @rbuffer.empty?
|
|
@eof && @rbuffer.empty?
|
|
end
|
|
alias eof eof?
|
|
|
|
#
|
|
# for writing.
|
|
#
|
|
private
|
|
|
|
def do_write(s)
|
|
@wbuffer = "" unless defined? @wbuffer
|
|
@wbuffer << s
|
|
@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
|
|
|
|
public
|
|
|
|
def write(s)
|
|
do_write(s)
|
|
s.length
|
|
end
|
|
|
|
# Writes _str_ in the non-blocking manner.
|
|
#
|
|
# If there are buffered data, it is flushed at 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
|
|
# 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 read from a underlying IO
|
|
# is the peer requests a new TLS/SSL handshake.
|
|
# See openssl FAQ for more details.
|
|
# http://www.openssl.org/support/faq.html
|
|
#
|
|
def write_nonblock(s)
|
|
flush
|
|
syswrite_nonblock(s)
|
|
end
|
|
|
|
def << (s)
|
|
do_write(s)
|
|
self
|
|
end
|
|
|
|
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
|
|
|
|
def print(*args)
|
|
s = ""
|
|
args.each{ |arg| s << arg.to_s }
|
|
do_write(s)
|
|
nil
|
|
end
|
|
|
|
def printf(s, *args)
|
|
do_write(s % args)
|
|
nil
|
|
end
|
|
|
|
def flush
|
|
osync = @sync
|
|
@sync = true
|
|
do_write ""
|
|
@sync = osync
|
|
end
|
|
|
|
def close
|
|
flush rescue nil
|
|
sysclose
|
|
end
|
|
end
|
|
end
|