модуль OpenSSL::Buffering
OpenSSL IO модуль-подмешивание для буферизации.
Этот модуль позволяет OpenSSL::SSL::SSLSocket вести себя как IO.
Обычно этот модуль не используют напрямую; пример его реализации можно найти в OpenSSL::SSL::SSLSocket.
Константы
- BLOCK_SIZE
-
Размер по умолчанию для чтения из SSLSocket и записи в него при операциях с буфером.
Атрибуты
Публичные методы класса
# File ext/openssl/lib/openssl/buffering.rb, line 59 def initialize(*) super @eof = false @rbuffer = Buffer.new @sync = @io.sync end
Создаёт экземпляр модуля IO OpenSSL для буферизации.
Публичные методы экземпляра
# File ext/openssl/lib/openssl/buffering.rb, line 440 def <<(s) do_write(s) self end
Записывает s в поток. s будет преобразовано в String с помощью метода .to_s.
# File ext/openssl/lib/openssl/buffering.rb, line 501 def close flush rescue nil sysclose end
Закрывает SSLSocket и сбрасывает все незаписанные данные.
# File ext/openssl/lib/openssl/buffering.rb, line 262
def each(eol=$/)
while line = self.gets(eol)
yield line
end
end Выполняет блок для каждой строки потока, где строки разделяются символом eol.
См. также gets
# File ext/openssl/lib/openssl/buffering.rb, line 303
def each_byte # :yields: byte
while c = getc
yield(c.ord)
end
end Вызывает переданный блок один раз для каждого байта в потоке.
# File ext/openssl/lib/openssl/buffering.rb, line 334 def eof? fill_rbuff if !@eof && @rbuffer.empty? @eof && @rbuffer.empty? end
Возвращает true, если поток достиг конца файла, то есть больше нет данных для чтения.
# File ext/openssl/lib/openssl/buffering.rb, line 489 def flush osync = @sync @sync = true do_write "" return self ensure @sync = osync end
Сбрасывает буферизованные данные в SSLSocket.
# File ext/openssl/lib/openssl/buffering.rb, line 102 def getbyte read(1)&.ord end
Получает следующий 8-битный байт из ‘ssl`. Возвращает `nil` при достижении конца файла.
# File ext/openssl/lib/openssl/buffering.rb, line 296 def getc read(1) end
Читает один символ из потока. Возвращает nil, если вызван в конце файла.
# File ext/openssl/lib/openssl/buffering.rb, line 234
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 Читает следующую «строку» из потока. Строки разделяются символом eol. Если задан limit, длина результата не превысит указанное количество байтов.
eol может быть String или Regexp.
В отличие от IO#gets, прочитанная строка не будет присвоена +$_+.
В отличие от IO#gets, если задано ограничение длины, необходимо указать разделитель.
# File ext/openssl/lib/openssl/buffering.rb, line 468
def print(*args)
s = Buffer.new
args.each{ |arg| s.append_as_bytes(arg.to_s) }
do_write(s)
nil
end Записывает args в поток.
Полное описание см. в IO#print.
# File ext/openssl/lib/openssl/buffering.rb, line 481 def printf(s, *args) do_write(s % args) nil end
Форматирует и записывает данные в поток, преобразуя параметры в соответствии со строкой формата.
Сведения о строке формата см. в Kernel#sprintf.
# File ext/openssl/lib/openssl/buffering.rb, line 450
def puts(*args)
s = Buffer.new
if args.empty?
s.append_as_bytes("\n")
end
args.each{|arg|
s.append_as_bytes(arg.to_s)
s.sub!(/(?<!\n)\z/, "\n")
}
do_write(s)
nil
end Записывает args в поток вместе с разделителем записей.
Полное описание см. в IO#puts.
# File ext/openssl/lib/openssl/buffering.rb, line 118
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 Читает из потока size байтов. Если задан buf, он должен ссылаться на строку, в которую будут записаны данные.
Полное описание см. в IO#read.
# File ext/openssl/lib/openssl/buffering.rb, line 203
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 Неблокирующим способом читает не более maxlen байтов.
Если данные невозможно прочитать без блокировки, возникает исключение OpenSSL::SSL::SSLError, дополненное модулем IO::WaitReadable или IO::WaitWritable.
IO::WaitReadable означает, что SSL необходимо выполнить внутреннее чтение, поэтому read_nonblock следует вызвать снова, когда нижележащий IO станет доступен для чтения.
IO::WaitWritable означает, что SSL необходимо выполнить внутреннюю запись, поэтому read_nonblock следует вызвать снова, когда нижележащий IO станет доступен для записи.
Для 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-рукопожатие. Подробнее см. в FAQ по openssl. www.openssl.org/support/faq.html
Указав ключевой аргумент exception для false, можно указать, что read_nonblock не должно вызывать исключение IO::Wait*able, а должно возвращать символ :wait_writable или :wait_readable. При достижении конца файла метод вернёт nil вместо вызова исключения EOFError.
# File ext/openssl/lib/openssl/buffering.rb, line 107 def readbyte raise EOFError if eof? getbyte end
Получает следующий 8-битный байт. При достижении конца файла вызывает исключение EOFError.
# File ext/openssl/lib/openssl/buffering.rb, line 313 def readchar raise EOFError if eof? getc end
Читает из потока строку из одного символа. В конце файла вызывает исключение EOFError.
# File ext/openssl/lib/openssl/buffering.rb, line 287 def readline(eol=$/) raise EOFError if eof? gets(eol) end
Читает из потока строку, разделённую символом eol.
В конце файла вызывает исключение EOFError.
# File ext/openssl/lib/openssl/buffering.rb, line 274
def readlines(eol=$/)
ary = []
while line = self.gets(eol)
ary << line
end
ary
end Читает из потока строки, разделённые символом eol.
См. также gets
# File ext/openssl/lib/openssl/buffering.rb, line 145
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 Читает из потока не более maxlen байтов. Если задан buf, он должен ссылаться на строку, в которую будут записаны данные.
Полное описание см. в IO#readpartial.
# File ext/openssl/lib/openssl/buffering.rb, line 326 def ungetc(c) @rbuffer[0,0] = c.chr end
Возвращает символ c в поток, чтобы при последующем буферизованном чтении символа был получен именно он.
В отличие от IO#getc, в поток можно вернуть несколько байтов.
Не влияет на чтение без буферизации (например, sysread).
# File ext/openssl/lib/openssl/buffering.rb, line 387
def write(*s)
s.inject(0) do |written, str|
do_write(str)
written + str.bytesize
end
end Записывает s в поток. Если аргумент не является String, он будет преобразован с помощью метода .to_s. Возвращает количество записанных байтов.
# File ext/openssl/lib/openssl/buffering.rb, line 431 def write_nonblock(s, exception: true) flush syswrite_nonblock(s, exception: exception) end
Неблокирующим способом записывает s.
Если имеются буферизованные данные, сначала они сбрасываются. Эта операция может блокировать выполнение.
write_nonblock возвращает количество байтов, записанных в соединение SSL.
Если данные невозможно записать без блокировки, возникает исключение OpenSSL::SSL::SSLError, дополненное модулем IO::WaitReadable или IO::WaitWritable.
IO::WaitReadable означает, что SSL необходимо выполнить внутреннее чтение, поэтому write_nonblock следует вызвать снова, когда нижележащий IO станет доступен для чтения.
IO::WaitWritable означает, что SSL необходимо выполнить внутреннюю запись, поэтому write_nonblock следует вызвать снова, когда нижележащий IO станет доступен для записи.
Таким образом, для 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-рукопожатие. Подробнее см. в FAQ по openssl. www.openssl.org/support/faq.html
Указав ключевой аргумент exception для false, можно указать, что write_nonblock не должно вызывать исключение IO::Wait*able, а должно возвращать символ :wait_writable или :wait_readable.
Приватные методы экземпляра
# File ext/openssl/lib/openssl/buffering.rb, line 87
def consume_rbuff(size=nil)
if @rbuffer.empty?
nil
else
size = @rbuffer.size unless size
@rbuffer.slice!(0, size)
end
end Потребляет из буфера size байтов.
# File ext/openssl/lib/openssl/buffering.rb, line 349
def do_write(s)
@wbuffer = Buffer.new unless defined? @wbuffer
@wbuffer.append_as_bytes(s)
@sync ||= false
buffer_size = @wbuffer.bytesize
if @sync or buffer_size > BLOCK_SIZE
nwrote = 0
begin
while nwrote < buffer_size do
begin
chunk = if nwrote > 0
@wbuffer.byteslice(nwrote, @wbuffer.bytesize)
else
@wbuffer
end
nwrote += syswrite(chunk)
rescue Errno::EAGAIN
retry
end
end
ensure
if nwrote < @wbuffer.bytesize
@wbuffer[0, nwrote] = ""
else
@wbuffer.clear
end
end
end
end Записывает s в буфер. Когда буфер заполнен или sync имеет значение true, буфер сбрасывается в нижележащий сокет.
# File ext/openssl/lib/openssl/buffering.rb, line 74
def fill_rbuff
begin
@rbuffer.append_as_bytes(self.sysread(BLOCK_SIZE))
rescue Errno::EAGAIN
retry
rescue EOFError
@eof = true
end
end Заполняет буфер данными из нижележащего SSLSocket.
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.