123456789_123456789_123456789_123456789_123456789_

Module: EventMachine::Protocols::LineText2

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Included In:
Defined in: lib/em/protocols/linetext2.rb

Overview

In the grand, time-honored tradition of re-inventing the wheel, we offer here YET ANOTHER protocol that handles line-oriented data with interspersed binary text. This one trades away some of the performance optimizations of EventMachine::Protocols::LineAndTextProtocol in order to get better correctness with regard to binary text blocks that can switch back to line mode. It also permits the line-delimiter to change in midstream. This was originally written to support Stomp.

Constant Summary

  • MaxBinaryLength =

    TODO! We're not enforcing the limits on header lengths and text-lengths. When we get around to that, call #receive_error if the user defined it, otherwise throw exceptions.

    # File 'lib/em/protocols/linetext2.rb', line 40
    32*1024*1024

Instance Method Summary

Instance Method Details

#receive_binary_data(data)

Stub. Should be subclassed by user code.

[ GitHub ]

  
# File 'lib/em/protocols/linetext2.rb', line 166

def receive_binary_data data
  # no-op
end

#receive_data(data)

[ GitHub ]

  
# File 'lib/em/protocols/linetext2.rb', line 47

def receive_data data
  return unless (data and data.length > 0)

  # Do this stuff in lieu of a constructor.
  @lt2_mode ||= :lines
  @lt2_delimiter ||= "\n"
  @lt2_linebuffer ||= []

  remaining_data = data

  while remaining_data.length > 0
    if @lt2_mode == :lines
      delimiter_string = case @lt2_delimiter
      when Regexp
        remaining_data.slice(@lt2_delimiter)
      else
        @lt2_delimiter
      end
      ix = remaining_data.index(delimiter_string) if delimiter_string
      if ix
        @lt2_linebuffer << remaining_data[0...ix]
        ln = @lt2_linebuffer.join
        @lt2_linebuffer.clear
        if @lt2_delimiter == "\n"
          ln.chomp!
        end
        receive_line ln
        remaining_data = remaining_data[(ix+delimiter_string.length)..-1]
      else
        @lt2_linebuffer << remaining_data
        remaining_data = ""
      end
    elsif @lt2_mode == :text
      if @lt2_textsize
        needed = @lt2_textsize - @lt2_textpos
        will_take = if remaining_data.length > needed
                      needed
                    else
                      remaining_data.length
                    end

        @lt2_textbuffer << remaining_data[0...will_take]
        tail = remaining_data[will_take..-1]

        @lt2_textpos += will_take
        if @lt2_textpos >= @lt2_textsize
          # Reset line mode (the default behavior) BEFORE calling the
          # receive_binary_data. This makes it possible for user code
          # to call set_text_mode, enabling chains of text blocks
          # (which can possibly be of different sizes).
          set_line_mode
          receive_binary_data @lt2_textbuffer.join
          receive_end_of_binary_data
        end

        remaining_data = tail
      else
        receive_binary_data remaining_data
        remaining_data = ""
      end
    end
  end
end

#receive_end_of_binary_data

Stub. Should be subclassed by user code. This is called when transitioning internally from text mode back to line mode. Useful when client code doesn't want to keep track of how much data it's received.

[ GitHub ]

  
# File 'lib/em/protocols/linetext2.rb', line 174

def receive_end_of_binary_data
  # no-op
end

#receive_line(ln)

Stub. Should be subclassed by user code.

[ GitHub ]

  
# File 'lib/em/protocols/linetext2.rb', line 161

def receive_line ln
  # no-op
end

#set_binary_mode(size = nil)

Alias for #set_text_mode, added for back-compatibility with LineAndTextProtocol.

[ GitHub ]

  
# File 'lib/em/protocols/linetext2.rb', line 146

def set_binary_mode size=nil
  set_text_mode size
end

#set_delimiter(delim)

The line delimiter may be a regular expression or a string. Anything passed to set_delimiter other than a regular expression will be converted to a string.

[ GitHub ]

  
# File 'lib/em/protocols/linetext2.rb', line 114

def set_delimiter delim
  @lt2_delimiter = case delim
  when Regexp
    delim
  else
    delim.to_s
  end
end

#set_line_mode(data = "")

Called internally but also exposed to user code, for the case in which processing of binary data creates a need to transition back to line mode. We support an optional parameter to "throw back" some data, which might be an umprocessed chunk of the transmitted binary data, or something else entirely.

[ GitHub ]

  
# File 'lib/em/protocols/linetext2.rb', line 128

def set_line_mode data=""
  @lt2_mode = :lines
  (@lt2_linebuffer ||= []).clear
  receive_data data.to_s
end

#set_text_mode(size = nil)

[ GitHub ]

  
# File 'lib/em/protocols/linetext2.rb', line 134

def set_text_mode size=nil
  if size == 0
    set_line_mode
  else
    @lt2_mode = :text
    (@lt2_textbuffer ||= []).clear
    @lt2_textsize = size # which can be nil, signifying no limit
    @lt2_textpos = 0
  end
end

#unbind

In case of a dropped connection, we'll send a partial buffer to user code when in sized text mode. User overrides of #receive_binary_data need to be aware that they may get a short buffer.

[ GitHub ]

  
# File 'lib/em/protocols/linetext2.rb', line 153

def unbind
  @lt2_mode ||= nil
  if @lt2_mode == :text and @lt2_textpos > 0
    receive_binary_data @lt2_textbuffer.join
  end
end