class RIMS::Protocol::RequestReader

Attributes

command_tag[R]

Public Class Methods

new(input, output, logger, line_length_limit: 1024*8, literal_size_limit: (1024**2)*10, command_size_limit: (1024**2)*10) click to toggle source
# File lib/rims/protocol/parser.rb, line 98
def initialize(input, output, logger, line_length_limit: 1024*8, literal_size_limit: (1024**2)*10, command_size_limit: (1024**2)*10)
  @input = input
  @output = output
  @logger = logger
  @line_length_limit = line_length_limit
  @literal_size_limit = literal_size_limit
  @command_size_limit = command_size_limit
  @command_tag = nil
  @read_size = 0
end
parse(atom_list, last_atom=nil) click to toggle source
# File lib/rims/protocol/parser.rb, line 72
def self.parse(atom_list, last_atom=nil)
  syntax_list = []
  while (atom = atom_list.shift)
    case (atom)
    when last_atom
      break
    when :'('
      syntax_list.push([ :group ] + parse(atom_list, :')'))
    when :'['
      syntax_list.push([ :block ] + parse(atom_list, :']'))
    else
      if ((atom.is_a? Array) && (atom[0] == :body)) then
        body = atom[1]
        body.section_list = parse(scan(body.section))
      end
      syntax_list.push(atom)
    end
  end

  if (atom == nil && last_atom != nil) then
    raise SyntaxError, "not found a terminator: `#{last_atom}'"
  end

  syntax_list
end
scan(line) click to toggle source
# File lib/rims/protocol/parser.rb, line 35
def self.scan(line)
  atom_list = line.scan(/BODY(?:\.\S+)?\[.*?\](?:<\d+\.\d+>)?|[\[\]()]|".*?"|[^\[\]()\s]+/i).map{|s|
    case (s)
    when '(', ')', '[', ']', /\A NIL \z/ix
      s.upcase.intern
    when /\A "/x
      s.sub(/\A "/x, '').sub(/" \z/x, '')
    when /
           \A
           (?<body_symbol>BODY) (?:\. (?<body_option>\S+))? \[ (?<body_section>.*) \]
           (?:< (?<partial_origin>\d+) \. (?<partial_size>\d+) >)?
           \z
         /ix
      body_symbol = $~[:body_symbol]
      body_option = $~[:body_option]
      body_section = $~[:body_section]
      partial_origin = $~[:partial_origin] && $~[:partial_origin].to_i
      partial_size = $~[:partial_size] && $~[:partial_size].to_i
      [ :body,
        Protocol.body(symbol: body_symbol,
                      option: body_option,
                      section: body_section,
                      partial_origin: partial_origin,
                      partial_size: partial_size)
      ]
    else
      s
    end
  }
  if ((atom_list[-1].is_a? String) && (atom_list[-1] =~ /\A {\d+} \z/x)) then
    literal_size = $&[1..-2].to_i
    atom_list[-1] = [ :literal, literal_size ]
  end

  atom_list
end

Public Instance Methods

gets() click to toggle source
# File lib/rims/protocol/parser.rb, line 111
def gets
  if (line = @input.gets($/, @line_length_limit)) then # arguments compatible with OpenSSL::Buffering#gets
    if (line.bytesize < @line_length_limit) then
      line
    elsif (line.bytesize == @line_length_limit && (line.end_with? $/)) then
      line
    else
      raise LineTooLongError.new('line too long.', line_fragment: line)
    end
  end
end
read_command() click to toggle source
# File lib/rims/protocol/parser.rb, line 170
def read_command
  @command_tag = nil
  @read_size = 0
  while (atom_list = read_line)
    if (atom_list.empty?) then
      @read_size = 0
      next
    end
    if (atom_list.length < 2) then
      raise SyntaxError, 'need for tag and command.'
    end
    return self.class.parse(atom_list)
  end

  nil
end

Private Instance Methods

read_line() click to toggle source
# File lib/rims/protocol/parser.rb, line 142
def read_line
  line = gets or return
  @logger.debug("read line: #{Protocol.io_data_log(line)}") if @logger.debug?
  line.chomp!("\n")
  line.chomp!("\r")
  @read_size += line.bytesize
  if (@read_size > @command_size_limit) then
    raise CommandSizeTooLargeError.new('command size too large', @command_tag)
  end
  atom_list = self.class.scan(line)

  if (@command_tag.nil? && ! atom_list.empty?) then
    unless ((atom_list[0].is_a? String) && ! (atom_list[0].start_with? '*', '+')) then
      raise SyntaxError, "invalid command tag: #{atom_list[0]}"
    end
    @command_tag = atom_list[0]
  end

  if ((atom_list[-1].is_a? Array) && (atom_list[-1][0] == :literal)) then
    atom_list[-1] = read_literal(atom_list[-1][1])
    next_atom_list = read_line or raise 'unexpected client close.'
    atom_list += next_atom_list
  end

  atom_list
end
read_literal(size) click to toggle source
# File lib/rims/protocol/parser.rb, line 123
def read_literal(size)
  @logger.debug("found literal: #{size} octets.") if @logger.debug?
  if (size > @literal_size_limit) then
    raise LiteralSizeTooLargeError.new('literal size too large', @command_tag)
  end
  if (@read_size + size > @command_size_limit) then
    raise CommandSizeTooLargeError.new('command size too large', @command_tag)
  end
  @output.write("+ continue\r\n")
  @output.flush
  @logger.debug('continue literal.') if @logger.debug?
  literal_string = @input.read(size) or raise 'unexpected client close.'
  @read_size += size
  @logger.debug("read literal: #{Protocol.io_data_log(literal_string)}") if @logger.debug?

  literal_string
end