module EventMachine::WebSocket::Framing76

Public Instance Methods

initialize_framing() click to toggle source
# File lib/em-websocket/framing76.rb, line 5
def initialize_framing
  @data = ''
end
process_data() click to toggle source
# File lib/em-websocket/framing76.rb, line 9
def process_data
  debug [:message, @data]

  # This algorithm comes straight from the spec
  # http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-5.3

  error = false

  while !error
    return if @data.size == 0

    pointer = 0
    frame_type = @data.getbyte(pointer)
    pointer += 1

    if (frame_type & 0x80) == 0x80
      # If the high-order bit of the /frame type/ byte is set
      length = 0

      loop do
        return false if !@data.getbyte(pointer)
        b = @data.getbyte(pointer)
        pointer += 1
        b_v = b & 0x7F
        length = length * 128 + b_v
        break unless (b & 0x80) == 0x80
      end

      if length > @connection.max_frame_size
        raise WSMessageTooBigError, "Frame length too long (#{length} bytes)"
      end

      if @data.getbyte(pointer+length-1) == nil
        debug [:buffer_incomplete, @data]
        # Incomplete data - leave @data to accumulate
        error = true
      else
        # Straight from spec - I'm sure this isn't crazy...
        # 6. Read /length/ bytes.
        # 7. Discard the read bytes.
        @data = @data[(pointer+length)..-1]

        # If the /frame type/ is 0xFF and the /length/ was 0, then close
        if length == 0
          @connection.send_data("\xff\x00")
          @state = :closing
          @connection.close_connection_after_writing
        else
          error = true
        end
      end
    else
      # If the high-order bit of the /frame type/ byte is _not_ set

      if @data.getbyte(0) != 0x00
        # Close the connection since this buffer can never match
        raise WSProtocolError, "Invalid frame received"
      end

      # Addition to the spec to protect against malicious requests
      if @data.size > @connection.max_frame_size
        raise WSMessageTooBigError, "Frame length too long (#{@data.size} bytes)"
      end

      msg = @data.slice!(/\A\x00[^\xff]*\xff/)
      if msg
        msg.gsub!(/\A\x00|\xff\z/, '')
        if @state == :closing
          debug [:ignored_message, msg]
        else
          msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
          @connection.trigger_on_message(msg)
        end
      else
        error = true
      end
    end
  end

  false
end
send_text_frame(data) click to toggle source

frames need to start with 0x00-0x7f byte and end with an 0xFF byte. Per spec, we can also set the first byte to a value betweent 0x80 and 0xFF, followed by a leading length indicator

# File lib/em-websocket/framing76.rb, line 95
def send_text_frame(data)
  debug [:sending_text_frame, data]
  ary = ["\x00", data, "\xff"]
  ary.collect{ |s| s.force_encoding('UTF-8') if s.respond_to?(:force_encoding) }
  @connection.send_data(ary.join)
end