Module: OpenSSL::Buffering

Includes:
Enumerable
Included in:
SSL::SSLSocket
Defined in:
lib/openssl/buffering.rb

Overview

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.

Constant Summary collapse

BLOCK_SIZE =

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

1024*16

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#syncObject

The “sync mode” of the SSLSocket.

See IO#sync for full details.



34
35
36
# File 'lib/openssl/buffering.rb', line 34

def sync
  @sync
end

Instance Method Details

#<<(s) ⇒ Object

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



390
391
392
393
# File 'lib/openssl/buffering.rb', line 390

def << (s)
  do_write(s)
  self
end

#closeObject

Closes the SSLSocket and flushes any unwritten data.



453
454
455
456
# File 'lib/openssl/buffering.rb', line 453

def close
  flush rescue nil
  sysclose
end

#each(eol = $/) ⇒ Object Also known as: each_line

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

See also #gets



228
229
230
231
232
# File 'lib/openssl/buffering.rb', line 228

def each(eol=$/)
  while line = self.gets(eol)
    yield line
  end
end

#each_byteObject

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



269
270
271
272
273
# File 'lib/openssl/buffering.rb', line 269

def each_byte # :yields: byte
  while c = getc
    yield(c.ord)
  end
end

#eof?Boolean Also known as: eof

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

Returns:

  • (Boolean)


300
301
302
303
# File 'lib/openssl/buffering.rb', line 300

def eof?
  fill_rbuff if !@eof && @rbuffer.empty?
  @eof && @rbuffer.empty?
end

#flushObject

Flushes buffered data to the SSLSocket.



441
442
443
444
445
446
447
448
# File 'lib/openssl/buffering.rb', line 441

def flush
  osync = @sync
  @sync = true
  do_write ""
  return self
ensure
  @sync = osync
end

#getcObject

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



262
263
264
# File 'lib/openssl/buffering.rb', line 262

def getc
  read(1)
end

#gets(eol = $/, limit = nil) ⇒ Object

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.



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/openssl/buffering.rb', line 204

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

#initializeObject

Creates an instance of OpenSSL's buffering IO module.



44
45
46
47
48
49
# File 'lib/openssl/buffering.rb', line 44

def initialize(*)
  super
  @eof = false
  @rbuffer = ""
  @sync = @io.sync
end

Writes args to the stream.

See IO#print for full details.



420
421
422
423
424
425
# File 'lib/openssl/buffering.rb', line 420

def print(*args)
  s = ""
  args.each{ |arg| s << arg.to_s }
  do_write(s)
  nil
end

#printf(s, *args) ⇒ Object

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

See Kernel#sprintf for format string details.



433
434
435
436
# File 'lib/openssl/buffering.rb', line 433

def printf(s, *args)
  do_write(s % args)
  nil
end

#puts(*args) ⇒ Object

Writes args to the stream along with a record separator.

See IO#puts for full details.



400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/openssl/buffering.rb', line 400

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) ⇒ Object

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.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/openssl/buffering.rb', line 91

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) ⇒ Object

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).
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

Raises:

  • (EOFError)


172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/openssl/buffering.rb', line 172

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

#readcharObject

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

Raises:

  • (EOFError)


279
280
281
282
# File 'lib/openssl/buffering.rb', line 279

def readchar
  raise EOFError if eof?
  getc
end

#readline(eol = $/) ⇒ Object

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

Raises EOFError if at end of file.

Raises:

  • (EOFError)


253
254
255
256
# File 'lib/openssl/buffering.rb', line 253

def readline(eol=$/)
  raise EOFError if eof?
  gets(eol)
end

#readlines(eol = $/) ⇒ Object

Reads lines from the stream which are separated by eol.

See also #gets



240
241
242
243
244
245
246
# File 'lib/openssl/buffering.rb', line 240

def readlines(eol=$/)
  ary = []
  while line = self.gets(eol)
    ary << line
  end
  ary
end

#readpartial(maxlen, buf = nil) ⇒ Object

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.

Raises:

  • (EOFError)


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/openssl/buffering.rb', line 118

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) ⇒ Object

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).



292
293
294
# File 'lib/openssl/buffering.rb', line 292

def ungetc(c)
  @rbuffer[0,0] = c.chr
end

#write(s) ⇒ Object

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.



343
344
345
346
# File 'lib/openssl/buffering.rb', line 343

def write(s)
  do_write(s)
  s.bytesize
end

#write_nonblock(s, exception: true) ⇒ Object

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



381
382
383
384
# File 'lib/openssl/buffering.rb', line 381

def write_nonblock(s, exception: true)
  flush
  syswrite_nonblock(s, exception: exception)
end