模块 OpenSSL::Buffering

OpenSSL IO 缓冲混合模块。

此模块允许 OpenSSL::SSL::SSLSocket 表现得像一个 IO

您通常不会直接使用此模块,您可以在 OpenSSL::SSL::SSLSocket 中看到它的实现。

常量

BLOCK_SIZE

用于缓冲操作从 SSLSocket 读取或写入的默认大小。

属性

sync[RW]

SSLSocket 的“同步模式”。

有关完整详细信息,请参阅 IO#sync。

公共类方法

new(*) 点击切换源代码

创建 OpenSSL 缓冲 IO 模块的实例。

调用超类方法
# File openssl/lib/openssl/buffering.rb, line 63
def initialize(*)
  super
  @eof = false
  @rbuffer = Buffer.new
  @sync = @io.sync
end

公共实例方法

<<(s) 点击切换源代码

s 写入流。s 将使用 .to_s 方法转换为 String。

# File openssl/lib/openssl/buffering.rb, line 434
def <<(s)
  do_write(s)
  self
end
close() 点击切换源代码

关闭 SSLSocket 并刷新任何未写入的数据。

# File openssl/lib/openssl/buffering.rb, line 495
def close
  flush rescue nil
  sysclose
end
each(eol=$/) { |line| ... } 点击切换源代码

对流中由 eol 分隔的每一行执行块。

另请参阅 gets

# File openssl/lib/openssl/buffering.rb, line 266
def each(eol=$/)
  while line = self.gets(eol)
    yield line
  end
end
也别名为:each_line
each_byte() { |byte| ... } 点击切换源代码

对流中的每个字节调用一次给定的块。

# File openssl/lib/openssl/buffering.rb, line 307
def each_byte # :yields: byte
  while c = getc
    yield(c.ord)
  end
end
each_line(eol=$/)
别名为:each
eof()
别名为:eof?
eof?() 点击切换源代码

如果流位于文件末尾,则返回 true,这意味着没有更多数据可读。

# File openssl/lib/openssl/buffering.rb, line 338
def eof?
  fill_rbuff if !@eof && @rbuffer.empty?
  @eof && @rbuffer.empty?
end
也别名为:eof
flush() 点击切换源代码

将缓冲的数据刷新到 SSLSocket。

# File openssl/lib/openssl/buffering.rb, line 483
def flush
  osync = @sync
  @sync = true
  do_write ""
  return self
ensure
  @sync = osync
end
getbyte → 81 点击切换源代码

从“ssl”获取下一个 8 位字节。在 EOF 时返回 `nil`

# File openssl/lib/openssl/buffering.rb, line 106
def getbyte
  read(1)&.ord
end
getc() 点击切换源代码

从流中读取一个字符。如果在文件末尾调用,则返回 nil。

# File openssl/lib/openssl/buffering.rb, line 300
def getc
  read(1)
end
gets(eol=$/, limit=nil, chomp: false) 点击切换源代码

从流中读取下一“行”。行由 eol 分隔。如果提供了 limit,则结果不会长于给定的字节数。

eol 可以是字符串或正则表达式。

与 IO#gets 不同,读取的行不会分配给 +$_+。

与 IO#gets 不同,如果提供了限制,则必须提供分隔符。

# File openssl/lib/openssl/buffering.rb, line 238
def gets(eol=$/, limit=nil, chomp: false)
  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
  line = consume_rbuff(size)
  if chomp && line
    line.chomp!(eol)
  end
  line
end
print(*args) 点击切换源代码

args 写入流。

有关完整详细信息,请参阅 IO#print。

printf(s, *args) 点击切换源代码

格式化并写入流,根据格式字符串控制转换参数。

有关格式字符串的详细信息,请参阅 Kernel#sprintf。

# File openssl/lib/openssl/buffering.rb, line 475
def printf(s, *args)
  do_write(s % args)
  nil
end
puts(*args) 点击切换源代码

args 与记录分隔符一起写入流。

有关完整详细信息,请参阅 IO#puts。

# File openssl/lib/openssl/buffering.rb, line 444
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
read(size=nil, buf=nil) 点击切换源代码

从流中读取 size 个字节。如果提供了 buf,则它必须引用将接收数据的字符串。

有关完整详细信息,请参阅 IO#read。

# File openssl/lib/openssl/buffering.rb, line 122
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) 点击切换源代码

以非阻塞方式最多读取 maxlen 个字节。

当无法在不阻塞的情况下读取数据时,它会引发由 IO::WaitReadableIO::WaitWritable 扩展的 OpenSSL::SSL::SSLError

IO::WaitReadable 表示 SSL 需要在内部读取,因此当底层 IO 可读时,应再次调用 read_nonblock

IO::WaitWritable 表示 SSL 需要在内部写入,因此在底层 IO 可写之后,应再次调用 read_nonblock

OpenSSL::Buffering#read_nonblock 需要两个如下所示的 rescue 子句

# 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

