class Fluent::PluginHelper::Server::EventHandler::TLSServer

Constants

NONBLOCK_ARG

Attributes

close_after_write_complete[W]
closing[R]

Public Class Methods

new(sock, context, socket_option_setter, close_callback, log, under_plugin_development, connect_callback) click to toggle source

It can't use Coolio::TCPSocket, because Coolio::TCPSocket checks that underlying socket (1st argument of super) is TCPSocket.

Calls superclass method
# File lib/fluent/plugin_helper/server.rb, line 644
def initialize(sock, context, socket_option_setter, close_callback, log, under_plugin_development, connect_callback)
  raise ArgumentError, "socket must be a TCPSocket: sock=#{sock}" unless sock.is_a?(TCPSocket)

  socket_option_setter.call(sock)
  @_handler_socket = OpenSSL::SSL::SSLSocket.new(sock, context)
  @_handler_socket.sync_close = true
  @_handler_write_buffer = ''.force_encoding('ascii-8bit')
  @_handler_accepted = false
  super(@_handler_socket)

  @log = log
  @under_plugin_development = under_plugin_development

  @connect_callback = connect_callback
  @data_callback = nil
  @close_callback = close_callback

  @callback_connection = nil
  @close_after_write_complete = false
  @closing = false

  @mutex = Mutex.new # to serialize #write and #close
end

Public Instance Methods

close() click to toggle source
Calls superclass method
# File lib/fluent/plugin_helper/server.rb, line 788
def close
  @mutex.synchronize do
    return if @closing
    @closing = true
    @close_callback.call(self)
    super
  end
end
data(&callback) click to toggle source
# File lib/fluent/plugin_helper/server.rb, line 672
def data(&callback)
  raise "data callback can be registered just once, but registered twice" if self.singleton_methods.include?(:on_read)
  @data_callback = callback
  on_read_impl = case callback.arity
                 when 1 then :on_read_without_connection
                 when 2 then :on_read_with_connection
                 else
                   raise "BUG: callback block must have 1 or 2 arguments"
                 end
  self.define_singleton_method(:on_read, method(on_read_impl))
end
on_connect() click to toggle source
# File lib/fluent/plugin_helper/server.rb, line 733
def on_connect
  try_tls_accept
end
on_read_with_connection(data) click to toggle source
# File lib/fluent/plugin_helper/server.rb, line 779
def on_read_with_connection(data)
  @data_callback.call(data, @callback_connection)
rescue => e
  @log.error "unexpected error on reading data", host: @callback_connection.remote_host, port: @callback_connection.remote_port, error: e
  @log.error_backtrace
  close rescue nil
  raise if @under_plugin_development
end
on_read_without_connection(data) click to toggle source
# File lib/fluent/plugin_helper/server.rb, line 770
def on_read_without_connection(data)
  @data_callback.call(data)
rescue => e
  @log.error "unexpected error on reading data", host: @callback_connection.remote_host, port: @callback_connection.remote_port, error: e
  @log.error_backtrace
  close rescue nil
  raise if @under_plugin_development
end
on_readable() click to toggle source
Calls superclass method
# File lib/fluent/plugin_helper/server.rb, line 737
def on_readable
  if try_tls_accept
    super
  end
rescue IO::WaitReadable, IO::WaitWritable
  # ignore and return with doing nothing
rescue OpenSSL::SSL::SSLError => e
  @log.warn "close socket due to unexpected ssl error: #{e}"
  close rescue nil
end
on_writable() click to toggle source
Calls superclass method
# File lib/fluent/plugin_helper/server.rb, line 748
def on_writable
  begin
    @mutex.synchronize do
      # Consider write_nonblock with {exception: false} when IO::WaitWritable error happens frequently.
      written_bytes = @_handler_socket.write_nonblock(@_handler_write_buffer)
      @_handler_write_buffer.slice!(0, written_bytes)
      super
    end
    close if @close_after_write_complete
  rescue IO::WaitWritable, IO::WaitReadable
    return
  rescue Errno::EINTR
    return
  rescue SystemCallError, IOError, SocketError
    # SystemCallError catches Errno::EPIPE & Errno::ECONNRESET amongst others.
    close rescue nil
    return
  rescue OpenSSL::SSL::SSLError => e
    @log.debug "unexpected SSLError while writing data into socket connected via TLS", error: e
  end
end
to_io() click to toggle source
# File lib/fluent/plugin_helper/server.rb, line 668
def to_io
  @_handler_socket.to_io
end
try_handshake() click to toggle source
# File lib/fluent/plugin_helper/server.rb, line 694
def try_handshake
  @_handler_socket.accept_nonblock(NONBLOCK_ARG)
end
try_tls_accept() click to toggle source
# File lib/fluent/plugin_helper/server.rb, line 707
def try_tls_accept
  return true if @_handler_accepted

  begin
    result = try_handshake # this method call actually try to do handshake via TLS
    if result == :wait_readable || result == :wait_writable
      # retry accept_nonblock: there aren't enough data in underlying socket buffer
    else
      @_handler_accepted = true

      @callback_connection = TLSCallbackSocket.new(self)
      @connect_callback.call(@callback_connection)
      unless @data_callback
        raise "connection callback must call #data to set data callback"
      end

      return true
    end
  rescue Errno::ECONNRESET, OpenSSL::SSL::SSLError => e
    @log.trace "unexpected error before accepting TLS connection", error: e
    close rescue nil
  end

  false
end
write(data) click to toggle source
# File lib/fluent/plugin_helper/server.rb, line 684
def write(data)
  @mutex.synchronize do
    @_handler_write_buffer << data
    schedule_write
    data.bytesize
  end
end