请注意,read_nonblock 写入底层 IO 的一个原因是当对等方请求新的 TLS/SSL 握手时。有关更多详细信息,请参阅 openssl FAQ。 www.openssl.org/support/faq.html

通过将关键字参数 exception 指定为 false,您可以指示 read_nonblock 不应引发 IO::Wait*able 异常,而是返回符号 :wait_writable:wait_readable。在 EOF 时,它将返回 nil 而不是引发 EOFError。

# File openssl/lib/openssl/buffering.rb, line 207
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
readbyte() 点击切换源代码

获取下一个 8 位字节。在 EOF 时引发 EOFError

# File openssl/lib/openssl/buffering.rb, line 111
def readbyte
  raise EOFError if eof?
  getbyte
end
readchar() 点击切换源代码

从流中读取一个字符的字符串。在文件末尾引发 EOFError。

# File openssl/lib/openssl/buffering.rb, line 317
def readchar
  raise EOFError if eof?
  getc
end
readline(eol=$/) 点击切换源代码

从流中读取由 eol 分隔的行。

如果在文件末尾引发 EOFError。

# File openssl/lib/openssl/buffering.rb, line 291
def readline(eol=$/)
  raise EOFError if eof?
  gets(eol)
end
readlines(eol=$/) 点击切换源代码

从流中读取由 eol 分隔的行。

另请参阅 gets

# File openssl/lib/openssl/buffering.rb, line 278
def readlines(eol=$/)
  ary = []
  while line = self.gets(eol)
    ary << line
  end
  ary
end
readpartial(maxlen, buf=nil) 点击切换源代码

从流中最多读取 maxlen 个字节。如果提供了 buf,则它必须引用将接收数据的字符串。

有关完整详细信息,请参阅 IO#readpartial。

# File openssl/lib/openssl/buffering.rb, line 149
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
ungetc(c) 点击切换源代码

将字符 c 推回到流中,以便后续的缓冲字符读取将返回它。

与 IO#getc 不同,可以将多个字节推回到流中。

对未缓冲的读取(例如 sysread)没有影响。

# File openssl/lib/openssl/buffering.rb, line 330
def ungetc(c)
  @rbuffer[0,0] = c.chr
end
write(*s) 点击切换源代码

s 写入流。如果参数不是 String,则将使用 .to_s 方法进行转换。返回写入的字节数。

# File openssl/lib/openssl/buffering.rb, line 381
def write(*s)
  s.inject(0) do |written, str|
    do_write(str)
    written + str.bytesize
  end
end
write_nonblock(s, exception: true) 点击切换源代码

以非阻塞方式写入 s

如果有缓冲数据,则首先刷新该数据。这可能会阻塞。

write_nonblock 返回写入到 SSL 连接的字节数。

当无法在不阻塞的情况下写入数据时,它会引发由 IO::WaitReadableIO::WaitWritable 扩展的 OpenSSL::SSL::SSLError

IO::WaitReadable 表示 SSL 需要在内部读取,因此在底层 IO 可读后,应再次调用 write_nonblock

IO::WaitWritable 表示 SSL 需要在内部写入,因此在底层 IO 可写后,应再次调用 write_nonblock

因此 OpenSSL::Buffering#write_nonblock 需要两个如下所示的 rescue 子句。

# 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

请注意,write_nonblock 从底层 IO 读取的一个原因是当对等方请求新的 TLS/SSL 握手时。有关更多详细信息,请参阅 openssl FAQ。 www.openssl.org/support/faq.html

通过将关键字参数 exception 指定为 false,您可以指示 write_nonblock 不应引发 IO::Wait*able 异常,而是返回符号 :wait_writable:wait_readable

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

私有实例方法

consume_rbuff(size=nil) 点击切换源代码

从缓冲区消耗 size 个字节

# File openssl/lib/openssl/buffering.rb, line 91
def consume_rbuff(size=nil)
  if @rbuffer.empty?
    nil
  else
    size = @rbuffer.size unless size
    @rbuffer.slice!(0, size)
  end
end
do_write(s) 点击切换源代码

s 写入缓冲区。当缓冲区已满或 sync 为 true 时,缓冲区将刷新到底层套接字。

# File openssl/lib/openssl/buffering.rb, line 353
def do_write(s)
  @wbuffer = Buffer.new unless defined? @wbuffer
  @wbuffer << s
  @wbuffer.force_encoding(Encoding::BINARY)
  @sync ||= false
  buffer_size = @wbuffer.size
  if @sync or buffer_size > BLOCK_SIZE
    nwrote = 0
    begin
      while nwrote < buffer_size do
        begin
          nwrote += syswrite(@wbuffer[nwrote, buffer_size - nwrote])
        rescue Errno::EAGAIN
          retry
        end
      end
    ensure
      @wbuffer[0, nwrote] = ""
    end
  end
end
fill_rbuff() 点击切换源代码

从底层 SSLSocket 填充缓冲区

# File 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